feat(workspace): a session in its own worktree, Claude's way
Desktop-parity phase 6, following Claude Desktop: - the workspace home's Worktree checkbox starts the pane with `claude --worktree=<name>`, so Claude Code makes, populates (.worktreeinclude, WorktreeCreate hook) and cleans up <repo>/.claude/worktrees/<name>; AgentHub only excludes /.claude/worktrees/ at the repo root (Claude does not) - server/lib/session-worktree.js resolves a session's worktree from the session row only (owned by the workspace, registered by git worktree list); every lane git route takes ?session= and answers ENOWORKTREE instead of falling back to the main folder - the session view's Changes panel works in the opened run's worktree (dashboard_runs.worktree, GitTarget in the client api) - Resume/Fork start in the session's worktree; a gone one is ENOWORKTREE - Archive of a worktree session removes the worktree after asking: git worktree remove without --force, git branch -d (unmerged work keeps its branch), a lock held by a live pid refused, a stale one cleared - listWorktrees reads `locked <reason>` locks - tests scrub GIT_* env so the pre-commit suite cannot act on this repo
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
|
||||
import { Checkbox } from "../Checkbox";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
@@ -19,6 +20,7 @@ import {
|
||||
ArchiveRestore,
|
||||
FileText,
|
||||
Folder,
|
||||
GitBranch,
|
||||
MoreHorizontal,
|
||||
Pencil,
|
||||
Play,
|
||||
@@ -41,7 +43,13 @@ import { Select } from "../Select";
|
||||
import { MarkdownPreview } from "../MarkdownPreview";
|
||||
import { WorkspaceRowMenu, type WorkspaceMenuAction } from "./WorkspaceRowMenu";
|
||||
import { workspaceName } from "./WorkspaceRow";
|
||||
import { liveRunFor, SessionFold, sessionDot, useSessionPage } from "./WorkspaceSessionList";
|
||||
import {
|
||||
liveRunFor,
|
||||
SessionFold,
|
||||
sessionDot,
|
||||
sessionWorktreeName,
|
||||
useSessionPage,
|
||||
} from "./WorkspaceSessionList";
|
||||
|
||||
/** The tmux session a pane slot of a lane owns (server/lib/pty-run.js). */
|
||||
export function paneRunId(laneId: number, slot: number): string {
|
||||
@@ -96,6 +104,9 @@ function HomeBody({
|
||||
const [model, setModel] = useState(defaultModel);
|
||||
useEffect(() => setModel(defaultModel), [defaultModel]);
|
||||
const [mode, setMode] = useState<PermissionMode>("auto");
|
||||
// Claude Desktop's worktree toggle: the new session gets a git worktree of
|
||||
// its own (`claude --worktree`), made and cleaned up by Claude Code.
|
||||
const [inWorktree, setInWorktree] = useState(false);
|
||||
const [starting, setStarting] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [liveRuns, setLiveRuns] = useState<RunHandle[]>([]);
|
||||
@@ -219,6 +230,7 @@ function HomeBody({
|
||||
prompt: prompt.trim(),
|
||||
...(model ? { model } : {}),
|
||||
...(claude ? { permissionMode: mode } : {}),
|
||||
...(claude && inWorktree && project?.root ? { worktree: true } : {}),
|
||||
});
|
||||
setPrompt("");
|
||||
const runId = r.run_id ?? paneRunId(lane.id, slot);
|
||||
@@ -344,6 +356,15 @@ function HomeBody({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{claude && project?.root && (
|
||||
<Checkbox
|
||||
checked={inWorktree}
|
||||
onChange={setInWorktree}
|
||||
label={t("home.worktree")}
|
||||
title={t("home.worktreeHint")}
|
||||
className="ml-1"
|
||||
/>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
data-testid="workspace-home-start"
|
||||
@@ -382,6 +403,15 @@ function HomeBody({
|
||||
<span className="min-w-0 flex-1 truncate text-sm text-fg-primary">
|
||||
{s.name || s.id.slice(0, 8)}
|
||||
</span>
|
||||
{sessionWorktreeName(lane, s) && (
|
||||
<span
|
||||
title={t("tree.worktreeSession", { name: sessionWorktreeName(lane, s) })}
|
||||
className="inline-flex shrink-0 items-center gap-1 font-mono text-[11px] text-fg-muted"
|
||||
>
|
||||
<GitBranch className="h-3 w-3" />
|
||||
{sessionWorktreeName(lane, s)}
|
||||
</span>
|
||||
)}
|
||||
{!!s.cost && s.cost > 0 && (
|
||||
<span className="shrink-0 text-xs tabular-nums text-fg-muted">
|
||||
{fmtCost(s.cost)}
|
||||
|
||||
Reference in New Issue
Block a user