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:
@@ -43,7 +43,12 @@ function Where() {
|
||||
return <span data-testid="where">{loc.pathname + loc.search}</span>;
|
||||
}
|
||||
|
||||
function renderButton(session: { id: string; cwd: string | null; tool?: string }) {
|
||||
function renderButton(session: {
|
||||
id: string;
|
||||
cwd: string | null;
|
||||
tool?: string;
|
||||
lane_id?: number;
|
||||
}) {
|
||||
return render(
|
||||
<MemoryRouter initialEntries={["/sessions/x"]}>
|
||||
<Routes>
|
||||
@@ -136,6 +141,17 @@ describe("SessionResumeButton", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("resumes a session in its own worktree through the workspace that owns it", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderButton({ id: "s-wt", cwd: "/r/.claude/worktrees/ah-3-x", tool: "claude", lane_id: 3 });
|
||||
await user.click(await screen.findByRole("button", { name: "Resume" }));
|
||||
await user.click(screen.getByRole("button", { name: "Resume session" }));
|
||||
expect(api.lanes.action).toHaveBeenCalledWith(3, "start", {
|
||||
slot: 1,
|
||||
resumeSessionId: "s-wt",
|
||||
});
|
||||
});
|
||||
|
||||
it("offers no Resume for a session started in a subfolder, but opens its live pane", async () => {
|
||||
const { unmount } = renderButton({ id: "s-9", cwd: "/r/lib", tool: "claude" });
|
||||
await waitFor(() => expect(api.run.list).toHaveBeenCalled());
|
||||
|
||||
Reference in New Issue
Block a user