@agentproto/worktree 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/workflow.ts","../src/agent-session-host.ts"],"names":[],"mappings":"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","file":"chunk-AJJPABO3.mjs","sourcesContent":["import { z } from \"zod\"\nimport type { Bindings, RuntimeWorkflow } from \"@agentproto/workflow-runtime\"\nimport { provisionWorktreeTool, cleanupWorktreeTool, runGateTool } from \"./tools/index.js\"\nimport { worktreeProvider } from \"./provider/index.js\"\n\n/**\n * Input to the worktree-agent workflow: launch a coding agent inside a\n * fresh git worktree, gate its work, and — on approval — clean up.\n */\nexport const worktreeAgentInputSchema = z.object({\n repoRoot: z.string().describe(\"Absolute path to the git repository root.\"),\n slug: z.string().describe(\"Worktree/branch identifier, e.g. 'fix-flaky-test'.\"),\n base: z.string().optional().describe(\"Ref to cut the worktree branch from. Default 'origin/main'.\"),\n depsCmd: z.string().optional().describe(\"Command to install deps inside the worktree, e.g. 'pnpm install --prefer-offline'.\"),\n copyGlobs: z.array(z.string()).optional().describe(\"Gitignored files to copy into the worktree, e.g. secrets.\"),\n linkPaths: z.array(z.string()).optional().describe(\"Gitignored dirs/files symlinked from the host repo into the worktree before depsCmd (node_modules, sibling workspace repos) so the graph resolves without a full reinstall.\"),\n task: z.string().describe(\"The prompt sent to the coding agent.\"),\n adapter: z.string().optional().describe(\"Agent adapter slug. Default 'claude-code'.\"),\n gateCmd: z.string().describe(\"Command run inside the worktree to check the agent's work, e.g. 'pnpm test'.\"),\n deleteBranch: z.boolean().optional().describe(\"Delete the worktree's branch on cleanup. Default true.\"),\n})\n\nexport type WorktreeAgentInput = z.infer<typeof worktreeAgentInputSchema>\n\nconst candidates = [worktreeProvider]\n\nfunction input(b: { input: unknown }): WorktreeAgentInput {\n return b.input as WorktreeAgentInput\n}\n\n/**\n * provision (tool) → code (AgentStep, cwd bound to provision's output) →\n * gate (tool) → on pass: approval → cleanup (tool); on fail: leave the\n * worktree in place for inspection.\n */\nexport const worktreeAgentWorkflow: RuntimeWorkflow = {\n id: \"worktree-agent\",\n description:\n \"Launch a coding agent inside a fresh git worktree, gate its work with a \" +\n \"check command, get a human ack, then clean up. On gate failure the \" +\n \"worktree is left in place for inspection instead of being cleaned up.\",\n steps: [\n {\n kind: \"tool\",\n id: \"provision\",\n tool: provisionWorktreeTool,\n candidates,\n input: (b: Bindings) => {\n const i = input(b)\n return {\n repoRoot: i.repoRoot,\n slug: i.slug,\n base: i.base,\n depsCmd: i.depsCmd,\n copyGlobs: i.copyGlobs,\n linkPaths: i.linkPaths,\n }\n },\n },\n {\n kind: \"agent\",\n id: \"code\",\n adapter: (b: Bindings) => input(b).adapter ?? \"claude-code\",\n cwd: (b: Bindings) => (b.steps.provision as { cwd: string }).cwd,\n prompt: (b: Bindings) => input(b).task,\n },\n {\n kind: \"tool\",\n id: \"gate\",\n tool: runGateTool,\n candidates,\n input: (b: Bindings) => ({\n cwd: (b.steps.provision as { cwd: string }).cwd,\n cmd: input(b).gateCmd,\n }),\n },\n {\n kind: \"branch\",\n id: \"route\",\n cond: (b: Bindings) => (b.steps.gate as { passed: boolean }).passed,\n then: [\n {\n kind: \"approval\",\n id: \"approval\",\n prompt: () => \"Gate passed. Approve worktree cleanup?\",\n onApprove: [\n {\n kind: \"tool\",\n id: \"cleanup\",\n tool: cleanupWorktreeTool,\n candidates,\n // discardUntracked/discardModified: worktree.cleanup refuses a\n // dirty tree by default (PLAN.md §5.2 layer 3). This workflow's\n // own approval step, just above, is that guard already — a\n // human explicitly approved tearing this worktree down, so\n // both flags are granted here rather than surfacing a second,\n // redundant refusal.\n input: (b: Bindings) => ({\n repoRoot: input(b).repoRoot,\n cwd: (b.steps.provision as { cwd: string }).cwd,\n branch: (b.steps.provision as { branch: string }).branch,\n deleteBranch: input(b).deleteBranch ?? true,\n discardUntracked: true,\n discardModified: true,\n }),\n },\n ],\n onReject: [\n {\n kind: \"transform\",\n id: \"held\",\n compute: () => ({ removed: false, reason: \"cleanup_rejected\" as const }),\n },\n ],\n },\n ],\n otherwise: [\n {\n kind: \"transform\",\n id: \"gate-failed\",\n compute: () => ({ removed: false, reason: \"gate_failed\" as const }),\n },\n ],\n },\n ],\n output: (b: Bindings) => ({\n cwd: (b.steps.provision as { cwd: string }).cwd,\n branch: (b.steps.provision as { branch: string }).branch,\n gate: b.steps.gate,\n cleanup: b.steps.cleanup ?? b.steps.held ?? b.steps[\"gate-failed\"],\n }),\n}\n","import {\n HarnessClient,\n resolveMcpUrl,\n type ConnectHarnessOptions,\n type SessionDescriptor,\n type StartAgentArgs,\n type TurnEvent,\n type TurnResult,\n} from \"@agentproto/harness\"\nimport type { AgentSessionHost } from \"@agentproto/workflow-runtime\"\n\n/** `session_monitor`'s max accepted long-poll window (`orchestration-tools.ts`). */\nconst MAX_POLL_MS = 49_000\n\n/** The subset of `HarnessClient` the host drives — narrowed for fake-client tests. */\nexport type DaemonClient = Pick<\n HarnessClient,\n \"start\" | \"prompt\" | \"output\" | \"kill\" | \"waitForAny\" | \"close\"\n>\n\nexport interface DaemonAgentSessionHost extends AgentSessionHost {\n close(): Promise<void>\n /** Full `agent_start`, returning the raw descriptor and forwarding every\n * `StartAgentArgs` field (`mcpServers`, `model`, `effort`, `label`, …) —\n * unlike `spawn()` above (the narrow AgentStep-shaped convenience\n * wrapper), which only takes `cwd`/`workspaceSlug`/`stepId`. Consumers\n * that need finer per-turn control than the AgentStep contract provides\n * (e.g. `@agentproto/sandbox`'s sandbox agent-session proxy) use this\n * plus `prompt`/`output`/`kill`/`waitForAny` directly instead of\n * `spawn`/`sendPromptAndWait`. */\n start(args: StartAgentArgs): Promise<SessionDescriptor>\n /** Send a follow-up turn — `opts.interrupt` cancels an in-flight turn and\n * redirects the session onto this prompt (mirrors `agent_prompt`). */\n prompt(sessionId: string, prompt: string, opts?: { interrupt?: boolean }): Promise<void>\n /** Tail the ring buffer (`agent_output`). */\n output(sessionId: string, lastN?: number): Promise<string>\n /** SIGTERM the session (`agent_kill`). */\n kill(sessionId: string): Promise<void>\n /** Multiplexed long-poll (`session_monitor`) — see `HarnessClient.waitForAny`. */\n waitForAny(sessionIds: string[], opts?: { timeoutMs?: number; event?: TurnEvent }): Promise<TurnResult>\n}\n\n/**\n * Connect to the agentproto daemon's MCP endpoint and return a real,\n * supervisable `AgentSessionHost` — every `AgentStep` becomes an `agent_start`\n * daemon session rather than a bare subprocess. Fails loudly (no silent\n * fallback) if the daemon isn't reachable.\n */\nexport async function connectDaemonAgentSessionHost(\n opts?: ConnectHarnessOptions,\n): Promise<DaemonAgentSessionHost> {\n const url = resolveMcpUrl(opts)\n let client: HarnessClient\n try {\n client = await HarnessClient.connect({ url })\n } catch (err) {\n throw new Error(\n `worktree-agent: could not reach the agentproto daemon's MCP endpoint at ${url}. ` +\n \"Start the daemon (`agentproto daemon start`), or point at a running one via \" +\n `AGENTPROTO_MCP_URL, before running this command.\\n` +\n `(${err instanceof Error ? err.message : String(err)})`,\n )\n }\n return makeDaemonAgentSessionHost(client)\n}\n\n/** Build the host over an already-connected client (exported for tests with a fake client). */\nexport function makeDaemonAgentSessionHost(client: DaemonClient): DaemonAgentSessionHost {\n const sessionByStepId = new Map<string, string>()\n\n return {\n async spawn(adapter, opts): Promise<string> {\n const desc = await client.start({\n adapter,\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.workspaceSlug !== undefined ? { workspaceSlug: opts.workspaceSlug } : {}),\n ...(opts.stepId !== undefined ? { label: opts.stepId } : {}),\n })\n if (opts.stepId) sessionByStepId.set(opts.stepId, desc.id)\n return desc.id\n },\n\n async sendPromptAndWait(sessionId, prompt): Promise<void> {\n // Subscribe before sending: an extremely fast turn could otherwise settle\n // before the wait call subscribes (same race `harness`'s `ask()` avoids).\n const waitPromise = waitForSettled(client, sessionId)\n await client.prompt(sessionId, prompt)\n await waitPromise\n },\n\n resolveByLabel(stepId): string | undefined {\n return sessionByStepId.get(stepId)\n },\n\n async start(args): Promise<SessionDescriptor> {\n return client.start(args)\n },\n\n async prompt(sessionId, prompt, opts): Promise<void> {\n await client.prompt(sessionId, prompt, opts)\n },\n\n async output(sessionId, lastN): Promise<string> {\n return client.output(sessionId, lastN)\n },\n\n async kill(sessionId): Promise<void> {\n await client.kill(sessionId)\n },\n\n async waitForAny(sessionIds, opts): Promise<TurnResult> {\n return client.waitForAny(sessionIds, opts)\n },\n\n async close(): Promise<void> {\n await client.close()\n },\n }\n}\n\n/**\n * `session_monitor` long-polls cap at 49s; loop past timeouts for longer\n * turns. A clean timeout comes back as `{ timedOut: true, sessionIds }` —\n * there is no `event: \"timeout\"` on the wire, `event` is only ever set on a\n * real match. Keep polling while `timedOut` is true; stop once it's\n * falsy/absent (a real turn-end/awaiting-input/exited match).\n */\nasync function waitForSettled(client: DaemonClient, sessionId: string): Promise<void> {\n for (;;) {\n const result = await client.waitForAny([sessionId], { event: \"any\", timeoutMs: MAX_POLL_MS })\n if (!result.timedOut) return\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/workflow.ts","../src/agent-session-host.ts"],"names":[],"mappings":"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","file":"chunk-PKV55ERJ.mjs","sourcesContent":["import { z } from \"zod\"\nimport type { Bindings, RuntimeWorkflow } from \"@agentproto/workflow-runtime\"\nimport { provisionWorktreeTool, cleanupWorktreeTool, runGateTool } from \"./tools/index.js\"\nimport { worktreeProvider } from \"./provider/index.js\"\n\n/**\n * Input to the worktree-agent workflow: launch a coding agent inside a\n * fresh git worktree, gate its work, and — on approval — clean up.\n */\nexport const worktreeAgentInputSchema = z.object({\n repoRoot: z.string().describe(\"Absolute path to the git repository root.\"),\n slug: z.string().describe(\"Worktree/branch identifier, e.g. 'fix-flaky-test'.\"),\n base: z.string().optional().describe(\"Ref to cut the worktree branch from. Default 'origin/main'.\"),\n depsCmd: z.string().optional().describe(\"Command to install deps inside the worktree, e.g. 'pnpm install --prefer-offline'.\"),\n copyGlobs: z.array(z.string()).optional().describe(\"Gitignored files to copy into the worktree, e.g. secrets.\"),\n linkPaths: z.array(z.string()).optional().describe(\"Gitignored dirs/files symlinked from the host repo into the worktree before depsCmd (node_modules, sibling workspace repos) so the graph resolves without a full reinstall.\"),\n task: z.string().describe(\"The prompt sent to the coding agent.\"),\n adapter: z.string().optional().describe(\"Agent adapter slug. Default 'claude-code'.\"),\n gateCmd: z.string().describe(\"Command run inside the worktree to check the agent's work, e.g. 'pnpm test'.\"),\n deleteBranch: z.boolean().optional().describe(\"Delete the worktree's branch on cleanup. Default true.\"),\n})\n\nexport type WorktreeAgentInput = z.infer<typeof worktreeAgentInputSchema>\n\nconst candidates = [worktreeProvider]\n\nfunction input(b: { input: unknown }): WorktreeAgentInput {\n return b.input as WorktreeAgentInput\n}\n\n/**\n * provision (tool) → code (AgentStep, cwd bound to provision's output) →\n * gate (tool) → on pass: approval → cleanup (tool); on fail: leave the\n * worktree in place for inspection.\n */\nexport const worktreeAgentWorkflow: RuntimeWorkflow = {\n id: \"worktree-agent\",\n description:\n \"Launch a coding agent inside a fresh git worktree, gate its work with a \" +\n \"check command, get a human ack, then clean up. On gate failure the \" +\n \"worktree is left in place for inspection instead of being cleaned up.\",\n steps: [\n {\n kind: \"tool\",\n id: \"provision\",\n tool: provisionWorktreeTool,\n candidates,\n input: (b: Bindings) => {\n const i = input(b)\n return {\n repoRoot: i.repoRoot,\n slug: i.slug,\n base: i.base,\n depsCmd: i.depsCmd,\n copyGlobs: i.copyGlobs,\n linkPaths: i.linkPaths,\n }\n },\n },\n {\n kind: \"agent\",\n id: \"code\",\n adapter: (b: Bindings) => input(b).adapter ?? \"claude-code\",\n cwd: (b: Bindings) => (b.steps.provision as { cwd: string }).cwd,\n prompt: (b: Bindings) => input(b).task,\n },\n {\n kind: \"tool\",\n id: \"gate\",\n tool: runGateTool,\n candidates,\n input: (b: Bindings) => ({\n cwd: (b.steps.provision as { cwd: string }).cwd,\n cmd: input(b).gateCmd,\n }),\n },\n {\n kind: \"branch\",\n id: \"route\",\n cond: (b: Bindings) => (b.steps.gate as { passed: boolean }).passed,\n then: [\n {\n kind: \"approval\",\n id: \"approval\",\n prompt: () => \"Gate passed. Approve worktree cleanup?\",\n onApprove: [\n {\n kind: \"tool\",\n id: \"cleanup\",\n tool: cleanupWorktreeTool,\n candidates,\n // discardUntracked/discardModified: worktree.cleanup refuses a\n // dirty tree by default (PLAN.md §5.2 layer 3). This workflow's\n // own approval step, just above, is that guard already — a\n // human explicitly approved tearing this worktree down, so\n // both flags are granted here rather than surfacing a second,\n // redundant refusal.\n input: (b: Bindings) => ({\n repoRoot: input(b).repoRoot,\n cwd: (b.steps.provision as { cwd: string }).cwd,\n branch: (b.steps.provision as { branch: string }).branch,\n deleteBranch: input(b).deleteBranch ?? true,\n discardUntracked: true,\n discardModified: true,\n }),\n },\n ],\n onReject: [\n {\n kind: \"transform\",\n id: \"held\",\n compute: () => ({ removed: false, reason: \"cleanup_rejected\" as const }),\n },\n ],\n },\n ],\n otherwise: [\n {\n kind: \"transform\",\n id: \"gate-failed\",\n compute: () => ({ removed: false, reason: \"gate_failed\" as const }),\n },\n ],\n },\n ],\n output: (b: Bindings) => ({\n cwd: (b.steps.provision as { cwd: string }).cwd,\n branch: (b.steps.provision as { branch: string }).branch,\n gate: b.steps.gate,\n cleanup: b.steps.cleanup ?? b.steps.held ?? b.steps[\"gate-failed\"],\n }),\n}\n","import {\n HarnessClient,\n resolveMcpUrl,\n type ConnectHarnessOptions,\n type SessionDescriptor,\n type StartAgentArgs,\n type TurnEvent,\n type TurnResult,\n} from \"@agentproto/harness\"\nimport type { AgentSessionHost } from \"@agentproto/workflow-runtime\"\n\n/** `session_monitor`'s max accepted long-poll window (`orchestration-tools.ts`). */\nconst MAX_POLL_MS = 49_000\n\n/** The subset of `HarnessClient` the host drives — narrowed for fake-client tests. */\nexport type DaemonClient = Pick<\n HarnessClient,\n \"start\" | \"prompt\" | \"output\" | \"kill\" | \"waitForAny\" | \"close\"\n>\n\nexport interface DaemonAgentSessionHost extends AgentSessionHost {\n close(): Promise<void>\n /** Full `agent_start`, returning the raw descriptor and forwarding every\n * `StartAgentArgs` field (`mcpServers`, `model`, `effort`, `label`, …) —\n * unlike `spawn()` above (the narrow AgentStep-shaped convenience\n * wrapper), which only takes `cwd`/`workspaceSlug`/`stepId`. Consumers\n * that need finer per-turn control than the AgentStep contract provides\n * (e.g. `@agentproto/sandbox`'s sandbox agent-session proxy) use this\n * plus `prompt`/`output`/`kill`/`waitForAny` directly instead of\n * `spawn`/`sendPromptAndWait`. */\n start(args: StartAgentArgs): Promise<SessionDescriptor>\n /** Send a follow-up turn — `opts.interrupt` cancels an in-flight turn and\n * redirects the session onto this prompt (mirrors `agent_prompt`). */\n prompt(sessionId: string, prompt: string, opts?: { interrupt?: boolean }): Promise<void>\n /** Tail the ring buffer (`agent_output`). */\n output(sessionId: string, lastN?: number): Promise<string>\n /** SIGTERM the session (`agent_kill`). */\n kill(sessionId: string): Promise<void>\n /** Multiplexed long-poll (`session_monitor`) — see `HarnessClient.waitForAny`. */\n waitForAny(sessionIds: string[], opts?: { timeoutMs?: number; event?: TurnEvent }): Promise<TurnResult>\n}\n\n/**\n * Connect to the agentproto daemon's MCP endpoint and return a real,\n * supervisable `AgentSessionHost` — every `AgentStep` becomes an `agent_start`\n * daemon session rather than a bare subprocess. Fails loudly (no silent\n * fallback) if the daemon isn't reachable.\n */\nexport async function connectDaemonAgentSessionHost(\n opts?: ConnectHarnessOptions,\n): Promise<DaemonAgentSessionHost> {\n const url = resolveMcpUrl(opts)\n let client: HarnessClient\n try {\n client = await HarnessClient.connect({ url })\n } catch (err) {\n throw new Error(\n `worktree-agent: could not reach the agentproto daemon's MCP endpoint at ${url}. ` +\n \"Start the daemon (`agentproto daemon start`), or point at a running one via \" +\n `AGENTPROTO_MCP_URL, before running this command.\\n` +\n `(${err instanceof Error ? err.message : String(err)})`,\n )\n }\n return makeDaemonAgentSessionHost(client)\n}\n\n/** Build the host over an already-connected client (exported for tests with a fake client). */\nexport function makeDaemonAgentSessionHost(client: DaemonClient): DaemonAgentSessionHost {\n const sessionByStepId = new Map<string, string>()\n\n return {\n async spawn(adapter, opts): Promise<string> {\n const desc = await client.start({\n adapter,\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.workspaceSlug !== undefined ? { workspaceSlug: opts.workspaceSlug } : {}),\n ...(opts.stepId !== undefined ? { label: opts.stepId } : {}),\n })\n if (opts.stepId) sessionByStepId.set(opts.stepId, desc.id)\n return desc.id\n },\n\n async sendPromptAndWait(sessionId, prompt): Promise<void> {\n // Subscribe before sending: an extremely fast turn could otherwise settle\n // before the wait call subscribes (same race `harness`'s `ask()` avoids).\n const waitPromise = waitForSettled(client, sessionId)\n await client.prompt(sessionId, prompt)\n await waitPromise\n },\n\n resolveByLabel(stepId): string | undefined {\n return sessionByStepId.get(stepId)\n },\n\n async start(args): Promise<SessionDescriptor> {\n return client.start(args)\n },\n\n async prompt(sessionId, prompt, opts): Promise<void> {\n await client.prompt(sessionId, prompt, opts)\n },\n\n async output(sessionId, lastN): Promise<string> {\n return client.output(sessionId, lastN)\n },\n\n async kill(sessionId): Promise<void> {\n await client.kill(sessionId)\n },\n\n async waitForAny(sessionIds, opts): Promise<TurnResult> {\n return client.waitForAny(sessionIds, opts)\n },\n\n async close(): Promise<void> {\n await client.close()\n },\n }\n}\n\n/**\n * `session_monitor` long-polls cap at 49s; loop past timeouts for longer\n * turns. A clean timeout comes back as `{ timedOut: true, sessionIds }` —\n * there is no `event: \"timeout\"` on the wire, `event` is only ever set on a\n * real match. Keep polling while `timedOut` is true; stop once it's\n * falsy/absent (a real turn-end/awaiting-input/exited match).\n */\nasync function waitForSettled(client: DaemonClient, sessionId: string): Promise<void> {\n for (;;) {\n const result = await client.waitForAny([sessionId], { event: \"any\", timeoutMs: MAX_POLL_MS })\n if (!result.timedOut) return\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -87,6 +87,12 @@ declare const sessionRefSchema: z.ZodObject<{
87
87
  endedAt: z.ZodOptional<z.ZodString>;
88
88
  status: z.ZodString;
89
89
  cwd: z.ZodOptional<z.ZodString>;
90
+ adapterSlug: z.ZodOptional<z.ZodString>;
91
+ model: z.ZodOptional<z.ZodString>;
92
+ authMode: z.ZodOptional<z.ZodString>;
93
+ costUsd: z.ZodOptional<z.ZodNumber>;
94
+ tokensIn: z.ZodOptional<z.ZodNumber>;
95
+ tokensOut: z.ZodOptional<z.ZodNumber>;
90
96
  }, z.core.$strip>;
91
97
  type SessionRef = z.infer<typeof sessionRefSchema>;
92
98
  type ProvenanceConfidence = "exact" | "best-effort";
@@ -127,8 +133,17 @@ type WorktreeMarker = z.infer<typeof worktreeMarkerSchema>;
127
133
  * Read the per-worktree creation marker, if PR-B has written one. This PR
128
134
  * never writes it — only reads, so a worktree created before PR-B shipped
129
135
  * (all of them, today) honestly reports `confidence: "best-effort"`.
136
+ *
137
+ * Spawn `cwd` is anchored to `repoRoot`, NOT `worktreePath` — `worktreePath`
138
+ * targets the git command via `-C`, but a linked worktree can be removed by
139
+ * a concurrent `gc` reap (or by this very call's caller, moments later) while
140
+ * `repoRoot` (the main checkout) never is (`gc.ts`'s docblock: git itself
141
+ * refuses to remove the main working tree). A spawn whose `cwd` no longer
142
+ * exists on disk fails `ENOENT` on the COMMAND itself (a Node/libuv quirk,
143
+ * not a "not found" for the directory) — see `exec.ts`'s `execGit` for the
144
+ * same anchor, established first.
130
145
  */
131
- declare function readWorktreeMarker(worktreePath: string): Promise<WorktreeMarker | null>;
146
+ declare function readWorktreeMarker(repoRoot: string, worktreePath: string): Promise<WorktreeMarker | null>;
132
147
  /**
133
148
  * Write the creation marker into `$(git rev-parse --git-dir)/agentproto-
134
149
  * worktree.json` — the worktree's own private gitdir, not the shared
@@ -137,8 +152,11 @@ declare function readWorktreeMarker(worktreePath: string): Promise<WorktreeMarke
137
152
  * config, so it wouldn't die with the worktree and would collide across
138
153
  * worktrees). Called once, by `worktree.provision`, right after the
139
154
  * worktree is created; nothing else ever rewrites it.
155
+ *
156
+ * Spawn `cwd` is anchored to `repoRoot`, not `worktreePath` — see
157
+ * `readWorktreeMarker`'s doc for why.
140
158
  */
141
- declare function writeWorktreeMarker(worktreePath: string, marker: WorktreeMarker): Promise<void>;
159
+ declare function writeWorktreeMarker(repoRoot: string, worktreePath: string, marker: WorktreeMarker): Promise<void>;
142
160
  interface ComputeProvenanceOptions {
143
161
  sessionsPath?: string;
144
162
  }
@@ -149,7 +167,7 @@ interface ComputeProvenanceOptions {
149
167
  * when that marker exists; otherwise `"best-effort"` — the join may span
150
168
  * generations and is bounded by `HISTORY_CAP`'s recency window.
151
169
  */
152
- declare function computeProvenance(worktreePath: string, options?: ComputeProvenanceOptions): Promise<ProvenanceInfo>;
170
+ declare function computeProvenance(repoRoot: string, worktreePath: string, options?: ComputeProvenanceOptions): Promise<ProvenanceInfo>;
153
171
 
154
172
  /**
155
173
  * Salvage-before-discard (PLAN.md §5.2 layer 4). `worktree archive` re-earns
@@ -181,6 +199,8 @@ interface SalvageManifest {
181
199
  provenance: ProvenanceInfo;
182
200
  }
183
201
  interface SalvageWorktreeInput {
202
+ /** The main repo root — the stable spawn anchor for this worktree's git reads (see `listUntrackedFiles`'s doc). */
203
+ repoRoot: string;
184
204
  repoName: string;
185
205
  worktreePath: string;
186
206
  branch: string | null;
@@ -748,13 +768,20 @@ type TreeState = {
748
768
  newestMtimeMs: number | null;
749
769
  };
750
770
  /**
751
- * `git status --porcelain=v2` in the worktree itself (not `repoRoot` — tree
752
- * state is per-worktree). Gitignored files never appear here (no
753
- * `--ignored` flag passed), matching git's own non-`--force` `worktree
754
- * remove` tolerance for them (PLAN.md §0.5) — the two notions of "clean"
755
- * agree by construction.
771
+ * `git status --porcelain=v2` in the worktree itself, via `-C worktreePath`
772
+ * (tree state is per-worktree, not `repoRoot`'s). The spawn's own `cwd`,
773
+ * though, is `repoRoot` — the main checkout, which git itself refuses to
774
+ * remove (`gc.ts`'s docblock) — NOT `worktreePath`: a linked worktree can be
775
+ * removed by a concurrent `gc` reap while this call (or a racing caller
776
+ * reading the same worktree, e.g. a status poll) is in flight, and a spawn
777
+ * whose `cwd` no longer exists on disk fails `ENOENT` on the COMMAND itself
778
+ * (a Node/libuv quirk, not "directory not found") — exactly the failure mode
779
+ * `exec.ts`'s `execGit` already anchors against. Gitignored files never
780
+ * appear here (no `--ignored` flag passed), matching git's own non-`--force`
781
+ * `worktree remove` tolerance for them (PLAN.md §0.5) — the two notions of
782
+ * "clean" agree by construction.
756
783
  */
757
- declare function computeTreeState(worktreePath: string): Promise<TreeState>;
784
+ declare function computeTreeState(repoRoot: string, worktreePath: string): Promise<TreeState>;
758
785
  declare const integrationStateSchema: z.ZodUnion<readonly [z.ZodObject<{
759
786
  state: z.ZodLiteral<"fresh">;
760
787
  checkedAt: z.ZodString;
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
- export { connectDaemonAgentSessionHost, makeDaemonAgentSessionHost, worktreeAgentInputSchema, worktreeAgentWorkflow } from './chunk-AJJPABO3.mjs';
1
+ export { connectDaemonAgentSessionHost, makeDaemonAgentSessionHost, worktreeAgentInputSchema, worktreeAgentWorkflow } from './chunk-PKV55ERJ.mjs';
2
2
  import './chunk-E3EM24BS.mjs';
3
- import { computeProvenance, execArgv, classify, computeWorktreeStatus, listGitWorktrees, worktreeProvider } from './chunk-ZRTFECIP.mjs';
4
- export { CONFIG_FILENAME, ConfigError, DEFAULT_PROXY_PORT, ENV_VARS, FileVerdictMemoStore, HookError, InMemoryVerdictMemoStore, ProxyTable, SESSIONS_BUCKETS_ROOT, SESSIONS_FILE_PATH, ServiceSupervisor, VERDICT_MEMO_PATH, WorktreeNotRemovableError, allocatePort, classify, computeLiveness, computeProvenance, computeTreeState, computeWorktreeStatus, detectDefaultBranch, disposeSupervisor, ephemeralPort, execArgv, execGit, execShell, expandGlob, getScript, getSupervisor, globToRegExp, hookEnv, isPortFree, listGitWorktrees, listScripts, listServices, listWorktreeStatuses, loadConfigFromBase, normalizeHook, parseConfig, peerEnv, peerEnvKey, readSessionsRegistry, readWorktreeMarker, reconcileIntegration, repoLabel, resolveSupervisor, runSetup, runTeardown, serviceEnv, serviceEnvToken, serviceHostname, serviceUrl, sessionInWorktree, sharedProxyTable, slugify, stripPort, worktreeProvider, writeWorktreeMarker } from './chunk-ZRTFECIP.mjs';
3
+ import { computeProvenance, execArgv, classify, computeWorktreeStatus, listGitWorktrees, worktreeProvider } from './chunk-OFO7QHCW.mjs';
4
+ export { CONFIG_FILENAME, ConfigError, DEFAULT_PROXY_PORT, ENV_VARS, FileVerdictMemoStore, HookError, InMemoryVerdictMemoStore, ProxyTable, SESSIONS_BUCKETS_ROOT, SESSIONS_FILE_PATH, ServiceSupervisor, VERDICT_MEMO_PATH, WorktreeNotRemovableError, allocatePort, classify, computeLiveness, computeProvenance, computeTreeState, computeWorktreeStatus, detectDefaultBranch, disposeSupervisor, ephemeralPort, execArgv, execGit, execShell, expandGlob, getScript, getSupervisor, globToRegExp, hookEnv, isPortFree, listGitWorktrees, listScripts, listServices, listWorktreeStatuses, loadConfigFromBase, normalizeHook, parseConfig, peerEnv, peerEnvKey, readSessionsRegistry, readWorktreeMarker, reconcileIntegration, repoLabel, resolveSupervisor, runSetup, runTeardown, serviceEnv, serviceEnvToken, serviceHostname, serviceUrl, sessionInWorktree, sharedProxyTable, slugify, stripPort, worktreeProvider, writeWorktreeMarker } from './chunk-OFO7QHCW.mjs';
5
5
  import { cleanupWorktreeTool } from './chunk-CM3XGWTY.mjs';
6
6
  export { cleanupWorktreeTool, listServicesTool, provisionWorktreeTool, runGateTool, runScriptTool, serviceStatusSchema, startServiceTool, stopServiceTool } from './chunk-CM3XGWTY.mjs';
7
7
  import { mkdir, copyFile, writeFile, open } from 'fs/promises';
@@ -17,15 +17,15 @@ import { runTool } from '@agentproto/driver';
17
17
  * git-worktree provision / gate / cleanup TOOL contracts + builtin PROVIDER.
18
18
  */
19
19
  var SALVAGE_ROOT = () => resolve(homedir(), ".agentproto", "worktree-salvage");
20
- async function listUntrackedFiles(worktreePath) {
21
- const res = await execArgv("git", ["-C", worktreePath, "ls-files", "--others", "--exclude-standard"], worktreePath);
20
+ async function listUntrackedFiles(repoRoot, worktreePath) {
21
+ const res = await execArgv("git", ["-C", worktreePath, "ls-files", "--others", "--exclude-standard"], repoRoot);
22
22
  if (res.exitCode !== 0) {
23
23
  throw new Error(`git ls-files --others failed in ${worktreePath} (exit ${res.exitCode}): ${res.stderr.trim()}`);
24
24
  }
25
25
  return res.stdout.split("\n").filter(Boolean);
26
26
  }
27
- async function diffHead(worktreePath) {
28
- const res = await execArgv("git", ["-C", worktreePath, "diff", "HEAD"], worktreePath);
27
+ async function diffHead(repoRoot, worktreePath) {
28
+ const res = await execArgv("git", ["-C", worktreePath, "diff", "HEAD"], repoRoot);
29
29
  if (res.exitCode !== 0) {
30
30
  throw new Error(`git diff HEAD failed in ${worktreePath} (exit ${res.exitCode}): ${res.stderr.trim()}`);
31
31
  }
@@ -56,9 +56,9 @@ async function salvageWorktree(input) {
56
56
  const dir = resolve(input.salvageRoot ?? SALVAGE_ROOT(), sanitizeSegment(input.repoName), dirName);
57
57
  await mkdir(dir, { recursive: true });
58
58
  const [patch, untrackedFiles, provenance] = await Promise.all([
59
- diffHead(input.worktreePath),
60
- listUntrackedFiles(input.worktreePath),
61
- computeProvenance(input.worktreePath, { sessionsPath: input.sessionsPath })
59
+ diffHead(input.repoRoot, input.worktreePath),
60
+ listUntrackedFiles(input.repoRoot, input.worktreePath),
61
+ computeProvenance(input.repoRoot, input.worktreePath, { sessionsPath: input.sessionsPath })
62
62
  ]);
63
63
  await writeFileSynced(join(dir, "changes.patch"), patch);
64
64
  for (const relPath of untrackedFiles) {
@@ -503,6 +503,7 @@ async function salvageOne(options, worktree) {
503
503
  let salvageDir;
504
504
  try {
505
505
  const result = await salvageWorktree({
506
+ repoRoot: options.repoRoot,
506
507
  repoName: options.repoName,
507
508
  worktreePath: worktree.path,
508
509
  branch: worktree.branch,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/salvage.ts","../src/services/proxy-server.ts","../src/forge.ts","../src/gc.ts"],"names":["netConnect","resolve","fetch"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Salvage-before-discard (PLAN.md §5.2 layer 4). `worktree archive` re-earns\n * its name by snapshotting a worktree's uncommitted state before it removes\n * anything: `git diff HEAD` → `changes.patch`, plus a byte-copy of every\n * unignored untracked file, into\n * `~/.agentproto/worktree-salvage/<repoName>/<slug>-<tipSha7>-<date>/` with a\n * `MANIFEST.json`. Every file is fsynced before this returns — only once the\n * snapshot is durable does the caller proceed to force-remove.\n *\n * Deliberately forge-independent: no PR lookup here. This writer's only job\n * is \"don't lose bytes before deletion,\" not classification — `ls --status`\n * (status.ts) already owns the forge-derived `integration` axis, and gc\n * (PR-D) is where a salvage manifest and a status entry would be cross-\n * referenced, not here.\n */\n\nimport { copyFile, mkdir, open, writeFile } from \"node:fs/promises\"\nimport { homedir } from \"node:os\"\nimport { dirname, join, resolve } from \"node:path\"\nimport { execArgv } from \"./exec.js\"\nimport { computeProvenance, type ProvenanceInfo } from \"./provenance.js\"\n\nexport const SALVAGE_ROOT = (): string => resolve(homedir(), \".agentproto\", \"worktree-salvage\")\n\nexport interface SalvageManifest {\n repo: string\n /** `null` for a detached HEAD. */\n branch: string | null\n tipSha: string\n createdAt: string\n /** Relative paths of every unignored untracked file that was copied in. */\n untrackedFiles: string[]\n /** Whether `changes.patch` has any content (tracked-file changes existed). */\n hasPatch: boolean\n provenance: ProvenanceInfo\n}\n\nexport interface SalvageWorktreeInput {\n repoName: string\n worktreePath: string\n branch: string | null\n tipSha: string\n /** Identifies this worktree in the salvage directory name, e.g. its slug or branch. */\n slug: string\n sessionsPath?: string\n /** Injectable clock so tests can freeze `createdAt` / the directory name. */\n now?: () => string\n /** Override for `~/.agentproto/worktree-salvage` — tests use a temp dir. */\n salvageRoot?: string\n}\n\nexport interface SalvageResult {\n dir: string\n manifest: SalvageManifest\n}\n\n/** Untracked, non-ignored files in the worktree — the same class `git worktree remove` refuses without `--force`. */\nasync function listUntrackedFiles(worktreePath: string): Promise<string[]> {\n const res = await execArgv(\"git\", [\"-C\", worktreePath, \"ls-files\", \"--others\", \"--exclude-standard\"], worktreePath)\n if (res.exitCode !== 0) {\n throw new Error(`git ls-files --others failed in ${worktreePath} (exit ${res.exitCode}): ${res.stderr.trim()}`)\n }\n return res.stdout.split(\"\\n\").filter(Boolean)\n}\n\n/** `git diff HEAD` — staged + unstaged changes to tracked files, uniformly. Empty string on a clean tree. */\nasync function diffHead(worktreePath: string): Promise<string> {\n const res = await execArgv(\"git\", [\"-C\", worktreePath, \"diff\", \"HEAD\"], worktreePath)\n if (res.exitCode !== 0) {\n throw new Error(`git diff HEAD failed in ${worktreePath} (exit ${res.exitCode}): ${res.stderr.trim()}`)\n }\n return res.stdout\n}\n\n/** Sanitize a path segment for use in the salvage directory name (no slashes, no leading dot-dot). */\nfunction sanitizeSegment(value: string): string {\n return value.replace(/[^a-zA-Z0-9._-]+/g, \"-\").replace(/^-+/, \"\") || \"worktree\"\n}\n\n/** fsync one file by path — the durability guarantee: only after this resolves is the byte \"saved\". */\nasync function fsyncFile(path: string): Promise<void> {\n const handle = await open(path, \"r+\")\n try {\n await handle.sync()\n } finally {\n await handle.close()\n }\n}\n\nasync function writeFileSynced(path: string, content: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, content, \"utf8\")\n await fsyncFile(path)\n}\n\n/**\n * Snapshot `worktreePath`'s uncommitted state into a fresh salvage\n * directory. Safe to call on a clean tree (writes an empty patch, no\n * untracked files, still a manifest) — every path this function returns is\n * fsynced before it resolves.\n */\nexport async function salvageWorktree(input: SalvageWorktreeInput): Promise<SalvageResult> {\n const now = input.now ?? (() => new Date().toISOString())\n const createdAt = now()\n const tipShort = input.tipSha.slice(0, 7)\n const dateStamp = createdAt.slice(0, 10)\n const dirName = `${sanitizeSegment(input.slug)}-${tipShort}-${dateStamp}`\n const dir = resolve(input.salvageRoot ?? SALVAGE_ROOT(), sanitizeSegment(input.repoName), dirName)\n await mkdir(dir, { recursive: true })\n\n const [patch, untrackedFiles, provenance] = await Promise.all([\n diffHead(input.worktreePath),\n listUntrackedFiles(input.worktreePath),\n computeProvenance(input.worktreePath, { sessionsPath: input.sessionsPath }),\n ])\n\n await writeFileSynced(join(dir, \"changes.patch\"), patch)\n\n for (const relPath of untrackedFiles) {\n const dest = join(dir, \"untracked\", relPath)\n await mkdir(dirname(dest), { recursive: true })\n await copyFile(join(input.worktreePath, relPath), dest)\n await fsyncFile(dest)\n }\n\n const manifest: SalvageManifest = {\n repo: input.repoName,\n branch: input.branch,\n tipSha: input.tipSha,\n createdAt,\n untrackedFiles,\n hasPatch: patch.length > 0,\n provenance,\n }\n await writeFileSynced(join(dir, \"MANIFEST.json\"), JSON.stringify(manifest, null, 2) + \"\\n\")\n\n return { dir, manifest }\n}\n","import http from \"node:http\"\nimport { connect as netConnect, type Socket } from \"node:net\"\nimport type { ProxyTable } from \"./proxy-table.js\"\n\n/**\n * A small HTTP reverse proxy over a {@link ProxyTable}. Routes\n * `http://<script>--<branch>--<repo>.localhost:<proxy-port>` to the matched\n * service's local port. Supports WebSocket (and any) `Upgrade` passthrough by\n * splicing raw sockets. Targets are always `127.0.0.1` — services bind\n * locally, the proxy is the only public-ish surface.\n */\n\nconst TARGET_HOST = \"127.0.0.1\"\n\nfunction notFound(table: ProxyTable, host: string | undefined): string {\n const known = table.entries().map(([h]) => h)\n return (\n `worktree-proxy: no service for host \"${host ?? \"(none)\"}\"\\n` +\n (known.length ? `known hosts:\\n${known.map((h) => ` ${h}`).join(\"\\n\")}\\n` : \"no services registered\\n\")\n )\n}\n\n/** Build (but don't start) the proxy HTTP server backed by `table`. */\nexport function createProxyServer(table: ProxyTable): http.Server {\n const server = http.createServer((req, res) => {\n const targetPort = table.lookup(req.headers.host)\n if (targetPort === undefined) {\n res.writeHead(502, { \"content-type\": \"text/plain\" })\n res.end(notFound(table, req.headers.host))\n return\n }\n\n const proxyReq = http.request(\n {\n host: TARGET_HOST,\n port: targetPort,\n method: req.method,\n path: req.url,\n headers: req.headers,\n },\n (proxyRes) => {\n res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers)\n proxyRes.pipe(res)\n },\n )\n proxyReq.on(\"error\", (err) => {\n if (!res.headersSent) res.writeHead(502, { \"content-type\": \"text/plain\" })\n res.end(`worktree-proxy: upstream error: ${err.message}\\n`)\n })\n req.pipe(proxyReq)\n })\n\n // WebSocket / generic Upgrade passthrough: splice the client socket to a\n // fresh TCP connection to the target, replaying the original request line\n // and headers so the upstream completes its own handshake.\n server.on(\"upgrade\", (req, clientSocket: Socket, head: Buffer) => {\n const targetPort = table.lookup(req.headers.host)\n if (targetPort === undefined) {\n clientSocket.end(\"HTTP/1.1 502 Bad Gateway\\r\\n\\r\\n\")\n return\n }\n\n const upstream = netConnect(targetPort, TARGET_HOST, () => {\n const headerLines = [`${req.method} ${req.url} HTTP/1.1`]\n for (let i = 0; i < req.rawHeaders.length; i += 2) {\n headerLines.push(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}`)\n }\n upstream.write(headerLines.join(\"\\r\\n\") + \"\\r\\n\\r\\n\")\n if (head && head.length) upstream.write(head)\n upstream.pipe(clientSocket)\n clientSocket.pipe(upstream)\n })\n\n const teardown = (): void => {\n clientSocket.destroy()\n upstream.destroy()\n }\n upstream.on(\"error\", teardown)\n clientSocket.on(\"error\", teardown)\n upstream.on(\"close\", () => clientSocket.destroy())\n clientSocket.on(\"close\", () => upstream.destroy())\n })\n\n return server\n}\n\nexport interface RunningProxy {\n /** The port the proxy is listening on. */\n port: number\n /** The underlying server. */\n server: http.Server\n /** Stop the proxy. */\n close(): Promise<void>\n}\n\n/** Start the proxy server on `port` (0 → OS-assigned). Resolves once listening. */\nexport function startProxy(table: ProxyTable, port = 0): Promise<RunningProxy> {\n const server = createProxyServer(table)\n // Track every accepted socket so shutdown can force-drop them. An upgraded\n // (WebSocket) connection is detached from the server's own tracking, so\n // `server.close()` alone would hang on it — we destroy them explicitly.\n const sockets = new Set<Socket>()\n server.on(\"connection\", (socket: Socket) => {\n sockets.add(socket)\n socket.once(\"close\", () => sockets.delete(socket))\n })\n return new Promise((resolve, reject) => {\n server.once(\"error\", reject)\n server.listen(port, () => {\n const address = server.address()\n const boundPort = address && typeof address === \"object\" ? address.port : port\n resolve({\n port: boundPort,\n server,\n close: () =>\n new Promise<void>((res) => {\n for (const socket of sockets) socket.destroy()\n sockets.clear()\n server.close(() => res())\n }),\n })\n })\n })\n}\n","/**\n * `ForgeClient` — the only network-facing surface the reconciliation rule\n * (`status.ts`) depends on. Kept as an interface (not a concrete `gh` call)\n * so:\n * 1. `status.ts` stays testable with a fake — the fixture-repo tests never\n * shell out to a real forge or touch the network.\n * 2. The shape is forge-portable by construction (PLAN.md §7.1): GitLab has\n * the same two facts — \"PRs/MRs whose head is this branch\" and \"PRs/MRs\n * that contain this commit\" — under different verbs\n * (`refs/merge-requests/<n>/head`, `GET /merge_requests?…`). Only\n * GitHub ships today; a `GitlabForgeClient` is a second implementation\n * of this same interface, not a redesign.\n *\n * Two implementations ship: `GhCliForgeClient` (shells out to the `gh` CLI,\n * inheriting the caller's own `gh auth` session — zero config) and\n * `RestForgeClient` (plain `fetch` against the GitHub REST API using\n * `GITHUB_TOKEN` — no new npm dependency). `createForgeClient` picks\n * whichever is usable; when neither is, callers get a client that always\n * reports itself unreachable, which the reconciliation rule already knows\n * how to fall back from (PLAN.md §1.3 step 5: memo, else `unknown(offline)`,\n * never a guess).\n */\n\nimport { z } from \"zod\"\nimport { execArgv } from \"./exec.js\"\n\n/** A pull/merge request as the reconciliation rule needs to see it. */\nexport interface ForgePullRequestRef {\n number: number\n state: \"open\" | \"closed\"\n merged: boolean\n mergedAt: string | null\n headRefName: string\n headRefOid: string\n}\n\n/**\n * Thrown by a `ForgeClient` method when the forge could not be reached or\n * queried at all (network failure, missing/unauthenticated CLI, non-2xx HTTP\n * response). Never thrown for a legitimate empty result (a branch/commit\n * with no PRs is a valid, resolvable answer — `[]`, not an error). Callers\n * treat this as PLAN.md §1.3 step 5: \"never guess.\"\n */\nexport class ForgeUnavailableError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options)\n this.name = \"ForgeUnavailableError\"\n }\n}\n\nexport interface ForgeClient {\n /** PRs/MRs (any state) whose head branch is exactly `branch`. */\n pullRequestsForBranch(branch: string): Promise<ForgePullRequestRef[]>\n /**\n * PRs/MRs associated with a specific commit SHA — the rename/retarget-proof\n * signal (PLAN.md §1.3 step 2b): finds the PR even if the branch was\n * renamed or deleted before merge.\n */\n pullRequestsForCommit(sha: string): Promise<ForgePullRequestRef[]>\n /**\n * Ensure `oid` (a PR's `headRefOid`) is present in the local object\n * database, fetching `refs/pull/<number>/head` if it isn't. Always\n * fetchable even after the head branch was deleted on the remote\n * (PLAN.md §1.3 step 2, verified for #273/#312/#325/#271).\n */\n ensurePullHeadFetched(prNumber: number, oid: string): Promise<void>\n}\n\n/** A client that always reports itself unreachable — the \"no gh, no token\" case. */\nexport class UnreachableForgeClient implements ForgeClient {\n constructor(private readonly reason: string) {}\n async pullRequestsForBranch(): Promise<ForgePullRequestRef[]> {\n throw new ForgeUnavailableError(this.reason)\n }\n async pullRequestsForCommit(): Promise<ForgePullRequestRef[]> {\n throw new ForgeUnavailableError(this.reason)\n }\n async ensurePullHeadFetched(): Promise<void> {\n throw new ForgeUnavailableError(this.reason)\n }\n}\n\n// ── gh CLI implementation ───────────────────────────────────────────────\n\nconst ghPrListItemSchema = z.object({\n number: z.number(),\n state: z.string(),\n headRefName: z.string(),\n headRefOid: z.string(),\n mergedAt: z.string().nullable(),\n})\ntype GhPrListItem = z.infer<typeof ghPrListItemSchema>\nconst ghPrListSchema = z.array(ghPrListItemSchema)\n\nfunction normalizeGhPrListItem(item: GhPrListItem): ForgePullRequestRef {\n const state = item.state.toUpperCase()\n return {\n number: item.number,\n state: state === \"OPEN\" ? \"open\" : \"closed\",\n merged: state === \"MERGED\" || item.mergedAt !== null,\n mergedAt: item.mergedAt,\n headRefName: item.headRefName,\n headRefOid: item.headRefOid,\n }\n}\n\nconst ghApiCommitPullSchema = z.object({\n number: z.number(),\n state: z.string(),\n merged_at: z.string().nullable(),\n head: z.object({ ref: z.string(), sha: z.string() }),\n})\ntype GhApiCommitPull = z.infer<typeof ghApiCommitPullSchema>\nconst ghApiCommitPullListSchema = z.array(ghApiCommitPullSchema)\n\nfunction normalizeGhApiCommitPull(item: GhApiCommitPull): ForgePullRequestRef {\n return {\n number: item.number,\n state: item.state === \"open\" ? \"open\" : \"closed\",\n merged: item.merged_at !== null,\n mergedAt: item.merged_at,\n headRefName: item.head.ref,\n headRefOid: item.head.sha,\n }\n}\n\n/**\n * GitHub's REST API answers \"commit unknown to this repo\" (e.g. a local\n * commit that was never pushed) with HTTP 422 on `commits/:sha/pulls` —\n * empirically verified against a real unpushed worktree commit, not the 404\n * the endpoint's own docs imply. `gh api` surfaces the status in its stderr\n * as `(HTTP nnn)`. Both 404 and 422 here mean the same legitimate thing:\n * zero associated PRs, not \"the forge is unreachable\" — never guess step 5\n * on a genuinely empty, well-formed answer.\n */\nfunction isCommitNotFoundError(output: string): boolean {\n return /\\(HTTP (404|422)\\)/.test(output)\n}\n\n/** Runs `gh` in `repoRoot` so it auto-detects owner/repo from the `origin` remote there. */\nexport class GhCliForgeClient implements ForgeClient {\n constructor(private readonly repoRoot: string) {}\n\n private async runRaw(args: readonly string[]): Promise<{ exitCode: number; stdout: string; stderr: string }> {\n try {\n return await execArgv(\"gh\", args, this.repoRoot)\n } catch (err) {\n throw new ForgeUnavailableError(\n `gh ${args.join(\" \")} could not be run: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n )\n }\n }\n\n private async run(args: readonly string[]): Promise<string> {\n const result = await this.runRaw(args)\n if (result.exitCode !== 0) {\n throw new ForgeUnavailableError(\n `gh ${args.join(\" \")} failed (exit ${result.exitCode}): ${result.stderr.trim() || result.stdout.trim()}`,\n )\n }\n return result.stdout\n }\n\n private parseOutput<T>(stdout: string, schema: z.ZodType<T[]>, args: readonly string[]): T[] {\n let parsed: unknown\n try {\n parsed = JSON.parse(stdout)\n } catch (err) {\n throw new ForgeUnavailableError(\n `gh ${args.join(\" \")} produced non-JSON output: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n const result = schema.safeParse(parsed)\n if (!result.success) {\n throw new ForgeUnavailableError(`gh ${args.join(\" \")} output did not match the expected shape`)\n }\n return result.data\n }\n\n async pullRequestsForBranch(branch: string): Promise<ForgePullRequestRef[]> {\n const args = [\n \"pr\",\n \"list\",\n \"--head\",\n branch,\n \"--state\",\n \"all\",\n \"--json\",\n \"number,state,headRefName,headRefOid,mergedAt\",\n ]\n const stdout = await this.run(args)\n return this.parseOutput(stdout, ghPrListSchema, args).map(normalizeGhPrListItem)\n }\n\n async pullRequestsForCommit(sha: string): Promise<ForgePullRequestRef[]> {\n const args = [\"api\", `repos/{owner}/{repo}/commits/${sha}/pulls`]\n const result = await this.runRaw(args)\n if (result.exitCode !== 0) {\n if (isCommitNotFoundError(result.stderr) || isCommitNotFoundError(result.stdout)) return []\n throw new ForgeUnavailableError(\n `gh ${args.join(\" \")} failed (exit ${result.exitCode}): ${result.stderr.trim() || result.stdout.trim()}`,\n )\n }\n return this.parseOutput(result.stdout, ghApiCommitPullListSchema, args).map(normalizeGhApiCommitPull)\n }\n\n async ensurePullHeadFetched(prNumber: number, oid: string): Promise<void> {\n const check = await execArgv(\"git\", [\"-C\", this.repoRoot, \"cat-file\", \"-e\", `${oid}^{commit}`], this.repoRoot)\n if (check.exitCode === 0) return\n const fetch = await execArgv(\n \"git\",\n [\"-C\", this.repoRoot, \"fetch\", \"origin\", `refs/pull/${prNumber}/head`],\n this.repoRoot,\n )\n if (fetch.exitCode !== 0) {\n throw new ForgeUnavailableError(\n `git fetch origin refs/pull/${prNumber}/head failed: ${fetch.stderr.trim() || fetch.stdout.trim()}`,\n )\n }\n }\n}\n\n// ── GITHUB_TOKEN REST fallback (no new npm dependency — global fetch) ───\n\nconst restPullRequestSchema = z.object({\n number: z.number(),\n state: z.string(),\n merged_at: z.string().nullable(),\n head: z.object({ ref: z.string(), sha: z.string() }),\n})\ntype RestPullRequest = z.infer<typeof restPullRequestSchema>\nconst restPullRequestListSchema = z.array(restPullRequestSchema)\n\nfunction normalizeRestPr(item: RestPullRequest): ForgePullRequestRef {\n return {\n number: item.number,\n state: item.state === \"open\" ? \"open\" : \"closed\",\n merged: item.merged_at !== null,\n mergedAt: item.merged_at,\n headRefName: item.head.ref,\n headRefOid: item.head.sha,\n }\n}\n\n/** Parse `owner/repo` out of a `git remote get-url origin` value (ssh or https form). */\nexport function parseGithubOwnerRepo(remoteUrl: string): { owner: string; repo: string } | null {\n const ssh = remoteUrl.match(/^git@github\\.com:([^/]+)\\/(.+?)(\\.git)?$/)\n if (ssh?.[1] && ssh[2]) return { owner: ssh[1], repo: ssh[2] }\n const https = remoteUrl.match(/^https:\\/\\/github\\.com\\/([^/]+)\\/(.+?)(\\.git)?\\/?$/)\n if (https?.[1] && https[2]) return { owner: https[1], repo: https[2] }\n return null\n}\n\nexport class RestForgeClient implements ForgeClient {\n constructor(\n private readonly owner: string,\n private readonly repo: string,\n private readonly repoRoot: string,\n private readonly token: string,\n private readonly fetchFn: typeof fetch = fetch,\n ) {}\n\n private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.token}`,\n Accept: \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n \"User-Agent\": \"agentproto-worktree-status\",\n }\n }\n\n private async get(path: string): Promise<Response> {\n try {\n return await this.fetchFn(`https://api.github.com${path}`, { headers: this.headers() })\n } catch (err) {\n throw new ForgeUnavailableError(\n `GitHub REST request to ${path} failed: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n )\n }\n }\n\n private async pullRequestList(res: Response): Promise<ForgePullRequestRef[]> {\n const body: unknown = await res.json()\n const result = restPullRequestListSchema.safeParse(body)\n if (!result.success) {\n throw new ForgeUnavailableError(`GitHub REST response did not match the expected PR list shape`)\n }\n return result.data.map(normalizeRestPr)\n }\n\n async pullRequestsForBranch(branch: string): Promise<ForgePullRequestRef[]> {\n const res = await this.get(\n `/repos/${this.owner}/${this.repo}/pulls?head=${this.owner}:${encodeURIComponent(branch)}&state=all&per_page=100`,\n )\n if (!res.ok) {\n throw new ForgeUnavailableError(`GitHub REST pulls?head= failed: ${res.status} ${res.statusText}`)\n }\n return this.pullRequestList(res)\n }\n\n async pullRequestsForCommit(sha: string): Promise<ForgePullRequestRef[]> {\n const res = await this.get(`/repos/${this.owner}/${this.repo}/commits/${sha}/pulls?per_page=100`)\n // 404 is what the endpoint's docs imply for an unknown commit; 422 is what\n // it empirically returns for one that was never pushed (verified live).\n // Both mean the same legitimate thing: zero associated PRs.\n if (res.status === 404 || res.status === 422) return []\n if (!res.ok) {\n throw new ForgeUnavailableError(`GitHub REST commits/:sha/pulls failed: ${res.status} ${res.statusText}`)\n }\n return this.pullRequestList(res)\n }\n\n async ensurePullHeadFetched(prNumber: number, oid: string): Promise<void> {\n const check = await execArgv(\"git\", [\"-C\", this.repoRoot, \"cat-file\", \"-e\", `${oid}^{commit}`], this.repoRoot)\n if (check.exitCode === 0) return\n const fetch2 = await execArgv(\n \"git\",\n [\"-C\", this.repoRoot, \"fetch\", \"origin\", `refs/pull/${prNumber}/head`],\n this.repoRoot,\n )\n if (fetch2.exitCode !== 0) {\n throw new ForgeUnavailableError(\n `git fetch origin refs/pull/${prNumber}/head failed: ${fetch2.stderr.trim() || fetch2.stdout.trim()}`,\n )\n }\n }\n}\n\n/** Cheap \"is `gh` on PATH and authenticated\" probe — avoids a slow failure per branch. */\nasync function ghIsUsable(repoRoot: string): Promise<boolean> {\n try {\n const res = await execArgv(\"gh\", [\"auth\", \"status\"], repoRoot)\n return res.exitCode === 0\n } catch {\n return false\n }\n}\n\n/**\n * Pick the best available `ForgeClient` for `repoRoot`: `gh` CLI first (zero\n * config, inherits the caller's own auth), else `GITHUB_TOKEN` REST, else a\n * client that always reports unreachable — the reconciliation rule already\n * knows how to fall back from that (memo, else `unknown(offline)`).\n */\nexport async function createForgeClient(repoRoot: string): Promise<ForgeClient> {\n if (await ghIsUsable(repoRoot)) return new GhCliForgeClient(repoRoot)\n\n const token = process.env.GITHUB_TOKEN\n if (token) {\n const remote = await execArgv(\"git\", [\"-C\", repoRoot, \"remote\", \"get-url\", \"origin\"], repoRoot)\n const parsed = remote.exitCode === 0 ? parseGithubOwnerRepo(remote.stdout.trim()) : null\n if (parsed) return new RestForgeClient(parsed.owner, parsed.repo, repoRoot, token)\n }\n\n return new UnreachableForgeClient(\n \"no usable forge client: `gh` is not installed/authenticated and GITHUB_TOKEN is not set (or origin isn't a GitHub remote)\",\n )\n}\n","/**\n * `gc` (PLAN.md §5, PR-D): plan → apply → salvage over the status engine's\n * classification (`status.ts`, PR-A). This module owns no new git knowledge\n * — it reuses `listGitWorktrees` / `computeWorktreeStatus` / `classify` for\n * every fact and `salvageWorktree` / `worktree.cleanup` (PR-C) for every\n * mutation. Its only original logic is sequencing the four safety layers\n * (PLAN.md §5.2):\n *\n * 1. `planGc` is pure read: it classifies every worktree and returns a\n * plan. Nothing is mutated by calling it — the CLI's dry-run default\n * *is* just \"call planGc, print it, stop.\"\n * 2. `applyGc` re-derives each worktree's class from scratch immediately\n * before touching it (a fresh `listGitWorktrees` + `computeWorktreeStatus`,\n * not a reuse of the plan's stale snapshot) and refuses to act if the\n * class changed since the plan was made.\n * 3. The actual removal is `worktree.cleanup` with NO discard flags for a\n * reclaim-class entry — git's own non-`--force` refusal is the backstop\n * this module leans on, never a force flag \"just in case.\"\n * 4. A salvage-class entry is only ever touched via `salvageWorktree`\n * followed by `worktree.cleanup` with both discard flags — snapshot\n * first, removal only after the snapshot is durable (mirrors `worktree\n * archive`'s CLI verb, PR-C).\n *\n * The main worktree (`repoRoot` itself) is never part of the plan: `git\n * worktree list`'s first entry is always the main checkout, and a repo\n * sitting on an up-to-date default branch is trivially `fresh ∧ clean` —\n * exactly what `reclaim` looks for. Git itself refuses to remove\n * the main working tree (\"is a main working tree\", verified empirically),\n * but this module doesn't lean on that as the only guard: the main worktree\n * is filtered out before classification even runs, so it can never appear\n * in a plan, let alone be attempted.\n */\n\nimport { basename } from \"node:path\"\nimport { runTool } from \"@agentproto/driver\"\nimport { cleanupWorktreeTool } from \"./tools/index.js\"\nimport { worktreeProvider } from \"./provider/index.js\"\nimport { salvageWorktree } from \"./salvage.js\"\nimport type { ForgeClient } from \"./forge.js\"\nimport {\n classify,\n listGitWorktrees,\n computeWorktreeStatus,\n type GitWorktreeRef,\n type IntegrationState,\n type LivenessState,\n type TreeState,\n type VerdictMemoStore,\n} from \"./status.js\"\n\nconst candidates = [worktreeProvider]\n\n// ── classification (PLAN.md §5.1) ───────────────────────────────────────\n\nexport type GcClass = \"reclaim\" | \"salvage\" | \"hold\"\n\nexport interface ClassifyForGcOptions {\n /**\n * A clean, idle/daemon-unreachable DETACHED worktree moves from `hold` to\n * `reclaim` (PLAN.md §5.1). This is the only reclassification power any\n * flag has: every other hold reason — partial, open, unknown(offline),\n * gone-unexplained, diverged — is untouched by any flag, ever. Default\n * false.\n */\n includeDetached?: boolean\n /** Threaded straight through to `classify`'s `RECENT_WRITE_HOLD_WINDOW_MS` guard. Defaults to `Date.now()`. */\n nowMs?: number\n}\n\n/**\n * gc's own classifier, layered on top of `classify` (status.ts) rather than\n * reimplementing it — reuse first (AGENTS.md, PLAN.md §0.6). The one thing\n * `classify` cannot know is `--include-detached`, a gc-specific flag; that\n * override lives here, not in the read-only status engine `ls --status`\n * also depends on.\n */\nexport function classifyForGc(\n tree: TreeState,\n integration: IntegrationState,\n liveness: LivenessState,\n options: ClassifyForGcOptions = {},\n): GcClass {\n if (options.includeDetached && integration.state === \"detached\") {\n const idleOrUnreachable = liveness.state === \"idle\" || liveness.state === \"daemon-unreachable\"\n if (tree.state === \"clean\" && idleOrUnreachable) return \"reclaim\"\n }\n return classify(tree, integration, liveness, options.nowMs).class\n}\n\n// ── plan ─────────────────────────────────────────────────────────────\n\nexport interface GcPlanEntry {\n path: string\n branch: string | null\n head: string\n tree: TreeState\n integration: IntegrationState\n liveness: LivenessState\n /** Classification per PLAN.md §5.1, after `--include-detached` (if set). */\n class: GcClass\n}\n\nexport interface PlanGcInput {\n repoRoot: string\n repoName: string\n forge: ForgeClient\n memo: VerdictMemoStore\n defaultBranchRef?: string\n sessionsPath?: string\n includeDetached?: boolean\n now?: () => string\n /** See `ComputeWorktreeStatusInput.nowMs` (status.ts) — kept separate from `now`. Defaults to `Date.now()`. */\n nowMs?: number\n}\n\n/** Every linked worktree of `repoRoot` except the main checkout itself — see the module docblock. */\nasync function linkedWorktreesOf(repoRoot: string): Promise<GitWorktreeRef[]> {\n const worktrees = await listGitWorktrees(repoRoot)\n return worktrees.filter((w) => w.path !== repoRoot)\n}\n\nfunction toPlanEntry(\n worktree: GitWorktreeRef,\n status: { tree: TreeState; integration: IntegrationState; liveness: LivenessState },\n includeDetached: boolean,\n nowMs: number,\n): GcPlanEntry {\n return {\n path: worktree.path,\n branch: worktree.branch,\n head: worktree.head,\n tree: status.tree,\n integration: status.integration,\n liveness: status.liveness,\n class: classifyForGc(status.tree, status.integration, status.liveness, { includeDetached, nowMs }),\n }\n}\n\n/**\n * The dry-run plan (PLAN.md §5.2 layer 1): classify every linked worktree,\n * mutate nothing. This is the human's decision surface — `gc` with no flags\n * is exactly \"call this, print it, stop.\"\n */\nexport async function planGc(input: PlanGcInput): Promise<GcPlanEntry[]> {\n const includeDetached = Boolean(input.includeDetached)\n // One frozen instant for the whole plan's recent-write checks.\n const nowMs = input.nowMs ?? Date.now()\n const worktrees = await linkedWorktreesOf(input.repoRoot)\n const entries: GcPlanEntry[] = []\n for (const worktree of worktrees) {\n const status = await computeWorktreeStatus({\n repoRoot: input.repoRoot,\n repoName: input.repoName,\n worktree,\n forge: input.forge,\n memo: input.memo,\n defaultBranchRef: input.defaultBranchRef,\n sessionsPath: input.sessionsPath,\n now: input.now,\n })\n entries.push(toPlanEntry(worktree, status, includeDetached, nowMs))\n }\n return entries\n}\n\n// ── apply ────────────────────────────────────────────────────────────\n\nexport interface ApplyGcOptions {\n repoRoot: string\n repoName: string\n forge: ForgeClient\n memo: VerdictMemoStore\n defaultBranchRef?: string\n sessionsPath?: string\n includeDetached?: boolean\n /** Archive (salvage-then-remove) every salvage-class entry. Default false — salvage entries are left untouched. */\n salvageDirty?: boolean\n now?: () => string\n /** See `ComputeWorktreeStatusInput.nowMs` (status.ts) — kept separate from `now`. Defaults to `Date.now()`. */\n nowMs?: number\n /** Override for `~/.agentproto/worktree-salvage` — tests use a temp dir. */\n salvageRoot?: string\n}\n\nexport type GcApplyOutcome =\n | { path: string; branch: string | null; result: \"reclaimed\" }\n | { path: string; branch: string | null; result: \"salvaged\"; salvageDir: string }\n | { path: string; branch: string | null; result: \"held\" }\n /** Salvage-class, but `--salvage-dirty` was not passed: left untouched by design. */\n | { path: string; branch: string | null; result: \"skipped-dirty\" }\n /** Layer 2: re-classified at apply time to something other than the plan said — refuses rather than acting on a stale plan. */\n | { path: string; branch: string | null; result: \"aborted-reclassified\"; from: GcClass; to: GcClass }\n /** The worktree named in the plan no longer exists (already removed by something else since the plan was made). */\n | { path: string; branch: string | null; result: \"aborted-vanished\" }\n /** git itself (or the salvage snapshot) refused — the TOCTOU backstop actually firing, or a real I/O failure. */\n | { path: string; branch: string | null; result: \"failed\"; message: string }\n\nasync function findWorktree(repoRoot: string, path: string): Promise<GitWorktreeRef | null> {\n const worktrees = await linkedWorktreesOf(repoRoot)\n return worktrees.find((w) => w.path === path) ?? null\n}\n\nasync function reclaimOne(options: ApplyGcOptions, worktree: GitWorktreeRef): Promise<GcApplyOutcome> {\n try {\n await runTool({\n tool: cleanupWorktreeTool,\n candidates,\n input: {\n repoRoot: options.repoRoot,\n cwd: worktree.path,\n ...(worktree.branch ? { branch: worktree.branch } : {}),\n // Branch deletion only ever runs for a `reclaim`-class entry, which by\n // construction requires `integration ∈ {merged(*), fresh}` and\n // `tree = clean` (PLAN.md's invariant: branch -D only when there's\n // nothing uncommitted to lose, never a hold class).\n deleteBranch: true,\n // No discardUntracked/discardModified, ever, on this path (PLAN.md\n // §5.2 layer 3): `reclaim` means `tree = clean`, so plain `git\n // worktree remove` should succeed on its own; if the tree turned\n // dirty in the instant between the re-check above and this call, git\n // itself refuses and that refusal surfaces below as `failed` — never\n // a force flag \"just in case.\"\n },\n })\n } catch (err) {\n return {\n path: worktree.path,\n branch: worktree.branch,\n result: \"failed\",\n message: err instanceof Error ? err.message : String(err),\n }\n }\n return { path: worktree.path, branch: worktree.branch, result: \"reclaimed\" }\n}\n\nasync function salvageOne(options: ApplyGcOptions, worktree: GitWorktreeRef): Promise<GcApplyOutcome> {\n let salvageDir: string\n try {\n const result = await salvageWorktree({\n repoName: options.repoName,\n worktreePath: worktree.path,\n branch: worktree.branch,\n tipSha: worktree.head,\n slug: worktree.branch ?? basename(worktree.path),\n sessionsPath: options.sessionsPath,\n now: options.now,\n salvageRoot: options.salvageRoot,\n })\n salvageDir = result.dir\n } catch (err) {\n return {\n path: worktree.path,\n branch: worktree.branch,\n result: \"failed\",\n message: `salvage failed, nothing removed: ${err instanceof Error ? err.message : String(err)}`,\n }\n }\n\n try {\n await runTool({\n tool: cleanupWorktreeTool,\n candidates,\n input: {\n repoRoot: options.repoRoot,\n cwd: worktree.path,\n ...(worktree.branch ? { branch: worktree.branch } : {}),\n deleteBranch: true,\n // Both discard flags: the snapshot above is already durable (fsynced)\n // before this call, so nothing on disk is lost by discarding it here\n // (PLAN.md §5.2 layer 4 — salvage before discard).\n discardUntracked: true,\n discardModified: true,\n },\n })\n } catch (err) {\n return {\n path: worktree.path,\n branch: worktree.branch,\n result: \"failed\",\n message: `salvaged to ${salvageDir}, but removal failed: ${err instanceof Error ? err.message : String(err)}`,\n }\n }\n\n return { path: worktree.path, branch: worktree.branch, result: \"salvaged\", salvageDir }\n}\n\nasync function applyOne(entry: GcPlanEntry, options: ApplyGcOptions): Promise<GcApplyOutcome> {\n if (entry.class === \"hold\") {\n return { path: entry.path, branch: entry.branch, result: \"held\" }\n }\n if (entry.class === \"salvage\" && !options.salvageDirty) {\n return { path: entry.path, branch: entry.branch, result: \"skipped-dirty\" }\n }\n\n // Layer 2 (PLAN.md §5.2): re-check immediately before touching anything.\n // Re-derive the worktree ref itself, not just its status — a branch can\n // flip between plan and apply (the rendezvous-deploy branch-flip mid-\n // session is the plan's own existence proof for this), so re-listing\n // `git worktree list` is required, not just recomputing against the\n // plan's stale (path, branch, head) tuple.\n const fresh = await findWorktree(options.repoRoot, entry.path)\n if (!fresh) {\n return { path: entry.path, branch: entry.branch, result: \"aborted-vanished\" }\n }\n const status = await computeWorktreeStatus({\n repoRoot: options.repoRoot,\n repoName: options.repoName,\n worktree: fresh,\n forge: options.forge,\n memo: options.memo,\n defaultBranchRef: options.defaultBranchRef,\n sessionsPath: options.sessionsPath,\n now: options.now,\n })\n const freshClass = classifyForGc(status.tree, status.integration, status.liveness, {\n includeDetached: Boolean(options.includeDetached),\n nowMs: options.nowMs,\n })\n if (freshClass !== entry.class) {\n return {\n path: entry.path,\n branch: fresh.branch,\n result: \"aborted-reclassified\",\n from: entry.class,\n to: freshClass,\n }\n }\n\n if (freshClass === \"reclaim\") return reclaimOne(options, fresh)\n // The only other class that reaches this point is \"salvage\" with\n // `options.salvageDirty` true (both earlier guards above already returned\n // for \"hold\" and for \"salvage\" without the flag).\n return salvageOne(options, fresh)\n}\n\n/**\n * Execute a plan (PLAN.md §5.2 layers 2–4). Every entry is re-classified\n * from scratch immediately before it's touched; a `hold` entry is never\n * touched, a `salvage` entry is only touched when `salvageDirty` is true,\n * and every removal goes through `worktree.cleanup` — never a hand-rolled\n * `git worktree remove`.\n */\nexport async function applyGc(plan: readonly GcPlanEntry[], options: ApplyGcOptions): Promise<GcApplyOutcome[]> {\n const outcomes: GcApplyOutcome[] = []\n for (const entry of plan) {\n outcomes.push(await applyOne(entry, options))\n }\n return outcomes\n}\n"]}
1
+ {"version":3,"sources":["../src/salvage.ts","../src/services/proxy-server.ts","../src/forge.ts","../src/gc.ts"],"names":["netConnect","resolve","fetch"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Salvage-before-discard (PLAN.md §5.2 layer 4). `worktree archive` re-earns\n * its name by snapshotting a worktree's uncommitted state before it removes\n * anything: `git diff HEAD` → `changes.patch`, plus a byte-copy of every\n * unignored untracked file, into\n * `~/.agentproto/worktree-salvage/<repoName>/<slug>-<tipSha7>-<date>/` with a\n * `MANIFEST.json`. Every file is fsynced before this returns — only once the\n * snapshot is durable does the caller proceed to force-remove.\n *\n * Deliberately forge-independent: no PR lookup here. This writer's only job\n * is \"don't lose bytes before deletion,\" not classification — `ls --status`\n * (status.ts) already owns the forge-derived `integration` axis, and gc\n * (PR-D) is where a salvage manifest and a status entry would be cross-\n * referenced, not here.\n */\n\nimport { copyFile, mkdir, open, writeFile } from \"node:fs/promises\"\nimport { homedir } from \"node:os\"\nimport { dirname, join, resolve } from \"node:path\"\nimport { execArgv } from \"./exec.js\"\nimport { computeProvenance, type ProvenanceInfo } from \"./provenance.js\"\n\nexport const SALVAGE_ROOT = (): string => resolve(homedir(), \".agentproto\", \"worktree-salvage\")\n\nexport interface SalvageManifest {\n repo: string\n /** `null` for a detached HEAD. */\n branch: string | null\n tipSha: string\n createdAt: string\n /** Relative paths of every unignored untracked file that was copied in. */\n untrackedFiles: string[]\n /** Whether `changes.patch` has any content (tracked-file changes existed). */\n hasPatch: boolean\n provenance: ProvenanceInfo\n}\n\nexport interface SalvageWorktreeInput {\n /** The main repo root — the stable spawn anchor for this worktree's git reads (see `listUntrackedFiles`'s doc). */\n repoRoot: string\n repoName: string\n worktreePath: string\n branch: string | null\n tipSha: string\n /** Identifies this worktree in the salvage directory name, e.g. its slug or branch. */\n slug: string\n sessionsPath?: string\n /** Injectable clock so tests can freeze `createdAt` / the directory name. */\n now?: () => string\n /** Override for `~/.agentproto/worktree-salvage` — tests use a temp dir. */\n salvageRoot?: string\n}\n\nexport interface SalvageResult {\n dir: string\n manifest: SalvageManifest\n}\n\n/**\n * Untracked, non-ignored files in the worktree — the same class `git\n * worktree remove` refuses without `--force`. Spawn `cwd` is `repoRoot`, not\n * `worktreePath` — a linked worktree can be removed by a concurrent `gc`\n * reap (or a racing status read) while this call is in flight, and a spawn\n * whose `cwd` no longer exists fails `ENOENT` on the COMMAND itself (a\n * Node/libuv quirk), not the directory — see `exec.ts`'s `execGit`, which\n * anchors the same way.\n */\nasync function listUntrackedFiles(repoRoot: string, worktreePath: string): Promise<string[]> {\n const res = await execArgv(\"git\", [\"-C\", worktreePath, \"ls-files\", \"--others\", \"--exclude-standard\"], repoRoot)\n if (res.exitCode !== 0) {\n throw new Error(`git ls-files --others failed in ${worktreePath} (exit ${res.exitCode}): ${res.stderr.trim()}`)\n }\n return res.stdout.split(\"\\n\").filter(Boolean)\n}\n\n/** `git diff HEAD` — staged + unstaged changes to tracked files, uniformly.\n * Empty string on a clean tree. Spawn `cwd` is `repoRoot`, not\n * `worktreePath` — see `listUntrackedFiles`'s doc. */\nasync function diffHead(repoRoot: string, worktreePath: string): Promise<string> {\n const res = await execArgv(\"git\", [\"-C\", worktreePath, \"diff\", \"HEAD\"], repoRoot)\n if (res.exitCode !== 0) {\n throw new Error(`git diff HEAD failed in ${worktreePath} (exit ${res.exitCode}): ${res.stderr.trim()}`)\n }\n return res.stdout\n}\n\n/** Sanitize a path segment for use in the salvage directory name (no slashes, no leading dot-dot). */\nfunction sanitizeSegment(value: string): string {\n return value.replace(/[^a-zA-Z0-9._-]+/g, \"-\").replace(/^-+/, \"\") || \"worktree\"\n}\n\n/** fsync one file by path — the durability guarantee: only after this resolves is the byte \"saved\". */\nasync function fsyncFile(path: string): Promise<void> {\n const handle = await open(path, \"r+\")\n try {\n await handle.sync()\n } finally {\n await handle.close()\n }\n}\n\nasync function writeFileSynced(path: string, content: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, content, \"utf8\")\n await fsyncFile(path)\n}\n\n/**\n * Snapshot `worktreePath`'s uncommitted state into a fresh salvage\n * directory. Safe to call on a clean tree (writes an empty patch, no\n * untracked files, still a manifest) — every path this function returns is\n * fsynced before it resolves.\n */\nexport async function salvageWorktree(input: SalvageWorktreeInput): Promise<SalvageResult> {\n const now = input.now ?? (() => new Date().toISOString())\n const createdAt = now()\n const tipShort = input.tipSha.slice(0, 7)\n const dateStamp = createdAt.slice(0, 10)\n const dirName = `${sanitizeSegment(input.slug)}-${tipShort}-${dateStamp}`\n const dir = resolve(input.salvageRoot ?? SALVAGE_ROOT(), sanitizeSegment(input.repoName), dirName)\n await mkdir(dir, { recursive: true })\n\n const [patch, untrackedFiles, provenance] = await Promise.all([\n diffHead(input.repoRoot, input.worktreePath),\n listUntrackedFiles(input.repoRoot, input.worktreePath),\n computeProvenance(input.repoRoot, input.worktreePath, { sessionsPath: input.sessionsPath }),\n ])\n\n await writeFileSynced(join(dir, \"changes.patch\"), patch)\n\n for (const relPath of untrackedFiles) {\n const dest = join(dir, \"untracked\", relPath)\n await mkdir(dirname(dest), { recursive: true })\n await copyFile(join(input.worktreePath, relPath), dest)\n await fsyncFile(dest)\n }\n\n const manifest: SalvageManifest = {\n repo: input.repoName,\n branch: input.branch,\n tipSha: input.tipSha,\n createdAt,\n untrackedFiles,\n hasPatch: patch.length > 0,\n provenance,\n }\n await writeFileSynced(join(dir, \"MANIFEST.json\"), JSON.stringify(manifest, null, 2) + \"\\n\")\n\n return { dir, manifest }\n}\n","import http from \"node:http\"\nimport { connect as netConnect, type Socket } from \"node:net\"\nimport type { ProxyTable } from \"./proxy-table.js\"\n\n/**\n * A small HTTP reverse proxy over a {@link ProxyTable}. Routes\n * `http://<script>--<branch>--<repo>.localhost:<proxy-port>` to the matched\n * service's local port. Supports WebSocket (and any) `Upgrade` passthrough by\n * splicing raw sockets. Targets are always `127.0.0.1` — services bind\n * locally, the proxy is the only public-ish surface.\n */\n\nconst TARGET_HOST = \"127.0.0.1\"\n\nfunction notFound(table: ProxyTable, host: string | undefined): string {\n const known = table.entries().map(([h]) => h)\n return (\n `worktree-proxy: no service for host \"${host ?? \"(none)\"}\"\\n` +\n (known.length ? `known hosts:\\n${known.map((h) => ` ${h}`).join(\"\\n\")}\\n` : \"no services registered\\n\")\n )\n}\n\n/** Build (but don't start) the proxy HTTP server backed by `table`. */\nexport function createProxyServer(table: ProxyTable): http.Server {\n const server = http.createServer((req, res) => {\n const targetPort = table.lookup(req.headers.host)\n if (targetPort === undefined) {\n res.writeHead(502, { \"content-type\": \"text/plain\" })\n res.end(notFound(table, req.headers.host))\n return\n }\n\n const proxyReq = http.request(\n {\n host: TARGET_HOST,\n port: targetPort,\n method: req.method,\n path: req.url,\n headers: req.headers,\n },\n (proxyRes) => {\n res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers)\n proxyRes.pipe(res)\n },\n )\n proxyReq.on(\"error\", (err) => {\n if (!res.headersSent) res.writeHead(502, { \"content-type\": \"text/plain\" })\n res.end(`worktree-proxy: upstream error: ${err.message}\\n`)\n })\n req.pipe(proxyReq)\n })\n\n // WebSocket / generic Upgrade passthrough: splice the client socket to a\n // fresh TCP connection to the target, replaying the original request line\n // and headers so the upstream completes its own handshake.\n server.on(\"upgrade\", (req, clientSocket: Socket, head: Buffer) => {\n const targetPort = table.lookup(req.headers.host)\n if (targetPort === undefined) {\n clientSocket.end(\"HTTP/1.1 502 Bad Gateway\\r\\n\\r\\n\")\n return\n }\n\n const upstream = netConnect(targetPort, TARGET_HOST, () => {\n const headerLines = [`${req.method} ${req.url} HTTP/1.1`]\n for (let i = 0; i < req.rawHeaders.length; i += 2) {\n headerLines.push(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}`)\n }\n upstream.write(headerLines.join(\"\\r\\n\") + \"\\r\\n\\r\\n\")\n if (head && head.length) upstream.write(head)\n upstream.pipe(clientSocket)\n clientSocket.pipe(upstream)\n })\n\n const teardown = (): void => {\n clientSocket.destroy()\n upstream.destroy()\n }\n upstream.on(\"error\", teardown)\n clientSocket.on(\"error\", teardown)\n upstream.on(\"close\", () => clientSocket.destroy())\n clientSocket.on(\"close\", () => upstream.destroy())\n })\n\n return server\n}\n\nexport interface RunningProxy {\n /** The port the proxy is listening on. */\n port: number\n /** The underlying server. */\n server: http.Server\n /** Stop the proxy. */\n close(): Promise<void>\n}\n\n/** Start the proxy server on `port` (0 → OS-assigned). Resolves once listening. */\nexport function startProxy(table: ProxyTable, port = 0): Promise<RunningProxy> {\n const server = createProxyServer(table)\n // Track every accepted socket so shutdown can force-drop them. An upgraded\n // (WebSocket) connection is detached from the server's own tracking, so\n // `server.close()` alone would hang on it — we destroy them explicitly.\n const sockets = new Set<Socket>()\n server.on(\"connection\", (socket: Socket) => {\n sockets.add(socket)\n socket.once(\"close\", () => sockets.delete(socket))\n })\n return new Promise((resolve, reject) => {\n server.once(\"error\", reject)\n server.listen(port, () => {\n const address = server.address()\n const boundPort = address && typeof address === \"object\" ? address.port : port\n resolve({\n port: boundPort,\n server,\n close: () =>\n new Promise<void>((res) => {\n for (const socket of sockets) socket.destroy()\n sockets.clear()\n server.close(() => res())\n }),\n })\n })\n })\n}\n","/**\n * `ForgeClient` — the only network-facing surface the reconciliation rule\n * (`status.ts`) depends on. Kept as an interface (not a concrete `gh` call)\n * so:\n * 1. `status.ts` stays testable with a fake — the fixture-repo tests never\n * shell out to a real forge or touch the network.\n * 2. The shape is forge-portable by construction (PLAN.md §7.1): GitLab has\n * the same two facts — \"PRs/MRs whose head is this branch\" and \"PRs/MRs\n * that contain this commit\" — under different verbs\n * (`refs/merge-requests/<n>/head`, `GET /merge_requests?…`). Only\n * GitHub ships today; a `GitlabForgeClient` is a second implementation\n * of this same interface, not a redesign.\n *\n * Two implementations ship: `GhCliForgeClient` (shells out to the `gh` CLI,\n * inheriting the caller's own `gh auth` session — zero config) and\n * `RestForgeClient` (plain `fetch` against the GitHub REST API using\n * `GITHUB_TOKEN` — no new npm dependency). `createForgeClient` picks\n * whichever is usable; when neither is, callers get a client that always\n * reports itself unreachable, which the reconciliation rule already knows\n * how to fall back from (PLAN.md §1.3 step 5: memo, else `unknown(offline)`,\n * never a guess).\n */\n\nimport { z } from \"zod\"\nimport { execArgv } from \"./exec.js\"\n\n/** A pull/merge request as the reconciliation rule needs to see it. */\nexport interface ForgePullRequestRef {\n number: number\n state: \"open\" | \"closed\"\n merged: boolean\n mergedAt: string | null\n headRefName: string\n headRefOid: string\n}\n\n/**\n * Thrown by a `ForgeClient` method when the forge could not be reached or\n * queried at all (network failure, missing/unauthenticated CLI, non-2xx HTTP\n * response). Never thrown for a legitimate empty result (a branch/commit\n * with no PRs is a valid, resolvable answer — `[]`, not an error). Callers\n * treat this as PLAN.md §1.3 step 5: \"never guess.\"\n */\nexport class ForgeUnavailableError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options)\n this.name = \"ForgeUnavailableError\"\n }\n}\n\nexport interface ForgeClient {\n /** PRs/MRs (any state) whose head branch is exactly `branch`. */\n pullRequestsForBranch(branch: string): Promise<ForgePullRequestRef[]>\n /**\n * PRs/MRs associated with a specific commit SHA — the rename/retarget-proof\n * signal (PLAN.md §1.3 step 2b): finds the PR even if the branch was\n * renamed or deleted before merge.\n */\n pullRequestsForCommit(sha: string): Promise<ForgePullRequestRef[]>\n /**\n * Ensure `oid` (a PR's `headRefOid`) is present in the local object\n * database, fetching `refs/pull/<number>/head` if it isn't. Always\n * fetchable even after the head branch was deleted on the remote\n * (PLAN.md §1.3 step 2, verified for #273/#312/#325/#271).\n */\n ensurePullHeadFetched(prNumber: number, oid: string): Promise<void>\n}\n\n/** A client that always reports itself unreachable — the \"no gh, no token\" case. */\nexport class UnreachableForgeClient implements ForgeClient {\n constructor(private readonly reason: string) {}\n async pullRequestsForBranch(): Promise<ForgePullRequestRef[]> {\n throw new ForgeUnavailableError(this.reason)\n }\n async pullRequestsForCommit(): Promise<ForgePullRequestRef[]> {\n throw new ForgeUnavailableError(this.reason)\n }\n async ensurePullHeadFetched(): Promise<void> {\n throw new ForgeUnavailableError(this.reason)\n }\n}\n\n// ── gh CLI implementation ───────────────────────────────────────────────\n\nconst ghPrListItemSchema = z.object({\n number: z.number(),\n state: z.string(),\n headRefName: z.string(),\n headRefOid: z.string(),\n mergedAt: z.string().nullable(),\n})\ntype GhPrListItem = z.infer<typeof ghPrListItemSchema>\nconst ghPrListSchema = z.array(ghPrListItemSchema)\n\nfunction normalizeGhPrListItem(item: GhPrListItem): ForgePullRequestRef {\n const state = item.state.toUpperCase()\n return {\n number: item.number,\n state: state === \"OPEN\" ? \"open\" : \"closed\",\n merged: state === \"MERGED\" || item.mergedAt !== null,\n mergedAt: item.mergedAt,\n headRefName: item.headRefName,\n headRefOid: item.headRefOid,\n }\n}\n\nconst ghApiCommitPullSchema = z.object({\n number: z.number(),\n state: z.string(),\n merged_at: z.string().nullable(),\n head: z.object({ ref: z.string(), sha: z.string() }),\n})\ntype GhApiCommitPull = z.infer<typeof ghApiCommitPullSchema>\nconst ghApiCommitPullListSchema = z.array(ghApiCommitPullSchema)\n\nfunction normalizeGhApiCommitPull(item: GhApiCommitPull): ForgePullRequestRef {\n return {\n number: item.number,\n state: item.state === \"open\" ? \"open\" : \"closed\",\n merged: item.merged_at !== null,\n mergedAt: item.merged_at,\n headRefName: item.head.ref,\n headRefOid: item.head.sha,\n }\n}\n\n/**\n * GitHub's REST API answers \"commit unknown to this repo\" (e.g. a local\n * commit that was never pushed) with HTTP 422 on `commits/:sha/pulls` —\n * empirically verified against a real unpushed worktree commit, not the 404\n * the endpoint's own docs imply. `gh api` surfaces the status in its stderr\n * as `(HTTP nnn)`. Both 404 and 422 here mean the same legitimate thing:\n * zero associated PRs, not \"the forge is unreachable\" — never guess step 5\n * on a genuinely empty, well-formed answer.\n */\nfunction isCommitNotFoundError(output: string): boolean {\n return /\\(HTTP (404|422)\\)/.test(output)\n}\n\n/** Runs `gh` in `repoRoot` so it auto-detects owner/repo from the `origin` remote there. */\nexport class GhCliForgeClient implements ForgeClient {\n constructor(private readonly repoRoot: string) {}\n\n private async runRaw(args: readonly string[]): Promise<{ exitCode: number; stdout: string; stderr: string }> {\n try {\n return await execArgv(\"gh\", args, this.repoRoot)\n } catch (err) {\n throw new ForgeUnavailableError(\n `gh ${args.join(\" \")} could not be run: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n )\n }\n }\n\n private async run(args: readonly string[]): Promise<string> {\n const result = await this.runRaw(args)\n if (result.exitCode !== 0) {\n throw new ForgeUnavailableError(\n `gh ${args.join(\" \")} failed (exit ${result.exitCode}): ${result.stderr.trim() || result.stdout.trim()}`,\n )\n }\n return result.stdout\n }\n\n private parseOutput<T>(stdout: string, schema: z.ZodType<T[]>, args: readonly string[]): T[] {\n let parsed: unknown\n try {\n parsed = JSON.parse(stdout)\n } catch (err) {\n throw new ForgeUnavailableError(\n `gh ${args.join(\" \")} produced non-JSON output: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n const result = schema.safeParse(parsed)\n if (!result.success) {\n throw new ForgeUnavailableError(`gh ${args.join(\" \")} output did not match the expected shape`)\n }\n return result.data\n }\n\n async pullRequestsForBranch(branch: string): Promise<ForgePullRequestRef[]> {\n const args = [\n \"pr\",\n \"list\",\n \"--head\",\n branch,\n \"--state\",\n \"all\",\n \"--json\",\n \"number,state,headRefName,headRefOid,mergedAt\",\n ]\n const stdout = await this.run(args)\n return this.parseOutput(stdout, ghPrListSchema, args).map(normalizeGhPrListItem)\n }\n\n async pullRequestsForCommit(sha: string): Promise<ForgePullRequestRef[]> {\n const args = [\"api\", `repos/{owner}/{repo}/commits/${sha}/pulls`]\n const result = await this.runRaw(args)\n if (result.exitCode !== 0) {\n if (isCommitNotFoundError(result.stderr) || isCommitNotFoundError(result.stdout)) return []\n throw new ForgeUnavailableError(\n `gh ${args.join(\" \")} failed (exit ${result.exitCode}): ${result.stderr.trim() || result.stdout.trim()}`,\n )\n }\n return this.parseOutput(result.stdout, ghApiCommitPullListSchema, args).map(normalizeGhApiCommitPull)\n }\n\n async ensurePullHeadFetched(prNumber: number, oid: string): Promise<void> {\n const check = await execArgv(\"git\", [\"-C\", this.repoRoot, \"cat-file\", \"-e\", `${oid}^{commit}`], this.repoRoot)\n if (check.exitCode === 0) return\n const fetch = await execArgv(\n \"git\",\n [\"-C\", this.repoRoot, \"fetch\", \"origin\", `refs/pull/${prNumber}/head`],\n this.repoRoot,\n )\n if (fetch.exitCode !== 0) {\n throw new ForgeUnavailableError(\n `git fetch origin refs/pull/${prNumber}/head failed: ${fetch.stderr.trim() || fetch.stdout.trim()}`,\n )\n }\n }\n}\n\n// ── GITHUB_TOKEN REST fallback (no new npm dependency — global fetch) ───\n\nconst restPullRequestSchema = z.object({\n number: z.number(),\n state: z.string(),\n merged_at: z.string().nullable(),\n head: z.object({ ref: z.string(), sha: z.string() }),\n})\ntype RestPullRequest = z.infer<typeof restPullRequestSchema>\nconst restPullRequestListSchema = z.array(restPullRequestSchema)\n\nfunction normalizeRestPr(item: RestPullRequest): ForgePullRequestRef {\n return {\n number: item.number,\n state: item.state === \"open\" ? \"open\" : \"closed\",\n merged: item.merged_at !== null,\n mergedAt: item.merged_at,\n headRefName: item.head.ref,\n headRefOid: item.head.sha,\n }\n}\n\n/** Parse `owner/repo` out of a `git remote get-url origin` value (ssh or https form). */\nexport function parseGithubOwnerRepo(remoteUrl: string): { owner: string; repo: string } | null {\n const ssh = remoteUrl.match(/^git@github\\.com:([^/]+)\\/(.+?)(\\.git)?$/)\n if (ssh?.[1] && ssh[2]) return { owner: ssh[1], repo: ssh[2] }\n const https = remoteUrl.match(/^https:\\/\\/github\\.com\\/([^/]+)\\/(.+?)(\\.git)?\\/?$/)\n if (https?.[1] && https[2]) return { owner: https[1], repo: https[2] }\n return null\n}\n\nexport class RestForgeClient implements ForgeClient {\n constructor(\n private readonly owner: string,\n private readonly repo: string,\n private readonly repoRoot: string,\n private readonly token: string,\n private readonly fetchFn: typeof fetch = fetch,\n ) {}\n\n private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.token}`,\n Accept: \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n \"User-Agent\": \"agentproto-worktree-status\",\n }\n }\n\n private async get(path: string): Promise<Response> {\n try {\n return await this.fetchFn(`https://api.github.com${path}`, { headers: this.headers() })\n } catch (err) {\n throw new ForgeUnavailableError(\n `GitHub REST request to ${path} failed: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n )\n }\n }\n\n private async pullRequestList(res: Response): Promise<ForgePullRequestRef[]> {\n const body: unknown = await res.json()\n const result = restPullRequestListSchema.safeParse(body)\n if (!result.success) {\n throw new ForgeUnavailableError(`GitHub REST response did not match the expected PR list shape`)\n }\n return result.data.map(normalizeRestPr)\n }\n\n async pullRequestsForBranch(branch: string): Promise<ForgePullRequestRef[]> {\n const res = await this.get(\n `/repos/${this.owner}/${this.repo}/pulls?head=${this.owner}:${encodeURIComponent(branch)}&state=all&per_page=100`,\n )\n if (!res.ok) {\n throw new ForgeUnavailableError(`GitHub REST pulls?head= failed: ${res.status} ${res.statusText}`)\n }\n return this.pullRequestList(res)\n }\n\n async pullRequestsForCommit(sha: string): Promise<ForgePullRequestRef[]> {\n const res = await this.get(`/repos/${this.owner}/${this.repo}/commits/${sha}/pulls?per_page=100`)\n // 404 is what the endpoint's docs imply for an unknown commit; 422 is what\n // it empirically returns for one that was never pushed (verified live).\n // Both mean the same legitimate thing: zero associated PRs.\n if (res.status === 404 || res.status === 422) return []\n if (!res.ok) {\n throw new ForgeUnavailableError(`GitHub REST commits/:sha/pulls failed: ${res.status} ${res.statusText}`)\n }\n return this.pullRequestList(res)\n }\n\n async ensurePullHeadFetched(prNumber: number, oid: string): Promise<void> {\n const check = await execArgv(\"git\", [\"-C\", this.repoRoot, \"cat-file\", \"-e\", `${oid}^{commit}`], this.repoRoot)\n if (check.exitCode === 0) return\n const fetch2 = await execArgv(\n \"git\",\n [\"-C\", this.repoRoot, \"fetch\", \"origin\", `refs/pull/${prNumber}/head`],\n this.repoRoot,\n )\n if (fetch2.exitCode !== 0) {\n throw new ForgeUnavailableError(\n `git fetch origin refs/pull/${prNumber}/head failed: ${fetch2.stderr.trim() || fetch2.stdout.trim()}`,\n )\n }\n }\n}\n\n/** Cheap \"is `gh` on PATH and authenticated\" probe — avoids a slow failure per branch. */\nasync function ghIsUsable(repoRoot: string): Promise<boolean> {\n try {\n const res = await execArgv(\"gh\", [\"auth\", \"status\"], repoRoot)\n return res.exitCode === 0\n } catch {\n return false\n }\n}\n\n/**\n * Pick the best available `ForgeClient` for `repoRoot`: `gh` CLI first (zero\n * config, inherits the caller's own auth), else `GITHUB_TOKEN` REST, else a\n * client that always reports unreachable — the reconciliation rule already\n * knows how to fall back from that (memo, else `unknown(offline)`).\n */\nexport async function createForgeClient(repoRoot: string): Promise<ForgeClient> {\n if (await ghIsUsable(repoRoot)) return new GhCliForgeClient(repoRoot)\n\n const token = process.env.GITHUB_TOKEN\n if (token) {\n const remote = await execArgv(\"git\", [\"-C\", repoRoot, \"remote\", \"get-url\", \"origin\"], repoRoot)\n const parsed = remote.exitCode === 0 ? parseGithubOwnerRepo(remote.stdout.trim()) : null\n if (parsed) return new RestForgeClient(parsed.owner, parsed.repo, repoRoot, token)\n }\n\n return new UnreachableForgeClient(\n \"no usable forge client: `gh` is not installed/authenticated and GITHUB_TOKEN is not set (or origin isn't a GitHub remote)\",\n )\n}\n","/**\n * `gc` (PLAN.md §5, PR-D): plan → apply → salvage over the status engine's\n * classification (`status.ts`, PR-A). This module owns no new git knowledge\n * — it reuses `listGitWorktrees` / `computeWorktreeStatus` / `classify` for\n * every fact and `salvageWorktree` / `worktree.cleanup` (PR-C) for every\n * mutation. Its only original logic is sequencing the four safety layers\n * (PLAN.md §5.2):\n *\n * 1. `planGc` is pure read: it classifies every worktree and returns a\n * plan. Nothing is mutated by calling it — the CLI's dry-run default\n * *is* just \"call planGc, print it, stop.\"\n * 2. `applyGc` re-derives each worktree's class from scratch immediately\n * before touching it (a fresh `listGitWorktrees` + `computeWorktreeStatus`,\n * not a reuse of the plan's stale snapshot) and refuses to act if the\n * class changed since the plan was made.\n * 3. The actual removal is `worktree.cleanup` with NO discard flags for a\n * reclaim-class entry — git's own non-`--force` refusal is the backstop\n * this module leans on, never a force flag \"just in case.\"\n * 4. A salvage-class entry is only ever touched via `salvageWorktree`\n * followed by `worktree.cleanup` with both discard flags — snapshot\n * first, removal only after the snapshot is durable (mirrors `worktree\n * archive`'s CLI verb, PR-C).\n *\n * The main worktree (`repoRoot` itself) is never part of the plan: `git\n * worktree list`'s first entry is always the main checkout, and a repo\n * sitting on an up-to-date default branch is trivially `fresh ∧ clean` —\n * exactly what `reclaim` looks for. Git itself refuses to remove\n * the main working tree (\"is a main working tree\", verified empirically),\n * but this module doesn't lean on that as the only guard: the main worktree\n * is filtered out before classification even runs, so it can never appear\n * in a plan, let alone be attempted.\n */\n\nimport { basename } from \"node:path\"\nimport { runTool } from \"@agentproto/driver\"\nimport { cleanupWorktreeTool } from \"./tools/index.js\"\nimport { worktreeProvider } from \"./provider/index.js\"\nimport { salvageWorktree } from \"./salvage.js\"\nimport type { ForgeClient } from \"./forge.js\"\nimport {\n classify,\n listGitWorktrees,\n computeWorktreeStatus,\n type GitWorktreeRef,\n type IntegrationState,\n type LivenessState,\n type TreeState,\n type VerdictMemoStore,\n} from \"./status.js\"\n\nconst candidates = [worktreeProvider]\n\n// ── classification (PLAN.md §5.1) ───────────────────────────────────────\n\nexport type GcClass = \"reclaim\" | \"salvage\" | \"hold\"\n\nexport interface ClassifyForGcOptions {\n /**\n * A clean, idle/daemon-unreachable DETACHED worktree moves from `hold` to\n * `reclaim` (PLAN.md §5.1). This is the only reclassification power any\n * flag has: every other hold reason — partial, open, unknown(offline),\n * gone-unexplained, diverged — is untouched by any flag, ever. Default\n * false.\n */\n includeDetached?: boolean\n /** Threaded straight through to `classify`'s `RECENT_WRITE_HOLD_WINDOW_MS` guard. Defaults to `Date.now()`. */\n nowMs?: number\n}\n\n/**\n * gc's own classifier, layered on top of `classify` (status.ts) rather than\n * reimplementing it — reuse first (AGENTS.md, PLAN.md §0.6). The one thing\n * `classify` cannot know is `--include-detached`, a gc-specific flag; that\n * override lives here, not in the read-only status engine `ls --status`\n * also depends on.\n */\nexport function classifyForGc(\n tree: TreeState,\n integration: IntegrationState,\n liveness: LivenessState,\n options: ClassifyForGcOptions = {},\n): GcClass {\n if (options.includeDetached && integration.state === \"detached\") {\n const idleOrUnreachable = liveness.state === \"idle\" || liveness.state === \"daemon-unreachable\"\n if (tree.state === \"clean\" && idleOrUnreachable) return \"reclaim\"\n }\n return classify(tree, integration, liveness, options.nowMs).class\n}\n\n// ── plan ─────────────────────────────────────────────────────────────\n\nexport interface GcPlanEntry {\n path: string\n branch: string | null\n head: string\n tree: TreeState\n integration: IntegrationState\n liveness: LivenessState\n /** Classification per PLAN.md §5.1, after `--include-detached` (if set). */\n class: GcClass\n}\n\nexport interface PlanGcInput {\n repoRoot: string\n repoName: string\n forge: ForgeClient\n memo: VerdictMemoStore\n defaultBranchRef?: string\n sessionsPath?: string\n includeDetached?: boolean\n now?: () => string\n /** See `ComputeWorktreeStatusInput.nowMs` (status.ts) — kept separate from `now`. Defaults to `Date.now()`. */\n nowMs?: number\n}\n\n/** Every linked worktree of `repoRoot` except the main checkout itself — see the module docblock. */\nasync function linkedWorktreesOf(repoRoot: string): Promise<GitWorktreeRef[]> {\n const worktrees = await listGitWorktrees(repoRoot)\n return worktrees.filter((w) => w.path !== repoRoot)\n}\n\nfunction toPlanEntry(\n worktree: GitWorktreeRef,\n status: { tree: TreeState; integration: IntegrationState; liveness: LivenessState },\n includeDetached: boolean,\n nowMs: number,\n): GcPlanEntry {\n return {\n path: worktree.path,\n branch: worktree.branch,\n head: worktree.head,\n tree: status.tree,\n integration: status.integration,\n liveness: status.liveness,\n class: classifyForGc(status.tree, status.integration, status.liveness, { includeDetached, nowMs }),\n }\n}\n\n/**\n * The dry-run plan (PLAN.md §5.2 layer 1): classify every linked worktree,\n * mutate nothing. This is the human's decision surface — `gc` with no flags\n * is exactly \"call this, print it, stop.\"\n */\nexport async function planGc(input: PlanGcInput): Promise<GcPlanEntry[]> {\n const includeDetached = Boolean(input.includeDetached)\n // One frozen instant for the whole plan's recent-write checks.\n const nowMs = input.nowMs ?? Date.now()\n const worktrees = await linkedWorktreesOf(input.repoRoot)\n const entries: GcPlanEntry[] = []\n for (const worktree of worktrees) {\n const status = await computeWorktreeStatus({\n repoRoot: input.repoRoot,\n repoName: input.repoName,\n worktree,\n forge: input.forge,\n memo: input.memo,\n defaultBranchRef: input.defaultBranchRef,\n sessionsPath: input.sessionsPath,\n now: input.now,\n })\n entries.push(toPlanEntry(worktree, status, includeDetached, nowMs))\n }\n return entries\n}\n\n// ── apply ────────────────────────────────────────────────────────────\n\nexport interface ApplyGcOptions {\n repoRoot: string\n repoName: string\n forge: ForgeClient\n memo: VerdictMemoStore\n defaultBranchRef?: string\n sessionsPath?: string\n includeDetached?: boolean\n /** Archive (salvage-then-remove) every salvage-class entry. Default false — salvage entries are left untouched. */\n salvageDirty?: boolean\n now?: () => string\n /** See `ComputeWorktreeStatusInput.nowMs` (status.ts) — kept separate from `now`. Defaults to `Date.now()`. */\n nowMs?: number\n /** Override for `~/.agentproto/worktree-salvage` — tests use a temp dir. */\n salvageRoot?: string\n}\n\nexport type GcApplyOutcome =\n | { path: string; branch: string | null; result: \"reclaimed\" }\n | { path: string; branch: string | null; result: \"salvaged\"; salvageDir: string }\n | { path: string; branch: string | null; result: \"held\" }\n /** Salvage-class, but `--salvage-dirty` was not passed: left untouched by design. */\n | { path: string; branch: string | null; result: \"skipped-dirty\" }\n /** Layer 2: re-classified at apply time to something other than the plan said — refuses rather than acting on a stale plan. */\n | { path: string; branch: string | null; result: \"aborted-reclassified\"; from: GcClass; to: GcClass }\n /** The worktree named in the plan no longer exists (already removed by something else since the plan was made). */\n | { path: string; branch: string | null; result: \"aborted-vanished\" }\n /** git itself (or the salvage snapshot) refused — the TOCTOU backstop actually firing, or a real I/O failure. */\n | { path: string; branch: string | null; result: \"failed\"; message: string }\n\nasync function findWorktree(repoRoot: string, path: string): Promise<GitWorktreeRef | null> {\n const worktrees = await linkedWorktreesOf(repoRoot)\n return worktrees.find((w) => w.path === path) ?? null\n}\n\nasync function reclaimOne(options: ApplyGcOptions, worktree: GitWorktreeRef): Promise<GcApplyOutcome> {\n try {\n await runTool({\n tool: cleanupWorktreeTool,\n candidates,\n input: {\n repoRoot: options.repoRoot,\n cwd: worktree.path,\n ...(worktree.branch ? { branch: worktree.branch } : {}),\n // Branch deletion only ever runs for a `reclaim`-class entry, which by\n // construction requires `integration ∈ {merged(*), fresh}` and\n // `tree = clean` (PLAN.md's invariant: branch -D only when there's\n // nothing uncommitted to lose, never a hold class).\n deleteBranch: true,\n // No discardUntracked/discardModified, ever, on this path (PLAN.md\n // §5.2 layer 3): `reclaim` means `tree = clean`, so plain `git\n // worktree remove` should succeed on its own; if the tree turned\n // dirty in the instant between the re-check above and this call, git\n // itself refuses and that refusal surfaces below as `failed` — never\n // a force flag \"just in case.\"\n },\n })\n } catch (err) {\n return {\n path: worktree.path,\n branch: worktree.branch,\n result: \"failed\",\n message: err instanceof Error ? err.message : String(err),\n }\n }\n return { path: worktree.path, branch: worktree.branch, result: \"reclaimed\" }\n}\n\nasync function salvageOne(options: ApplyGcOptions, worktree: GitWorktreeRef): Promise<GcApplyOutcome> {\n let salvageDir: string\n try {\n const result = await salvageWorktree({\n repoRoot: options.repoRoot,\n repoName: options.repoName,\n worktreePath: worktree.path,\n branch: worktree.branch,\n tipSha: worktree.head,\n slug: worktree.branch ?? basename(worktree.path),\n sessionsPath: options.sessionsPath,\n now: options.now,\n salvageRoot: options.salvageRoot,\n })\n salvageDir = result.dir\n } catch (err) {\n return {\n path: worktree.path,\n branch: worktree.branch,\n result: \"failed\",\n message: `salvage failed, nothing removed: ${err instanceof Error ? err.message : String(err)}`,\n }\n }\n\n try {\n await runTool({\n tool: cleanupWorktreeTool,\n candidates,\n input: {\n repoRoot: options.repoRoot,\n cwd: worktree.path,\n ...(worktree.branch ? { branch: worktree.branch } : {}),\n deleteBranch: true,\n // Both discard flags: the snapshot above is already durable (fsynced)\n // before this call, so nothing on disk is lost by discarding it here\n // (PLAN.md §5.2 layer 4 — salvage before discard).\n discardUntracked: true,\n discardModified: true,\n },\n })\n } catch (err) {\n return {\n path: worktree.path,\n branch: worktree.branch,\n result: \"failed\",\n message: `salvaged to ${salvageDir}, but removal failed: ${err instanceof Error ? err.message : String(err)}`,\n }\n }\n\n return { path: worktree.path, branch: worktree.branch, result: \"salvaged\", salvageDir }\n}\n\nasync function applyOne(entry: GcPlanEntry, options: ApplyGcOptions): Promise<GcApplyOutcome> {\n if (entry.class === \"hold\") {\n return { path: entry.path, branch: entry.branch, result: \"held\" }\n }\n if (entry.class === \"salvage\" && !options.salvageDirty) {\n return { path: entry.path, branch: entry.branch, result: \"skipped-dirty\" }\n }\n\n // Layer 2 (PLAN.md §5.2): re-check immediately before touching anything.\n // Re-derive the worktree ref itself, not just its status — a branch can\n // flip between plan and apply (the rendezvous-deploy branch-flip mid-\n // session is the plan's own existence proof for this), so re-listing\n // `git worktree list` is required, not just recomputing against the\n // plan's stale (path, branch, head) tuple.\n const fresh = await findWorktree(options.repoRoot, entry.path)\n if (!fresh) {\n return { path: entry.path, branch: entry.branch, result: \"aborted-vanished\" }\n }\n const status = await computeWorktreeStatus({\n repoRoot: options.repoRoot,\n repoName: options.repoName,\n worktree: fresh,\n forge: options.forge,\n memo: options.memo,\n defaultBranchRef: options.defaultBranchRef,\n sessionsPath: options.sessionsPath,\n now: options.now,\n })\n const freshClass = classifyForGc(status.tree, status.integration, status.liveness, {\n includeDetached: Boolean(options.includeDetached),\n nowMs: options.nowMs,\n })\n if (freshClass !== entry.class) {\n return {\n path: entry.path,\n branch: fresh.branch,\n result: \"aborted-reclassified\",\n from: entry.class,\n to: freshClass,\n }\n }\n\n if (freshClass === \"reclaim\") return reclaimOne(options, fresh)\n // The only other class that reaches this point is \"salvage\" with\n // `options.salvageDirty` true (both earlier guards above already returned\n // for \"hold\" and for \"salvage\" without the flag).\n return salvageOne(options, fresh)\n}\n\n/**\n * Execute a plan (PLAN.md §5.2 layers 2–4). Every entry is re-classified\n * from scratch immediately before it's touched; a `hold` entry is never\n * touched, a `salvage` entry is only touched when `salvageDirty` is true,\n * and every removal goes through `worktree.cleanup` — never a hand-rolled\n * `git worktree remove`.\n */\nexport async function applyGc(plan: readonly GcPlanEntry[], options: ApplyGcOptions): Promise<GcApplyOutcome[]> {\n const outcomes: GcApplyOutcome[] = []\n for (const entry of plan) {\n outcomes.push(await applyOne(entry, options))\n }\n return outcomes\n}\n"]}
@@ -1,4 +1,4 @@
1
- export { worktreeProvider } from '../chunk-ZRTFECIP.mjs';
1
+ export { worktreeProvider } from '../chunk-OFO7QHCW.mjs';
2
2
  import '../chunk-CM3XGWTY.mjs';
3
3
  //# sourceMappingURL=index.mjs.map
4
4
  //# sourceMappingURL=index.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentproto/worktree",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "AIP-14 TOOL contracts + builtin AIP-30 PROVIDER for provisioning, gating, and cleaning up a git worktree — the primitive an AgentStep binds its `cwd` to for a launch-agent-in-a-worktree workflow.",
5
5
  "keywords": [
6
6
  "agentproto",
@@ -49,6 +49,7 @@
49
49
  },
50
50
  "files": [
51
51
  "dist",
52
+ "routines",
52
53
  "README.md",
53
54
  "LICENSE"
54
55
  ],
@@ -58,16 +59,17 @@
58
59
  "dependencies": {
59
60
  "@modelcontextprotocol/sdk": "^1.29.0",
60
61
  "zod": "^4.4.3",
61
- "@agentproto/driver": "0.1.3",
62
- "@agentproto/harness": "0.2.1",
63
- "@agentproto/tool": "0.2.1",
64
- "@agentproto/workflow-runtime": "0.4.0"
62
+ "@agentproto/harness": "0.4.0",
63
+ "@agentproto/driver": "0.2.0",
64
+ "@agentproto/workflow-runtime": "0.6.0",
65
+ "@agentproto/tool": "0.2.1"
65
66
  },
66
67
  "devDependencies": {
67
68
  "@types/node": "^25.6.2",
68
69
  "tsup": "^8.5.1",
69
70
  "typescript": "^5.9.3",
70
71
  "vitest": "^3.2.4",
72
+ "@agentproto/routine": "0.2.0",
71
73
  "@agentproto/tooling": "0.1.0-alpha.0"
72
74
  },
73
75
  "scripts": {