@agentproto/worktree 0.5.0 → 0.5.2
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.
- package/README.md +6 -3
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -147,6 +147,9 @@ you turn it on. To activate it in a workspace:
|
|
|
147
147
|
|
|
148
148
|
The safety invariants are enforced by the engine and cannot be weakened by the
|
|
149
149
|
routine: reclaim is **merge-gated** (integration ∈ {merged, fresh} **and** the
|
|
150
|
-
tree is clean)
|
|
151
|
-
|
|
152
|
-
only
|
|
150
|
+
tree is clean) **except** for a narrow dep-bump exemption: a clean `unpushed`
|
|
151
|
+
worktree whose commits are all mechanical dependency bumps (`chore(deps)` /
|
|
152
|
+
`fix(deps)` subjects and a diff touching only lockfiles + `package.json`) is
|
|
153
|
+
also promoted to `reclaim`. An **open** PR or live-session worktree is always
|
|
154
|
+
**held**, and a **dirty** integrated worktree is only ever archived — never
|
|
155
|
+
discarded — and only when `salvageDirty` is `true`.
|
package/dist/index.mjs
CHANGED
|
@@ -402,8 +402,8 @@ var RestForgeClient = class {
|
|
|
402
402
|
};
|
|
403
403
|
async function ghIsUsable(repoRoot) {
|
|
404
404
|
try {
|
|
405
|
-
const res = await execArgv("gh", ["auth", "
|
|
406
|
-
return res.exitCode === 0;
|
|
405
|
+
const res = await execArgv("gh", ["auth", "token"], repoRoot);
|
|
406
|
+
return res.exitCode === 0 && res.stdout.trim() !== "";
|
|
407
407
|
} catch {
|
|
408
408
|
return false;
|
|
409
409
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -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 /** 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 * One reclassification on top of `classify`'s three classes:\n * `resolveGcClass` promotes a clean, `unpushed` worktree from `hold` to\n * `reclaim` when every commit ahead of the default branch is a mechanical\n * dependency bump (subject AND cumulative diff both checked — see that\n * function's doc). It's applied identically at plan time and re-verified at\n * apply time (layer 2), so a worktree that grew a real commit in between is\n * still caught.\n */\n\nimport { basename } from \"node:path\"\nimport { runTool } from \"@agentproto/driver\"\nimport { execArgv } from \"./exec.js\"\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// ── dep-bump reclaim exemption ──────────────────────────────────────────\n//\n// A worktree whose only unpushed commits are a mechanical dependency bump\n// (`chore(deps): weekly minor/patch bump`, re-run every week by every\n// session that happens to pick it up) is not \"unpushed work to protect\" —\n// it reproduces by re-running the routine, and 25 of these piling up as\n// permanent `hold`s is exactly what emptied a 102GB external SSD of gc\n// coverage. This is deliberately narrow and additive: it only ever promotes\n// a worktree that `classifyForGc` already put in `hold` for the single\n// reason `integration.state === \"unpushed\"` with a clean tree — every other\n// hold reason (partial, open, diverged, gone-unexplained, unknown(offline),\n// a dirty tree, `unpushed` while forge-unreachable — which can't happen, see\n// `reconcileIntegration`) is completely untouched by this and still holds.\n\n/** The only reclaim reason this module can attach today. */\nexport type GcReclaimReason = \"dep-bump\"\n\n/**\n * Mechanical dep-bump commit subjects: `chore(deps)` / `fix(deps)`, with or\n * without a scope (`chore(deps-dev)`, `fix(deps-optional)`, …). A commit\n * message is cheap to type and easy to lie in — this regex alone NEVER\n * licenses anything; `isMechanicalDepBumpRange` below only trusts it in\n * conjunction with the actual cumulative diff.\n */\nconst DEP_BUMP_SUBJECT_RE = /^(?:chore|fix)\\(deps[\\w-]*\\):\\s/\n\nfunction isDepBumpSubject(subject: string): boolean {\n return DEP_BUMP_SUBJECT_RE.test(subject)\n}\n\n/** The only paths a mechanical dep bump is allowed to touch, matched by basename so a nested package's `package.json` counts too. */\nconst DEP_BUMP_ALLOWED_BASENAMES = new Set([\"pnpm-lock.yaml\", \"package-lock.json\", \"yarn.lock\", \"package.json\"])\n\nfunction isDepBumpAllowedPath(path: string): boolean {\n return DEP_BUMP_ALLOWED_BASENAMES.has(basename(path))\n}\n\n/**\n * Layer 3+4 of the exemption: EVERY commit ahead of `baseRef` must have a\n * dep-bump subject (`git log --format=%s`, two-dot range — exactly the\n * commits `aheadBy` counts, PLAN-comment on `readUpstreamTrack`), AND the\n * cumulative diff of that whole range (`git diff`, three-dot/merge-base\n * range, so it isolates the branch's own changes from anything `baseRef`\n * did independently in the meantime) must touch nothing but lockfiles and\n * `package.json`. Both checks run unconditionally together — a subject that\n * lies is cheap, a diff cannot lie, so the diff is the one that actually\n * gates. Two extra git spawns, only ever paid for a worktree that would\n * otherwise be `hold` for exactly `clean ∧ unpushed` — every other class\n * pays nothing.\n */\nexport async function isMechanicalDepBumpRange(repoRoot: string, baseRef: string, tipSha: string): Promise<boolean> {\n const [subjectsRes, diffRes] = await Promise.all([\n execArgv(\"git\", [\"-C\", repoRoot, \"log\", \"--format=%s\", `${baseRef}..${tipSha}`], repoRoot),\n execArgv(\"git\", [\"-C\", repoRoot, \"diff\", \"--name-only\", `${baseRef}...${tipSha}`], repoRoot),\n ])\n if (subjectsRes.exitCode !== 0 || diffRes.exitCode !== 0) return false\n\n const subjects = subjectsRes.stdout.split(\"\\n\").filter((line) => line.length > 0)\n if (subjects.length === 0) return false\n if (!subjects.every(isDepBumpSubject)) return false\n\n const files = diffRes.stdout.split(\"\\n\").filter((line) => line.length > 0)\n if (files.length === 0) return false\n return files.every(isDepBumpAllowedPath)\n}\n\nexport interface ResolveGcClassOptions extends ClassifyForGcOptions {\n repoRoot: string\n /** The tip commit of the worktree being classified — `worktree.head`. */\n tipSha: string\n /** Default `\"origin/main\"` — matches `reconcileIntegration`'s own default. */\n defaultBranchRef?: string\n}\n\nexport interface ResolvedGcClass {\n class: GcClass\n /** Set only when `class === \"reclaim\"` via the dep-bump exemption rather than the ordinary merged/fresh path. */\n reclaimReason?: GcReclaimReason\n}\n\n/**\n * `classifyForGc` plus the one async, git-touching layer on top: a clean,\n * `unpushed` worktree whose entire ahead range is a mechanical dep bump is\n * promoted from `hold` to `reclaim`. Every other class classifyForGc\n * returns is passed through untouched — this never widens any other hold\n * reason, and never touches git for a worktree it wouldn't otherwise hold\n * on `unpushed` alone.\n */\nexport async function resolveGcClass(\n tree: TreeState,\n integration: IntegrationState,\n liveness: LivenessState,\n options: ResolveGcClassOptions,\n): Promise<ResolvedGcClass> {\n const baseClass = classifyForGc(tree, integration, liveness, options)\n if (baseClass !== \"hold\") return { class: baseClass }\n if (tree.state !== \"clean\" || integration.state !== \"unpushed\") return { class: \"hold\" }\n\n const baseRef = options.defaultBranchRef ?? \"origin/main\"\n const isDepBump = await isMechanicalDepBumpRange(options.repoRoot, baseRef, options.tipSha)\n if (!isDepBump) return { class: \"hold\" }\n return { class: \"reclaim\", reclaimReason: \"dep-bump\" }\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 /** Set only when `class === \"reclaim\"` via the dep-bump exemption — see `resolveGcClass`. */\n reclaimReason?: GcReclaimReason\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\nasync function toPlanEntry(\n repoRoot: string,\n defaultBranchRef: string | undefined,\n worktree: GitWorktreeRef,\n status: { tree: TreeState; integration: IntegrationState; liveness: LivenessState },\n includeDetached: boolean,\n nowMs: number,\n): Promise<GcPlanEntry> {\n const resolved = await resolveGcClass(status.tree, status.integration, status.liveness, {\n repoRoot,\n tipSha: worktree.head,\n defaultBranchRef,\n includeDetached,\n nowMs,\n })\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: resolved.class,\n ...(resolved.reclaimReason ? { reclaimReason: resolved.reclaimReason } : {}),\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(await toPlanEntry(input.repoRoot, input.defaultBranchRef, 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\"; reclaimReason?: GcReclaimReason }\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(\n options: ApplyGcOptions,\n worktree: GitWorktreeRef,\n reclaimReason?: GcReclaimReason,\n): 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 {\n path: worktree.path,\n branch: worktree.branch,\n result: \"reclaimed\",\n ...(reclaimReason ? { reclaimReason } : {}),\n }\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 // Re-resolved from scratch — including the dep-bump exemption, not just\n // `entry`'s stale plan-time verdict, so a worktree that picked up a real\n // (non-bump) commit between plan and apply is re-caught here, not just at\n // plan time.\n const resolved = await resolveGcClass(status.tree, status.integration, status.liveness, {\n repoRoot: options.repoRoot,\n tipSha: fresh.head,\n defaultBranchRef: options.defaultBranchRef,\n includeDetached: Boolean(options.includeDetached),\n nowMs: options.nowMs,\n })\n if (resolved.class !== entry.class) {\n return {\n path: entry.path,\n branch: fresh.branch,\n result: \"aborted-reclassified\",\n from: entry.class,\n to: resolved.class,\n }\n }\n\n if (resolved.class === \"reclaim\") return reclaimOne(options, fresh, resolved.reclaimReason)\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/**\n * Cheap \"is `gh` on PATH and authenticated\" probe — avoids a slow failure per\n * branch.\n *\n * Deliberately `gh auth token`, not `gh auth status`: `status` audits **every**\n * configured account and exits non-zero when *any* of them is broken, even\n * while the active credential works fine for real calls. A single stale keyring\n * entry for a second account is enough to make it exit 1 — and because a false\n * negative here lands on `UnreachableForgeClient`, every worktree's integration\n * silently degrades to `unknown(offline)` and `worktree gc` holds everything it\n * would otherwise reclaim. Observed exactly that: `gh auth status` exiting 1 on\n * a stale account while `gh pr list` and `gh api rate_limit` both answered\n * normally.\n *\n * `gh auth token` answers the question this probe actually asks — \"is gh on\n * PATH with a usable credential\" — and does it locally, with no network call.\n * It cannot prove the token is still valid server-side, but neither could\n * `status`: a token revoked upstream fails at call time either way, which is\n * exactly what `ForgeUnavailableError` and the memo/`unknown(offline)` fallback\n * already handle.\n */\nasync function ghIsUsable(repoRoot: string): Promise<boolean> {\n try {\n const res = await execArgv(\"gh\", [\"auth\", \"token\"], repoRoot)\n return res.exitCode === 0 && res.stdout.trim() !== \"\"\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 * One reclassification on top of `classify`'s three classes:\n * `resolveGcClass` promotes a clean, `unpushed` worktree from `hold` to\n * `reclaim` when every commit ahead of the default branch is a mechanical\n * dependency bump (subject AND cumulative diff both checked — see that\n * function's doc). It's applied identically at plan time and re-verified at\n * apply time (layer 2), so a worktree that grew a real commit in between is\n * still caught.\n */\n\nimport { basename } from \"node:path\"\nimport { runTool } from \"@agentproto/driver\"\nimport { execArgv } from \"./exec.js\"\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// ── dep-bump reclaim exemption ──────────────────────────────────────────\n//\n// A worktree whose only unpushed commits are a mechanical dependency bump\n// (`chore(deps): weekly minor/patch bump`, re-run every week by every\n// session that happens to pick it up) is not \"unpushed work to protect\" —\n// it reproduces by re-running the routine, and 25 of these piling up as\n// permanent `hold`s is exactly what emptied a 102GB external SSD of gc\n// coverage. This is deliberately narrow and additive: it only ever promotes\n// a worktree that `classifyForGc` already put in `hold` for the single\n// reason `integration.state === \"unpushed\"` with a clean tree — every other\n// hold reason (partial, open, diverged, gone-unexplained, unknown(offline),\n// a dirty tree, `unpushed` while forge-unreachable — which can't happen, see\n// `reconcileIntegration`) is completely untouched by this and still holds.\n\n/** The only reclaim reason this module can attach today. */\nexport type GcReclaimReason = \"dep-bump\"\n\n/**\n * Mechanical dep-bump commit subjects: `chore(deps)` / `fix(deps)`, with or\n * without a scope (`chore(deps-dev)`, `fix(deps-optional)`, …). A commit\n * message is cheap to type and easy to lie in — this regex alone NEVER\n * licenses anything; `isMechanicalDepBumpRange` below only trusts it in\n * conjunction with the actual cumulative diff.\n */\nconst DEP_BUMP_SUBJECT_RE = /^(?:chore|fix)\\(deps[\\w-]*\\):\\s/\n\nfunction isDepBumpSubject(subject: string): boolean {\n return DEP_BUMP_SUBJECT_RE.test(subject)\n}\n\n/** The only paths a mechanical dep bump is allowed to touch, matched by basename so a nested package's `package.json` counts too. */\nconst DEP_BUMP_ALLOWED_BASENAMES = new Set([\"pnpm-lock.yaml\", \"package-lock.json\", \"yarn.lock\", \"package.json\"])\n\nfunction isDepBumpAllowedPath(path: string): boolean {\n return DEP_BUMP_ALLOWED_BASENAMES.has(basename(path))\n}\n\n/**\n * Layer 3+4 of the exemption: EVERY commit ahead of `baseRef` must have a\n * dep-bump subject (`git log --format=%s`, two-dot range — exactly the\n * commits `aheadBy` counts, PLAN-comment on `readUpstreamTrack`), AND the\n * cumulative diff of that whole range (`git diff`, three-dot/merge-base\n * range, so it isolates the branch's own changes from anything `baseRef`\n * did independently in the meantime) must touch nothing but lockfiles and\n * `package.json`. Both checks run unconditionally together — a subject that\n * lies is cheap, a diff cannot lie, so the diff is the one that actually\n * gates. Two extra git spawns, only ever paid for a worktree that would\n * otherwise be `hold` for exactly `clean ∧ unpushed` — every other class\n * pays nothing.\n */\nexport async function isMechanicalDepBumpRange(repoRoot: string, baseRef: string, tipSha: string): Promise<boolean> {\n const [subjectsRes, diffRes] = await Promise.all([\n execArgv(\"git\", [\"-C\", repoRoot, \"log\", \"--format=%s\", `${baseRef}..${tipSha}`], repoRoot),\n execArgv(\"git\", [\"-C\", repoRoot, \"diff\", \"--name-only\", `${baseRef}...${tipSha}`], repoRoot),\n ])\n if (subjectsRes.exitCode !== 0 || diffRes.exitCode !== 0) return false\n\n const subjects = subjectsRes.stdout.split(\"\\n\").filter((line) => line.length > 0)\n if (subjects.length === 0) return false\n if (!subjects.every(isDepBumpSubject)) return false\n\n const files = diffRes.stdout.split(\"\\n\").filter((line) => line.length > 0)\n if (files.length === 0) return false\n return files.every(isDepBumpAllowedPath)\n}\n\nexport interface ResolveGcClassOptions extends ClassifyForGcOptions {\n repoRoot: string\n /** The tip commit of the worktree being classified — `worktree.head`. */\n tipSha: string\n /** Default `\"origin/main\"` — matches `reconcileIntegration`'s own default. */\n defaultBranchRef?: string\n}\n\nexport interface ResolvedGcClass {\n class: GcClass\n /** Set only when `class === \"reclaim\"` via the dep-bump exemption rather than the ordinary merged/fresh path. */\n reclaimReason?: GcReclaimReason\n}\n\n/**\n * `classifyForGc` plus the one async, git-touching layer on top: a clean,\n * `unpushed` worktree whose entire ahead range is a mechanical dep bump is\n * promoted from `hold` to `reclaim`. Every other class classifyForGc\n * returns is passed through untouched — this never widens any other hold\n * reason, and never touches git for a worktree it wouldn't otherwise hold\n * on `unpushed` alone.\n */\nexport async function resolveGcClass(\n tree: TreeState,\n integration: IntegrationState,\n liveness: LivenessState,\n options: ResolveGcClassOptions,\n): Promise<ResolvedGcClass> {\n const baseClass = classifyForGc(tree, integration, liveness, options)\n if (baseClass !== \"hold\") return { class: baseClass }\n if (tree.state !== \"clean\" || integration.state !== \"unpushed\") return { class: \"hold\" }\n\n const baseRef = options.defaultBranchRef ?? \"origin/main\"\n const isDepBump = await isMechanicalDepBumpRange(options.repoRoot, baseRef, options.tipSha)\n if (!isDepBump) return { class: \"hold\" }\n return { class: \"reclaim\", reclaimReason: \"dep-bump\" }\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 /** Set only when `class === \"reclaim\"` via the dep-bump exemption — see `resolveGcClass`. */\n reclaimReason?: GcReclaimReason\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\nasync function toPlanEntry(\n repoRoot: string,\n defaultBranchRef: string | undefined,\n worktree: GitWorktreeRef,\n status: { tree: TreeState; integration: IntegrationState; liveness: LivenessState },\n includeDetached: boolean,\n nowMs: number,\n): Promise<GcPlanEntry> {\n const resolved = await resolveGcClass(status.tree, status.integration, status.liveness, {\n repoRoot,\n tipSha: worktree.head,\n defaultBranchRef,\n includeDetached,\n nowMs,\n })\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: resolved.class,\n ...(resolved.reclaimReason ? { reclaimReason: resolved.reclaimReason } : {}),\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(await toPlanEntry(input.repoRoot, input.defaultBranchRef, 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\"; reclaimReason?: GcReclaimReason }\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(\n options: ApplyGcOptions,\n worktree: GitWorktreeRef,\n reclaimReason?: GcReclaimReason,\n): 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 {\n path: worktree.path,\n branch: worktree.branch,\n result: \"reclaimed\",\n ...(reclaimReason ? { reclaimReason } : {}),\n }\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 // Re-resolved from scratch — including the dep-bump exemption, not just\n // `entry`'s stale plan-time verdict, so a worktree that picked up a real\n // (non-bump) commit between plan and apply is re-caught here, not just at\n // plan time.\n const resolved = await resolveGcClass(status.tree, status.integration, status.liveness, {\n repoRoot: options.repoRoot,\n tipSha: fresh.head,\n defaultBranchRef: options.defaultBranchRef,\n includeDetached: Boolean(options.includeDetached),\n nowMs: options.nowMs,\n })\n if (resolved.class !== entry.class) {\n return {\n path: entry.path,\n branch: fresh.branch,\n result: \"aborted-reclassified\",\n from: entry.class,\n to: resolved.class,\n }\n }\n\n if (resolved.class === \"reclaim\") return reclaimOne(options, fresh, resolved.reclaimReason)\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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentproto/worktree",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
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",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"@agentproto/driver": "0.2.0",
|
|
63
63
|
"@agentproto/harness": "0.4.1",
|
|
64
64
|
"@agentproto/tool": "0.2.1",
|
|
65
|
-
"@agentproto/workflow-runtime": "0.
|
|
65
|
+
"@agentproto/workflow-runtime": "0.7.0"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@types/node": "^25.6.2",
|