@use-aistack/cli 0.6.3 → 0.7.0

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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/version.ts","../src/api.ts","../src/commands/collect.ts","../src/classifier.ts","../src/stableKey.ts","../src/config.ts","../src/git.ts","../src/github-repo.ts","../src/hooks.ts","../src/mcp.ts","../src/plugins.ts","../src/scanner.ts","../src/theme.ts","../src/commands/connect.ts","../src/commands/create.ts","../src/commands/login.ts","../src/commands/sync.ts","../src/autosync/codexHook.ts","../src/autosync/optin.ts","../src/autosync/hook.ts","../src/autosync/run.ts","../src/sync/stage.ts","../src/harness/claude/adapter.ts","../src/harness/shared/pricing.ts","../src/harness/shared/aggregate.ts","../src/harness/shared/bundled-allowlist.ts","../src/harness/shared/allowlist.ts","../src/harness/claude/analyzer.ts","../src/harness/claude/scan.ts","../src/harness/shared/window.ts","../src/harness/codex/adapter.ts","../src/harness/codex/analyzer.ts","../src/harness/codex/scan.ts","../src/harness/shared/payload.ts","../src/harness/index.ts","../src/sync/summary.ts","../src/sync/server.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { BASE_URL } from \"./api.js\";\nimport { collectCommand } from \"./commands/collect.js\";\nimport { connectCommand } from \"./commands/connect.js\";\nimport { createCommand } from \"./commands/create.js\";\nimport { loginCommand } from \"./commands/login.js\";\nimport { syncCommand } from \"./commands/sync.js\";\nimport { runStdioSyncServer } from \"./sync/server.js\";\nimport { CLI_VERSION } from \"./version.js\";\n\nconst program = new Command();\n\nprogram\n\t.name(\"aistack\")\n\t.description(\"Measure and share your AI stack from your terminal\")\n\t.version(CLI_VERSION);\n\nprogram\n\t.command(\"login\")\n\t.description(\"Authenticate with AI Stack\")\n\t.action(loginCommand);\n\nprogram\n\t.command(\"collect\")\n\t.description(\"Scan and upload AI config files from your project\")\n\t.option(\"--no-global\", \"Exclude global config files (~/.claude, etc.)\")\n\t.action((options) => collectCommand({ global: options.global ?? true }));\n\nprogram\n\t.command(\"create\")\n\t.description(\"Download and write your stack's AI config files\")\n\t.action(createCommand);\n\nprogram\n\t.command(\"mcp\")\n\t.description(\n\t\t\"Run the aistack MCP server on stdio (sync preview + gated publish)\",\n\t)\n\t.action(() => {\n\t\t// stdout belongs to the protocol. Diagnostics go to stderr only.\n\t\trunStdioSyncServer({\n\t\t\tbaseUrl: BASE_URL,\n\t\t\tlog: (line) => process.stderr.write(`[aistack-mcp] ${line}\\n`),\n\t\t});\n\t});\n\n// The documented default sync surface (#56): terminal-first, TTY gate.\nprogram\n\t.command(\"sync\")\n\t.description(\"Scan, preview, and publish measured usage (rolling 30 days)\")\n\t.option(\n\t\t\"--auto [state]\",\n\t\t\"silent background sync; 'on' enables the SessionStart hook, 'off' revokes it\",\n\t)\n\t.option(\n\t\t\"--every <hours>\",\n\t\t\"with --auto on: hours between auto-syncs (default 24)\",\n\t)\n\t.action((options) => syncCommand(options));\n\nprogram\n\t.command(\"connect\")\n\t.description(\"Install the in-session sync surface (MCP server + Skill)\")\n\t.argument(\"<harness>\", 'the harness to connect (\"claude\")')\n\t.action(connectCommand);\n\nprogram.parse();\n","// The one place the CLI's version string lives.\n//\n// `tsup` replaces `__AISTACK_CLI_VERSION__` at build time with the version in\n// `package.json`, so a release cannot ship a stale number. The fallback covers\n// running from source (tests, `tsx src/index.ts`), where no define happens.\ndeclare const __AISTACK_CLI_VERSION__: string | undefined;\n\nexport const CLI_VERSION: string =\n\ttypeof __AISTACK_CLI_VERSION__ === \"string\"\n\t\t? __AISTACK_CLI_VERSION__\n\t\t: \"0.0.0-dev\";\n","import { CLI_VERSION } from \"./version.js\";\n\nexport const BASE_URL = process.env.AISTACK_URL || \"https://aistack.to\";\n\nasync function request(\n\tpath: string,\n\toptions: RequestInit = {},\n): Promise<Response> {\n\treturn fetch(`${BASE_URL}${path}`, {\n\t\t...options,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t...options.headers,\n\t\t},\n\t});\n}\n\nfunction authHeaders(token: string): HeadersInit {\n\treturn { Authorization: `Bearer ${token}` };\n}\n\n/**\n * Turn the two statuses #52 introduced into sentences.\n *\n * A bare `429` tells the user nothing they can act on, and a bare `403` reads\n * like a bug rather than a machine that is no longer allowed to do this. Every\n * other status keeps its number, because the number is all we know about it.\n */\nfunction failure(what: string, res: Response): Error {\n\tif (res.status === 429) {\n\t\tconst retry = res.headers.get(\"Retry-After\");\n\t\treturn new Error(\n\t\t\tretry\n\t\t\t\t? `${what}: too many requests. Try again in ${retry} seconds.`\n\t\t\t\t: `${what}: too many requests. Try again in a minute.`,\n\t\t);\n\t}\n\tif (res.status === 403) {\n\t\treturn new Error(\n\t\t\t`${what}: this machine is not allowed to do that. Run \\`aistack login\\` again to re-link it.`,\n\t\t);\n\t}\n\treturn new Error(`${what}: ${res.status}`);\n}\n\n/**\n * Open a device-code session.\n *\n * `machineName` is a PROPOSAL, not a fact: the approval page renders it in an\n * editable field, so the user sees the string before it is stored and can\n * overwrite or clear it. That is why the hostname may be sent automatically —\n * the consent happens in the browser, a moment later, with the string on screen.\n */\nexport async function authStart(machineName?: string): Promise<{\n\tsecretId: string;\n\tuserCode: string;\n\tauthUrl: string;\n}> {\n\tconst res = await request(\"/api/cli/auth/start\", {\n\t\tmethod: \"POST\",\n\t\t// `cliVersion` rides along so `cli_login_completed` can report which\n\t\t// version linked the machine (#78). The server carries it on the pending\n\t\t// session and reads it at the token exchange.\n\t\tbody: JSON.stringify(\n\t\t\tmachineName\n\t\t\t\t? { machineName, cliVersion: CLI_VERSION }\n\t\t\t\t: { cliVersion: CLI_VERSION },\n\t\t),\n\t});\n\tif (!res.ok) throw failure(\"Auth start failed\", res);\n\treturn res.json();\n}\n\nexport async function authPoll(\n\tsecretId: string,\n): Promise<{ status: string; token?: string; userId?: string }> {\n\tconst res = await request(\n\t\t`/api/cli/auth/poll?secretId=${encodeURIComponent(secretId)}`,\n\t);\n\tif (!res.ok) throw failure(\"Auth poll failed\", res);\n\treturn res.json();\n}\n\nexport async function stackCollect(\n\ttoken: string,\n\tdata: { resources: Resource[] },\n): Promise<{ slug: string; shortId: string; url: string }> {\n\tconst res = await request(\"/api/cli/stacks/collect\", {\n\t\tmethod: \"POST\",\n\t\theaders: authHeaders(token),\n\t\tbody: JSON.stringify(data),\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (!res.ok) {\n\t\tthrow new Error(await formatHttpError(res, \"Collect failed\"));\n\t}\n\treturn res.json();\n}\n\nasync function formatHttpError(res: Response, label: string): Promise<string> {\n\tconst prefix = `${label}: ${res.status} ${res.statusText || \"\"}`.trim();\n\tconst text = await res.text().catch(() => \"\");\n\tif (!text) return prefix;\n\ttry {\n\t\tconst body = JSON.parse(text) as { error?: string; message?: string };\n\t\tconst detail = body.error || body.message;\n\t\tif (detail) return `${prefix} — ${detail}`;\n\t} catch {}\n\tconst snippet = text.trim().slice(0, 500);\n\treturn snippet ? `${prefix} — ${snippet}` : prefix;\n}\n\nexport type SyncPublishResult = {\n\treceivedAt: number;\n\tstackSlug: string;\n\turl: string;\n\tkeptPrivate: { stored: number; refused: boolean };\n};\n\n/**\n * Publish one approved snapshot.\n *\n * Takes the staged body as an ALREADY-SERIALIZED string: the bytes the user\n * approved at the gate are the bytes on the wire, with no re-serialization\n * step between them (#35's binding constraint, #41).\n */\nexport async function syncPublish(\n\ttoken: string,\n\tbodyJson: string,\n): Promise<SyncPublishResult> {\n\tconst res = await request(\"/api/cli/sync\", {\n\t\tmethod: \"POST\",\n\t\theaders: authHeaders(token),\n\t\tbody: bodyJson,\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (res.status === 403 || res.status === 429)\n\t\tthrow failure(\"Sync failed\", res);\n\tif (!res.ok) {\n\t\tthrow new Error(await formatHttpError(res, \"Sync failed\"));\n\t}\n\treturn res.json();\n}\n\nexport async function stackGet(token: string): Promise<StackData | null> {\n\tconst res = await request(\"/api/cli/stacks\", {\n\t\theaders: authHeaders(token),\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (res.status === 404) return null;\n\tif (!res.ok) throw failure(\"Stack fetch failed\", res);\n\treturn res.json();\n}\n\n// Types used across the CLI\nexport interface ResourceFile {\n\tname: string;\n\tcontent: string;\n\tpath?: string;\n\ttags?: string[];\n}\n\nexport interface Resource {\n\ttype: string;\n\tname: string;\n\tdescription?: string;\n\tgroup: string;\n\tstableKey: string;\n\tfiles?: ResourceFile[];\n\tupstream?: {\n\t\trepoUrl: string;\n\t\tpath?: string;\n\t\tlicense?: string;\n\t\tstars?: number;\n\t\tlastCommitSha?: string;\n\t\tlastSyncAt?: number;\n\t};\n\tpkg?: {\n\t\tregistry: \"npm\" | \"pypi\" | \"oci\" | \"url\";\n\t\tid: string;\n\t\tversion?: string;\n\t\ttransport?: \"stdio\" | \"http\" | \"sse\";\n\t};\n}\n\nexport interface StackData {\n\tname: string;\n\tslug: string;\n\tshortId: string;\n\tresources: Resource[];\n}\n","import * as p from \"@clack/prompts\";\nimport { type Resource, stackCollect, stackGet } from \"../api.js\";\nimport { classify } from \"../classifier.js\";\nimport { getExcludedPaths, getToken, saveExcludedPaths } from \"../config.js\";\nimport { buildRepoLinkResource, detectRepoUrl } from \"../git.js\";\nimport { repoNameFromCanonical } from \"../github-repo.js\";\nimport { detectHooks } from \"../hooks.js\";\nimport { detectMcpServers } from \"../mcp.js\";\nimport { detectInstalledPlugins } from \"../plugins.js\";\nimport { type ScannedFile, scanGlobal, scanLocal } from \"../scanner.js\";\nimport {\n\tbold,\n\tdim,\n\tdivider,\n\tintro,\n\tlime,\n\tlimeBold,\n\tlines,\n\toutro,\n\toutroCancel,\n\toutroError,\n\toutroSkipped,\n\tred,\n\tsection,\n\tyellow,\n} from \"../theme.js\";\n\n// Sentinel selection key for the detected repo link. NUL-prefixed so it can\n// never collide with a real ScannedFile.relativePath, letting links ride the\n// existing excluded[] selection/persistence model with no config.ts changes.\nconst REPO_LINK_KEY = \"\\0repo-link\";\n\n/**\n * A non-file resource surfaced during collect — the repo link, an installed\n * plugin, etc. Toggleable and persisted exactly like a scanned file, keyed by\n * its sentinel. Everything attaches to the single stack (global) server-side.\n */\ninterface DetectedLink {\n\tkey: string;\n\tresource: Resource;\n\tlabel: string;\n}\n\nexport async function collectCommand(options: { global: boolean }) {\n\tintro(\"collect\");\n\n\tconst token = getToken();\n\tif (!token) {\n\t\tp.log.error(\n\t\t\t`Not authenticated. Run ${limeBold(\"npx @use-aistack/cli login\")} first.`,\n\t\t);\n\t\toutroError(\"not authenticated\");\n\t\tprocess.exit(1);\n\t}\n\n\tconst cwd = process.cwd();\n\tconst savedExcluded = getExcludedPaths(cwd);\n\n\tconst s = p.spinner();\n\ts.start(\"Scanning...\");\n\n\tconst localFiles = scanLocal(cwd);\n\tconst globalFiles = options.global ? scanGlobal() : [];\n\ts.stop(\"Scan complete\");\n\n\tif (localFiles.length === 0 && globalFiles.length === 0) {\n\t\tp.log.warn(\"No AI configuration files found.\");\n\t\toutroSkipped(\"nothing to collect\");\n\t\treturn;\n\t}\n\n\t// Apply saved exclusions\n\tconst allFiles = [...localFiles, ...globalFiles];\n\tlet selectedFiles = allFiles.filter(\n\t\t(f) => !savedExcluded.includes(f.relativePath),\n\t);\n\tlet excluded = allFiles.filter((f) => savedExcluded.includes(f.relativePath));\n\n\t// Show file counts\n\tp.log.info(\n\t\t`${lime(String(selectedFiles.length))} included${excluded.length > 0 ? ` · ${dim(String(excluded.length) + \" excluded\")}` : \"\"}`,\n\t);\n\n\t// Detect non-file links: the repo this lives in, installed Claude Code\n\t// plugins, MCP servers, hooks. Each is toggleable and included by default\n\t// unless previously deselected. All graceful no-ops.\n\tconst detectedLinks: DetectedLink[] = [];\n\tconst repoUrl = detectRepoUrl(cwd);\n\tif (repoUrl) {\n\t\tdetectedLinks.push({\n\t\t\tkey: REPO_LINK_KEY,\n\t\t\tresource: buildRepoLinkResource(repoUrl),\n\t\t\tlabel: `repo · ${repoNameFromCanonical(repoUrl)}`,\n\t\t});\n\t}\n\tfor (const resource of detectInstalledPlugins()) {\n\t\tdetectedLinks.push({\n\t\t\tkey: `\\0plugin:${resource.stableKey}`,\n\t\t\tresource,\n\t\t\tlabel: `plugin · ${resource.name}`,\n\t\t});\n\t}\n\tfor (const resource of detectMcpServers(cwd)) {\n\t\tdetectedLinks.push({\n\t\t\tkey: `\\0mcp:${resource.stableKey}`,\n\t\t\tresource,\n\t\t\tlabel: `mcp · ${resource.name}`,\n\t\t});\n\t}\n\tfor (const resource of detectHooks(cwd)) {\n\t\tdetectedLinks.push({\n\t\t\tkey: `\\0hook:${resource.stableKey}`,\n\t\t\tresource,\n\t\t\tlabel: `hook · ${resource.name}`,\n\t\t});\n\t}\n\n\tconst includedLinks = new Set(\n\t\tdetectedLinks\n\t\t\t.filter((l) => !savedExcluded.includes(l.key))\n\t\t\t.map((l) => l.key),\n\t);\n\tconst withLinks = (base: Resource[]): Resource[] => [\n\t\t...base,\n\t\t...detectedLinks\n\t\t\t.filter((l) => includedLinks.has(l.key))\n\t\t\t.map((l) => l.resource),\n\t];\n\n\t// Classify selected files\n\tlet allResources = withLinks(classify(selectedFiles));\n\n\t// Fetch the existing stack and diff against its resources.\n\tlet existingStack: Awaited<ReturnType<typeof stackGet>> = null;\n\ttry {\n\t\texistingStack = await stackGet(token);\n\t} catch (err) {\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\toutroError(\"error\");\n\t\tprocess.exit(1);\n\t}\n\n\t// Show file list or diff\n\tif (existingStack) {\n\t\tconst diff = diffResources(allResources, existingStack.resources);\n\t\tconst changeCount = diff.added + diff.changed + diff.removed;\n\n\t\tif (changeCount === 0) {\n\t\t\tp.log.info(\"No changes since last collect.\");\n\t\t\toutroSkipped(\"nothing to upload\");\n\t\t\treturn;\n\t\t}\n\n\t\tdivider();\n\t\tsection(\"changes\");\n\t\tlines(\n\t\t\tdiff.details.map((f) => {\n\t\t\t\tif (f.status === \"added\") return lime(`+ ${f.name}`);\n\t\t\t\tif (f.status === \"changed\") return yellow(`~ ${f.name}`);\n\t\t\t\treturn red(`- ${f.name}`);\n\t\t\t}),\n\t\t);\n\t\tif (diff.unchanged > 0) {\n\t\t\tlines([dim(`${diff.unchanged} unchanged`)]);\n\t\t}\n\t\tdivider();\n\t} else {\n\t\tconst local = selectedFiles.filter((f) => f.source === \"local\");\n\t\tconst global = selectedFiles.filter((f) => f.source === \"global\");\n\n\t\tif (local.length > 0) {\n\t\t\tp.log.step(`${bold(\"LOCAL\")} ${dim(String(local.length))}`);\n\t\t\tdivider();\n\t\t\tfor (const [type, files] of groupByType(local)) {\n\t\t\t\tlines([`${lime(type.toUpperCase())} ${dim(`${files.length}`)}`]);\n\t\t\t\tlines(files.map((f) => dim(` ${f.relativePath}`)));\n\t\t\t}\n\t\t\tdivider();\n\t\t}\n\t\tif (global.length > 0) {\n\t\t\tp.log.step(`${bold(\"GLOBAL\")} ${dim(String(global.length))}`);\n\t\t\tdivider();\n\t\t\tfor (const [type, files] of groupByType(global)) {\n\t\t\t\tlines([`${lime(type.toUpperCase())} ${dim(`${files.length}`)}`]);\n\t\t\t\tlines(files.map((f) => dim(` ${f.relativePath}`)));\n\t\t\t}\n\t\t\tdivider();\n\t\t}\n\t\tconst shownLinks = detectedLinks.filter((l) => includedLinks.has(l.key));\n\t\tif (shownLinks.length > 0) {\n\t\t\tp.log.step(`${bold(\"LINKS\")} ${dim(String(shownLinks.length))}`);\n\t\t\tdivider();\n\t\t\tlines(shownLinks.map((l) => dim(` ${l.label}`)));\n\t\t\tdivider();\n\t\t}\n\t}\n\n\t// Action: upload, customize, or cancel\n\tconst action = await p.select({\n\t\tmessage: existingStack\n\t\t\t? \"Upload changes?\"\n\t\t\t: `Upload ${bold(String(selectedFiles.length))} files to your stack?`,\n\t\toptions: [\n\t\t\t{ value: \"upload\", label: \"Upload\" },\n\t\t\t{ value: \"customize\", label: \"Select files\" },\n\t\t\t{ value: \"cancel\", label: \"Cancel\" },\n\t\t],\n\t});\n\n\tif (p.isCancel(action) || action === \"cancel\") {\n\t\toutroCancel();\n\t\tprocess.exit(0);\n\t}\n\n\tif (action === \"customize\") {\n\t\tconst linkOptions = detectedLinks.map((l) => ({\n\t\t\tvalue: l.key,\n\t\t\tlabel: l.label,\n\t\t\thint: \"link\",\n\t\t}));\n\t\tconst selected = await p.multiselect({\n\t\t\tmessage: \"Select files to include:\",\n\t\t\toptions: [\n\t\t\t\t...linkOptions,\n\t\t\t\t...allFiles.map((f) => ({\n\t\t\t\t\tvalue: f.relativePath,\n\t\t\t\t\tlabel: f.relativePath,\n\t\t\t\t\thint: `${f.type}${f.source === \"global\" ? \" · global\" : \"\"}`,\n\t\t\t\t})),\n\t\t\t],\n\t\t\tinitialValues: [\n\t\t\t\t...detectedLinks\n\t\t\t\t\t.filter((l) => includedLinks.has(l.key))\n\t\t\t\t\t.map((l) => l.key),\n\t\t\t\t...selectedFiles.map((f) => f.relativePath),\n\t\t\t],\n\t\t});\n\n\t\tif (p.isCancel(selected)) {\n\t\t\toutroCancel();\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\tconst selectedSet = new Set(selected as string[]);\n\t\tselectedFiles = allFiles.filter((f) => selectedSet.has(f.relativePath));\n\t\texcluded = allFiles.filter((f) => !selectedSet.has(f.relativePath));\n\t\tincludedLinks.clear();\n\t\tfor (const l of detectedLinks) {\n\t\t\tif (selectedSet.has(l.key)) includedLinks.add(l.key);\n\t\t}\n\t\tallResources = withLinks(classify(selectedFiles));\n\n\t\tif (selectedFiles.length === 0 && includedLinks.size === 0) {\n\t\t\tp.log.warn(\"No files selected.\");\n\t\t\toutroSkipped(\"nothing to collect\");\n\t\t\tprocess.exit(0);\n\t\t}\n\t}\n\n\ts.start(\"Uploading...\");\n\ttry {\n\t\tconst result = await stackCollect(token, { resources: allResources });\n\t\ts.stop(lime(\"Uploaded\"));\n\t\tconst excludedKeys = excluded.map((f) => f.relativePath);\n\t\tfor (const l of detectedLinks) {\n\t\t\tif (!includedLinks.has(l.key)) excludedKeys.push(l.key);\n\t\t}\n\t\tsaveExcludedPaths(cwd, excludedKeys);\n\t\tp.log.success(dim(result.url));\n\t\toutro(lime(\"done\"));\n\t} catch (err) {\n\t\ts.stop(\"Upload failed\");\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\toutroError(\"upload failed\");\n\t\tprocess.exit(1);\n\t}\n}\n\nconst TYPE_ORDER = [\n\t\"config\",\n\t\"prompt\",\n\t\"rule\",\n\t\"command\",\n\t\"skill\",\n\t\"subagent\",\n\t\"mcp\",\n\t\"hook\",\n\t\"custom\",\n];\n\nfunction groupByType(files: ScannedFile[]): Map<string, ScannedFile[]> {\n\tconst map = new Map<string, ScannedFile[]>();\n\tfor (const f of files) {\n\t\tconst existing = map.get(f.type) ?? [];\n\t\texisting.push(f);\n\t\tmap.set(f.type, existing);\n\t}\n\tconst sorted = new Map<string, ScannedFile[]>();\n\tfor (const type of TYPE_ORDER) {\n\t\tconst group = map.get(type);\n\t\tif (group) sorted.set(type, group);\n\t}\n\tfor (const [type, group] of map) {\n\t\tif (!sorted.has(type)) sorted.set(type, group);\n\t}\n\treturn sorted;\n}\n\ninterface DiffResult {\n\tadded: number;\n\tchanged: number;\n\tremoved: number;\n\tunchanged: number;\n\tdetails: Array<{ name: string; status: \"added\" | \"changed\" | \"removed\" }>;\n}\n\nexport function diffResources(\n\tcurrent: Resource[],\n\texisting: Resource[],\n): DiffResult {\n\tconst existingMap = new Map<string, string>();\n\tfor (const item of existing) {\n\t\tfor (const file of item.files ?? []) {\n\t\t\texistingMap.set(file.path ?? file.name, file.content);\n\t\t}\n\t}\n\n\tconst currentMap = new Map<string, string>();\n\tfor (const item of current) {\n\t\tfor (const file of item.files ?? []) {\n\t\t\tcurrentMap.set(file.path ?? file.name, file.content);\n\t\t}\n\t}\n\n\tconst details: DiffResult[\"details\"] = [];\n\tlet added = 0;\n\tlet changed = 0;\n\tlet unchanged = 0;\n\n\tfor (const [key, content] of currentMap) {\n\t\tconst prev = existingMap.get(key);\n\t\tif (prev === undefined) {\n\t\t\tadded++;\n\t\t\tdetails.push({ name: key, status: \"added\" });\n\t\t} else if (prev !== content) {\n\t\t\tchanged++;\n\t\t\tdetails.push({ name: key, status: \"changed\" });\n\t\t} else {\n\t\t\tunchanged++;\n\t\t}\n\t}\n\n\tlet removed = 0;\n\tfor (const key of existingMap.keys()) {\n\t\tif (!currentMap.has(key)) {\n\t\t\tremoved++;\n\t\t\tdetails.push({ name: key, status: \"removed\" });\n\t\t}\n\t}\n\n\t// Linked resources (GitHub repos AND package refs like MCP servers) carry no\n\t// files, so the file maps above can't see them. Diff them by stableKey —\n\t// unique for both `linked:<repo>:<path>` and `linked:pkg:<registry>:<id>` —\n\t// otherwise a link-only change is invisible and collect wrongly reports\n\t// \"nothing to upload\".\n\tconst linkLabel = (item: Resource): string => {\n\t\tif (item.upstream)\n\t\t\treturn `link: ${repoNameFromCanonical(item.upstream.repoUrl)}`;\n\t\tif (item.pkg) return `link: ${item.pkg.id}`;\n\t\treturn `link: ${item.name}`;\n\t};\n\tconst linkMap = (items: Resource[]): Map<string, Resource> => {\n\t\tconst map = new Map<string, Resource>();\n\t\tfor (const item of items) {\n\t\t\tif ((item.upstream || item.pkg) && !item.files?.length) {\n\t\t\t\tmap.set(item.stableKey, item);\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t};\n\tconst existingLinks = linkMap(existing);\n\tconst currentLinks = linkMap(current);\n\tfor (const [key, item] of currentLinks) {\n\t\tif (!existingLinks.has(key)) {\n\t\t\tadded++;\n\t\t\tdetails.push({ name: linkLabel(item), status: \"added\" });\n\t\t}\n\t}\n\tfor (const [key, item] of existingLinks) {\n\t\tif (!currentLinks.has(key)) {\n\t\t\tremoved++;\n\t\t\tdetails.push({ name: linkLabel(item), status: \"removed\" });\n\t\t}\n\t}\n\n\treturn { added, changed, removed, unchanged, details };\n}\n","import { basename, dirname } from \"node:path\";\nimport type { Resource } from \"./api.js\";\nimport type { ScannedFile } from \"./scanner.js\";\nimport { computeStableKey } from \"./stableKey.js\";\n\nexport function classify(files: ScannedFile[]): Resource[] {\n\t// Group by {group, source, type, containing directory}\n\tconst groups = new Map<string, ScannedFile[]>();\n\tconst singletons: ScannedFile[] = [];\n\n\tconst singletonRoots = new Set([\n\t\t\".\",\n\t\t\"~\",\n\t\t\"~/.claude\",\n\t\t\"~/.cursor\",\n\t\t\"~/.continue\",\n\t\t\".claude\",\n\t\t\".cursor\",\n\t\t\".github\",\n\t]);\n\n\tfor (const file of files) {\n\t\tconst dir = dirname(file.relativePath);\n\t\tconst isSingleton = singletonRoots.has(dir);\n\n\t\tif (isSingleton) {\n\t\t\tsingletons.push(file);\n\t\t} else {\n\t\t\tconst key = `${file.group}:${file.source}:${file.type}:${dir}`;\n\t\t\tconst existing = groups.get(key) ?? [];\n\t\t\texisting.push(file);\n\t\t\tgroups.set(key, existing);\n\t\t}\n\t}\n\n\tconst items: Resource[] = [];\n\n\t// Singletons: one Resource per file\n\tfor (const file of singletons) {\n\t\tconst relPath = file.relativePath\n\t\t\t.replace(/^~\\/\\.[^/]+\\//, \"\")\n\t\t\t.replace(/^\\.[^/]+\\//, \"\");\n\t\titems.push({\n\t\t\ttype: file.type,\n\t\t\tname: file.relativePath,\n\t\t\tgroup: file.group,\n\t\t\tstableKey: computeStableKey(file.group, file.type, relPath),\n\t\t\tfiles: [\n\t\t\t\t{\n\t\t\t\t\tname: basename(file.relativePath),\n\t\t\t\t\tcontent: file.content,\n\t\t\t\t\tpath: file.relativePath,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n\n\t// Groups: one Resource per directory group\n\tfor (const [, groupFiles] of groups) {\n\t\tconst first = groupFiles[0];\n\t\tconst dir = dirname(first.relativePath);\n\t\tconst relPath = dir\n\t\t\t.replace(/^~\\/\\.claude\\//, \"\")\n\t\t\t.replace(/^\\.claude\\//, \"\")\n\t\t\t.replace(/^~\\/\\.cursor\\//, \"\")\n\t\t\t.replace(/^\\.cursor\\//, \"\");\n\t\tconst typeLabel =\n\t\t\tfirst.type === \"subagent\" ? \"subagents\" : `${first.type}s`;\n\n\t\titems.push({\n\t\t\ttype: first.type,\n\t\t\tname: dir,\n\t\t\tdescription: `${groupFiles.length} ${typeLabel}`,\n\t\t\tgroup: first.group,\n\t\t\tstableKey: computeStableKey(first.group, first.type, relPath),\n\t\t\tfiles: groupFiles.map((f) => ({\n\t\t\t\tname: basename(f.relativePath),\n\t\t\t\tcontent: f.content,\n\t\t\t\tpath: f.relativePath,\n\t\t\t})),\n\t\t});\n\t}\n\n\treturn items;\n}\n","export function computeStableKey(\n\tgroup: string,\n\ttype: string,\n\trelPath: string,\n): string {\n\treturn `${group}:${type}:${relPath}`;\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { BASE_URL } from \"./api.js\";\n\nconst CONFIG_DIR = join(homedir(), \".config\", \"aistack\");\nconst CREDENTIALS_FILE = join(CONFIG_DIR, \"credentials.json\");\n\ninterface ServerCredentials {\n\ttoken: string;\n\tuserId?: string;\n}\n\n/**\n * Credentials keyed by server URL (#61). Since hash-at-rest (#52) the server\n * stores only a hash, so this file holds the only plaintext copy of each\n * token. The old flat `{token, userId}` form let a localhost login overwrite\n * the prod token, which was unrecoverable. The map keeps one entry per server.\n */\ninterface CredentialsFile {\n\tservers: Record<string, ServerCredentials>;\n}\n\n/** Where a legacy flat token is assumed to come from. */\nconst DEFAULT_SERVER_URL = \"https://aistack.to\";\n\n/**\n * Read the credentials file and lift the legacy flat form into the map.\n *\n * A legacy token carries no record of which server issued it. It is assigned\n * to the default prod URL, not the caller's current server: every real flat\n * token came from prod, and keying it under a localhost caller would put it\n * exactly where the next localhost login overwrites it.\n */\nfunction readCredentials(file: string): {\n\tdata: CredentialsFile;\n\tlegacy: boolean;\n} {\n\tconst empty = { data: { servers: {} }, legacy: false };\n\tif (!existsSync(file)) return empty;\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\tif (!raw || typeof raw !== \"object\") return empty;\n\t\tif (raw.servers && typeof raw.servers === \"object\") {\n\t\t\treturn {\n\t\t\t\tdata: { servers: raw.servers as Record<string, ServerCredentials> },\n\t\t\t\tlegacy: false,\n\t\t\t};\n\t\t}\n\t\tif (typeof raw.token === \"string\" && raw.token) {\n\t\t\treturn {\n\t\t\t\tdata: {\n\t\t\t\t\tservers: {\n\t\t\t\t\t\t[DEFAULT_SERVER_URL]: { token: raw.token, userId: raw.userId },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tlegacy: true,\n\t\t\t};\n\t\t}\n\t\t// A cleared legacy file is `{}` — empty, but safe to rewrite.\n\t\treturn { data: { servers: {} }, legacy: true };\n\t} catch {\n\t\t// Do not rewrite an unreadable file. It may still hold a token that a\n\t\t// human can recover, and this file holds the only plaintext copy.\n\t\treturn empty;\n\t}\n}\n\nfunction writeCredentials(file: string, data: CredentialsFile): void {\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, JSON.stringify(data, null, 2));\n}\n\nexport function getToken(\n\tserverUrl: string = BASE_URL,\n\tfile: string = CREDENTIALS_FILE,\n): string | null {\n\tconst { data, legacy } = readCredentials(file);\n\tif (legacy) writeCredentials(file, data);\n\treturn data.servers[serverUrl]?.token ?? null;\n}\n\nexport function saveToken(\n\ttoken: string,\n\tuserId?: string,\n\tserverUrl: string = BASE_URL,\n\tfile: string = CREDENTIALS_FILE,\n): void {\n\tconst { data } = readCredentials(file);\n\tdata.servers[serverUrl] = { token, userId };\n\twriteCredentials(file, data);\n}\n\n/** Remove only the current server's entry. Other servers keep their tokens. */\nexport function clearToken(\n\tserverUrl: string = BASE_URL,\n\tfile: string = CREDENTIALS_FILE,\n): void {\n\tif (!existsSync(file)) return;\n\tconst { data } = readCredentials(file);\n\tdelete data.servers[serverUrl];\n\twriteCredentials(file, data);\n}\n\nconst SETTINGS_FILE = join(CONFIG_DIR, \"settings.json\");\n\n/**\n * Machine-local switches (#56). A separate file from credentials.json so a\n * login overwrite never resets an answered upsell, and clearing settings never\n * touches the token.\n */\nexport interface AutoSyncConfig {\n\t/** The standing opt-in. `sync --auto` publishes nothing when false. */\n\tenabled: boolean;\n\t/** Minimum hours between auto-sync attempts. Default 24. */\n\tfrequencyHours: number;\n}\n\n/** Bookkeeping the `sync --auto` runs write. Separate from the opt-in. */\nexport interface AutoSyncState {\n\t/** Epoch ms of the last attempt (success or failure). The freshness gate. */\n\tlastRunAt?: number;\n\tlastSuccessAt?: number;\n\t/** One line about the last run, shown on the next interactive sync. */\n\tlastResult?: string;\n\tconsecutiveFailures?: number;\n\t/** The 3-failure systemMessage went out. Reset on success. */\n\tfailureWarned?: boolean;\n}\n\nexport const DEFAULT_FREQUENCY_HOURS = 24;\n\nexport interface Settings {\n\t/** The post-sync connect-claude upsell was answered (either way). */\n\tconnectClaudeAnswered?: boolean;\n\t/** The post-sync auto-sync ask was answered (either way). */\n\tautoSyncAnswered?: boolean;\n\tautoSync?: AutoSyncConfig;\n\tautoSyncState?: AutoSyncState;\n}\n\nexport function getSettings(file: string = SETTINGS_FILE): Settings {\n\tif (!existsSync(file)) return {};\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\treturn raw && typeof raw === \"object\" ? (raw as Settings) : {};\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nexport function saveSettings(\n\tpatch: Partial<Settings>,\n\tfile: string = SETTINGS_FILE,\n): void {\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(\n\t\tfile,\n\t\tJSON.stringify({ ...getSettings(file), ...patch }, null, 2),\n\t);\n}\n\nconst PROJECTS_FILE = join(CONFIG_DIR, \"projects.json\");\n\ninterface ProjectEntry {\n\texcluded?: string[];\n}\n\ninterface ProjectsData {\n\t[directory: string]: ProjectEntry;\n}\n\nfunction readProjects(): ProjectsData {\n\tif (!existsSync(PROJECTS_FILE)) return {};\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(PROJECTS_FILE, \"utf-8\"));\n\t\t// Tolerate legacy entries: string values (oldest) and objects that still\n\t\t// carry a `name` field. Only `excluded` is read going forward.\n\t\tconst data: ProjectsData = {};\n\t\tfor (const [key, value] of Object.entries(raw)) {\n\t\t\tif (typeof value === \"string\") {\n\t\t\t\tdata[key] = {};\n\t\t\t} else if (value && typeof value === \"object\") {\n\t\t\t\tconst excluded = (value as { excluded?: string[] }).excluded;\n\t\t\t\tdata[key] = Array.isArray(excluded) ? { excluded } : {};\n\t\t\t}\n\t\t}\n\t\treturn data;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction writeProjects(data: ProjectsData): void {\n\tmkdirSync(CONFIG_DIR, { recursive: true });\n\twriteFileSync(PROJECTS_FILE, JSON.stringify(data, null, 2));\n}\n\nexport function getExcludedPaths(directory: string): string[] {\n\treturn readProjects()[directory]?.excluded ?? [];\n}\n\nexport function saveExcludedPaths(directory: string, excluded: string[]): void {\n\tconst data = readProjects();\n\tdata[directory] = {\n\t\texcluded: excluded.length > 0 ? excluded : undefined,\n\t};\n\twriteProjects(data);\n}\n","import { execFileSync } from \"node:child_process\";\nimport type { Resource } from \"./api.js\";\nimport {\n\tcanonicalizeRepoUrl,\n\tnormalizeUpstreamPath,\n\trepoNameFromCanonical,\n} from \"./github-repo.js\";\n\n/**\n * Returns the raw `origin` remote URL for a working directory, or null when it\n * can't be determined. Injectable so `detectRepoUrl` stays unit-testable\n * without spawning git.\n */\nexport type GitRemoteRunner = (cwd: string) => string | null;\n\nexport const defaultGitRemoteRunner: GitRemoteRunner = (cwd) => {\n\ttry {\n\t\t// argv form (no shell) — git walks up to the repo root itself, and\n\t\t// stderr is swallowed so \"not a git repository\" never leaks into the\n\t\t// CLI's output. git missing / no repo / no origin all throw → null.\n\t\treturn execFileSync(\"git\", [\"-C\", cwd, \"remote\", \"get-url\", \"origin\"], {\n\t\t\tencoding: \"utf-8\",\n\t\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t\t}).trim();\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n/**\n * Detect the canonical GitHub repo URL for `cwd`, or null. Non-GitHub origins\n * (GitLab, Bitbucket, …) canonicalize to null, so this is a graceful no-op\n * outside of GitHub repos.\n */\nexport function detectRepoUrl(\n\tcwd: string,\n\trun: GitRemoteRunner = defaultGitRemoteRunner,\n): string | null {\n\tconst raw = run(cwd);\n\tif (!raw) return null;\n\treturn canonicalizeRepoUrl(raw);\n}\n\nexport interface LinkSpec {\n\t/** Canonical GitHub URL (https://github.com/owner/repo). */\n\tcanonical: string;\n\t/** Optional subpath within the repo. */\n\tpath?: string;\n\tname: string;\n\ttype: string;\n\tgroup: string;\n\t/** Optional pinned commit, stored as upstream.lastCommitSha. */\n\tsha?: string;\n}\n\n/**\n * Build a linked-resource payload. Mirrors the web `linkResource` mutation: no\n * files (upstream presence is the storage discriminator) and the exact\n * `linked:${canonical}:${normPath}` stableKey so the web unlink UI — which\n * matches by stableKey — recognizes it. `path`/`lastCommitSha` are omitted when\n * empty so the by_upstream dedup index matches at both write and query.\n */\nexport function buildLinkResource(spec: LinkSpec): Resource {\n\tconst normPath = normalizeUpstreamPath(spec.path);\n\treturn {\n\t\ttype: spec.type,\n\t\tname: spec.name,\n\t\tgroup: spec.group,\n\t\tstableKey: `linked:${spec.canonical}:${normPath}`,\n\t\tupstream: {\n\t\t\trepoUrl: spec.canonical,\n\t\t\t...(normPath ? { path: normPath } : {}),\n\t\t\t...(spec.sha ? { lastCommitSha: spec.sha } : {}),\n\t\t},\n\t};\n}\n\n/** The repo this project lives in: a GitHub link (stack-owned server-side). */\nexport function buildRepoLinkResource(canonical: string): Resource {\n\treturn buildLinkResource({\n\t\tcanonical,\n\t\tname: repoNameFromCanonical(canonical),\n\t\ttype: \"custom\",\n\t\tgroup: \"generic\",\n\t});\n}\n","/**\n * Trimmed copy of `src/lib/github-repo.ts` — the CANONICAL parser, whose\n * `github-repo.test.ts` is the canonical test table. Copied (not imported)\n * because the CLI ships as an independent npm package and its tsconfig\n * (`rootDir: \"src\"` + `declaration: true`) forbids cross-rootDir imports.\n * Keep these functions in sync with the canonical source.\n *\n * Only the pieces the CLI needs are included: `parseRepo`,\n * `canonicalizeRepoUrl`, `repoNameFromCanonical`, and `normalizeUpstreamPath`\n * (a null from `canonicalizeRepoUrl` is the CLI's graceful-skip signal, so\n * `isGithubRepoUrl` is intentionally omitted).\n */\n\nfunction isGithubHost(host: string): boolean {\n\tconst h = host.toLowerCase();\n\treturn h === \"github.com\" || h === \"www.github.com\";\n}\n\nexport function parseRepo(\n\tinput: string,\n): { owner: string; repo: string } | null {\n\tconst trimmed = input.trim();\n\tif (!trimmed) return null;\n\n\t// SCP-like form `git@host:owner/repo`: take the host and the path after the\n\t// colon. Otherwise strip the scheme, then split the leading host off the path.\n\tconst scpMatch = trimmed.match(/^[^@]+@([^:]+):(.+)$/);\n\tlet host: string;\n\tlet withoutHost: string;\n\tif (scpMatch) {\n\t\thost = scpMatch[1];\n\t\twithoutHost = scpMatch[2];\n\t} else {\n\t\tconst withoutScheme = trimmed.replace(/^[a-z]+:\\/\\//i, \"\");\n\t\tconst slash = withoutScheme.indexOf(\"/\");\n\t\tif (slash === -1) return null;\n\t\thost = withoutScheme.slice(0, slash);\n\t\twithoutHost = withoutScheme.slice(slash + 1);\n\t}\n\tif (!isGithubHost(host)) return null;\n\n\t// Drop any query string or anchor, then split into path segments.\n\tconst pathPart = withoutHost.replace(/[?#].*$/, \"\");\n\tconst segments = pathPart.split(\"/\").filter(Boolean);\n\n\tconst owner = segments[0];\n\tconst repo = segments[1]?.replace(/\\.git$/, \"\");\n\tif (!owner || !repo) return null;\n\n\treturn { owner: owner.toLowerCase(), repo: repo.toLowerCase() };\n}\n\nexport function canonicalizeRepoUrl(input: string): string | null {\n\tconst parsed = parseRepo(input);\n\tif (!parsed) return null;\n\treturn `https://github.com/${parsed.owner}/${parsed.repo}`;\n}\n\nexport function repoNameFromCanonical(canonical: string): string {\n\treturn parseRepo(canonical)?.repo ?? \"\";\n}\n\nexport function normalizeUpstreamPath(path: string | undefined): string {\n\tif (!path) return \"\";\n\treturn path.split(\"/\").filter(Boolean).join(\"/\");\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { Resource } from \"./api.js\";\n\n/**\n * Extract hooks defined inline in Claude Code settings as discrete `hook`\n * resources — one per event (PreToolUse, PostToolUse, …). Without this they\n * only ride inside the collected settings.json config blob and never surface as\n * first-class hooks.\n *\n * These are HOSTED resources (the event's config block is the content), so they\n * participate in the normal file-based diff. They intentionally duplicate data\n * also present in the settings.json resource; the distinct `hooks:` stableKeys\n * mean no collision, and first-class visibility was the deliberate tradeoff.\n */\n\ninterface SettingsFile {\n\thooks?: Record<string, unknown>;\n}\n\nfunction readJson<T>(path: string): T | null {\n\ttry {\n\t\tif (!existsSync(path)) return null;\n\t\treturn JSON.parse(readFileSync(path, \"utf-8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction hooksFrom(\n\tpath: string,\n\tsource: \"local\" | \"global\",\n\tout: Resource[],\n\tseen: Set<string>,\n) {\n\tconst hooks = readJson<SettingsFile>(path)?.hooks;\n\tif (!hooks || typeof hooks !== \"object\") return;\n\tfor (const [event, config] of Object.entries(hooks)) {\n\t\tconst stableKey = `hooks:${source}:${event}`;\n\t\tif (seen.has(stableKey)) continue;\n\t\tseen.add(stableKey);\n\t\tout.push({\n\t\t\ttype: \"hook\",\n\t\t\tname: event,\n\t\t\tgroup: \"claude-code\",\n\t\t\tstableKey,\n\t\t\tfiles: [\n\t\t\t\t{\n\t\t\t\t\tname: `${event}.json`,\n\t\t\t\t\tcontent: JSON.stringify(config, null, 2),\n\t\t\t\t\tpath: `hooks/${event}.json`,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n}\n\n/**\n * Detect inline hooks from project settings (`.claude/settings.json` +\n * `.claude/settings.local.json`) and global settings (`~/.claude/settings.json`).\n * Project settings win over the `.local` override on the same event.\n */\nexport function detectHooks(cwd: string, home: string = homedir()): Resource[] {\n\tconst out: Resource[] = [];\n\tconst seen = new Set<string>();\n\thooksFrom(join(cwd, \".claude\", \"settings.json\"), \"local\", out, seen);\n\thooksFrom(join(cwd, \".claude\", \"settings.local.json\"), \"local\", out, seen);\n\thooksFrom(join(home, \".claude\", \"settings.json\"), \"global\", out, seen);\n\treturn out;\n}\n","import { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join } from \"node:path\";\nimport { parse as parseToml } from \"smol-toml\";\nimport { parse as parseYaml } from \"yaml\";\nimport type { Resource } from \"./api.js\";\n\n/**\n * Detect configured MCP servers and resolve each to a `pkg` reference (its\n * package identity), parsed from the launch `command`/`args` — npm/PyPI/OCI for\n * stdio servers, or a URL for remote (http/sse) servers. `env` is intentionally\n * dropped (it carries secrets), so this is a safer representation than uploading\n * the raw config file.\n */\n\nexport interface McpServerConfig {\n\tcommand?: string;\n\targs?: string[];\n\ttype?: string;\n\ttransport?: string;\n\turl?: string;\n}\n\nexport interface PkgRef {\n\tregistry: \"npm\" | \"pypi\" | \"oci\" | \"url\";\n\tid: string;\n\tversion?: string;\n\ttransport?: \"stdio\" | \"http\" | \"sse\";\n}\n\nfunction commandName(cmd: string): string {\n\treturn cmd.replace(/\\\\/g, \"/\").split(\"/\").pop() ?? cmd;\n}\n\n/** Split an npm/PyPI spec into id + version, handling scoped npm (@scope/n@v). */\nfunction splitVersion(spec: string): { id: string; version?: string } {\n\tconst at = spec.indexOf(\"@\", spec.startsWith(\"@\") ? 1 : 0);\n\tif (at <= 0) return { id: spec };\n\treturn { id: spec.slice(0, at), version: spec.slice(at + 1) || undefined };\n}\n\n/** First arg that isn't a flag (and isn't in `skip`). */\nfunction firstPositional(args: string[], skip = 0): string | undefined {\n\tfor (const a of args.slice(skip)) {\n\t\tif (!a.startsWith(\"-\")) return a;\n\t}\n\treturn undefined;\n}\n\n// docker/podman flags that consume the following token (so it isn't the image).\nconst CONTAINER_VALUE_FLAGS = new Set([\n\t\"-e\",\n\t\"--env\",\n\t\"-v\",\n\t\"--volume\",\n\t\"-p\",\n\t\"--publish\",\n\t\"-w\",\n\t\"--workdir\",\n\t\"--name\",\n\t\"--mount\",\n\t\"--network\",\n\t\"-u\",\n\t\"--user\",\n\t\"-l\",\n\t\"--label\",\n]);\n\nfunction containerImage(args: string[]): string | undefined {\n\tconst runIdx = args.indexOf(\"run\");\n\tconst rest = runIdx >= 0 ? args.slice(runIdx + 1) : args;\n\tfor (let i = 0; i < rest.length; i++) {\n\t\tconst a = rest[i];\n\t\tif (a.startsWith(\"-\")) {\n\t\t\tif (CONTAINER_VALUE_FLAGS.has(a) && !a.includes(\"=\")) i++;\n\t\t\tcontinue;\n\t\t}\n\t\treturn a; // first positional after `run` is the image\n\t}\n\treturn undefined;\n}\n\n/** Split a container image ref into id + tag (ignoring a registry host:port). */\nfunction splitImageTag(image: string): { id: string; version?: string } {\n\tconst colon = image.lastIndexOf(\":\");\n\tif (colon > 0 && !image.slice(colon + 1).includes(\"/\")) {\n\t\treturn { id: image.slice(0, colon), version: image.slice(colon + 1) };\n\t}\n\treturn { id: image };\n}\n\n/** Parse a single MCP server config into a package reference, or null. */\nexport function parseMcpPackage(server: McpServerConfig): PkgRef | null {\n\t// Remote server: a URL endpoint (http/sse).\n\tif (server.url) {\n\t\tconst t = (server.type ?? server.transport ?? \"\").toLowerCase();\n\t\treturn {\n\t\t\tregistry: \"url\",\n\t\t\tid: server.url,\n\t\t\ttransport: t === \"sse\" ? \"sse\" : \"http\",\n\t\t};\n\t}\n\n\tconst command = server.command ? commandName(server.command) : \"\";\n\tif (!command) return null;\n\tconst args = server.args ?? [];\n\n\t// npm-family runners.\n\tif (command === \"npx\" || command === \"bunx\" || command === \"pnpx\") {\n\t\tconst spec = firstPositional(args);\n\t\treturn spec\n\t\t\t? { registry: \"npm\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif ((command === \"pnpm\" || command === \"yarn\") && args[0] === \"dlx\") {\n\t\tconst spec = firstPositional(args, 1);\n\t\treturn spec\n\t\t\t? { registry: \"npm\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\n\t// Python-family runners.\n\tif (command === \"uvx\") {\n\t\tconst spec = firstPositional(args);\n\t\treturn spec\n\t\t\t? { registry: \"pypi\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif (command === \"pipx\" && args[0] === \"run\") {\n\t\tconst spec = firstPositional(args, 1);\n\t\treturn spec\n\t\t\t? { registry: \"pypi\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif (command === \"uv\" && args[0] === \"tool\" && args[1] === \"run\") {\n\t\tconst spec = firstPositional(args, 2);\n\t\treturn spec\n\t\t\t? { registry: \"pypi\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif (/^python[0-9.]*$/.test(command)) {\n\t\tconst i = args.indexOf(\"-m\");\n\t\tconst mod = i >= 0 ? args[i + 1] : undefined;\n\t\treturn mod ? { registry: \"pypi\", id: mod, transport: \"stdio\" } : null;\n\t}\n\n\t// Container runners.\n\tif (command === \"docker\" || command === \"podman\") {\n\t\tconst image = containerImage(args);\n\t\tif (!image) return null;\n\t\treturn { registry: \"oci\", ...splitImageTag(image), transport: \"stdio\" };\n\t}\n\n\t// node/deno/bun running a local script, or an unknown command → skip.\n\treturn null;\n}\n\n/** Build a `type:\"mcp\"` linked resource from a parsed package reference. */\nexport function buildMcpResource(\n\tname: string,\n\tgroup: string,\n\tpkg: PkgRef,\n): Resource {\n\treturn {\n\t\ttype: \"mcp\",\n\t\tname,\n\t\tgroup,\n\t\tstableKey: `linked:pkg:${pkg.registry}:${pkg.id}`,\n\t\tpkg,\n\t};\n}\n\nfunction readText(path: string): string | null {\n\ttry {\n\t\tif (!existsSync(path)) return null;\n\t\treturn readFileSync(path, \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction readParsed<T>(\n\tpath: string,\n\tparse: (raw: string) => unknown,\n): T | null {\n\tconst raw = readText(path);\n\tif (raw === null) return null;\n\ttry {\n\t\treturn parse(raw) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction readJson<T>(path: string): T | null {\n\treturn readParsed<T>(path, JSON.parse);\n}\nfunction readYaml<T>(path: string): T | null {\n\treturn readParsed<T>(path, parseYaml);\n}\nfunction readToml<T>(path: string): T | null {\n\treturn readParsed<T>(path, parseToml);\n}\n\ntype ServerMap = Record<string, McpServerConfig> | undefined;\ninterface McpFile {\n\tmcpServers?: ServerMap;\n\tservers?: ServerMap; // VS Code uses `servers`\n}\ninterface ClaudeJson {\n\tmcpServers?: ServerMap;\n\tprojects?: Record<string, { mcpServers?: ServerMap }>;\n}\n// Continue uses a LIST under mcpServers; Codex (TOML) uses `mcp_servers`.\ninterface ContinueYaml {\n\tmcpServers?: Array<{ name?: string } & McpServerConfig>;\n}\ninterface CodexToml {\n\tmcp_servers?: Record<string, McpServerConfig>;\n}\n\n/** Normalize Continue's list form to the common name→config map. */\nfunction continueListToMap(file: ContinueYaml | null): ServerMap {\n\tif (!file?.mcpServers?.length) return undefined;\n\tconst map: Record<string, McpServerConfig> = {};\n\tfile.mcpServers.forEach((s, i) => {\n\t\tmap[s.name ?? `server-${i}`] = s;\n\t});\n\treturn map;\n}\n\n/** Per-OS VS Code globalStorage bases (+ Code-OSS/VSCodium variants). */\nfunction vscodeGlobalStorageBases(home: string): string[] {\n\tconst apps = [\"Code\", \"Code - OSS\", \"VSCodium\"];\n\tlet root: string;\n\tif (platform() === \"darwin\") {\n\t\troot = join(home, \"Library\", \"Application Support\");\n\t} else if (platform() === \"win32\") {\n\t\troot = process.env.APPDATA ?? join(home, \"AppData\", \"Roaming\");\n\t} else {\n\t\troot = process.env.XDG_CONFIG_HOME ?? join(home, \".config\");\n\t}\n\treturn apps.map((app) => join(root, app, \"User\", \"globalStorage\"));\n}\n\n/**\n * Read MCP server configs across the known tool locations and return one\n * `type:\"mcp\"` pkg-link resource per server, deduped by package identity.\n * Project-level configs win over global ones on the same identity.\n */\nexport function detectMcpServers(\n\tcwd: string,\n\thome: string = homedir(),\n): Resource[] {\n\tconst out: Resource[] = [];\n\tconst seen = new Set<string>();\n\tconst add = (servers: ServerMap, group: string) => {\n\t\tfor (const [name, cfg] of Object.entries(servers ?? {})) {\n\t\t\tconst pkg = parseMcpPackage(cfg);\n\t\t\tif (!pkg) continue;\n\t\t\tconst resource = buildMcpResource(name, group, pkg);\n\t\t\tif (seen.has(resource.stableKey)) continue;\n\t\t\tseen.add(resource.stableKey);\n\t\t\tout.push(resource);\n\t\t}\n\t};\n\n\t// Project configs first (so they win dedup over global).\n\tadd(readJson<McpFile>(join(cwd, \".mcp.json\"))?.mcpServers, \"claude-code\");\n\tadd(readJson<McpFile>(join(cwd, \"mcp.json\"))?.mcpServers, \"generic\");\n\tadd(\n\t\treadJson<McpFile>(join(cwd, \".cursor\", \"mcp.json\"))?.mcpServers,\n\t\t\"cursor\",\n\t);\n\tadd(readJson<McpFile>(join(cwd, \".vscode\", \"mcp.json\"))?.servers, \"generic\");\n\tadd(\n\t\treadJson<McpFile>(join(cwd, \"claude_desktop_config.json\"))?.mcpServers,\n\t\t\"claude-desktop\",\n\t);\n\n\t// Continue (project): a list per YAML file under .continue/mcpServers/.\n\tfor (const file of listYamlFiles(join(cwd, \".continue\", \"mcpServers\"))) {\n\t\tadd(continueListToMap(readYaml<ContinueYaml>(file)), \"continue\");\n\t}\n\t// Roo (project).\n\tadd(readJson<McpFile>(join(cwd, \".roo\", \"mcp.json\"))?.mcpServers, \"roo\");\n\n\t// --- Global / stack-scoped: user-level tool configs ---\n\tconst claudeJson = readJson<ClaudeJson>(join(home, \".claude.json\"));\n\tadd(claudeJson?.projects?.[cwd]?.mcpServers, \"claude-code\");\n\tadd(claudeJson?.mcpServers, \"claude-code\");\n\tadd(\n\t\treadJson<McpFile>(join(home, \".cursor\", \"mcp.json\"))?.mcpServers,\n\t\t\"cursor\",\n\t);\n\t// Windsurf (global only).\n\tadd(\n\t\treadJson<McpFile>(join(home, \".codeium\", \"windsurf\", \"mcp_config.json\"))\n\t\t\t?.mcpServers,\n\t\t\"windsurf\",\n\t);\n\t// Cline + Roo: VS Code extension globalStorage (OS-specific base).\n\tfor (const base of vscodeGlobalStorageBases(home)) {\n\t\tadd(\n\t\t\treadJson<McpFile>(\n\t\t\t\tjoin(\n\t\t\t\t\tbase,\n\t\t\t\t\t\"saoudrizwan.claude-dev\",\n\t\t\t\t\t\"settings\",\n\t\t\t\t\t\"cline_mcp_settings.json\",\n\t\t\t\t),\n\t\t\t)?.mcpServers,\n\t\t\t\"cline\",\n\t\t);\n\t\tadd(\n\t\t\treadJson<McpFile>(\n\t\t\t\tjoin(\n\t\t\t\t\tbase,\n\t\t\t\t\t\"rooveterinaryinc.roo-cline\",\n\t\t\t\t\t\"settings\",\n\t\t\t\t\t\"mcp_settings.json\",\n\t\t\t\t),\n\t\t\t)?.mcpServers,\n\t\t\t\"roo\",\n\t\t);\n\t}\n\t// Continue (global).\n\tfor (const file of listYamlFiles(join(home, \".continue\", \"mcpServers\"))) {\n\t\tadd(continueListToMap(readYaml<ContinueYaml>(file)), \"continue\");\n\t}\n\t// Gemini CLI (global).\n\tadd(\n\t\treadJson<McpFile>(join(home, \".gemini\", \"settings.json\"))?.mcpServers,\n\t\t\"gemini\",\n\t);\n\t// Codex CLI (global, TOML).\n\tadd(\n\t\treadToml<CodexToml>(join(home, \".codex\", \"config.toml\"))?.mcp_servers,\n\t\t\"codex\",\n\t);\n\n\treturn out;\n}\n\n/** List `*.yaml`/`*.yml` files in a directory (empty if absent). */\nfunction listYamlFiles(dir: string): string[] {\n\ttry {\n\t\treturn readdirSync(dir)\n\t\t\t.filter((f) => f.endsWith(\".yaml\") || f.endsWith(\".yml\"))\n\t\t\t.map((f) => join(dir, f));\n\t} catch {\n\t\treturn [];\n\t}\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { Resource } from \"./api.js\";\nimport { buildLinkResource } from \"./git.js\";\nimport { canonicalizeRepoUrl } from \"./github-repo.js\";\n\n/**\n * Detect installed Claude Code plugins from the on-disk registry and resolve\n * each to a GitHub link pointing at its TRUE upstream source (not the\n * aggregator marketplace), attached at stack scope (the user's toolchain).\n *\n * Registry shape (~/.claude/plugins/):\n * installed_plugins.json → { plugins: { \"<name>@<marketplace>\": [{ gitCommitSha, version, ... }] } }\n * known_marketplaces.json → { \"<marketplace>\": { source: { repo }, installLocation } }\n * <installLocation>/.claude-plugin/marketplace.json → { plugins: [{ name, source, ... }] }\n */\n\ninterface InstalledEntry {\n\tscope?: string;\n\tversion?: string;\n\tgitCommitSha?: string;\n}\n\nexport interface InstalledPlugins {\n\tplugins?: Record<string, InstalledEntry[]>;\n}\n\nexport interface KnownMarketplace {\n\tsource?: { source?: string; repo?: string; url?: string };\n\tinstallLocation?: string;\n}\n\nexport type KnownMarketplaces = Record<string, KnownMarketplace>;\n\n/** A plugin's source in marketplace.json — polymorphic. */\ntype PluginSource =\n\t| string\n\t| {\n\t\t\tsource?: string;\n\t\t\turl?: string;\n\t\t\tpath?: string;\n\t\t\tref?: string;\n\t\t\tsha?: string;\n\t };\n\ninterface ManifestPlugin {\n\tname: string;\n\tsource?: PluginSource;\n\trepository?: string;\n\thomepage?: string;\n}\n\nexport interface Manifest {\n\tplugins?: ManifestPlugin[];\n}\n\nfunction marketplaceRepoUrl(mp: KnownMarketplace | undefined): string | null {\n\tconst src = mp?.source;\n\tif (!src) return null;\n\tif (src.repo) return `https://github.com/${src.repo}`;\n\treturn src.url ?? null;\n}\n\n/** Resolve a plugin entry's source to a repo URL (+ optional subpath / sha). */\nfunction resolveSource(\n\tentry: ManifestPlugin,\n\tmpRepoUrl: string | null,\n): { url: string; path?: string; sha?: string } | null {\n\tconst src = entry.source;\n\tif (typeof src === \"string\") {\n\t\tif (!mpRepoUrl) return null;\n\t\tconst path = src.replace(/^\\.\\//, \"\").replace(/\\/+$/, \"\");\n\t\treturn { url: mpRepoUrl, path: path || undefined };\n\t}\n\tif (src && typeof src === \"object\" && src.url) {\n\t\treturn { url: src.url, path: src.path, sha: src.sha };\n\t}\n\tconst fallback = entry.repository ?? entry.homepage ?? mpRepoUrl;\n\treturn fallback ? { url: fallback } : null;\n}\n\n/** Pure: map the parsed registry + manifests to plugin link resources. */\nexport function resolvePluginLinks(\n\tinstalled: InstalledPlugins,\n\tmarketplaces: KnownMarketplaces,\n\tmanifests: Record<string, Manifest>,\n): Resource[] {\n\tconst out: Resource[] = [];\n\tfor (const [key, entries] of Object.entries(installed.plugins ?? {})) {\n\t\tconst at = key.lastIndexOf(\"@\");\n\t\tif (at <= 0) continue;\n\t\tconst pluginName = key.slice(0, at);\n\t\tconst marketplace = key.slice(at + 1);\n\n\t\tconst mpRepoUrl = marketplaceRepoUrl(marketplaces[marketplace]);\n\t\tconst entry = manifests[marketplace]?.plugins?.find(\n\t\t\t(p) => p.name === pluginName,\n\t\t);\n\t\tif (!entry) continue;\n\n\t\tconst resolved = resolveSource(entry, mpRepoUrl);\n\t\tif (!resolved) continue;\n\n\t\tconst canonical = canonicalizeRepoUrl(resolved.url);\n\t\tif (!canonical) continue; // non-GitHub source → skip (graceful)\n\n\t\tout.push(\n\t\t\tbuildLinkResource({\n\t\t\t\tcanonical,\n\t\t\t\tpath: resolved.path,\n\t\t\t\tname: pluginName,\n\t\t\t\ttype: \"plugin\",\n\t\t\t\tgroup: \"claude-code\",\n\t\t\t\tsha: resolved.sha ?? entries[0]?.gitCommitSha,\n\t\t\t}),\n\t\t);\n\t}\n\treturn out;\n}\n\nfunction readJson<T>(path: string): T | null {\n\ttry {\n\t\tif (!existsSync(path)) return null;\n\t\treturn JSON.parse(readFileSync(path, \"utf-8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** IO wrapper: read the registry + manifests, return plugin link resources. */\nexport function detectInstalledPlugins(\n\tpluginsDir: string = join(homedir(), \".claude\", \"plugins\"),\n): Resource[] {\n\tconst installed = readJson<InstalledPlugins>(\n\t\tjoin(pluginsDir, \"installed_plugins.json\"),\n\t);\n\tif (!installed?.plugins) return [];\n\n\tconst marketplaces =\n\t\treadJson<KnownMarketplaces>(join(pluginsDir, \"known_marketplaces.json\")) ??\n\t\t{};\n\n\tconst manifests: Record<string, Manifest> = {};\n\tfor (const key of Object.keys(installed.plugins)) {\n\t\tconst mp = key.slice(key.lastIndexOf(\"@\") + 1);\n\t\tif (!mp || manifests[mp]) continue;\n\t\tconst installLocation =\n\t\t\tmarketplaces[mp]?.installLocation ?? join(pluginsDir, \"marketplaces\", mp);\n\t\tconst manifest = readJson<Manifest>(\n\t\t\tjoin(installLocation, \".claude-plugin\", \"marketplace.json\"),\n\t\t);\n\t\tif (manifest) manifests[mp] = manifest;\n\t}\n\n\treturn resolvePluginLinks(installed, marketplaces, manifests);\n}\n","import { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, relative } from \"node:path\";\nimport ignore from \"ignore\";\n\nexport type FileType =\n\t| \"rule\"\n\t| \"mcp\"\n\t| \"skill\"\n\t| \"command\"\n\t| \"prompt\"\n\t| \"hook\"\n\t| \"subagent\"\n\t| \"config\"\n\t| \"custom\";\n\nexport interface ScannedFile {\n\tpath: string;\n\trelativePath: string;\n\tcontent: string;\n\ttype: FileType;\n\tsource: \"local\" | \"global\";\n\tgroup: string;\n}\n\nconst MAX_FILE_SIZE = 100 * 1024; // 100KB\n\ninterface FilePattern {\n\tpath: string;\n\ttype: FileType;\n\tgroup: string;\n}\n\nconst LOCAL_PATTERNS: FilePattern[] = [\n\t// Rules\n\t{ path: \"CLAUDE.md\", type: \"rule\", group: \"claude-code\" },\n\t{ path: \"AGENTS.md\", type: \"rule\", group: \"claude-code\" },\n\t{ path: \"GEMINI.md\", type: \"rule\", group: \"gemini\" },\n\t{ path: \".cursorrules\", type: \"rule\", group: \"cursor\" },\n\t{ path: \".windsurfrules\", type: \"rule\", group: \"windsurf\" },\n\t{ path: \".clinerules\", type: \"rule\", group: \"cline\" },\n\t{ path: \".roorules\", type: \"rule\", group: \"roo\" },\n\t{ path: \".github/copilot-instructions.md\", type: \"rule\", group: \"copilot\" },\n\t// MCP servers are detected separately as pkg-reference links (see mcp.ts) —\n\t// their config files are intentionally NOT collected as content here (which\n\t// would also upload `env` secrets).\n\t// Config\n\t{ path: \".aider.conf.yml\", type: \"config\", group: \"aider\" },\n\t{ path: \".continue/config.json\", type: \"config\", group: \"continue\" },\n\t{ path: \".continue/config.yaml\", type: \"config\", group: \"continue\" },\n\t{ path: \".claude/settings.json\", type: \"config\", group: \"claude-code\" },\n\t{\n\t\tpath: \".claude/settings.local.json\",\n\t\ttype: \"config\",\n\t\tgroup: \"claude-code\",\n\t},\n\t// Prompts\n\t{ path: \"system-prompt.md\", type: \"prompt\", group: \"generic\" },\n];\n\nconst LOCAL_DIR_PATTERNS: { dir: string; type: FileType; group: string }[] = [\n\t{ dir: \".cursor/rules\", type: \"rule\", group: \"cursor\" },\n\t{ dir: \".clinerules\", type: \"rule\", group: \"cline\" },\n\t{ dir: \".windsurf/rules\", type: \"rule\", group: \"windsurf\" },\n\t{ dir: \".roo/rules\", type: \"rule\", group: \"roo\" },\n\t{ dir: \".github/instructions\", type: \"rule\", group: \"copilot\" },\n\t{ dir: \".github/prompts\", type: \"prompt\", group: \"copilot\" },\n\t{ dir: \".claude/commands\", type: \"command\", group: \"claude-code\" },\n\t{ dir: \".claude/agents\", type: \"subagent\", group: \"claude-code\" },\n\t{ dir: \".claude/hooks\", type: \"hook\", group: \"claude-code\" },\n\t{ dir: \"prompts\", type: \"prompt\", group: \"generic\" },\n\t{ dir: \".ai\", type: \"custom\", group: \"generic\" },\n];\n\nfunction loadGitignore(cwd: string): ReturnType<typeof ignore> {\n\tconst ig = ignore();\n\tconst gitignorePath = join(cwd, \".gitignore\");\n\tif (existsSync(gitignorePath)) {\n\t\tig.add(readFileSync(gitignorePath, \"utf-8\"));\n\t}\n\tig.add([\"node_modules\", \".git\", \"dist\", \"build\", \".next\", \".output\"]);\n\treturn ig;\n}\n\nfunction readFileSafe(filePath: string): string | null {\n\ttry {\n\t\tconst stat = statSync(filePath);\n\t\tif (stat.size > MAX_FILE_SIZE) return null;\n\t\treturn readFileSync(filePath, \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction walkDir(dir: string, maxDepth = 3, currentDepth = 0): string[] {\n\tif (currentDepth >= maxDepth || !existsSync(dir)) return [];\n\tconst results: string[] = [];\n\ttry {\n\t\tfor (const entry of readdirSync(dir, { withFileTypes: true })) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\t\t\tif (entry.isFile()) {\n\t\t\t\tresults.push(fullPath);\n\t\t\t} else if (entry.isDirectory()) {\n\t\t\t\tresults.push(...walkDir(fullPath, maxDepth, currentDepth + 1));\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* permission errors, etc */\n\t}\n\treturn results;\n}\n\nexport function scanLocal(cwd: string): ScannedFile[] {\n\tconst ig = loadGitignore(cwd);\n\tconst results: ScannedFile[] = [];\n\n\tfor (const pattern of LOCAL_PATTERNS) {\n\t\tconst filePath = join(cwd, pattern.path);\n\t\tconst content = readFileSafe(filePath);\n\t\tif (content !== null) {\n\t\t\tconst rel = relative(cwd, filePath);\n\t\t\tif (!ig.ignores(rel)) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: rel,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype: pattern.type,\n\t\t\t\t\tsource: \"local\",\n\t\t\t\t\tgroup: pattern.group,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const { dir, type, group } of LOCAL_DIR_PATTERNS) {\n\t\tconst dirPath = join(cwd, dir);\n\t\tconst files = walkDir(dirPath);\n\t\tfor (const filePath of files) {\n\t\t\tconst rel = relative(cwd, filePath);\n\t\t\tif (ig.ignores(rel)) continue;\n\t\t\tconst content = readFileSafe(filePath);\n\t\t\tif (content !== null) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: rel,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype,\n\t\t\t\t\tsource: \"local\",\n\t\t\t\t\tgroup,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// Scan for skill directories (dirs with SKILL.md, 3 levels deep)\n\ttry {\n\t\tfor (const entry of readdirSync(cwd, { withFileTypes: true }).filter((e) =>\n\t\t\te.isDirectory(),\n\t\t)) {\n\t\t\tif (ig.ignores(entry.name + \"/\")) continue;\n\t\t\tscanSkillDirs(join(cwd, entry.name), cwd, ig, results, 1);\n\t\t}\n\t} catch {\n\t\t/* permission errors */\n\t}\n\n\treturn results;\n}\n\nfunction scanSkillDirs(\n\tdir: string,\n\tcwd: string,\n\tig: ReturnType<typeof ignore>,\n\tresults: ScannedFile[],\n\tdepth: number,\n) {\n\tif (depth > 3) return;\n\tconst skillMd = join(dir, \"SKILL.md\");\n\tif (existsSync(skillMd)) {\n\t\tconst files = walkDir(dir, 1);\n\t\tfor (const filePath of files) {\n\t\t\tconst rel = relative(cwd, filePath);\n\t\t\tif (ig.ignores(rel)) continue;\n\t\t\tconst content = readFileSafe(filePath);\n\t\t\tif (content !== null) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: rel,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype: \"skill\",\n\t\t\t\t\tsource: \"local\",\n\t\t\t\t\tgroup: \"generic\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\ttry {\n\t\tfor (const entry of readdirSync(dir, { withFileTypes: true })) {\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tconst rel = relative(cwd, join(dir, entry.name));\n\t\t\t\tif (!ig.ignores(rel + \"/\")) {\n\t\t\t\t\tscanSkillDirs(join(dir, entry.name), cwd, ig, results, depth + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* permission errors */\n\t}\n}\n\nexport function scanGlobal(): ScannedFile[] {\n\tconst home = homedir();\n\tconst results: ScannedFile[] = [];\n\n\tconst globalPatterns: FilePattern[] = [\n\t\t{ path: \".claude/CLAUDE.md\", type: \"rule\", group: \"claude-code\" },\n\t\t{ path: \".claude/settings.json\", type: \"config\", group: \"claude-code\" },\n\t\t{ path: \".continue/config.json\", type: \"config\", group: \"continue\" },\n\t\t{ path: \".continue/config.yaml\", type: \"config\", group: \"continue\" },\n\t\t{ path: \".aider.conf.yml\", type: \"config\", group: \"aider\" },\n\t\t{ path: \".gemini/GEMINI.md\", type: \"rule\", group: \"gemini\" },\n\t\t{ path: \".gemini/settings.json\", type: \"config\", group: \"gemini\" },\n\t\t{ path: \".codex/config.toml\", type: \"config\", group: \"codex\" },\n\t\t{\n\t\t\tpath: \".codeium/windsurf/memories/global_rules.md\",\n\t\t\ttype: \"rule\",\n\t\t\tgroup: \"windsurf\",\n\t\t},\n\t];\n\n\tfor (const pattern of globalPatterns) {\n\t\tconst filePath = join(home, pattern.path);\n\t\tconst content = readFileSafe(filePath);\n\t\tif (content !== null) {\n\t\t\tresults.push({\n\t\t\t\tpath: filePath,\n\t\t\t\trelativePath: `~/${pattern.path}`,\n\t\t\t\tcontent,\n\t\t\t\ttype: pattern.type,\n\t\t\t\tsource: \"global\",\n\t\t\t\tgroup: pattern.group,\n\t\t\t});\n\t\t}\n\t}\n\n\tconst globalDirs: { dir: string; type: FileType; group: string }[] = [\n\t\t{ dir: \".claude/commands\", type: \"command\", group: \"claude-code\" },\n\t\t{ dir: \".claude/agents\", type: \"subagent\", group: \"claude-code\" },\n\t\t{ dir: \".claude/hooks\", type: \"hook\", group: \"claude-code\" },\n\t\t{ dir: \".cursor/rules\", type: \"rule\", group: \"cursor\" },\n\t];\n\n\tfor (const { dir, type, group } of globalDirs) {\n\t\tconst dirPath = join(home, dir);\n\t\tconst files = walkDir(dirPath, 2);\n\t\tfor (const filePath of files) {\n\t\t\tconst content = readFileSafe(filePath);\n\t\t\tif (content !== null) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: `~/${relative(home, filePath)}`,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype,\n\t\t\t\t\tsource: \"global\",\n\t\t\t\t\tgroup,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// Global skills: ~/.claude/skills/<name>/SKILL.md (+ supporting files). Not\n\t// covered by globalDirs since each skill is its own dir keyed by SKILL.md.\n\tconst skillsRoot = join(home, \".claude\", \"skills\");\n\ttry {\n\t\tfor (const entry of readdirSync(skillsRoot, { withFileTypes: true })) {\n\t\t\tif (!entry.isDirectory()) continue;\n\t\t\tconst skillDir = join(skillsRoot, entry.name);\n\t\t\tif (!existsSync(join(skillDir, \"SKILL.md\"))) continue;\n\t\t\tfor (const filePath of walkDir(skillDir, 2)) {\n\t\t\t\tconst content = readFileSafe(filePath);\n\t\t\t\tif (content !== null) {\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\tpath: filePath,\n\t\t\t\t\t\trelativePath: `~/${relative(home, filePath)}`,\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t\ttype: \"skill\",\n\t\t\t\t\t\tsource: \"global\",\n\t\t\t\t\t\tgroup: \"claude-code\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* skills dir absent / permission errors */\n\t}\n\n\treturn results;\n}\n","import * as p from \"@clack/prompts\";\n\nconst esc = (code: string) => `\\x1b[${code}m`;\nconst reset = esc(\"0\");\n\nconst LIME = \"163;230;53\";\nconst BLACK = \"0;0;0\";\nconst YELLOW = \"250;204;21\";\nconst RED = \"248;113;113\";\nconst MUTED = \"120;120;120\";\n\nexport const lime = (s: string) => `${esc(`38;2;${LIME}`)}${s}${reset}`;\nexport const limeBold = (s: string) =>\n\t`${esc(\"1\")}${esc(`38;2;${LIME}`)}${s}${reset}`;\nexport const bgLime = (s: string) =>\n\t`${esc(`48;2;${LIME}`)}${esc(`38;2;${BLACK}`)}${s}${reset}`;\nexport const yellow = (s: string) => `${esc(`38;2;${YELLOW}`)}${s}${reset}`;\nexport const red = (s: string) => `${esc(`38;2;${RED}`)}${s}${reset}`;\nexport const dim = (s: string) => `${esc(`38;2;${MUTED}`)}${s}${reset}`;\nexport const bold = (s: string) => `${esc(\"1\")}${s}${reset}`;\n\n// ■ logo square in lime + AISTACK in bold on lime bg\nexport const banner = (cmd: string) =>\n\t`${lime(\"■\")} ${bgLime(` AISTACK `)} ${bold(cmd.toUpperCase())}`;\n\n// Compact line with clack-style bar\nconst BAR = `${esc(`38;2;${MUTED}`)}│${reset}`;\n\nexport function lines(items: string[]) {\n\tfor (const item of items) {\n\t\tconsole.log(`${BAR} ${item}`);\n\t}\n}\n\nexport function section(label: string, count?: number) {\n\tconsole.log(`${BAR}`);\n\tconst countStr = count !== undefined ? ` ${dim(String(count))}` : \"\";\n\tconsole.log(`${BAR} ${bold(label.toUpperCase())}${countStr}`);\n}\n\nexport function divider() {\n\tconsole.log(`${BAR} ${dim(\"─\".repeat(40))}`);\n}\n\nexport function intro(cmd: string) {\n\tconsole.log();\n\tp.intro(banner(cmd));\n}\n\nexport function outro(msg: string) {\n\tp.outro(msg);\n\tconsole.log();\n}\n\nexport function outroError(msg: string) {\n\tp.outro(red(msg));\n\tconsole.log();\n}\n\nexport function outroCancel(msg = \"cancelled\") {\n\tp.cancel(dim(msg));\n\tconsole.log();\n}\n\nexport function outroSkipped(msg: string) {\n\tp.outro(dim(msg));\n\tconsole.log();\n}\n","// `aistack connect claude` — the opt-in in-session sync surface (#56, #57).\n//\n// Installs BOTH halves or NEITHER: the user-scoped MCP server registration and\n// the Skill copy travel together, because the Skill drives `sync_preview` /\n// `sync_publish` and has nothing to do without the server (#56 decision 3).\n// The harness argument leaves room for `connect codex` later without a rename.\n\nimport { spawnSync } from \"node:child_process\";\nimport { cpSync, existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as p from \"@clack/prompts\";\nimport { getSettings, saveSettings } from \"../config.js\";\nimport {\n\tdim,\n\tintro,\n\tlimeBold,\n\toutro,\n\toutroError,\n\toutroSkipped,\n} from \"../theme.js\";\n\n/** The documented manual install line, printed when we cannot run it. */\nexport const MANUAL_MCP_ADD =\n\t\"claude mcp add --scope user aistack -- npx -y @use-aistack/cli mcp\";\n\nconst MCP_ADD_ARGS = [\n\t\"mcp\",\n\t\"add\",\n\t\"--scope\",\n\t\"user\",\n\t\"aistack\",\n\t\"--\",\n\t\"npx\",\n\t\"-y\",\n\t\"@use-aistack/cli\",\n\t\"mcp\",\n];\n\nconst MCP_REMOVE_ARGS = [\"mcp\", \"remove\", \"--scope\", \"user\", \"aistack\"];\n\nexport const SKILL_DEST = join(homedir(), \".claude\", \"skills\", \"aistack-sync\");\n\nexport interface RunResult {\n\t/** The binary was not found on PATH. */\n\tnotFound: boolean;\n\tstatus: number | null;\n\toutput: string;\n}\n\nexport type Runner = (args: string[]) => RunResult;\n\nfunction runClaude(args: string[]): RunResult {\n\tconst r = spawnSync(\"claude\", args, { encoding: \"utf-8\" });\n\tconst notFound =\n\t\tr.error !== undefined &&\n\t\t(r.error as NodeJS.ErrnoException).code === \"ENOENT\";\n\treturn {\n\t\tnotFound,\n\t\tstatus: r.status,\n\t\toutput: `${r.stdout ?? \"\"}${r.stderr ?? \"\"}`,\n\t};\n}\n\n/** Is the `claude` binary reachable? Cheap check used to skip the upsell. */\nexport function claudeOnPath(run: Runner = runClaude): boolean {\n\treturn !run([\"--version\"]).notFound;\n}\n\n/**\n * The bundled Skill directory, resolved relative to this module. In the\n * published package that is `<pkg>/skills/aistack-sync` next to `dist/`; in\n * dev it is two levels up from `src/commands/`. Walking up covers both.\n */\nexport function findSkillSource(\n\tfromDir: string = dirname(fileURLToPath(import.meta.url)),\n): string | null {\n\tlet dir = fromDir;\n\tfor (let i = 0; i < 4; i++) {\n\t\tconst candidate = join(dir, \"skills\", \"aistack-sync\");\n\t\tif (existsSync(join(candidate, \"SKILL.md\"))) return candidate;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) break;\n\t\tdir = parent;\n\t}\n\treturn null;\n}\n\nexport interface ConnectOutcome {\n\tok: boolean;\n\tmessage: string;\n}\n\n/**\n * Install the server registration, then the Skill. If the Skill copy fails\n * after a fresh registration, the registration is rolled back — both halves\n * or neither.\n */\nexport function installClaudeConnect(\n\trun: Runner = runClaude,\n\tcopySkill: (src: string, dest: string) => void = (src, dest) =>\n\t\tcpSync(src, dest, { recursive: true }),\n): ConnectOutcome {\n\tconst source = findSkillSource();\n\tif (source === null) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage:\n\t\t\t\t\"this install is missing its bundled Skill (skills/aistack-sync) — nothing was installed\",\n\t\t};\n\t}\n\n\tconst add = run(MCP_ADD_ARGS);\n\tif (add.notFound) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `claude was not found on PATH — nothing was installed. Manual install:\\n${MANUAL_MCP_ADD}`,\n\t\t};\n\t}\n\tconst alreadyRegistered =\n\t\tadd.status !== 0 && add.output.includes(\"already exists\");\n\tif (add.status !== 0 && !alreadyRegistered) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `claude mcp add failed — nothing was installed.\\n${add.output.trim()}`,\n\t\t};\n\t}\n\n\ttry {\n\t\tcopySkill(source, SKILL_DEST);\n\t} catch (e) {\n\t\t// Both halves or neither: a fresh registration without its Skill is\n\t\t// rolled back. A pre-existing registration is left as it was.\n\t\tif (!alreadyRegistered) run(MCP_REMOVE_ARGS);\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `copying the Skill to ${SKILL_DEST} failed — the MCP registration was ${\n\t\t\t\talreadyRegistered ? \"left as it was\" : \"rolled back\"\n\t\t\t}.\\n${e instanceof Error ? e.message : String(e)}`,\n\t\t};\n\t}\n\n\treturn {\n\t\tok: true,\n\t\tmessage: `Installed. Say ${limeBold('\"sync my stack\"')} in any Claude Code session.`,\n\t};\n}\n\nexport async function connectCommand(harness: string): Promise<void> {\n\tintro(\"connect\");\n\n\tif (harness !== \"claude\") {\n\t\toutroError(`unknown harness \"${harness}\" — supported: claude`);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tif (!claudeOnPath()) {\n\t\tp.log.warn(\n\t\t\t`claude was not found on PATH. Manual install:\\n${dim(MANUAL_MCP_ADD)}\\nplus copy skills/aistack-sync from this package to ${dim(SKILL_DEST)}`,\n\t\t);\n\t\toutroSkipped(\"nothing was installed\");\n\t\treturn;\n\t}\n\n\tconst result = installClaudeConnect();\n\tif (!result.ok) {\n\t\toutroError(result.message);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\tp.log.success(result.message);\n\toutro(\"done\");\n}\n\n/**\n * The post-sync upsell (#56 decision 2), asked once per machine. Any explicit\n * answer persists to ~/.config/aistack/settings.json; ctrl-C is not an answer\n * and the question returns on the next sync. Skipped silently when claude is\n * not on PATH — the offer would be noise on a machine that cannot take it.\n */\nexport async function offerConnectUpsell(): Promise<void> {\n\tif (getSettings().connectClaudeAnswered === true) return;\n\tif (!claudeOnPath()) return;\n\n\tconst answer = await p.select({\n\t\tmessage: \"Sync from inside Claude Code too?\",\n\t\toptions: [\n\t\t\t{\n\t\t\t\tvalue: \"later\",\n\t\t\t\tlabel: \"Not now\",\n\t\t\t\thint: \"this question will not come back\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalue: \"install\",\n\t\t\t\tlabel: \"Install\",\n\t\t\t\thint: \"adds the aistack MCP server + Skill to Claude Code\",\n\t\t\t},\n\t\t],\n\t\tinitialValue: \"later\",\n\t});\n\n\tif (p.isCancel(answer)) return;\n\tsaveSettings({ connectClaudeAnswered: true });\n\n\tif (answer === \"install\") {\n\t\tconst result = installClaudeConnect();\n\t\tif (result.ok) {\n\t\t\tp.log.success(result.message);\n\t\t} else {\n\t\t\tp.log.error(result.message);\n\t\t}\n\t\treturn;\n\t}\n\n\tp.log.message(\n\t\t`If you change your mind: ${limeBold(\"npx @use-aistack/cli connect claude\")}`,\n\t);\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport { stackGet } from \"../api.js\";\nimport { getToken } from \"../config.js\";\nimport {\n\tbold,\n\tdim,\n\tdivider,\n\tintro,\n\tlime,\n\tlimeBold,\n\tlines,\n\toutro,\n\toutroCancel,\n\toutroError,\n\toutroSkipped,\n\tsection,\n\tyellow,\n} from \"../theme.js\";\n\nexport async function createCommand() {\n\tintro(\"create\");\n\n\tconst token = getToken();\n\tif (!token) {\n\t\tp.log.error(\n\t\t\t`Not authenticated. Run ${limeBold(\"npx @use-aistack/cli login\")} first.`,\n\t\t);\n\t\toutroError(\"not authenticated\");\n\t\tprocess.exit(1);\n\t}\n\n\tconst s = p.spinner();\n\ts.start(\"Fetching stack...\");\n\n\tlet stack: Awaited<ReturnType<typeof stackGet>>;\n\ttry {\n\t\tstack = await stackGet(token);\n\t\tif (!stack) {\n\t\t\ts.stop(\"Not found\");\n\t\t\tp.log.error(\"No stack found. Create a stack on aistack.to first.\");\n\t\t\toutroError(\"not found\");\n\t\t\tprocess.exit(1);\n\t\t}\n\t\ts.stop(bold(stack.name));\n\t} catch (err) {\n\t\ts.stop(\"Failed to fetch stack\");\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\toutroError(\"error\");\n\t\tprocess.exit(1);\n\t}\n\n\tconst localFiles: FileToWrite[] = [];\n\n\tfor (const item of stack.resources) {\n\t\tfor (const file of item.files ?? []) {\n\t\t\tlocalFiles.push({ path: file.path ?? file.name, content: file.content });\n\t\t}\n\t}\n\n\t// Linked resources have no files to write — surface them so they aren't\n\t// silently dropped on download (GitHub repos + package refs like MCP servers).\n\tconst linked = stack.resources.filter(\n\t\t(item) => (item.upstream || item.pkg) && !item.files?.length,\n\t);\n\tif (linked.length > 0) {\n\t\tsection(\"linked\", linked.length);\n\t\tlines([dim(\"view only\")]);\n\t\tlines(\n\t\t\tlinked.map((item) =>\n\t\t\t\tdim(\n\t\t\t\t\titem.upstream?.repoUrl ??\n\t\t\t\t\t\t(item.pkg ? `${item.pkg.registry}:${item.pkg.id}` : \"\"),\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t}\n\n\tif (localFiles.length === 0) {\n\t\tp.log.warn(\"No local files to write.\");\n\t\toutroSkipped(\"nothing to create\");\n\t\treturn;\n\t}\n\n\tconst cwd = process.cwd();\n\tconst toWrite: FileToWrite[] = [];\n\tconst skipped: { path: string; differs: boolean }[] = [];\n\n\tfor (const f of localFiles) {\n\t\tconst fullPath = join(cwd, f.path);\n\t\tif (existsSync(fullPath)) {\n\t\t\tconst existing = readFileSync(fullPath, \"utf-8\");\n\t\t\tskipped.push({ path: f.path, differs: existing !== f.content });\n\t\t} else {\n\t\t\ttoWrite.push(f);\n\t\t}\n\t}\n\n\tsection(\"local files\", localFiles.length);\n\tlines(toWrite.map((f) => lime(`+ ${f.path}`)));\n\tlines(\n\t\tskipped.map((f) =>\n\t\t\tf.differs\n\t\t\t\t? `${yellow(`= ${f.path}`)} ${dim(\"(differs)\")}`\n\t\t\t\t: dim(`= ${f.path} (identical)`),\n\t\t),\n\t);\n\n\tif (toWrite.length === 0) {\n\t\tdivider();\n\t\tp.log.info(\"All local files already exist.\");\n\t\toutroSkipped(\"nothing to write\");\n\t\treturn;\n\t}\n\n\tdivider();\n\n\tconst confirm = await p.confirm({\n\t\tmessage: `Write ${lime(String(toWrite.length))} new files? ${dim(`(${skipped.length} skipped)`)}`,\n\t});\n\n\tif (p.isCancel(confirm) || !confirm) {\n\t\toutroCancel();\n\t\tprocess.exit(0);\n\t}\n\n\tfor (const f of toWrite) {\n\t\tconst fullPath = join(cwd, f.path);\n\t\tconst dir = dirname(fullPath);\n\t\tmkdirSync(dir, { recursive: true });\n\t\twriteFileSync(fullPath, f.content);\n\t}\n\n\tp.log.success(\n\t\t`${lime(String(toWrite.length))} written, ${dim(String(skipped.length) + \" skipped\")}`,\n\t);\n\toutro(lime(\"done\"));\n}\n\ninterface FileToWrite {\n\tpath: string;\n\tcontent: string;\n}\n","import { hostname } from \"node:os\";\nimport * as p from \"@clack/prompts\";\nimport open from \"open\";\nimport { authPoll, authStart } from \"../api.js\";\nimport { saveToken } from \"../config.js\";\nimport { dim, intro, lime, limeBold, outro, outroError } from \"../theme.js\";\n\n/**\n * What to call this machine on the account's linked-machines list (#49).\n *\n * The hostname is only a proposal — the approval page shows it in an editable\n * field before anything is stored. Trimmed to the server's 64-character bound so\n * a long hostname is dropped by us rather than silently by the server, and\n * `.local` is stripped because mDNS suffixes carry no information for a reader.\n */\nexport function proposedMachineName(\n\tread: () => string = hostname,\n): string | undefined {\n\ttry {\n\t\tconst name = read()\n\t\t\t.trim()\n\t\t\t.replace(/\\.local$/i, \"\");\n\t\tif (!name || name.length > 64) return undefined;\n\t\treturn name;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * The device-auth flow itself, without intro/outro framing or process.exit —\n * so `sync` can run it inline on an unlinked machine (#74) and `login` stays\n * the standalone command. Logs its own progress and errors; returns whether a\n * token was saved.\n */\nexport async function performLogin(): Promise<boolean> {\n\tconst s = p.spinner();\n\ts.start(\"Starting authentication...\");\n\n\tlet session: Awaited<ReturnType<typeof authStart>>;\n\ttry {\n\t\tsession = await authStart(proposedMachineName());\n\t\ts.stop(\"Session created\");\n\t} catch (err) {\n\t\ts.stop(\"Failed to start authentication\");\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\treturn false;\n\t}\n\n\tp.log.info(`${dim(\"CODE\")} ${limeBold(session.userCode)}`);\n\tp.log.info(`${dim(\"OPEN\")} ${dim(session.authUrl)}`);\n\n\ttry {\n\t\tawait open(session.authUrl);\n\t} catch {\n\t\tp.log.warn(\n\t\t\t\"Could not open browser automatically. Please visit the URL above.\",\n\t\t);\n\t}\n\n\ts.start(\"Waiting for approval...\");\n\n\tconst maxAttempts = 36;\n\tfor (let i = 0; i < maxAttempts; i++) {\n\t\tawait new Promise((resolve) => setTimeout(resolve, 5000));\n\n\t\ttry {\n\t\t\tconst result = await authPoll(session.secretId);\n\n\t\t\tif (result.status === \"approved\" && result.token) {\n\t\t\t\ts.stop(lime(\"Authenticated\"));\n\t\t\t\tsaveToken(result.token, result.userId);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (result.status === \"expired\") {\n\t\t\t\ts.stop(\"Session expired\");\n\t\t\t\tp.log.error(\"Authentication session expired. Please try again.\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} catch (err) {\n\t\t\ts.stop(\"Error polling\");\n\t\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\ts.stop(\"Timed out\");\n\tp.log.error(\"Authentication timed out after 3 minutes. Please try again.\");\n\treturn false;\n}\n\nexport async function loginCommand() {\n\tintro(\"login\");\n\n\tif (!(await performLogin())) {\n\t\toutroError(\"error\");\n\t\tprocess.exit(1);\n\t}\n\n\tp.log.success(\n\t\t`Token saved. Run ${limeBold(\"npx @use-aistack/cli sync\")} to publish your usage.`,\n\t);\n\toutro(lime(\"done\"));\n}\n","// The documented default sync surface (#56, built by #55/#57).\n//\n// The MCP-free channel: a human types `aistack sync` in their own terminal,\n// so a real TTY exists and the gate can be a @clack/prompts select. Same\n// staged-bytes property as the MCP server (#41): the summary and the confirm\n// derive from the exact serialized `bodyJson`, and that string goes on the\n// wire byte-identical. One gate policy, two renderings.\n//\n// Fail-closed: ctrl-C, ESC, EOF, and a missing TTY all resolve to \"nothing\n// was sent\" before any network call.\n\nimport * as p from \"@clack/prompts\";\nimport { BASE_URL, syncPublish } from \"../api.js\";\nimport {\n\tCODEX_TRUST_INSTRUCTION,\n\tcodexAutoSyncHookInstalled,\n\tcodexHookTrusted,\n} from \"../autosync/codexHook.js\";\nimport {\n\tdisableAutoSync,\n\tenableAutoSync,\n\tofferAutoSyncOptIn,\n} from \"../autosync/optin.js\";\nimport { runAutoSync } from \"../autosync/run.js\";\nimport { DEFAULT_FREQUENCY_HOURS, getSettings, getToken } from \"../config.js\";\nimport { stageSync } from \"../sync/stage.js\";\nimport { dim, intro, lime, outro, outroCancel, outroError } from \"../theme.js\";\nimport { offerConnectUpsell } from \"./connect.js\";\nimport { performLogin } from \"./login.js\";\n\nexport interface SyncOptions {\n\t/** `--auto` → true, `--auto on` → \"on\", `--auto off` → \"off\". */\n\tauto?: boolean | string;\n\t/** `--every <hours>`, applied with `--auto on`. */\n\tevery?: string;\n}\n\nexport async function syncCommand(options: SyncOptions = {}): Promise<void> {\n\t// The silent path (#62): no TTY, no prompts, no upsells. Publishes only\n\t// under the standing opt-in and always exits 0 — the hook command's `||`\n\t// offline fallback must never fire on a mere sync failure.\n\tif (options.auto === true) {\n\t\tawait runAutoSync({ baseUrl: BASE_URL });\n\t\treturn;\n\t}\n\n\tif (options.auto === \"on\" || options.auto === \"off\") {\n\t\tintro(\"sync\");\n\t\tconst result =\n\t\t\toptions.auto === \"on\"\n\t\t\t\t? enableAutoSync(\n\t\t\t\t\t\toptions.every\n\t\t\t\t\t\t\t? Number.parseInt(options.every, 10) || DEFAULT_FREQUENCY_HOURS\n\t\t\t\t\t\t\t: DEFAULT_FREQUENCY_HOURS,\n\t\t\t\t\t)\n\t\t\t\t: disableAutoSync();\n\t\tif (result.ok) {\n\t\t\tp.log.success(result.message);\n\t\t\toutro(\"done\");\n\t\t} else {\n\t\t\toutroError(result.message);\n\t\t\tprocess.exitCode = 1;\n\t\t}\n\t\treturn;\n\t}\n\tif (options.auto !== undefined) {\n\t\tintro(\"sync\");\n\t\toutroError(`unknown --auto value \"${options.auto}\" — use on or off`);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tintro(\"sync\");\n\n\t// The interactive surface is where a silent failure becomes visible (#62):\n\t// report the last auto-sync outcome, whatever it was.\n\tconst lastAuto = getSettings().autoSyncState?.lastResult;\n\tif (lastAuto !== undefined) {\n\t\tp.log.message(dim(`auto-sync: ${lastAuto}`));\n\t}\n\n\t// The Codex hook does not run until the user trusts it via /hooks (#65 §6).\n\t// Repeat the one-time instruction while the hook is installed but the trust\n\t// hash is verifiably absent; an unreadable config stays silent.\n\tif (codexAutoSyncHookInstalled() && codexHookTrusted() === false) {\n\t\tp.log.warn(CODEX_TRUST_INSTRUCTION);\n\t}\n\n\t// The whole premise of this channel is a human at a terminal. A pipe or a\n\t// model-launched Bash call has no TTY, and a gate that cannot ask must not\n\t// send (#31) — refuse before scanning anything.\n\tif (!process.stdin.isTTY || !process.stdout.isTTY) {\n\t\toutroError(\"sync needs an interactive terminal — nothing was sent\");\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\t// An unlinked machine used to hard-block with \"run login first\" (#74). The\n\t// TTY gate above guarantees a human is present, so the device-auth browser\n\t// hop fits here — `sync` is the whole onboarding command.\n\tif (getToken() === null) {\n\t\tp.log.message(\n\t\t\t\"This machine is not linked to an aistack account yet — linking it first.\",\n\t\t);\n\t\tif (!(await performLogin())) {\n\t\t\toutroError(\"login failed — nothing was sent\");\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tconst s = p.spinner();\n\ts.start(\"Scanning local agent transcripts\");\n\tlet staged: Awaited<ReturnType<typeof stageSync>>;\n\ttry {\n\t\tstaged = await stageSync({ baseUrl: BASE_URL });\n\t} catch (e) {\n\t\ts.stop(\"Scan failed\");\n\t\toutroError(e instanceof Error ? e.message : String(e));\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\ts.stop(\"Scan complete\");\n\n\t// Beat one — the same full summary the MCP preview returns, verbatim,\n\t// printed behind the clack bar so it reads as one flow.\n\tp.log.message(staged.summary.split(\"\\n\").join(\"\\n\"));\n\n\tif (staged.blockedReason !== null) {\n\t\toutroError(staged.blockedReason);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\t// Beat two — the same short dialog text, as a select. The enum mirrors the\n\t// elicitation's {publish, cancel}; cancel is the initial value, so Enter\n\t// alone publishes nothing.\n\tconst decision = await p.select({\n\t\tmessage: staged.dialog.split(\"\\n\").join(dim(\" · \")),\n\t\toptions: [\n\t\t\t{ value: \"cancel\", label: \"Cancel\", hint: \"nothing leaves this machine\" },\n\t\t\t{ value: \"publish\", label: \"Publish\" },\n\t\t],\n\t\tinitialValue: \"cancel\",\n\t});\n\n\tif (p.isCancel(decision) || decision !== \"publish\") {\n\t\toutroCancel(\"nothing was sent\");\n\t\treturn;\n\t}\n\n\ts.start(\"Publishing\");\n\ttry {\n\t\tconst res = await syncPublish(staged.token as string, staged.bodyJson);\n\t\ts.stop(\"Published\");\n\t\tconst lines = [\n\t\t\t`Snapshot received at ${new Date(res.receivedAt).toISOString()}`,\n\t\t\tlime(res.url),\n\t\t];\n\t\tif (res.keptPrivate.refused && staged.body.keptPrivate !== undefined) {\n\t\t\tlines.push(\n\t\t\t\t\"Note: the kept-private names were refused by the server — the review switch is off there now. They stayed on this machine.\",\n\t\t\t);\n\t\t} else if (res.keptPrivate.stored > 0) {\n\t\t\tlines.push(\n\t\t\t\t`${res.keptPrivate.stored} kept-private names went up for your review at ${res.url}/changes`,\n\t\t\t);\n\t\t}\n\t\tp.log.message(lines.join(\"\\n\"));\n\t\t// At most one ask per sync (#62): the auto-sync opt-in is the primary\n\t\t// ask; the connect upsell yields and waits for a later sync.\n\t\tconst asked = await offerAutoSyncOptIn();\n\t\tif (!asked) await offerConnectUpsell();\n\t\toutro(\"done\");\n\t} catch (e) {\n\t\ts.stop(\"Publish failed\");\n\t\toutroError(e instanceof Error ? e.message : String(e));\n\t\tprocess.exitCode = 1;\n\t}\n}\n","// The Codex half of the background trigger (#66 decision 4, built in #67):\n// a `SessionStart` hook in ~/.codex/hooks.json, matcher `startup` only.\n//\n// Two ways this differs from the Claude hook (hook.ts):\n//\n// 1. THE COMMAND SELF-DETACHES. Codex parses `async` but does not honor it —\n// the runner awaits the hook with a timeout and kill_on_drop (#65 §6).\n// So the command backgrounds the real work under `setsid nohup … &` and\n// exits 0 immediately; kill_on_drop kills only the already-exited shell.\n// 2. THE TRUST GATE. Codex pins each hook command's sha256 as a\n// `trusted_hash` in config.toml. An untrusted or CHANGED command silently\n// does not run, and only the user can trust it, via /hooks inside Codex.\n// That is why the command text uses `@latest` — the text (and therefore\n// the hash) stays stable across CLI updates — and why install prints the\n// one-time trust instruction.\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nimport type { HookResult } from \"./hook.js\";\n\nfunction codexHome(): string {\n\treturn process.env.CODEX_HOME || join(homedir(), \".codex\");\n}\n\nexport function codexHooksFile(): string {\n\treturn join(codexHome(), \"hooks.json\");\n}\n\n/**\n * Is Codex on this machine at all? Keyed on $CODEX_HOME existing, not on\n * session logs: a hook belongs wherever Codex will RUN, including a fresh\n * install that has no sessions yet.\n */\nexport function codexPresent(): boolean {\n\treturn existsSync(codexHome());\n}\n\nexport function codexConfigFile(): string {\n\treturn join(codexHome(), \"config.toml\");\n}\n\n/**\n * EXACT quoting, settled here (#66 left it to this ticket): the outer layer is\n * a JSON string in hooks.json; Codex runs it through a shell, and the single\n * `sh -c '…'` wrapper makes the detach group unambiguous regardless of how\n * that outer shell tokenizes. No `||` fallback like the Claude command — the\n * fallback semantics live INSIDE the detached shell so the hook process itself\n * still exits instantly.\n *\n * DO NOT REFORMAT THIS STRING. Its sha256 is the trust hash; any byte change\n * un-trusts the hook on every machine until each user re-runs /hooks.\n */\nexport const CODEX_HOOK_COMMAND =\n\t\"sh -c 'setsid nohup sh -c \\\"npx -y @use-aistack/cli@latest sync --auto || npx -y --prefer-offline @use-aistack/cli sync --auto\\\" >/dev/null 2>&1 &'\";\n\ninterface HookEntry {\n\ttype: string;\n\tcommand?: string;\n\ttimeout?: number;\n}\n\ninterface HookMatcher {\n\tmatcher?: string;\n\thooks?: HookEntry[];\n}\n\ninterface CodexHooksJson {\n\thooks?: Record<string, HookMatcher[]>;\n\t[key: string]: unknown;\n}\n\n/** Recognize our hook across versions: package name plus the auto flag. */\nfunction isOurs(entry: HookEntry): boolean {\n\treturn (\n\t\ttypeof entry.command === \"string\" &&\n\t\tentry.command.includes(\"@use-aistack/cli\") &&\n\t\tentry.command.includes(\"sync --auto\")\n\t);\n}\n\nfunction readHooksJson(\n\tfile: string,\n): { settings: CodexHooksJson } | { error: string } {\n\tif (!existsSync(file)) return { settings: {} };\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\tif (raw && typeof raw === \"object\" && !Array.isArray(raw)) {\n\t\t\treturn { settings: raw as CodexHooksJson };\n\t\t}\n\t\treturn { error: `${file} does not hold a JSON object` };\n\t} catch {\n\t\t// Never rewrite a file we cannot parse — it is the user's Codex\n\t\t// configuration, and a rewrite would destroy whatever is in it.\n\t\treturn { error: `${file} is not valid JSON — fix it, then retry` };\n\t}\n}\n\n/** The instruction install prints; the interactive sync repeats it while untrusted. */\nexport const CODEX_TRUST_INSTRUCTION =\n\t\"Codex hook written — open Codex and run /hooks once to trust it, or it will not run.\";\n\n/**\n * Add the SessionStart auto-sync hook, matcher `startup` only (resume/clear/\n * compact would multiply runs; the freshness gate would drop them anyway).\n * Idempotent: an existing aistack entry is replaced, not duplicated.\n */\nexport function installCodexAutoSyncHook(\n\tfile: string = codexHooksFile(),\n): HookResult {\n\tconst read = readHooksJson(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst hooks = settings.hooks ?? {};\n\tconst sessionStart = Array.isArray(hooks.SessionStart)\n\t\t? hooks.SessionStart\n\t\t: [];\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tkept.push({\n\t\tmatcher: \"startup\",\n\t\thooks: [{ type: \"command\", command: CODEX_HOOK_COMMAND, timeout: 30 }],\n\t});\n\n\tsettings.hooks = { ...hooks, SessionStart: kept };\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: CODEX_TRUST_INSTRUCTION };\n}\n\n/**\n * Remove only our hook. Other hooks and events stay. A missing file or an\n * absent hook is success — the goal state already holds.\n */\nexport function removeCodexAutoSyncHook(\n\tfile: string = codexHooksFile(),\n): HookResult {\n\tif (!existsSync(file))\n\t\treturn { ok: true, message: \"no Codex hook to remove\" };\n\tconst read = readHooksJson(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst sessionStart = settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) {\n\t\treturn { ok: true, message: \"no Codex hook to remove\" };\n\t}\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tconst hooks = { ...settings.hooks };\n\tif (kept.length > 0) {\n\t\thooks.SessionStart = kept;\n\t} else {\n\t\tdelete hooks.SessionStart;\n\t}\n\tif (Object.keys(hooks).length > 0) {\n\t\tsettings.hooks = hooks;\n\t} else {\n\t\tdelete settings.hooks;\n\t}\n\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: `hook removed from ${file}` };\n}\n\n/** Is the Codex auto-sync hook present? */\nexport function codexAutoSyncHookInstalled(\n\tfile: string = codexHooksFile(),\n): boolean {\n\tconst read = readHooksJson(file);\n\tif (\"error\" in read) return false;\n\tconst sessionStart = read.settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) return false;\n\treturn sessionStart.some((m) => (m.hooks ?? []).some((h) => isOurs(h)));\n}\n\n/**\n * Best-effort trust check: Codex pins each trusted hook's sha256 under\n * `[hooks.state]` in config.toml, so the command's hash appearing anywhere in\n * that file reads as trusted. `null` when the file cannot be read — unknown,\n * not untrusted, so the caller does not nag on a parse quirk.\n */\nexport function codexHookTrusted(\n\tconfigFile: string = codexConfigFile(),\n): boolean | null {\n\tlet text: string;\n\ttry {\n\t\ttext = readFileSync(configFile, \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n\tconst hash = createHash(\"sha256\").update(CODEX_HOOK_COMMAND).digest(\"hex\");\n\treturn text.includes(hash);\n}\n","// The auto-sync opt-in (#62, map #60).\n//\n// This ask is the PRIMARY post-sync ask: it runs first, and the connect-claude\n// upsell yields to a later sync (at most one ask per sync, each asked once,\n// each persisted). Any explicit answer persists to\n// ~/.config/aistack/settings.json; ctrl-C is not an answer and the question\n// returns on the next sync.\n\nimport * as p from \"@clack/prompts\";\nimport {\n\tDEFAULT_FREQUENCY_HOURS,\n\tgetSettings,\n\tsaveSettings,\n} from \"../config.js\";\nimport { dim, limeBold } from \"../theme.js\";\nimport {\n\tcodexPresent,\n\tinstallCodexAutoSyncHook,\n\tremoveCodexAutoSyncHook,\n} from \"./codexHook.js\";\nimport {\n\ttype HookResult,\n\tinstallAutoSyncHook,\n\tremoveAutoSyncHook,\n} from \"./hook.js\";\n\nexport interface EnableDeps {\n\tsettingsFile?: string;\n\tinstallHook?: () => HookResult;\n\tremoveHook?: () => HookResult;\n\tinstallCodexHook?: () => HookResult;\n\tremoveCodexHook?: () => HookResult;\n\tcodexPresentImpl?: () => boolean;\n}\n\n/**\n * Turn the standing opt-in on: persist the flag, write the SessionStart hooks\n * — Claude Code always, Codex when it is on this machine (#66 decision 4; one\n * `sync --auto` covers all detected harnesses, so both hooks run the same\n * command). When a hook write fails, the flag is NOT persisted — a\n * half-enabled state (flag on, no hook) would claim a freshness the machine\n * cannot deliver.\n */\nexport function enableAutoSync(\n\tfrequencyHours: number = DEFAULT_FREQUENCY_HOURS,\n\tdeps: EnableDeps = {},\n): HookResult {\n\tconst install = deps.installHook ?? installAutoSyncHook;\n\tconst result = install();\n\tif (!result.ok) return result;\n\n\tconst hasCodex = (deps.codexPresentImpl ?? codexPresent)();\n\tlet trustLine: string | null = null;\n\tif (hasCodex) {\n\t\tconst codexResult = (deps.installCodexHook ?? installCodexAutoSyncHook)();\n\t\tif (!codexResult.ok) return codexResult;\n\t\t// The one-time /hooks trust step (#65 §6) — repeated by the next\n\t\t// interactive sync while the hook stays untrusted.\n\t\ttrustLine = codexResult.message;\n\t}\n\n\tsaveSettings(\n\t\t{\n\t\t\tautoSyncAnswered: true,\n\t\t\tautoSync: { enabled: true, frequencyHours },\n\t\t},\n\t\tdeps.settingsFile,\n\t);\n\tconst sessionWord = hasCodex ? \"Claude Code or Codex\" : \"Claude Code\";\n\treturn {\n\t\tok: true,\n\t\tmessage: [\n\t\t\t`Auto-sync is on — about every ${frequencyHours}h when a ${sessionWord} session starts. Turn it off any time: npx @use-aistack/cli sync --auto off`,\n\t\t\t...(trustLine ? [trustLine] : []),\n\t\t].join(\"\\n\"),\n\t};\n}\n\n/**\n * Revoke: remove the hook AND flip the flag. The flag flips even when the\n * hook file cannot be edited, because `sync --auto` gates on the flag — a\n * stale hook without the flag publishes nothing.\n */\nexport function disableAutoSync(deps: EnableDeps = {}): HookResult {\n\tconst remove = deps.removeHook ?? removeAutoSyncHook;\n\tconst settings = getSettings(deps.settingsFile);\n\tsaveSettings(\n\t\t{\n\t\t\tautoSyncAnswered: true,\n\t\t\tautoSync: {\n\t\t\t\tenabled: false,\n\t\t\t\tfrequencyHours:\n\t\t\t\t\tsettings.autoSync?.frequencyHours ?? DEFAULT_FREQUENCY_HOURS,\n\t\t\t},\n\t\t},\n\t\tdeps.settingsFile,\n\t);\n\tconst result = remove();\n\tconst codexResult = (deps.codexPresentImpl ?? codexPresent)()\n\t\t? (deps.removeCodexHook ?? removeCodexAutoSyncHook)()\n\t\t: { ok: true, message: \"\" };\n\tconst failures = [result, codexResult]\n\t\t.filter((r) => !r.ok)\n\t\t.map((r) => r.message);\n\tif (failures.length > 0) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `Auto-sync is off (nothing will publish), but a hook could not be removed: ${failures.join(\"; \")}`,\n\t\t};\n\t}\n\treturn { ok: true, message: \"Auto-sync is off. The hooks were removed.\" };\n}\n\n/**\n * The post-sync ask. Returns true when it asked (so the caller skips the\n * connect upsell this sync), false when it had nothing to ask.\n */\nexport async function offerAutoSyncOptIn(): Promise<boolean> {\n\tif (getSettings().autoSyncAnswered === true) return false;\n\n\tconst answer = await p.select({\n\t\tmessage: \"Keep this stack fresh automatically?\",\n\t\toptions: [\n\t\t\t{\n\t\t\t\tvalue: \"later\",\n\t\t\t\tlabel: \"Not now\",\n\t\t\t\thint: \"this question will not come back\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalue: \"enable\",\n\t\t\t\tlabel: \"Enable\",\n\t\t\t\thint: \"a silent daily sync when a Claude Code session starts\",\n\t\t\t},\n\t\t],\n\t\tinitialValue: \"later\",\n\t});\n\n\tif (p.isCancel(answer)) return true;\n\n\tif (answer === \"enable\") {\n\t\tconst result = enableAutoSync();\n\t\tif (result.ok) {\n\t\t\tp.log.success(result.message);\n\t\t} else {\n\t\t\tp.log.error(result.message);\n\t\t}\n\t\treturn true;\n\t}\n\n\tsaveSettings({ autoSyncAnswered: true });\n\tp.log.message(\n\t\t`If you change your mind: ${limeBold(\"npx @use-aistack/cli sync --auto on\")} ${dim(\n\t\t\t\"(and --auto off to revoke)\",\n\t\t)}`,\n\t);\n\treturn true;\n}\n","// The background trigger (#62, map #60): a `SessionStart` hook in\n// ~/.claude/settings.json, `async: true`.\n//\n// SessionStart, not SessionEnd — teardown is not guaranteed (crash, SIGKILL,\n// closed terminal), and at start-of-session the previous sessions are fully on\n// disk. The command runs `@latest` through npx, so unattended machines update\n// by construction. The `||` fallback covers the offline case: when the network\n// resolve of `@latest` fails, the second npx runs the cached copy.\n// `sync --auto` always exits 0, so the fallback never fires on a sync failure.\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nexport const CLAUDE_SETTINGS_FILE = join(homedir(), \".claude\", \"settings.json\");\n\nexport const AUTO_SYNC_HOOK_COMMAND =\n\t\"npx -y @use-aistack/cli@latest sync --auto || npx -y --prefer-offline @use-aistack/cli sync --auto\";\n\ninterface HookEntry {\n\ttype: string;\n\tcommand?: string;\n\tasync?: boolean;\n}\n\ninterface HookMatcher {\n\tmatcher?: string;\n\thooks?: HookEntry[];\n}\n\ninterface ClaudeSettings {\n\thooks?: Record<string, HookMatcher[]>;\n\t[key: string]: unknown;\n}\n\n/** Recognize our hook across versions: package name plus the auto flag. */\nfunction isOurs(entry: HookEntry): boolean {\n\treturn (\n\t\ttypeof entry.command === \"string\" &&\n\t\tentry.command.includes(\"@use-aistack/cli\") &&\n\t\tentry.command.includes(\"sync --auto\")\n\t);\n}\n\nexport interface HookResult {\n\tok: boolean;\n\tmessage: string;\n}\n\nfunction readClaudeSettings(\n\tfile: string,\n): { settings: ClaudeSettings } | { error: string } {\n\tif (!existsSync(file)) return { settings: {} };\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\tif (raw && typeof raw === \"object\" && !Array.isArray(raw)) {\n\t\t\treturn { settings: raw as ClaudeSettings };\n\t\t}\n\t\treturn { error: `${file} does not hold a JSON object` };\n\t} catch {\n\t\t// Never rewrite a file we cannot parse — it is the user's Claude Code\n\t\t// configuration, and a rewrite would destroy whatever is in it.\n\t\treturn { error: `${file} is not valid JSON — fix it, then retry` };\n\t}\n}\n\n/**\n * Add the SessionStart auto-sync hook. Idempotent: an existing aistack\n * auto-sync entry (any version of the command) is replaced, not duplicated.\n * All other hooks are preserved byte-for-byte in structure.\n */\nexport function installAutoSyncHook(\n\tfile: string = CLAUDE_SETTINGS_FILE,\n): HookResult {\n\tconst read = readClaudeSettings(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst hooks = settings.hooks ?? {};\n\tconst sessionStart = Array.isArray(hooks.SessionStart)\n\t\t? hooks.SessionStart\n\t\t: [];\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tkept.push({\n\t\thooks: [{ type: \"command\", command: AUTO_SYNC_HOOK_COMMAND, async: true }],\n\t});\n\n\tsettings.hooks = { ...hooks, SessionStart: kept };\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: `SessionStart hook written to ${file}` };\n}\n\n/**\n * Remove only our hook. Other SessionStart hooks and other events stay. A\n * missing file or an absent hook is success — the goal state already holds.\n */\nexport function removeAutoSyncHook(\n\tfile: string = CLAUDE_SETTINGS_FILE,\n): HookResult {\n\tif (!existsSync(file)) return { ok: true, message: \"no hook to remove\" };\n\tconst read = readClaudeSettings(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst sessionStart = settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) {\n\t\treturn { ok: true, message: \"no hook to remove\" };\n\t}\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tconst hooks = { ...settings.hooks };\n\tif (kept.length > 0) {\n\t\thooks.SessionStart = kept;\n\t} else {\n\t\tdelete hooks.SessionStart;\n\t}\n\tif (Object.keys(hooks).length > 0) {\n\t\tsettings.hooks = hooks;\n\t} else {\n\t\tdelete settings.hooks;\n\t}\n\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: `hook removed from ${file}` };\n}\n\n/** Is the auto-sync hook present? Used by status reporting. */\nexport function autoSyncHookInstalled(\n\tfile: string = CLAUDE_SETTINGS_FILE,\n): boolean {\n\tconst read = readClaudeSettings(file);\n\tif (\"error\" in read) return false;\n\tconst sessionStart = read.settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) return false;\n\treturn sessionStart.some((m) => (m.hooks ?? []).some((h) => isOurs(h)));\n}\n","// `sync --auto` — the silent background run (#62, map #60).\n//\n// The tenets from map #29 hold: passive analysis, never passive publish. The\n// standing opt-in (`autoSync.enabled` in ~/.config/aistack/settings.json) is\n// the ONLY thing that lets this path publish, and the user can revoke it with\n// one command. No prompts, no upsells, no email, no dialogs. The escalation\n// ladder is: one log line per run → the next interactive sync reports the last\n// result → after 3 consecutive failures, one visible systemMessage line.\n//\n// This function never sets a nonzero exit code. The hook command falls back to\n// the npx cache on `||`, and a nonzero exit from a mere sync failure would\n// fire that fallback and run the whole sync twice.\n\nimport {\n\tappendFileSync,\n\tmkdirSync,\n\treadFileSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { syncPublish } from \"../api.js\";\nimport {\n\ttype AutoSyncState,\n\tDEFAULT_FREQUENCY_HOURS,\n\tgetSettings,\n\tsaveSettings,\n} from \"../config.js\";\nimport { stageSync } from \"../sync/stage.js\";\n\nexport const SYNC_LOG_FILE = join(homedir(), \".config\", \"aistack\", \"sync.log\");\n\n/** The log stays small: newest 200 lines, older lines fall off. */\nexport const SYNC_LOG_MAX_LINES = 200;\n\n/** The one-line fix, named in the escalation message and nowhere vaguer. */\nconst FIX_COMMAND = \"npx @use-aistack/cli sync\";\n\nexport type AutoSyncDeps = {\n\tbaseUrl: string;\n\tnow?: () => number;\n\tsettingsFile?: string;\n\tlogFile?: string;\n\tstageImpl?: typeof stageSync;\n\tpublishImpl?: typeof syncPublish;\n\t/** Where the systemMessage JSON goes. Defaults to stdout. */\n\temit?: (line: string) => void;\n};\n\nexport function appendLogLine(file: string, line: string): void {\n\tmkdirSync(dirname(file), { recursive: true });\n\tappendFileSync(file, `${line}\\n`);\n\tconst lines = readFileSync(file, \"utf-8\").split(\"\\n\").filter(Boolean);\n\tif (lines.length > SYNC_LOG_MAX_LINES) {\n\t\twriteFileSync(file, `${lines.slice(-SYNC_LOG_MAX_LINES).join(\"\\n\")}\\n`);\n\t}\n}\n\nexport async function runAutoSync(deps: AutoSyncDeps): Promise<void> {\n\tconst now = (deps.now ?? Date.now)();\n\tconst settingsFile = deps.settingsFile;\n\tconst logFile = deps.logFile ?? SYNC_LOG_FILE;\n\tconst emit =\n\t\tdeps.emit ?? ((line: string) => process.stdout.write(`${line}\\n`));\n\tconst stamp = new Date(now).toISOString();\n\n\tconst settings = getSettings(settingsFile);\n\tconst config = settings.autoSync;\n\n\t// The hard gate. A hook left behind after a revoke publishes nothing.\n\tif (config?.enabled !== true) {\n\t\tappendLogLine(logFile, `${stamp} skipped — auto-sync is not enabled`);\n\t\treturn;\n\t}\n\n\t// The freshness gate keys on the last ATTEMPT, not the last success. A\n\t// broken setup then retries once per frequency window, not once per\n\t// session start, and still reaches the 3-failure escalation.\n\tconst frequencyHours = config.frequencyHours || DEFAULT_FREQUENCY_HOURS;\n\tconst state: AutoSyncState = settings.autoSyncState ?? {};\n\tconst lastRunAt = state.lastRunAt ?? 0;\n\tif (now - lastRunAt < frequencyHours * 3_600_000) return;\n\n\tconst stage = deps.stageImpl ?? stageSync;\n\tconst publish = deps.publishImpl ?? syncPublish;\n\n\tlet failure: string | null = null;\n\tlet url: string | undefined;\n\ttry {\n\t\tconst staged = await stage({ baseUrl: deps.baseUrl, now: () => now });\n\t\tif (staged.blockedReason !== null) {\n\t\t\tfailure = staged.blockedReason;\n\t\t} else {\n\t\t\tconst res = await publish(staged.token as string, staged.bodyJson);\n\t\t\turl = res.url;\n\t\t}\n\t} catch (e) {\n\t\tfailure = e instanceof Error ? e.message : String(e);\n\t}\n\n\tif (failure === null) {\n\t\tsaveSettings(\n\t\t\t{\n\t\t\t\tautoSyncState: {\n\t\t\t\t\tlastRunAt: now,\n\t\t\t\t\tlastSuccessAt: now,\n\t\t\t\t\tlastResult: `ok — published at ${stamp}`,\n\t\t\t\t\tconsecutiveFailures: 0,\n\t\t\t\t\tfailureWarned: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\tsettingsFile,\n\t\t);\n\t\tappendLogLine(logFile, `${stamp} ok — published${url ? ` ${url}` : \"\"}`);\n\t\treturn;\n\t}\n\n\tconst consecutiveFailures = (state.consecutiveFailures ?? 0) + 1;\n\tconst shouldWarn = consecutiveFailures >= 3 && state.failureWarned !== true;\n\tsaveSettings(\n\t\t{\n\t\t\tautoSyncState: {\n\t\t\t\t...state,\n\t\t\t\tlastRunAt: now,\n\t\t\t\tlastResult: `failed at ${stamp} — ${failure}`,\n\t\t\t\tconsecutiveFailures,\n\t\t\t\tfailureWarned: state.failureWarned === true || shouldWarn,\n\t\t\t},\n\t\t},\n\t\tsettingsFile,\n\t);\n\tappendLogLine(\n\t\tlogFile,\n\t\t`${stamp} fail (${consecutiveFailures} in a row) — ${failure}`,\n\t);\n\n\t// One visible line, once per failure streak. SessionStart hook JSON:\n\t// Claude Code shows `systemMessage` to the user when the async hook lands.\n\tif (shouldWarn) {\n\t\temit(\n\t\t\tJSON.stringify({\n\t\t\t\tsystemMessage: `aistack auto-sync failed ${consecutiveFailures} times in a row (${failure}). Run \\`${FIX_COMMAND}\\` in a terminal to fix it, or \\`${FIX_COMMAND} --auto off\\` to stop these runs.`,\n\t\t\t}),\n\t\t);\n\t}\n}\n","// Stage one send: scan every detected harness → build → derive the gate's\n// text from the exact bytes.\n//\n// Wayfinder ticket #41 (map #29), widened to the adapter seam by #67 (map\n// #60). The staged `bodyJson` string IS what a publish transmits — the summary\n// and the dialog are derived from it and from nothing else, so the user can\n// never approve a sentence about different bytes (#35's binding constraint).\n// The publish tool takes only the stage id; it can name WHICH staged send to\n// release, never what is in it.\n//\n// One stage covers ALL detected harnesses (#66 decision 4): the payloads ride\n// in one request so the server can land them atomically, and the kept-private\n// union is one list because consent is per name, not per harness.\n\nimport { createHash } from \"node:crypto\";\nimport { getSettings, getToken, type Settings } from \"../config.js\";\nimport { detectedAdapters } from \"../harness/index.js\";\nimport {\n\ttype KeptPrivateAtom,\n\ttype LoadedSyncConfig,\n\tloadSyncConfig,\n\ttype NameCategory,\n\ttype SyncConfig,\n} from \"../harness/shared/allowlist.js\";\nimport {\n\ttype BuiltPayload,\n\tbuildPayload,\n\tbuildSyncBody,\n\tmergeKeptPrivate,\n\ttype SyncBody,\n} from \"../harness/shared/payload.js\";\nimport {\n\tDEFAULT_WINDOW_DAYS,\n\ttype ScanStats,\n\twindowStartMs,\n} from \"../harness/shared/window.js\";\nimport type { HarnessAdapter } from \"../harness/types.js\";\nimport { buildGateDialog, buildGateSummary } from \"./summary.js\";\n\nexport type StagedSend = {\n\t/** Content-derived: the sha256 prefix of `bodyJson`. Same bytes, same id. */\n\tid: string;\n\t/** The exact request body a publish sends, already serialized. */\n\tbodyJson: string;\n\tbody: SyncBody;\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>;\n\tsummary: string;\n\tdialog: string;\n\tconfig: SyncConfig;\n\ttoken: string | null;\n\tstagedAt: number;\n\t/**\n\t * `null` when this stage may not publish, with `blockedReason` saying why.\n\t * A gate that cannot name its destination must not send (#33 decision 7),\n\t * so no token and no resolved stack both block here, before any dialog.\n\t */\n\tblockedReason: string | null;\n};\n\nexport type StageDeps = {\n\tbaseUrl: string;\n\tnow?: () => number;\n\tgetTokenImpl?: () => string | null;\n\tloadConfigImpl?: (opts: {\n\t\tbaseUrl: string;\n\t\ttoken?: string;\n\t}) => Promise<LoadedSyncConfig>;\n\t/** Override the adapter set. Tests only. */\n\tadaptersImpl?: () => Promise<HarnessAdapter[]>;\n\tgetSettingsImpl?: () => Settings;\n\twindowDays?: number;\n};\n\nexport function stageId(bodyJson: string): string {\n\treturn createHash(\"sha256\").update(bodyJson).digest(\"hex\").slice(0, 12);\n}\n\nexport async function stageSync(deps: StageDeps): Promise<StagedSend> {\n\tconst now = (deps.now ?? Date.now)();\n\tconst token = (deps.getTokenImpl ?? getToken)();\n\tconst loadConfig = deps.loadConfigImpl ?? loadSyncConfig;\n\tconst adapters = deps.adaptersImpl ?? detectedAdapters;\n\tconst windowDays = deps.windowDays ?? DEFAULT_WINDOW_DAYS;\n\n\tconst { config, source } = await loadConfig({\n\t\tbaseUrl: deps.baseUrl,\n\t\t...(token ? { token } : {}),\n\t});\n\n\tconst built: BuiltPayload[] = [];\n\tconst scanStats: Record<string, ScanStats> = {};\n\tconst sinceMs = windowStartMs(now, windowDays);\n\tfor (const adapter of await adapters()) {\n\t\tconst { aggregate, stats } = await adapter.scan({ sinceMs });\n\t\tscanStats[adapter.name] = stats;\n\t\tbuilt.push(\n\t\t\tbuildPayload({\n\t\t\t\taggregate,\n\t\t\t\tstats,\n\t\t\t\tsyncConfig: config,\n\t\t\t\tnow,\n\t\t\t\twindowDays,\n\t\t\t\tharnessName: adapter.name,\n\t\t\t\tbuiltinTools: adapter.builtinTools,\n\t\t\t\tpricingTableVersion: adapter.pricingTableVersion,\n\t\t\t}),\n\t\t);\n\t}\n\n\t// The opt-in the machine currently holds, read at stage time so the gate's\n\t// bytes are the bytes sent (#78). Absent from the settings file means this\n\t// machine has never answered, which the backend reads as \"never told us\".\n\tconst settings = (deps.getSettingsImpl ?? getSettings)();\n\tconst body = buildSyncBody(built, config, settings.autoSync);\n\tconst bodyJson = JSON.stringify(body);\n\tconst keptPrivate = mergeKeptPrivate(built.map((b) => b.keptPrivate));\n\n\tconst ctx = {\n\t\tbody,\n\t\tkeptPrivate,\n\t\tconfig,\n\t\tsource,\n\t\tbaseUrl: deps.baseUrl,\n\t\tscanStats,\n\t};\n\n\tlet blockedReason: string | null = null;\n\tif (built.length === 0) {\n\t\tblockedReason =\n\t\t\t\"No supported harness was found on this machine — no Claude Code and no Codex logs to read.\";\n\t} else if (token === null) {\n\t\tblockedReason =\n\t\t\t\"This machine is not linked. Run `npx @use-aistack/cli login` first.\";\n\t} else if (config.stack === null) {\n\t\tblockedReason =\n\t\t\tsource === \"bundled\"\n\t\t\t\t? \"Could not fetch your settings from aistack, so the destination stack is unknown. Publish needs it. Check the network and preview again.\"\n\t\t\t\t: `The token resolves no destination stack. Create one at ${deps.baseUrl}/stacks/new, then sync again.`;\n\t}\n\n\treturn {\n\t\tid: stageId(bodyJson),\n\t\tbodyJson,\n\t\tbody,\n\t\tkeptPrivate,\n\t\tsummary: buildGateSummary(ctx),\n\t\tdialog: buildGateDialog(ctx),\n\t\tconfig,\n\t\ttoken,\n\t\tstagedAt: now,\n\t\tblockedReason,\n\t};\n}\n","// The Claude Code harness behind the seam (#67). The parsing lives in\n// analyzer.ts/scan.ts, unchanged from the single-harness era; this file only\n// gives it the adapter shape.\n\nimport { stat } from \"node:fs/promises\";\nimport { BUILTIN_TOOLS } from \"../shared/allowlist.js\";\nimport { PRICING_TABLE_VERSION } from \"../shared/pricing.js\";\nimport type {\n\tHarnessAdapter,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"../types.js\";\nimport { createAggregate } from \"./analyzer.js\";\nimport { scan, transcriptRoots } from \"./scan.js\";\n\nexport const CLAUDE_HARNESS_NAME = \"claude-code\";\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport const claudeAdapter: HarnessAdapter = {\n\tname: CLAUDE_HARNESS_NAME,\n\tbuiltinTools: BUILTIN_TOOLS,\n\tpricingTableVersion: PRICING_TABLE_VERSION,\n\n\tasync detect(): Promise<boolean> {\n\t\tfor (const root of transcriptRoots()) {\n\t\t\tif (await exists(root)) return true;\n\t\t}\n\t\treturn false;\n\t},\n\n\tasync scan(opts: HarnessScanOptions): Promise<HarnessScan> {\n\t\tconst aggregate = createAggregate();\n\t\tconst stats = await scan(aggregate, {\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.onProgress ? { onProgress: opts.onProgress } : {}),\n\t\t});\n\t\treturn { aggregate, stats };\n\t},\n};\n","// Time-aware pinned price table for API-equivalent cost.\n//\n// Wayfinder ticket #37 (map #29), decision 8 of the wire-format grilling #33.\n//\n// WHY THIS IS A LIST OF PERIODS AND NOT A FLAT MAP\n// A published \"API-equivalent cost\" covers a rolling 30-day window, and a\n// window can straddle a repricing. On 2026-09-05 the window covers Aug 6 →\n// Sep 5, but `claude-sonnet-5`'s introductory rate ends Aug 31 — so 25 days\n// price at $2/$10 and 5 days at $3/$15. A flat table misprices one side or the\n// other for a month after every repricing, which breaks the honesty tenet the\n// measured layer is built on.\n//\n// So each model's price is a list of effective-from ranges, and every response\n// is priced at the rate in effect at ITS OWN timestamp. Cost therefore has to\n// accumulate at ingest (see analyzer.ts) — summing tokens per model and pricing\n// once at the end cannot express a mid-window rate change.\n//\n// Sources: Anthropic public list prices as of 2026-07-25 (cache multipliers\n// from https://platform.claude.com/docs/en/build-with-claude/prompt-caching:\n// 5m cache write = 1.25x input, 1h cache write = 2x input, read = 0.1x input)\n// and OpenAI public list prices as of 2026-08-02\n// (https://developers.openai.com/api/docs/pricing — cached input is 10% of\n// input, the same multiplier `cacheRead` already uses; Codex reports no cache\n// writes, so the write multipliers never fire for OpenAI rows).\n//\n// Each harness's payload is stamped with ITS vendor's table id — the id is a\n// citation for the dollars in that payload, and one payload never mixes\n// vendors.\n\nexport const PRICING_TABLE_VERSION = \"anthropic-list-2026-07-25\";\nexport const OPENAI_PRICING_TABLE_VERSION = \"openai-list-2026-08-02\";\n\nexport const CACHE_WRITE_5M_MULTIPLIER = 1.25;\nexport const CACHE_WRITE_1H_MULTIPLIER = 2.0;\nexport const CACHE_READ_MULTIPLIER = 0.1;\n\n/**\n * End of the `claude-sonnet-5` introductory rate. Anthropic documents it as \"in\n * effect through 2026-08-31\", so the post-intro period opens at the following\n * UTC midnight.\n *\n * The boundary is approximated in UTC because the announcement names a date,\n * not a timezone. A response written within a few hours of the boundary can\n * therefore be priced on the wrong side of it — worth a handful of cents on a\n * single day, and the alternative (guessing US/Pacific) is no more defensible.\n */\nexport const SONNET_5_INTRO_ENDS_MS = Date.UTC(2026, 8, 1); // 2026-09-01T00:00:00Z\n\n/** USD per million tokens, valid over `[from, to)`. */\nexport type PricePeriod = {\n\t/** Inclusive lower bound, epoch ms. `null` = since the model existed. */\n\tfrom: number | null;\n\t/** Exclusive upper bound, epoch ms. `null` = still in effect. */\n\tto: number | null;\n\tinput: number;\n\toutput: number;\n};\n\n/**\n * Only rates we can actually cite are encoded. Inventing historical periods to\n * make the table look complete would fabricate cost for old records, so every\n * model with one known rate gets one open-ended period.\n */\nconst PRICES: Record<string, PricePeriod[]> = {\n\t\"claude-fable-5\": [{ from: null, to: null, input: 10, output: 50 }],\n\t\"claude-mythos-5\": [{ from: null, to: null, input: 10, output: 50 }],\n\t\"claude-opus-5\": [{ from: null, to: null, input: 5, output: 25 }],\n\t\"claude-opus-4-8\": [{ from: null, to: null, input: 5, output: 25 }],\n\t\"claude-opus-4-7\": [{ from: null, to: null, input: 5, output: 25 }],\n\t\"claude-opus-4-6\": [{ from: null, to: null, input: 5, output: 25 }],\n\t\"claude-sonnet-5\": [\n\t\t{ from: null, to: SONNET_5_INTRO_ENDS_MS, input: 2, output: 10 },\n\t\t{ from: SONNET_5_INTRO_ENDS_MS, to: null, input: 3, output: 15 },\n\t],\n\t\"claude-sonnet-4-6\": [{ from: null, to: null, input: 3, output: 15 }],\n\t\"claude-haiku-4-5\": [{ from: null, to: null, input: 1, output: 5 }],\n\t// Fast mode (research preview) — Claude API only, Opus 5 / Opus 4.8 only.\n\t// Opus 4.7 fast mode was removed, so there is deliberately no 4-7 entry.\n\t\"claude-opus-5#fast\": [{ from: null, to: null, input: 10, output: 50 }],\n\t\"claude-opus-4-8#fast\": [{ from: null, to: null, input: 10, output: 50 }],\n\t// OpenAI (Codex) — standard-context tier (<272K; observed context window is\n\t// 258,400).\n\t\"gpt-5.5\": [{ from: null, to: null, input: 5, output: 30 }],\n\t\"gpt-5.4\": [{ from: null, to: null, input: 2.5, output: 15 }],\n\t\"gpt-5.4-mini\": [{ from: null, to: null, input: 0.75, output: 4.5 }],\n\t\"gpt-5.3-codex\": [{ from: null, to: null, input: 1.75, output: 14 }],\n\t// The gpt-5.6 family launched 2026-07-29; Terra and Luna were repriced on\n\t// 2026-07-30 (-20% / -80%). The one-day launch rates are not on the list\n\t// page and are NOT encoded — a July-29 Terra/Luna record underprices for\n\t// one day rather than carrying a rate we cannot cite (#72).\n\t\"gpt-5.6-sol\": [{ from: null, to: null, input: 5, output: 30 }],\n\t\"gpt-5.6-terra\": [{ from: null, to: null, input: 2, output: 12 }],\n\t\"gpt-5.6-luna\": [{ from: null, to: null, input: 0.2, output: 1.2 }],\n\t// NOT on OpenAI's list page — an internal Codex routing label with no\n\t// official price (openai/codex#20981). Rate is the aggregator consensus\n\t// ($2.50 / $15.00), scoped in explicitly by ticket #72 because it carries\n\t// real token volume in Codex rollouts.\n\t\"codex-auto-review\": [{ from: null, to: null, input: 2.5, output: 15 }],\n};\n\nexport type TokenCounts = {\n\tinput: number;\n\toutput: number;\n\tcacheWrite5m: number;\n\tcacheWrite1h: number;\n\t/** `cache_creation_input_tokens` not covered by the TTL breakdown; priced at the 5m rate. */\n\tcacheWriteUnsplit: number;\n\tcacheRead: number;\n};\n\n/**\n * Normalize an observed `message.model` into a pricing key. Handles the\n * dated-suffix variants (`claude-haiku-4-5-20251001`). The `#fast` suffix is\n * appended by the caller from `usage.speed`.\n */\nexport function normalizeModel(model: string): string {\n\tconst [base, suffix] = model.split(\"#\");\n\tconst stripped = base.replace(/-\\d{8}$/, \"\");\n\treturn suffix ? `${stripped}#${suffix}` : stripped;\n}\n\n/** Drop the analyzer's synthetic `#fast` suffix, leaving the vendor-assigned id. */\nexport function baseModelId(modelKey: string): string {\n\treturn modelKey.split(\"#\")[0];\n}\n\n/**\n * The rate in effect for `modelKey` at `atMs`, or `null` when the model is\n * unknown or the timestamp predates every period we can cite.\n *\n * A `null` timestamp also yields `null`: a record with no parseable timestamp\n * cannot be priced time-awarely, and inventing a price for it (say, today's)\n * would silently attribute the wrong rate. Its tokens surface as unpriced.\n */\nexport function priceAt(\n\tmodelKey: string,\n\tatMs: number | null,\n): PricePeriod | null {\n\tif (atMs === null) return null;\n\tconst periods = PRICES[modelKey];\n\tif (!periods) return null;\n\tfor (const p of periods) {\n\t\tif ((p.from === null || atMs >= p.from) && (p.to === null || atMs < p.to)) {\n\t\t\treturn p;\n\t\t}\n\t}\n\treturn null;\n}\n\n/** True when we hold at least one citable rate for this model, at any time. */\nexport function isPricedModel(modelKey: string): boolean {\n\treturn PRICES[modelKey] !== undefined;\n}\n\n/**\n * Cost of one response's tokens at the rate in effect at its own timestamp.\n * Returns `null` when no rate applies — the caller must surface that as\n * unpriced tokens rather than zeroing it.\n */\nexport function apiEquivalentCost(\n\tmodelKey: string,\n\tt: TokenCounts,\n\tatMs: number | null,\n): number | null {\n\tconst p = priceAt(modelKey, atMs);\n\tif (!p) return null;\n\tconst M = 1_000_000;\n\treturn (\n\t\t(t.input * p.input +\n\t\t\tt.output * p.output +\n\t\t\t(t.cacheWrite5m + t.cacheWriteUnsplit) *\n\t\t\t\tp.input *\n\t\t\t\tCACHE_WRITE_5M_MULTIPLIER +\n\t\t\tt.cacheWrite1h * p.input * CACHE_WRITE_1H_MULTIPLIER +\n\t\t\tt.cacheRead * p.input * CACHE_READ_MULTIPLIER) /\n\t\tM\n\t);\n}\n","// Harness-agnostic aggregate machinery: the fold target every adapter fills,\n// and the finalize step that turns it into display-ready rows.\n//\n// Extracted from the Claude analyzer by ticket #67 (map #60) so the Codex\n// adapter can reuse the same totals, name hygiene, and model rows without\n// inheriting Claude's record-dedup logic. Everything here is pure — no I/O,\n// no console.\n\nimport { isPricedModel, type TokenCounts } from \"./pricing.js\";\n\n// ---------------------------------------------------------------------------\n// Narrowing helpers — records are untrusted external JSON\n// ---------------------------------------------------------------------------\n\nexport type Obj = Record<string, unknown>;\n\nexport const asObj = (v: unknown): Obj | null =>\n\ttypeof v === \"object\" && v !== null && !Array.isArray(v) ? (v as Obj) : null;\nexport const asStr = (v: unknown): string | null =>\n\ttypeof v === \"string\" && v.length > 0 ? v : null;\nexport const asNum = (v: unknown): number =>\n\ttypeof v === \"number\" && Number.isFinite(v) ? v : 0;\nexport const asArr = (v: unknown): unknown[] => (Array.isArray(v) ? v : []);\n\n/**\n * Every name that becomes a Map key or leaves this module goes through here.\n *\n * These are user-chosen strings (skill names, MCP servers, subagent types,\n * slash commands, model ids) and a hostile one is a real vector: control\n * characters move a terminal cursor, and an unterminated bidi override (U+202E)\n * reorders the rest of the rendered line — including the count and percentage\n * printed beside the name. Both survive `JSON.stringify`, which escapes C0 but\n * not bidi. See CVE-2021-42574 (\"Trojan Source\").\n *\n * Sanitizing at ingest rather than at print means the guarantee travels with\n * the module: `finalize()`'s output is safe for any consumer, not just the\n * renderer that happens to sit in front of it today.\n */\nconst NAME_UNSAFE_RE =\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: stripping them is the point\n\t/[\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u061c\\u200b-\\u200f\\u2028-\\u202e\\u2060-\\u2064\\u2066-\\u2069\\ufeff]/g;\nconst NAME_MAX = 64;\n\nexport function cleanName(s: string): string {\n\tconst stripped = s.replace(NAME_UNSAFE_RE, \"�\").trim();\n\tif (stripped.length === 0) return \"(unnamed)\";\n\treturn stripped.length > NAME_MAX\n\t\t? `${stripped.slice(0, NAME_MAX - 1)}…`\n\t\t: stripped;\n}\n\n/**\n * The same bar as `cleanName`, asked as a question.\n *\n * Used on names arriving from the NETWORK — the per-stack opt-ins the sync\n * config carries (#44). Those are the user's own strings, so the curated list's\n * conventional charset is the wrong bar: parentheses, accents and CJK are all\n * legitimate names someone runs. What is refused is what cannot be rendered\n * safely, which is exactly what `cleanName` strips on the way in.\n */\nexport function isDisplaySafeName(s: string): boolean {\n\tif (s.length === 0 || s.trim().length === 0) return false;\n\tif (s.length > NAME_MAX) return false;\n\t// A `g`-flagged regex carries `lastIndex` across `.test` calls, so this uses\n\t// a fresh non-global copy rather than the shared literal.\n\treturn !new RegExp(NAME_UNSAFE_RE.source).test(s);\n}\n\n/** `asStr` for anything that will be used as a name. */\nexport const asName = (v: unknown): string | null => {\n\tconst s = asStr(v);\n\treturn s === null ? null : cleanName(s);\n};\n\n// ---------------------------------------------------------------------------\n// Aggregate\n// ---------------------------------------------------------------------------\n\nexport type ModelUsage = TokenCounts & {\n\tmessages: number;\n\t/**\n\t * API-equivalent cost accumulated per response at that response's own rate\n\t * (#33 decision 8). Not derivable from the token totals above once a window\n\t * straddles a repricing.\n\t */\n\tcostUSD: number;\n\t/** Tokens whose own timestamp had no citable rate. Surfaced, never zeroed. */\n\tunpricedTokens: number;\n};\n\n/**\n * The fold target. `Seen` is the adapter's own dedup bookkeeping type —\n * Claude keys responses by `message.id`, Codex needs none — kept generic so\n * the shared shape does not import any one harness's record semantics.\n */\nexport type Aggregate<Seen = unknown> = {\n\t// provenance / scan health\n\tfiles: number;\n\tlines: number;\n\tparseErrors: number;\n\trecords: number;\n\tassistantRecords: number;\n\t/** Distinct API responses actually counted. */\n\tdistinctResponses: number;\n\t/** Extra records of a response already counted (same message.id AND requestId). */\n\tcontinuationsFolded: number;\n\t/** Same message.id under a NEW requestId — a genuine replay (e.g. /btw sidechain). */\n\trealReplaysFolded: number;\n\t/** Times a later record superseded an earlier one because it carried a larger total. */\n\tsupersededByLarger: number;\n\t/** Assistant records with no message.id — counted without dedup protection. */\n\tunkeyedResponses: number;\n\tsyntheticRecords: number;\n\tsyntheticTokens: number;\n\ttoolBlocksWithoutId: number;\n\t/** Responses whose first attempt ran on a different model (#33 decision 9). */\n\tfallbackAttempts: number;\n\tuntypedMirrors: number;\n\t/** Records with no parseable timestamp — cannot be priced time-awarely. */\n\tuntimestampedResponses: number;\n\tprojectDirs: Set<string>; // held only to count — names never leave this module\n\tccVersions: Set<string>;\n\tmirroredIterationTypes: Map<string, number>;\n\n\t// tokens\n\tbyModel: Map<string, ModelUsage>;\n\tsidechainTokens: number;\n\tmainTokens: number;\n\n\t// activity\n\tsessions: Set<string>;\n\tactiveDays: Set<string>; // UTC YYYY-MM-DD\n\tfirstTs: number | null;\n\tlastTs: number | null;\n\n\t// tools / skills / mcp / agents\n\ttoolCalls: Map<string, number>;\n\tskillCalls: Map<string, number>;\n\tmcpServerCalls: Map<string, number>;\n\tmcpToolCalls: Map<string, number>;\n\tsubagentCalls: Map<string, number>;\n\tslashCommands: Map<string, number>;\n\ttoolCallDedup: Set<string>;\n\n\t// content-block shape\n\tthinkingBlocks: number;\n\ttextBlocks: number;\n\twebSearchRequests: number;\n\twebFetchRequests: number;\n\n\t// adapter-owned dedup bookkeeping\n\tseen: Map<string, Seen>;\n};\n\nexport function createAggregate<Seen = unknown>(): Aggregate<Seen> {\n\treturn {\n\t\tfiles: 0,\n\t\tlines: 0,\n\t\tparseErrors: 0,\n\t\trecords: 0,\n\t\tassistantRecords: 0,\n\t\tdistinctResponses: 0,\n\t\tcontinuationsFolded: 0,\n\t\trealReplaysFolded: 0,\n\t\tsupersededByLarger: 0,\n\t\tunkeyedResponses: 0,\n\t\tsyntheticRecords: 0,\n\t\tsyntheticTokens: 0,\n\t\ttoolBlocksWithoutId: 0,\n\t\tfallbackAttempts: 0,\n\t\tuntypedMirrors: 0,\n\t\tuntimestampedResponses: 0,\n\t\tprojectDirs: new Set(),\n\t\tccVersions: new Set(),\n\t\tmirroredIterationTypes: new Map(),\n\t\tbyModel: new Map(),\n\t\tsidechainTokens: 0,\n\t\tmainTokens: 0,\n\t\tsessions: new Set(),\n\t\tactiveDays: new Set(),\n\t\tfirstTs: null,\n\t\tlastTs: null,\n\t\ttoolCalls: new Map(),\n\t\tskillCalls: new Map(),\n\t\tmcpServerCalls: new Map(),\n\t\tmcpToolCalls: new Map(),\n\t\tsubagentCalls: new Map(),\n\t\tslashCommands: new Map(),\n\t\ttoolCallDedup: new Set(),\n\t\tthinkingBlocks: 0,\n\t\ttextBlocks: 0,\n\t\twebSearchRequests: 0,\n\t\twebFetchRequests: 0,\n\t\tseen: new Map(),\n\t};\n}\n\nexport const bump = (m: Map<string, number>, k: string, n = 1) =>\n\tm.set(k, (m.get(k) ?? 0) + n);\n\nexport function emptyUsage(): ModelUsage {\n\treturn {\n\t\tinput: 0,\n\t\toutput: 0,\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: 0,\n\t\tcacheRead: 0,\n\t\tmessages: 0,\n\t\tcostUSD: 0,\n\t\tunpricedTokens: 0,\n\t};\n}\n\nexport const countsTotal = (t: TokenCounts): number =>\n\tt.input +\n\tt.output +\n\tt.cacheWrite5m +\n\tt.cacheWrite1h +\n\tt.cacheWriteUnsplit +\n\tt.cacheRead;\n\n/**\n * Fold one priced usage delta into the per-model totals. The Claude adapter\n * has its own apply/retract pair (dedup can un-count a response); an adapter\n * whose records are already deltas — Codex — adds through here.\n */\nexport function addModelUsage(\n\tagg: Aggregate<never> | Aggregate<unknown>,\n\tmodelKey: string,\n\tcounts: TokenCounts,\n\tcostUSD: number | null,\n\tmessages = 1,\n): void {\n\tlet m = agg.byModel.get(modelKey);\n\tif (!m) {\n\t\tm = emptyUsage();\n\t\tagg.byModel.set(modelKey, m);\n\t}\n\tm.messages += messages;\n\tm.input += counts.input;\n\tm.output += counts.output;\n\tm.cacheWrite5m += counts.cacheWrite5m;\n\tm.cacheWrite1h += counts.cacheWrite1h;\n\tm.cacheWriteUnsplit += counts.cacheWriteUnsplit;\n\tm.cacheRead += counts.cacheRead;\n\tif (costUSD === null) m.unpricedTokens += countsTotal(counts);\n\telse m.costUSD += costUSD;\n}\n\n// ---------------------------------------------------------------------------\n// Finalize — the shape the wire payload is derived from\n// ---------------------------------------------------------------------------\n\nexport type ModelRow = {\n\t/** Pricing key: normalized vendor id, plus `#fast` when speed was fast. */\n\tmodelKey: string;\n\ttokens: TokenCounts;\n\ttotalTokens: number;\n\tmessages: number;\n\tshare: number;\n\t/** Accumulated at each response's own rate. `null` when nothing was priced. */\n\tcostUSD: number | null;\n\t/** Tokens inside this row that no rate covered. */\n\tunpricedTokens: number;\n};\n\nexport type Finalized = {\n\tmodels: ModelRow[];\n\ttotalTokens: number;\n\ttotalCostUSD: number;\n\tunpricedModels: string[];\n\tunpricedTokens: number;\n\tcacheHitShare: number;\n\tsidechainShare: number;\n\tactiveDays: number;\n\tfirstTs: number | null;\n\tlastTs: number | null;\n\tsessions: number;\n\tprojects: number;\n\ttools: Array<[string, number]>;\n\tskills: Array<[string, number]>;\n\tmcpServers: Array<[string, number]>;\n\tsubagents: Array<[string, number]>;\n\tslashCommands: Array<[string, number]>;\n\ttotalToolCalls: number;\n\t/** Newest harness version observed, or null when none was recorded. */\n\tharnessVersion: string | null;\n};\n\nfunction buildModelRows(agg: Aggregate): {\n\trows: ModelRow[];\n\ttotalTokens: number;\n\ttotalCostUSD: number;\n\tunpricedModels: string[];\n\tunpricedTokens: number;\n} {\n\tconst rows: ModelRow[] = [];\n\tlet totalTokens = 0;\n\tlet totalCostUSD = 0;\n\tconst unpricedModels: string[] = [];\n\tlet unpricedTokens = 0;\n\n\tfor (const [modelKey, u] of agg.byModel) {\n\t\tconst tokens: TokenCounts = {\n\t\t\tinput: u.input,\n\t\t\toutput: u.output,\n\t\t\tcacheWrite5m: u.cacheWrite5m,\n\t\t\tcacheWrite1h: u.cacheWrite1h,\n\t\t\tcacheWriteUnsplit: u.cacheWriteUnsplit,\n\t\t\tcacheRead: u.cacheRead,\n\t\t};\n\t\tconst sum = countsTotal(tokens);\n\t\ttotalTokens += sum;\n\t\tif (u.unpricedTokens > 0) {\n\t\t\tunpricedModels.push(modelKey);\n\t\t\tunpricedTokens += u.unpricedTokens;\n\t\t}\n\t\ttotalCostUSD += u.costUSD;\n\t\trows.push({\n\t\t\tmodelKey,\n\t\t\ttokens,\n\t\t\ttotalTokens: sum,\n\t\t\tmessages: u.messages,\n\t\t\tshare: 0,\n\t\t\t// A model we hold no rate for at all reports null rather than $0.00,\n\t\t\t// so \"we can't price this\" never reads as \"this was free\".\n\t\t\tcostUSD: isPricedModel(modelKey) ? u.costUSD : null,\n\t\t\tunpricedTokens: u.unpricedTokens,\n\t\t});\n\t}\n\tfor (const r of rows) r.share = totalTokens ? r.totalTokens / totalTokens : 0;\n\trows.sort(\n\t\t(a, b) =>\n\t\t\tb.totalTokens - a.totalTokens || a.modelKey.localeCompare(b.modelKey),\n\t);\n\treturn { rows, totalTokens, totalCostUSD, unpricedModels, unpricedTokens };\n}\n\nfunction computeCacheHitShare(rows: ModelRow[]): number {\n\tlet cacheRead = 0;\n\tlet inputClass = 0;\n\tfor (const r of rows) {\n\t\tcacheRead += r.tokens.cacheRead;\n\t\tinputClass +=\n\t\t\tr.tokens.input +\n\t\t\tr.tokens.cacheRead +\n\t\t\tr.tokens.cacheWrite5m +\n\t\t\tr.tokens.cacheWrite1h +\n\t\t\tr.tokens.cacheWriteUnsplit;\n\t}\n\treturn inputClass ? cacheRead / inputClass : 0;\n}\n\n/**\n * Newest observed harness version, compared numerically per dotted segment\n * so `2.1.9` doesn't sort above `2.1.220`.\n */\nexport function newestVersion(versions: Iterable<string>): string | null {\n\tlet best: string | null = null;\n\tlet bestParts: number[] = [];\n\tfor (const v of versions) {\n\t\tconst parts = v.split(\".\").map((p) => Number.parseInt(p, 10));\n\t\tif (parts.some((n) => !Number.isFinite(n))) continue;\n\t\tif (best === null || compareParts(parts, bestParts) > 0) {\n\t\t\tbest = v;\n\t\t\tbestParts = parts;\n\t\t}\n\t}\n\treturn best;\n}\n\nfunction compareParts(a: number[], b: number[]): number {\n\tconst len = Math.max(a.length, b.length);\n\tfor (let i = 0; i < len; i++) {\n\t\tconst d = (a[i] ?? 0) - (b[i] ?? 0);\n\t\tif (d !== 0) return d;\n\t}\n\treturn 0;\n}\n\nexport function finalize(agg: Aggregate): Finalized {\n\tconst { rows, totalTokens, totalCostUSD, unpricedModels, unpricedTokens } =\n\t\tbuildModelRows(agg);\n\n\tconst byCount = (m: Map<string, number>): Array<[string, number]> =>\n\t\t[...m.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));\n\n\tlet totalToolCalls = 0;\n\tfor (const v of agg.toolCalls.values()) totalToolCalls += v;\n\tfor (const v of agg.mcpToolCalls.values()) totalToolCalls += v;\n\n\tconst sideTotal = agg.sidechainTokens + agg.mainTokens;\n\n\treturn {\n\t\tmodels: rows,\n\t\ttotalTokens,\n\t\ttotalCostUSD,\n\t\tunpricedModels,\n\t\tunpricedTokens,\n\t\tcacheHitShare: computeCacheHitShare(rows),\n\t\tsidechainShare: sideTotal ? agg.sidechainTokens / sideTotal : 0,\n\t\tactiveDays: agg.activeDays.size,\n\t\tfirstTs: agg.firstTs,\n\t\tlastTs: agg.lastTs,\n\t\tsessions: agg.sessions.size,\n\t\tprojects: agg.projectDirs.size,\n\t\ttools: byCount(agg.toolCalls),\n\t\tskills: byCount(agg.skillCalls),\n\t\tmcpServers: byCount(agg.mcpServerCalls),\n\t\tsubagents: byCount(agg.subagentCalls),\n\t\tslashCommands: byCount(agg.slashCommands),\n\t\ttotalToolCalls,\n\t\tharnessVersion: newestVersion(agg.ccVersions),\n\t};\n}\n","// The bundled curated allowlist — the fallback copy for `/api/sync-config`.\n//\n// Wayfinder ticket #37 (map #29), decision 4 of the wire-format grilling #33.\n//\n// WHAT BELONGS HERE, AND WHY IT IS SHORT\n// These four classes of name are user-chosen. A Skill called `acme-q3-pricing`,\n// an MCP server called `internal-billing`, a subagent called `client-migration`\n// — each is a real leak, and none of them is distinguishable from a public name\n// by shape.\n//\n// THE BAR (grilling #42): a name qualifies if the STRING carries no private\n// information no matter who typed it. That is a property of the string, not of\n// the user and not of the artifact.\n//\n// The bar is deliberately NOT \"the name identifies a public artifact, so\n// publishing it reveals nothing the user hasn't already published\". That was the\n// original wording and it is wrong: `stripe` is on this list, and publishing it\n// plainly does reveal something the user never published — that they use Stripe.\n// It cannot be the harm, because revealing what you use is the entire product.\n// The harm is narrower: strings drawn from the user's private vocabulary, which\n// leak a relationship (an employer, a client, a codename) rather than a\n// preference. `stripe` and `filesystem` are safe even for someone who named\n// their own server that by coincidence.\n//\n// Three sources meet that bar:\n// 1. Claude Code's own built-in subagent types and slash commands (vendor-\n// assigned, same class as a built-in tool name).\n// 2. Skills that ship with Claude Code itself.\n// 3. MCP servers with a public, documented, first-party endpoint.\n//\n// WHY THIS LIST DOES NOT NEED TO BE LONG (#42 decision 1)\n// It is no longer the only road to publishing a name. The approve gate offers\n// every kept-private name as an explicit, default-off tick, and the tick set\n// comes back down with the rest of the sync config. This list only exists to\n// spare a user from ticking boxes nobody would think twice about — so it can\n// stay strict, and every user-chosen name goes through the person who knows\n// whether it is a secret.\n//\n// The author's own `alp-river:*` plugin is deliberately NOT seeded, even though\n// it is genuinely published. This list is GLOBAL: seeding it would publish those\n// names for every user who installs the plugin without any of them ticking\n// anything, and an author adding their own names to the default everyone else\n// inherits is what would make the list untrustworthy for every other entry.\n//\n// `/api/sync-config` (ticket #38) serves the AUTHORITATIVE list. This copy only\n// covers the case where that endpoint can't be reached, which for an installed\n// user is permanent if the plugin never auto-updates. Growing the curated list\n// is server-side work; adding entries here only helps the offline case.\n\nimport type { CuratedAllowlist } from \"./allowlist.js\";\n\n/** Claude Code's own subagent types. Vendor-assigned, not user-chosen. */\nconst BUILTIN_SUBAGENTS = [\n\t\"(default)\",\n\t\"claude\",\n\t\"claude-code-guide\",\n\t\"Explore\",\n\t\"fork\",\n\t\"general-purpose\",\n\t\"Plan\",\n\t\"statusline-setup\",\n] as const;\n\n/** Skills bundled with Claude Code. */\nconst BUILTIN_SKILLS = [\n\t\"artifact-capabilities\",\n\t\"artifact-design\",\n\t\"claude-api\",\n\t\"code-review\",\n\t\"codebase-design\",\n\t\"dataviz\",\n\t\"diagnosing-bugs\",\n\t\"domain-modeling\",\n\t\"fewer-permission-prompts\",\n\t\"grilling\",\n\t\"init\",\n\t\"keybindings-help\",\n\t\"loop\",\n\t\"prototype\",\n\t\"research\",\n\t\"review\",\n\t\"run\",\n\t\"schedule\",\n\t\"security-review\",\n\t\"simplify\",\n\t\"tdd\",\n\t\"update-config\",\n] as const;\n\n/** Claude Code's own slash commands. */\nconst BUILTIN_SLASH_COMMANDS = [\n\t\"add-dir\",\n\t\"agents\",\n\t\"bug\",\n\t\"clear\",\n\t\"compact\",\n\t\"config\",\n\t\"context\",\n\t\"cost\",\n\t\"doctor\",\n\t\"effort\",\n\t\"exit\",\n\t\"export\",\n\t\"fast\",\n\t\"help\",\n\t\"hooks\",\n\t\"ide\",\n\t\"init\",\n\t\"login\",\n\t\"logout\",\n\t\"mcp\",\n\t\"memory\",\n\t\"model\",\n\t\"output-style\",\n\t\"permissions\",\n\t\"plugin\",\n\t\"privacy-settings\",\n\t\"release-notes\",\n\t\"resume\",\n\t\"review\",\n\t\"rewind\",\n\t\"security-review\",\n\t\"status\",\n\t\"statusline\",\n\t\"terminal-setup\",\n\t\"todos\",\n\t\"upgrade\",\n\t\"usage\",\n\t\"vim\",\n\t\"workflows\",\n] as const;\n\n/**\n * MCP servers with a public first-party endpoint.\n *\n * Matched against the server segment the analyzer parses out of an\n * `mcp__<server>__<tool>` name, which is the LOCAL alias the user configured —\n * so this only fires when the user kept the conventional name. A renamed server\n * is kept private, which is the correct direction to fail.\n *\n * ONE normalization applies first (#42 decision 5): a server provided by a\n * plugin is observed as `plugin_<plugin>_<server>`, a string Claude Code\n * generates rather than one the user typed. Strip that wrapper before matching,\n * and publish the NORMALIZED name. The safety property is that normalization can\n * only ever emit a string already on this list — a non-matching inner segment\n * emits nothing and the raw name falls through to the gate's review list — so a\n * bug here is bounded by an already-vetted set. If the upstream convention\n * changes, matching reverts to keeping names private: a fail-safe regression.\n */\nconst PUBLIC_MCP_SERVERS = [\n\t\"chrome-devtools\",\n\t\"context7\",\n\t\"deepwiki\",\n\t\"figma\",\n\t\"filesystem\",\n\t\"git\",\n\t\"github\",\n\t\"huggingface\",\n\t\"ide\",\n\t\"linear\",\n\t\"notion\",\n\t\"playwright\",\n\t\"puppeteer\",\n\t\"sentry\",\n\t\"slack\",\n\t\"stripe\",\n] as const;\n\nexport const BUNDLED_CURATED_ALLOWLIST: CuratedAllowlist = {\n\tmcpServers: PUBLIC_MCP_SERVERS,\n\tskills: BUILTIN_SKILLS,\n\tsubagents: BUILTIN_SUBAGENTS,\n\tslashCommands: BUILTIN_SLASH_COMMANDS,\n};\n","// Fail-closed name filtering for the measured layer.\n//\n// Wayfinder ticket #37 (map #29), decisions 2-4 of the wire-format grilling #33.\n//\n// THE INVERSION THIS FILE EXISTS TO PERFORM\n// The prototype's `toolCalls` map was a catch-all: anything that wasn't an\n// `mcp__*` tool, a Skill, or an Agent fell THROUGH into it, and from there into\n// the payload. That is denylist-shaped — a tool name nobody anticipated\n// publishes by default. Here a name publishes only if it matches a known list,\n// and everything else is withheld and published as a per-category count.\n//\n// Two classes of name, two mechanisms:\n// - Built-in Claude Code tool names are VENDOR-assigned and enumerable, so\n// they match a hardcoded literal set (BUILTIN_TOOLS below).\n// - MCP servers / Skills / subagents / slash commands are USER-chosen and can\n// carry a client name, a project codename, or an internal system's name.\n// They match a curated list fetched from aistack, with the bundled copy\n// below as the fallback.\n//\n// Model ids are exempt from all of this — see decision 3 and payload.ts.\n//\n// WHY FETCHED AND NOT ONLY BUNDLED (decision 4)\n// Third-party marketplace plugin auto-update defaults to OFF, and a\n// `plugin.json` whose `version` isn't bumped ships nothing. A bundled-only list\n// is, for an installed user, frozen forever — a Skill that becomes public next\n// month would never publish. The filtering itself still runs client-side:\n// fail-closed only means something if it happens before the send.\n\nimport { isDisplaySafeName } from \"./aggregate.js\";\nimport { BUNDLED_CURATED_ALLOWLIST } from \"./bundled-allowlist.js\";\n\n/**\n * Every built-in tool Claude Code can emit as a `tool_use` block name.\n *\n * Deliberately a literal set and not a pattern: a pattern is a denylist wearing\n * a hat. Grounded in the observed corpus (22 distinct names across 235,961\n * records) plus the documented tool surface, including tools that are deferred\n * or unavailable in most sessions — an unknown-but-real built-in withheld as a\n * count is a small loss; an unknown-and-user-named tool published verbatim is\n * the leak this whole file prevents.\n *\n * `Task` is the pre-rename spelling of `Agent`; the analyzer folds it into\n * `Agent` at ingest, so it is here only to make the set self-documenting.\n */\nexport const BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"Agent\",\n\t\"Artifact\",\n\t\"AskUserQuestion\",\n\t\"Bash\",\n\t\"BashOutput\",\n\t\"CronCreate\",\n\t\"CronDelete\",\n\t\"CronList\",\n\t\"DesignSync\",\n\t\"Edit\",\n\t\"EndConversation\",\n\t\"EnterPlanMode\",\n\t\"EnterWorktree\",\n\t\"ExitPlanMode\",\n\t\"ExitWorktree\",\n\t\"Glob\",\n\t\"Grep\",\n\t\"KillBash\",\n\t\"KillShell\",\n\t\"ListMcpResourcesTool\",\n\t\"LS\",\n\t\"Monitor\",\n\t\"MultiEdit\",\n\t\"NotebookEdit\",\n\t\"NotebookRead\",\n\t\"PushNotification\",\n\t\"Read\",\n\t\"ReadMcpResourceDirTool\",\n\t\"ReadMcpResourceTool\",\n\t\"RemoteTrigger\",\n\t\"ReportFindings\",\n\t\"ScheduleWakeup\",\n\t\"SendMessage\",\n\t\"SendUserFile\",\n\t\"Skill\",\n\t\"SlashCommand\",\n\t\"Task\",\n\t\"TaskCreate\",\n\t\"TaskGet\",\n\t\"TaskList\",\n\t\"TaskOutput\",\n\t\"TaskStop\",\n\t\"TaskUpdate\",\n\t\"TodoWrite\",\n\t\"ToolSearch\",\n\t\"WebFetch\",\n\t\"WebSearch\",\n\t\"Workflow\",\n\t\"Write\",\n]);\n\n/** The four user-chosen atom classes that need the curated list. */\nexport type CuratedAllowlist = {\n\tmcpServers: readonly string[];\n\tskills: readonly string[];\n\tsubagents: readonly string[];\n\tslashCommands: readonly string[];\n};\n\n/** The five inventory classes the payload carries. */\nexport const NAME_CATEGORIES = [\n\t\"builtinTools\",\n\t\"mcpServers\",\n\t\"skills\",\n\t\"subagents\",\n\t\"slashCommands\",\n] as const;\n\nexport type NameCategory = (typeof NAME_CATEGORIES)[number];\n\n/**\n * Names this stack's owner has explicitly ticked for publication (#42\n * decision 1), served per-stack by the authenticated half of `/api/sync-config`.\n *\n * The curated list is a convenience default, not the coverage mechanism: every\n * user-chosen name class is unbounded and unenumerable, so a hand-curated list\n * can only ever be a rounding error against the real population. Coverage comes\n * from here — from the person who knows which of their names are secret.\n *\n * `builtinTools` is included for symmetry even though that class is\n * vendor-assigned: a built-in this version of the client has never heard of is\n * kept private like anything else, and the owner can tick it.\n */\nexport type OptInNames = Record<NameCategory, readonly string[]>;\n\nexport const EMPTY_OPT_INS: OptInNames = {\n\tbuiltinTools: [],\n\tmcpServers: [],\n\tskills: [],\n\tsubagents: [],\n\tslashCommands: [],\n};\n\nexport type SyncConfig = {\n\tallowlist: CuratedAllowlist;\n\t/**\n\t * Stack-level cost preference (decision 11). When false the payload omits\n\t * cost entirely rather than zeroing it — see payload.ts.\n\t */\n\tpublishCost: boolean;\n\t/** Per-stack ticked names, unioned into the allowlist before filtering. */\n\toptIns: OptInNames;\n\t/**\n\t * Whether this stack stages its kept-private names on the web so the owner\n\t * can tick them there (#48). Off means the machine sends the payload alone\n\t * and the names never leave it.\n\t */\n\treviewKeptPrivate: boolean;\n\t/**\n\t * The stack the bearer token is bound to — where a publish would land.\n\t *\n\t * The approve gate must name its destination BEFORE the send (#33\n\t * decision 7, #41), and beat one points at `/stacks/{slug}/changes` (#48),\n\t * so both ride on the authenticated half of the config fetch. `null` when\n\t * the fetch was anonymous, failed, or the token resolved no stack — and a\n\t * gate that cannot name its destination must not publish.\n\t */\n\tstack: { name: string; slug: string } | null;\n};\n\n/**\n * Used when `/api/sync-config` can't be reached.\n *\n * `publishCost: false` is deliberate. The toggle is a stack-level preference we\n * do not hold locally, and the fail-closed default for a preference we can't\n * read is the one that transmits less. A user whose fetch failed sees cost\n * missing from the gate and can retry; the reverse — publishing cost the stack\n * had opted out of — is not recoverable, because the snapshot is immutable.\n */\nexport const BUNDLED_SYNC_CONFIG: SyncConfig = {\n\tallowlist: BUNDLED_CURATED_ALLOWLIST,\n\tpublishCost: false,\n\t// Empty for the same reason, and it is the load-bearing half of #42\n\t// decision 2: a failed config fetch reverts every ticked name to\n\t// kept-private. Losing the network publishes LESS, never more.\n\toptIns: EMPTY_OPT_INS,\n\t// Same direction again (#48): a machine that cannot read the switch does not\n\t// upload the names it is holding back. The default is ON server-side, so this\n\t// costs the owner one retry and never costs them a name.\n\treviewKeptPrivate: false,\n\t// No fetch, no destination — and the gate refuses to publish without one.\n\tstack: null,\n};\n\n// ---------------------------------------------------------------------------\n// Fetch\n// ---------------------------------------------------------------------------\n\nconst SYNC_CONFIG_PATH = \"/api/sync-config\";\nconst FETCH_TIMEOUT_MS = 5_000;\n\nexport type SyncConfigSource = \"fetched\" | \"bundled\";\n\nexport type LoadedSyncConfig = {\n\tconfig: SyncConfig;\n\tsource: SyncConfigSource;\n\t/** Present when the fetch failed and the bundled copy was used. */\n\terror?: string;\n};\n\n/**\n * A name arriving from the network is no more trusted than one from a\n * transcript. Names are matched by exact equality, so a hostile list can widen\n * what publishes but can never smuggle a wildcard — and the approve gate\n * renders every name that will publish, which is what defuses that residual\n * trust (decision 4). Charset and length are still bounded so a pathological\n * entry can't reach a terminal or a database column.\n */\nconst CURATED_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9 ._:@/-]{0,63}$/;\n\nfunction readNameList(v: unknown): string[] {\n\tif (!Array.isArray(v)) return [];\n\tconst out: string[] = [];\n\tfor (const item of v) {\n\t\tif (typeof item === \"string\" && CURATED_NAME_RE.test(item)) out.push(item);\n\t}\n\treturn out;\n}\n\n/**\n * Opt-ins are read against a LOOSER bar than the curated list.\n *\n * A curated entry is ours and conventional, so the tight charset costs nothing.\n * An opt-in is the user's own name — `(default)`, an accented word, a CJK skill\n * — and dropping it here would silently un-tick a decision they made at the\n * gate. The bar that survives is the one that matters for a string we print and\n * store: no control characters, no bidi overrides, bounded length.\n */\nfunction readOptInList(v: unknown): string[] {\n\tif (!Array.isArray(v)) return [];\n\tconst out: string[] = [];\n\tfor (const item of v) {\n\t\tif (typeof item === \"string\" && isDisplaySafeName(item)) out.push(item);\n\t}\n\treturn out;\n}\n\nfunction readOptIns(v: unknown): OptInNames {\n\tif (typeof v !== \"object\" || v === null || Array.isArray(v))\n\t\treturn EMPTY_OPT_INS;\n\tconst obj = v as Record<string, unknown>;\n\treturn {\n\t\tbuiltinTools: readOptInList(obj.builtinTools),\n\t\tmcpServers: readOptInList(obj.mcpServers),\n\t\tskills: readOptInList(obj.skills),\n\t\tsubagents: readOptInList(obj.subagents),\n\t\tslashCommands: readOptInList(obj.slashCommands),\n\t};\n}\n\n/**\n * A slug becomes a URL path segment the gate prints, so it gets the tightest\n * bar of any string here. The name is display text and gets `isDisplaySafeName`.\n */\nconst STACK_SLUG_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;\n\nfunction readStack(v: unknown): SyncConfig[\"stack\"] {\n\tif (typeof v !== \"object\" || v === null || Array.isArray(v)) return null;\n\tconst obj = v as Record<string, unknown>;\n\tif (typeof obj.name !== \"string\" || !isDisplaySafeName(obj.name)) return null;\n\tif (typeof obj.slug !== \"string\" || !STACK_SLUG_RE.test(obj.slug))\n\t\treturn null;\n\treturn { name: obj.name, slug: obj.slug };\n}\n\nfunction readSyncConfig(raw: unknown): SyncConfig | null {\n\tif (typeof raw !== \"object\" || raw === null || Array.isArray(raw))\n\t\treturn null;\n\tconst obj = raw as Record<string, unknown>;\n\tconst listRaw = obj.allowlist;\n\tif (typeof listRaw !== \"object\" || listRaw === null) return null;\n\tconst list = listRaw as Record<string, unknown>;\n\treturn {\n\t\tallowlist: {\n\t\t\tmcpServers: readNameList(list.mcpServers),\n\t\t\tskills: readNameList(list.skills),\n\t\t\tsubagents: readNameList(list.subagents),\n\t\t\tslashCommands: readNameList(list.slashCommands),\n\t\t},\n\t\t// Anything other than an explicit `true` fails closed.\n\t\tpublishCost: obj.publishCost === true,\n\t\t// Absent means \"no stack resolved\" — an anonymous fetch, or a token bound\n\t\t// to nothing. Both fail closed to publishing no user-chosen names.\n\t\toptIns: readOptIns(obj.optIns),\n\t\t// Anything other than an explicit `true` keeps the names on the machine.\n\t\treviewKeptPrivate: obj.reviewKeptPrivate === true,\n\t\tstack: readStack(obj.stack),\n\t};\n}\n\n/**\n * Fetch the curated allowlist and the cost preference, falling back to the\n * bundled copy on any failure. Never throws — an unreachable aistack must not\n * prevent a local analysis from running, it must only narrow what could publish.\n */\nexport async function loadSyncConfig(opts: {\n\tbaseUrl: string;\n\t/**\n\t * Bearer for the authenticated half: `publishCost`, `optIns`,\n\t * `reviewKeptPrivate` and the destination stack. Absent, the server answers\n\t * with the anonymous fail-closed body — same allowlist, everything else off.\n\t */\n\ttoken?: string;\n\tfetchImpl?: typeof fetch;\n\ttimeoutMs?: number;\n}): Promise<LoadedSyncConfig> {\n\tconst doFetch = opts.fetchImpl ?? fetch;\n\ttry {\n\t\tconst res = await doFetch(`${opts.baseUrl}${SYNC_CONFIG_PATH}`, {\n\t\t\tsignal: AbortSignal.timeout(opts.timeoutMs ?? FETCH_TIMEOUT_MS),\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/json\",\n\t\t\t\t...(opts.token ? { Authorization: `Bearer ${opts.token}` } : {}),\n\t\t\t},\n\t\t});\n\t\tif (!res.ok) {\n\t\t\treturn {\n\t\t\t\tconfig: BUNDLED_SYNC_CONFIG,\n\t\t\t\tsource: \"bundled\",\n\t\t\t\terror: `sync-config returned ${res.status}`,\n\t\t\t};\n\t\t}\n\t\tconst parsed = readSyncConfig(await res.json());\n\t\tif (!parsed) {\n\t\t\treturn {\n\t\t\t\tconfig: BUNDLED_SYNC_CONFIG,\n\t\t\t\tsource: \"bundled\",\n\t\t\t\terror: \"sync-config response was not the expected shape\",\n\t\t\t};\n\t\t}\n\t\treturn { config: parsed, source: \"fetched\" };\n\t} catch (err) {\n\t\treturn {\n\t\t\tconfig: BUNDLED_SYNC_CONFIG,\n\t\t\tsource: \"bundled\",\n\t\t\terror: err instanceof Error ? err.message : \"sync-config fetch failed\",\n\t\t};\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Filtering\n// ---------------------------------------------------------------------------\n\nexport type Atom = { name: string; count: number };\n\n/**\n * One observed name that will NOT publish, as the approve gate needs to render\n * it: the raw string, how often it ran, and the plugin it came from.\n *\n * Local only — this never enters the payload. It exists because the gate offers\n * every kept-private name as an explicit, default-off tick (#42 decision 1), and\n * it cannot offer what the analyzer does not hand back.\n */\nexport type KeptPrivateAtom = {\n\tname: string;\n\tcount: number;\n\t/** Plugin prefix, for the gate's grouped bulk tick. `null` when standalone. */\n\tgroup: string | null;\n};\n\nexport type FilteredAtoms = {\n\t/** Publishable names, ordered by count descending. */\n\tallowed: Atom[];\n\t/** The rest, with everything the gate needs to offer them as ticks. */\n\tkeptPrivate: KeptPrivateAtom[];\n\t/** How many DISTINCT names were kept private. */\n\twithheld: number;\n};\n\n/**\n * A server an MCP plugin provides is observed as `plugin_<plugin>_<server>`.\n *\n * That whole string is GENERATED by Claude Code — the user typed none of it —\n * which is a different class from a hand-edited `.mcp.json` alias. Strip the\n * wrapper before matching (#42 decision 5).\n *\n * The split takes the FIRST underscore-free segment as the plugin name. A plugin\n * whose own name carries an underscore therefore splits wrong, the inner segment\n * matches nothing, and the raw name stays kept private — the same direction\n * every other miss fails in.\n */\nconst PLUGIN_MCP_RE = /^plugin_([^_]+)_(.+)$/;\n\n/** `plugin:artifact` is the convention for a plugin's skills and subagents. */\nconst PLUGIN_PREFIX_RE = /^([^:\\s]+):(.+)$/;\n\n/**\n * The plugin a name came from, for the gate's grouped bulk tick.\n *\n * Grouping is a UI affordance only. What the gate STORES is every name in the\n * group, expanded (#42 decision 3): a stored `alp-river:*` would be a standing\n * grant to names that do not exist yet, and nobody can consent to a name they\n * have not thought of.\n */\nexport function pluginGroup(name: string): string | null {\n\treturn (\n\t\tPLUGIN_MCP_RE.exec(name)?.[1] ?? PLUGIN_PREFIX_RE.exec(name)?.[1] ?? null\n\t);\n}\n\nexport type FilterSets = {\n\t/** Curated list UNION this stack's opt-ins. A match here publishes verbatim. */\n\tpublishable: ReadonlySet<string>;\n\t/**\n\t * The curated list alone — the only target normalization may match.\n\t *\n\t * This is what makes the normalization safe to state in one line:\n\t * normalization can only ever emit a string that is already curated. The\n\t * blast radius of a bug in it is an already-vetted set, by construction.\n\t */\n\tcurated: ReadonlySet<string>;\n};\n\n/**\n * Resolve the name an atom would publish under, or `null` to keep it private.\n *\n * Raw match first, so a name the owner ticked publishes exactly as they saw it\n * at the gate. Only an unmatched name is normalized, and only against the\n * curated list.\n */\nfunction publishedName(name: string, sets: FilterSets): string | null {\n\tif (sets.publishable.has(name)) return name;\n\tconst inner = PLUGIN_MCP_RE.exec(name)?.[2];\n\tif (inner && sets.curated.has(inner)) return inner;\n\treturn null;\n}\n\n/**\n * Split observed atoms into what publishes and what stays on the machine.\n *\n * The withheld figure counts distinct names, not calls: it answers \"how much of\n * my inventory is not shown\", which is the honesty question, without leaking\n * how heavily any single kept-private thing is used.\n *\n * Counts are merged by PUBLISHED name, because normalization can map two\n * observed names onto one — a plugin-provided `chrome-devtools` and a directly\n * configured one both publish as `chrome-devtools`, and two rows with the same\n * name would double-count that server in the rendered inventory.\n */\nexport function filterAtoms(\n\tatoms: readonly Atom[],\n\tsets: FilterSets,\n): FilteredAtoms {\n\tconst merged = new Map<string, number>();\n\tconst keptPrivate: KeptPrivateAtom[] = [];\n\tfor (const atom of atoms) {\n\t\tconst published = publishedName(atom.name, sets);\n\t\tif (published === null) {\n\t\t\tkeptPrivate.push({\n\t\t\t\tname: atom.name,\n\t\t\t\tcount: atom.count,\n\t\t\t\tgroup: pluginGroup(atom.name),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tmerged.set(published, (merged.get(published) ?? 0) + atom.count);\n\t}\n\tconst allowed = [...merged].map(([name, count]) => ({ name, count }));\n\tallowed.sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));\n\tkeptPrivate.sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));\n\treturn { allowed, keptPrivate, withheld: keptPrivate.length };\n}\n","// Pure fold over parsed Claude Code transcript records. No I/O, no console.\n//\n// Wayfinder ticket #37 (map #29), productizing the #32 prototype. Field\n// semantics come from docs/research/claude-code-transcripts-2026-07.md (#30),\n// as corrected by #32 and #33. Every field is treated as untrusted and\n// optional: records arrive as `unknown` and are narrowed here.\n//\n// The shared aggregate/finalize machinery lives in ../shared/aggregate.ts\n// (#67); this file owns what is CLAUDE-specific — the record shapes, and the\n// response dedup below.\n//\n// THE LOAD-BEARING SUBTLETY — read before touching `ingestAssistant`.\n// Claude Code writes ONE API response as SEVERAL JSONL records: each carries a\n// distinct content block (thinking, then tool_use, then tool_use...) and a\n// *cumulative* `usage` snapshot that grows with each record. Measured on a real\n// corpus: 20,073 of 44,280 response groups have differing usage across their\n// records, 20,071 of them monotonically increasing.\n//\n// So there are three wrong ways to count and one right way:\n// - sum every record -> ~2x over\n// - keep the first record -> ~2.1x under\n// - keep the last record -> right, but relies on file order\n// - keep the largest total -> right, order-independent <- this\n// Keeping the largest total is also ccusage's documented rule\n// (`should_replace_deduped_entry`).\n//\n// THE SECOND SUBTLETY — cost accumulates HERE, not in `finalize`.\n// Decision 8 of #33 made pricing time-aware, so a response is priced at the\n// rate in effect at its own timestamp. Summing tokens per model and pricing\n// once at the end cannot express a mid-window rate change, so each response's\n// cost is computed as it is ingested and un-applied on replace.\n\nimport {\n\tasArr,\n\tasName,\n\tasNum,\n\tasObj,\n\tasStr,\n\tbump,\n\tcleanName,\n\tcountsTotal,\n\tcreateAggregate as createSharedAggregate,\n\temptyUsage,\n\ttype Obj,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\nimport {\n\tapiEquivalentCost,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"../shared/pricing.js\";\n\n// Re-exported for the existing import sites (tests, stage, summary); the\n// definitions moved to ../shared/aggregate.ts in #67.\nexport {\n\tcleanName,\n\ttype Finalized,\n\tfinalize,\n\tisDisplaySafeName,\n\ttype ModelRow,\n\ttype ModelUsage,\n\tnewestVersion,\n} from \"../shared/aggregate.js\";\n\ntype Entry = {\n\tmodelKey: string;\n\tcounts: TokenCounts;\n\t/** `null` = no rate applied at this response's timestamp. */\n\tcostUSD: number | null;\n};\n\n/** One API response's full contribution, kept so it can be un-applied on replace. */\ntype Contribution = {\n\tentries: Entry[];\n\ttotal: number;\n\tsidechain: boolean;\n\twebSearch: number;\n\twebFetch: number;\n\t/** Iteration types that mirrored top-level usage, for the diagnostics line. */\n\tmirroredIterationTypes: Array<[string, number]>;\n\t/** Iterations naming a different model, attributed to that model (#33 dec. 9). */\n\tfallbackAttempts: number;\n\t/** Mirror-suspected iterations with no `model` field — skipped, not billed. */\n\tuntypedMirrors: number;\n};\n\ntype SeenEntry = { requestId: string | null; contribution: Contribution };\n\n/**\n * The Claude adapter's aggregate: the shared fold target, with `seen` keyed\n * by `message.id` holding this adapter's replay/continuation bookkeeping.\n */\nexport type Aggregate = SharedAggregate<SeenEntry>;\n\nexport function createAggregate(): Aggregate {\n\treturn createSharedAggregate<SeenEntry>();\n}\n\n// ---------------------------------------------------------------------------\n// Ingest\n// ---------------------------------------------------------------------------\n\nexport type IngestContext = { projectDir: string };\n\n/** Fold one parsed JSONL record into the aggregate. */\nexport function ingestRecord(\n\tagg: Aggregate,\n\traw: unknown,\n\tctx: IngestContext,\n): void {\n\tconst rec = asObj(raw);\n\tif (!rec) return;\n\n\tagg.records++;\n\tagg.projectDirs.add(ctx.projectDir);\n\n\tconst version = asStr(rec.version);\n\tif (version) agg.ccVersions.add(cleanName(version));\n\tconst sessionId = asStr(rec.sessionId);\n\tif (sessionId) agg.sessions.add(sessionId);\n\n\tlet tsMs: number | null = null;\n\tconst timestamp = asStr(rec.timestamp);\n\tif (timestamp) {\n\t\tconst ts = Date.parse(timestamp);\n\t\tif (!Number.isNaN(ts)) {\n\t\t\ttsMs = ts;\n\t\t\tagg.activeDays.add(timestamp.slice(0, 10));\n\t\t\tagg.firstTs = agg.firstTs === null ? ts : Math.min(agg.firstTs, ts);\n\t\t\tagg.lastTs = agg.lastTs === null ? ts : Math.max(agg.lastTs, ts);\n\t\t}\n\t}\n\n\tconst type = asStr(rec.type);\n\tif (type === \"assistant\") ingestAssistant(agg, rec, tsMs);\n\telse if (type === \"user\") ingestUser(agg, rec);\n}\n\nfunction ingestAssistant(agg: Aggregate, rec: Obj, tsMs: number | null): void {\n\tagg.assistantRecords++;\n\tconst msg = asObj(rec.message);\n\tif (!msg) return;\n\n\tconst messageId = asStr(msg.id);\n\tconst requestId = asStr(rec.requestId);\n\tconst existing = messageId === null ? undefined : agg.seen.get(messageId);\n\t// A genuine replay is the same message.id under a NEW requestId. Its records\n\t// repeat content already counted; a continuation's records do not.\n\tconst isReplay = existing !== undefined && existing.requestId !== requestId;\n\n\t// Content blocks are counted per RECORD, deliberately outside the token\n\t// fold: the records of ONE response carry disjoint blocks (verified across\n\t// 44,478 groups — zero overlap), so folding them would drop real blocks.\n\t// Replays are the exception and must be skipped, because `tool_use` has\n\t// `block.id` to dedup on but thinking/text blocks have no identity at all.\n\tif (!isReplay) ingestContentBlocks(agg, msg.content);\n\n\tconst usage = asObj(msg.usage);\n\tif (!usage) return;\n\n\tconst model = asName(msg.model) ?? \"(unknown)\";\n\t// `<synthetic>` is the harness's own pseudo-model for records it generates\n\t// itself. Not a tool the user chose — excluded from inventory and pricing,\n\t// but its tokens are surfaced rather than silently dropped.\n\tif (model.startsWith(\"<\")) {\n\t\tagg.syntheticRecords++;\n\t\tagg.syntheticTokens += countsTotal(readCounts(usage));\n\t\treturn;\n\t}\n\n\tif (tsMs === null) agg.untimestampedResponses++;\n\n\tconst sidechain = rec.isSidechain === true;\n\tconst contribution = buildContribution(usage, model, sidechain, tsMs);\n\n\tif (messageId === null) {\n\t\t// No dedup key available — count it and record that we were unprotected.\n\t\tagg.unkeyedResponses++;\n\t\tacceptContribution(agg, contribution);\n\t\treturn;\n\t}\n\n\tif (existing === undefined) {\n\t\tagg.distinctResponses++;\n\t\tacceptContribution(agg, contribution);\n\t\tagg.seen.set(messageId, { requestId, contribution });\n\t\treturn;\n\t}\n\n\tif (isReplay) agg.realReplaysFolded++;\n\telse agg.continuationsFolded++;\n\n\tif (!supersedes(contribution, existing.contribution)) return;\n\n\tagg.supersededByLarger++;\n\tretractContribution(agg, existing.contribution);\n\tacceptContribution(agg, contribution);\n\t// Keep the FIRST-seen requestId, not this record's. If a genuine replay wins\n\t// on tokens, overwriting it would make the replay's own later records compare\n\t// equal to the stored id, read as continuations, and get their thinking/text\n\t// blocks counted a second time — reopening exactly what the `isReplay` gate\n\t// above exists to close. (tool_use survives either way via `block.id`.)\n\tagg.seen.set(messageId, { requestId: existing.requestId, contribution });\n}\n\n/**\n * Apply a contribution and tally its diagnostics. Paired with\n * `retractContribution` so every per-response census stays per-RESPONSE rather\n * than per-record — these used to be bumped while merely *building* a\n * contribution, which counted every folded continuation too.\n */\nfunction acceptContribution(agg: Aggregate, c: Contribution): void {\n\tapplyContribution(agg, c, +1);\n}\n\nfunction retractContribution(agg: Aggregate, c: Contribution): void {\n\tapplyContribution(agg, c, -1);\n}\n\n/**\n * ccusage's collision rule: a non-sidechain copy beats a sidechain one;\n * otherwise the larger token total wins. Order-independent by construction,\n * so the result does not depend on filesystem traversal order.\n */\nfunction supersedes(next: Contribution, prev: Contribution): boolean {\n\tif (prev.sidechain !== next.sidechain)\n\t\treturn prev.sidechain && !next.sidechain;\n\treturn next.total > prev.total;\n}\n\nfunction readCounts(usage: Obj): TokenCounts {\n\tconst t: TokenCounts = {\n\t\tinput: asNum(usage.input_tokens),\n\t\toutput: asNum(usage.output_tokens),\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: 0,\n\t\tcacheRead: asNum(usage.cache_read_input_tokens),\n\t};\n\tconst cacheWriteTotal = asNum(usage.cache_creation_input_tokens);\n\tconst cc = asObj(usage.cache_creation);\n\tif (cc) {\n\t\tt.cacheWrite5m = asNum(cc.ephemeral_5m_input_tokens);\n\t\tt.cacheWrite1h = asNum(cc.ephemeral_1h_input_tokens);\n\t\tconst residual = cacheWriteTotal - (t.cacheWrite5m + t.cacheWrite1h);\n\t\tif (residual > 0) t.cacheWriteUnsplit = residual;\n\t} else {\n\t\tt.cacheWriteUnsplit = cacheWriteTotal;\n\t}\n\treturn t;\n}\n\n/** `usage.speed === \"fast\"` prices under a separate, higher rate. */\nfunction modelKeyFor(model: string, speed: string | null): string {\n\treturn normalizeModel(speed === \"fast\" ? `${model}#fast` : model);\n}\n\nfunction makeEntry(\n\tmodelKey: string,\n\tcounts: TokenCounts,\n\ttsMs: number | null,\n): Entry {\n\treturn {\n\t\tmodelKey,\n\t\tcounts,\n\t\tcostUSD: apiEquivalentCost(modelKey, counts, tsMs),\n\t};\n}\n\nfunction buildContribution(\n\tusage: Obj,\n\tmodel: string,\n\tsidechain: boolean,\n\ttsMs: number | null,\n): Contribution {\n\tconst modelKey = modelKeyFor(model, asStr(usage.speed));\n\tconst entries: Entry[] = [makeEntry(modelKey, readCounts(usage), tsMs)];\n\tconst mirrored = new Map<string, number>();\n\tlet fallbackAttempts = 0;\n\tlet untypedMirrors = 0;\n\n\tfor (const rawIt of asArr(usage.iterations)) {\n\t\tconst it = asObj(rawIt);\n\t\tif (!it) continue;\n\t\tconst itType = asName(it.type) ?? \"(untyped)\";\n\t\tconst itModel = asName(it.model);\n\t\tconst itKey =\n\t\t\titModel === null ? null : modelKeyFor(itModel, asStr(it.speed));\n\n\t\t// Advisor iterations are a genuinely separate billed call under their own\n\t\t// model, never a mirror of top-level usage (ccusage prices them apart).\n\t\tif (itType === \"advisor_message\") {\n\t\t\tentries.push(makeEntry(itKey ?? modelKey, readCounts(it), tsMs));\n\t\t\tcontinue;\n\t\t}\n\n\t\t// #33 decision 9, SHARPENED — read the whole comment before touching this.\n\t\t//\n\t\t// The prototype skipped EVERY `type: \"message\"` iteration as a mirror of\n\t\t// top-level usage, which was correct by luck rather than construction: a\n\t\t// real `fallback_message` record showed top-level usage equal to the\n\t\t// fallback iteration EXACTLY, while a sibling `type: message` iteration\n\t\t// named a DIFFERENT model and carried tokens recorded nowhere else. So\n\t\t// `message.model` is already the serving model, and the mirror test is the\n\t\t// MODEL, not the type.\n\t\t//\n\t\t// #33 phrased the fix as \"skip it only when `iter.model === message.model`\".\n\t\t// Taken literally that is a ~2x overcount, because the corpus says the\n\t\t// `model` field is almost never there: of 63,638 non-advisor iterations,\n\t\t// 63,634 carry NO `model` at all — and all 63,634 are byte-exact mirrors of\n\t\t// their record's top-level usage (measured: zero differ). They carry 7.24\n\t\t// BILLION tokens, nearly double the corpus total, so attributing them as\n\t\t// separate entries would roughly double both tokens and cost. Only 8\n\t\t// iterations name a model: 4 matching (the `fallback_message` entries) and\n\t\t// 4 differing (the real first attempts).\n\t\t//\n\t\t// So the operative rule is: SKIP UNLESS THE ITERATION NAMES A DIFFERENT\n\t\t// MODEL. Absent is treated as matching — mis-attributing is a double-bill,\n\t\t// skipping is at worst an undercount, and the measurement above says it is\n\t\t// not even that.\n\t\tif (itKey === null) {\n\t\t\tuntypedMirrors++;\n\t\t\tbump(mirrored, itType);\n\t\t\tcontinue;\n\t\t}\n\t\tif (itKey === modelKey) {\n\t\t\tbump(mirrored, itType);\n\t\t\tcontinue;\n\t\t}\n\t\tentries.push(makeEntry(itKey, readCounts(it), tsMs));\n\t\tfallbackAttempts++;\n\t}\n\n\tconst serverTools = asObj(usage.server_tool_use);\n\treturn {\n\t\tentries,\n\t\ttotal: entries.reduce((a, e) => a + countsTotal(e.counts), 0),\n\t\tsidechain,\n\t\twebSearch: serverTools ? asNum(serverTools.web_search_requests) : 0,\n\t\twebFetch: serverTools ? asNum(serverTools.web_fetch_requests) : 0,\n\t\tmirroredIterationTypes: [...mirrored],\n\t\tfallbackAttempts,\n\t\tuntypedMirrors,\n\t};\n}\n\n/** Add (sign +1) or remove (sign -1) a response's contribution from the totals. */\nfunction applyContribution(\n\tagg: Aggregate,\n\tc: Contribution,\n\tsign: 1 | -1,\n): void {\n\tc.entries.forEach(({ modelKey, counts, costUSD }, i) => {\n\t\tlet m = agg.byModel.get(modelKey);\n\t\tif (!m) {\n\t\t\tm = emptyUsage();\n\t\t\tagg.byModel.set(modelKey, m);\n\t\t}\n\t\t// One response is one message, even when a fallback attempt or an advisor\n\t\t// iteration attributes tokens to a second model — counting per entry would\n\t\t// inflate the response total past distinctResponses.\n\t\tif (i === 0) m.messages += sign;\n\t\tm.input += sign * counts.input;\n\t\tm.output += sign * counts.output;\n\t\tm.cacheWrite5m += sign * counts.cacheWrite5m;\n\t\tm.cacheWrite1h += sign * counts.cacheWrite1h;\n\t\tm.cacheWriteUnsplit += sign * counts.cacheWriteUnsplit;\n\t\tm.cacheRead += sign * counts.cacheRead;\n\t\tif (costUSD === null) m.unpricedTokens += sign * countsTotal(counts);\n\t\telse m.costUSD += sign * costUSD;\n\t});\n\tif (c.sidechain) agg.sidechainTokens += sign * c.total;\n\telse agg.mainTokens += sign * c.total;\n\tagg.webSearchRequests += sign * c.webSearch;\n\tagg.webFetchRequests += sign * c.webFetch;\n\tagg.fallbackAttempts += sign * c.fallbackAttempts;\n\tagg.untypedMirrors += sign * c.untypedMirrors;\n\tfor (const [type, count] of c.mirroredIterationTypes) {\n\t\tbump(agg.mirroredIterationTypes, type, sign * count);\n\t}\n}\n\nfunction ingestContentBlocks(agg: Aggregate, content: unknown): void {\n\tfor (const rawBlock of asArr(content)) {\n\t\tconst block = asObj(rawBlock);\n\t\tif (!block) continue;\n\t\tconst type = asStr(block.type);\n\t\tif (type === \"thinking\") agg.thinkingBlocks++;\n\t\telse if (type === \"text\") agg.textBlocks++;\n\t\telse if (type === \"tool_use\") ingestToolUse(agg, block);\n\t}\n}\n\nfunction ingestToolUse(agg: Aggregate, block: Obj): void {\n\tconst name = asName(block.name);\n\tif (!name) return;\n\n\t// `toolu_...` block ids are globally unique, which makes this key both\n\t// collision-proof and replay-proof without a record-level prefix. A block\n\t// with no id is skipped rather than folded under a name-only key, which\n\t// would silently collapse every call to that tool into one.\n\tconst blockId = asStr(block.id);\n\tif (!blockId) {\n\t\tagg.toolBlocksWithoutId++;\n\t\treturn;\n\t}\n\tif (agg.toolCallDedup.has(blockId)) return;\n\tagg.toolCallDedup.add(blockId);\n\n\tconst input = asObj(block.input) ?? {};\n\n\tif (name.startsWith(\"mcp__\")) {\n\t\tconst parts = name.slice(\"mcp__\".length).split(\"__\");\n\t\tbump(agg.mcpServerCalls, parts[0] || \"(unknown)\");\n\t\tbump(agg.mcpToolCalls, name);\n\t\treturn;\n\t}\n\tif (name === \"Skill\") {\n\t\tbump(agg.skillCalls, asName(input.skill) ?? \"(unnamed)\");\n\t\tbump(agg.toolCalls, \"Skill\");\n\t\treturn;\n\t}\n\t// `Task` is the pre-rename spelling of `Agent`.\n\tif (name === \"Agent\" || name === \"Task\") {\n\t\tbump(agg.subagentCalls, asName(input.subagent_type) ?? \"(default)\");\n\t\tbump(agg.toolCalls, \"Agent\");\n\t\treturn;\n\t}\n\tbump(agg.toolCalls, name);\n}\n\nconst SLASH_RE = /<command-name>\\/?([^<\\n\\r]{1,64})<\\/command-name>/g;\n\nfunction ingestUser(agg: Aggregate, rec: Obj): void {\n\tconst msg = asObj(rec.message);\n\tif (!msg) return;\n\tconst content = msg.content;\n\n\tlet text = \"\";\n\tif (typeof content === \"string\") text = content;\n\telse {\n\t\tfor (const rawBlock of asArr(content)) {\n\t\t\tconst block = asObj(rawBlock);\n\t\t\tif (!block) continue;\n\t\t\tif (asStr(block.type) === \"text\") text += asStr(block.text) ?? \"\";\n\t\t}\n\t}\n\tif (!text.includes(\"<command-name>\")) return;\n\n\t// `matchAll` over `exec` in a loop: the regex is module-level and `g`-flagged,\n\t// so an `exec` loop carries a shared `lastIndex` that a forgotten reset turns\n\t// into records being skipped at random.\n\tfor (const match of text.matchAll(SLASH_RE)) {\n\t\tbump(agg.slashCommands, cleanName(match[1]));\n\t}\n}\n","// I/O shell around the pure analyzer: find transcript roots, stream JSONL, hand\n// each parsed record to ingestRecord. Nothing leaves this machine.\n//\n// Wayfinder ticket #37 (map #29).\n//\n// STANDING NON-GOAL (locked in #13): raw transcripts, prompts, absolute paths,\n// and repo names never leave the machine. Two consequences visible here:\n// project directories are counted but their names never escape this module, and\n// read errors are swallowed rather than thrown, because the error object carries\n// the absolute path and the munged project directory.\n\nimport { createReadStream, type Dirent } from \"node:fs\";\nimport { readdir, realpath, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport readline from \"node:readline\";\n\nimport {\n\temptyScanStats,\n\ttype ScanStats,\n\twindowStartMs,\n} from \"../shared/window.js\";\nimport { type Aggregate, ingestRecord } from \"./analyzer.js\";\n\nexport { type ScanStats, windowStartMs };\n\n/** Discovery order mirrors ccusage's adapter: CLAUDE_CONFIG_DIR, then the defaults. */\nexport function transcriptRoots(): string[] {\n\tconst env = process.env.CLAUDE_CONFIG_DIR;\n\tif (env) {\n\t\treturn env\n\t\t\t.split(\",\")\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean)\n\t\t\t.map((s) => path.join(s, \"projects\"));\n\t}\n\tconst roots = [path.join(homedir(), \".claude\", \"projects\")];\n\tconst xdg = process.env.XDG_CONFIG_HOME ?? path.join(homedir(), \".config\");\n\troots.push(path.join(xdg, \"claude\", \"projects\"));\n\treturn roots;\n}\n\n/** Recursive *.jsonl walk — the nested `<sessionId>/subagents/` layout is real. */\nasync function* walkJsonl(dir: string): AsyncGenerator<string> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const e of entries) {\n\t\tconst full = path.join(dir, e.name);\n\t\tif (e.isDirectory()) yield* walkJsonl(full);\n\t\telse if (e.isFile() && e.name.endsWith(\".jsonl\")) yield full;\n\t}\n}\n\nexport type ScanOptions = {\n\t/** Only ingest records with a timestamp at or after this epoch ms. */\n\tsinceMs?: number;\n\tonProgress?: (files: number) => void;\n\t/** Override the discovered roots. Tests only. */\n\troots?: string[];\n};\n\n/**\n * KNOWN PERFORMANCE FLOOR — measured, decided, deliberately not fixed.\n *\n * Enumeration walks every project directory and `realpath`+`stat`s every file\n * BEFORE the mtime filter can skip anything, so a narrow window still pays a\n * floor proportional to TOTAL history (~60 ms over 3,206 files today, growing\n * linearly). The obvious fix — prune whole project directories by directory\n * mtime — is UNSOUND, and this was verified rather than assumed: appending to a\n * file does not update its parent directory's mtime (only adding, removing, or\n * renaming an entry does). A session resumed with `--resume` appends to a\n * transcript created before the window opened, inside a directory whose mtime\n * never moves, so dir-mtime pruning would silently drop live in-window records\n * — a wrong number, which is worse than a slow one for a tool whose whole claim\n * is measured-not-claimed.\n *\n * The sound version is a persisted enumeration cache, which cuts against #33\n * decision 1 (a sync is a stateless snapshot-replace, no durable client scan\n * state). So: DEFERRED. 60 ms is two orders of magnitude under the ~3 s full\n * scan and invisible next to the send round-trip; it becomes worth revisiting\n * only when total history reaches a scale where the floor dominates.\n */\nexport async function scan(\n\tagg: Aggregate,\n\topts: ScanOptions = {},\n): Promise<ScanStats> {\n\tconst stats: ScanStats = emptyScanStats();\n\t// Roots can overlap (CLAUDE_CONFIG_DIR may repeat a dir; ~/.claude and\n\t// ~/.config/claude may be symlinked together). Without this guard the same\n\t// file is ingested twice and the record/line/block counters silently double.\n\tconst visited = new Set<string>();\n\n\tfor (const root of opts.roots ?? transcriptRoots()) {\n\t\tif (!(await exists(root))) continue;\n\t\tfor await (const file of walkJsonl(root)) {\n\t\t\tstats.filesFound++;\n\n\t\t\tlet resolved: string;\n\t\t\ttry {\n\t\t\t\tresolved = await realpath(file);\n\t\t\t} catch {\n\t\t\t\tresolved = file;\n\t\t\t}\n\t\t\tif (visited.has(resolved)) {\n\t\t\t\tstats.filesSkippedAsDuplicate++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvisited.add(resolved);\n\n\t\t\t// Transcripts are append-only and chronological, so a file untouched\n\t\t\t// since the window opened cannot hold an in-window record. This is what\n\t\t\t// makes a narrow window actually cheaper rather than merely narrower.\n\t\t\tif (opts.sinceMs !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tconst st = await stat(file);\n\t\t\t\t\tif (st.mtimeMs < opts.sinceMs) {\n\t\t\t\t\t\tstats.filesSkippedByMtime++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t/* unreadable stat — fall through and try to read it */\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Project dir = first path segment under projects/ (privacy-sensitive:\n\t\t\t// it is a munged absolute path, so it is only ever counted, never shown).\n\t\t\tconst rel = path.relative(root, file);\n\t\t\tconst projectDir = rel.split(path.sep)[0] ?? \"(root)\";\n\t\t\tagg.files++;\n\t\t\tstats.filesRead++;\n\t\t\tif (opts.onProgress && agg.files % 200 === 0) opts.onProgress(agg.files);\n\t\t\ttry {\n\t\t\t\tawait ingestFile(agg, file, projectDir, opts.sinceMs);\n\t\t\t} catch {\n\t\t\t\t// Swallow deliberately: the error object carries the absolute path.\n\t\t\t\tstats.filesUnreadable++;\n\t\t\t\tstats.filesRead--;\n\t\t\t}\n\t\t}\n\t}\n\treturn stats;\n}\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function ingestFile(\n\tagg: Aggregate,\n\tfile: string,\n\tprojectDir: string,\n\tsinceMs?: number,\n): Promise<void> {\n\tconst rl = readline.createInterface({\n\t\tinput: createReadStream(file, { encoding: \"utf8\" }),\n\t\tcrlfDelay: Number.POSITIVE_INFINITY,\n\t});\n\tfor await (const line of rl) {\n\t\tif (!line) continue;\n\t\tagg.lines++;\n\t\tlet rec: unknown;\n\t\ttry {\n\t\t\trec = JSON.parse(line);\n\t\t} catch {\n\t\t\tagg.parseErrors++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (sinceMs !== undefined) {\n\t\t\tconst ts =\n\t\t\t\trec &&\n\t\t\t\ttypeof rec === \"object\" &&\n\t\t\t\t\"timestamp\" in rec &&\n\t\t\t\ttypeof (rec as { timestamp?: unknown }).timestamp === \"string\"\n\t\t\t\t\t? Date.parse((rec as { timestamp: string }).timestamp)\n\t\t\t\t\t: Number.NaN;\n\t\t\tif (Number.isNaN(ts) || ts < sinceMs) continue;\n\t\t}\n\t\tingestRecord(agg, rec, { projectDir });\n\t}\n}\n","// The rolling window and the scan-health shape every adapter reports.\n// Shared so the payload builder and each harness scanner agree by import\n// rather than by convention (#67).\n\n/** Rolling window locked by the owner in the #32 prototype resolution. */\nexport const DEFAULT_WINDOW_DAYS = 30;\n\n/**\n * UTC midnight opening a rolling window of `days` calendar days ending on the\n * day containing `now` (inclusive). `days = 30` therefore spans today plus the\n * 29 preceding days.\n *\n * Defined once and shared by the scan filter and the payload's `window.from`, so\n * the reported window and the records actually counted cannot drift apart.\n */\nexport function windowStartMs(now: number, days: number): number {\n\tconst startOfToday = Date.UTC(\n\t\tnew Date(now).getUTCFullYear(),\n\t\tnew Date(now).getUTCMonth(),\n\t\tnew Date(now).getUTCDate(),\n\t);\n\treturn startOfToday - (days - 1) * 86_400_000;\n}\n\nexport type ScanStats = {\n\t/** Files found on disk before any window filter. */\n\tfilesFound: number;\n\t/** Files actually opened and read. */\n\tfilesRead: number;\n\t/** Files skipped because their mtime predates the window. */\n\tfilesSkippedByMtime: number;\n\t/** Files skipped because a resolved path was already scanned (overlapping roots). */\n\tfilesSkippedAsDuplicate: number;\n\t/**\n\t * Files that could not be read (permissions, or pruned mid-scan). Counted\n\t * rather than thrown: an unhandled read error would surface the absolute path\n\t * AND the munged project directory in the crash output, which is exactly what\n\t * this tool promises never to emit.\n\t */\n\tfilesUnreadable: number;\n\t/**\n\t * Files excluded because they fail the genuine-rollout fingerprint (#73):\n\t * another tool wrote them into the harness's log directory, so their usage\n\t * would distort the numbers. Codex sets this; Claude has no known impostors.\n\t */\n\tfilesForeign: number;\n\t/**\n\t * LOCAL-ONLY detail behind the counts above. `buildPayload` copies coverage\n\t * fields one by one, so nothing below can reach the wire.\n\t *\n\t * `session_meta.originator` values seen on foreign files, value → file count.\n\t */\n\tforeignOriginators: Map<string, number>;\n\t/** LOCAL-ONLY: one `{path relative to the scan root, error class}` per unreadable file. */\n\tunreadableFiles: Array<{ path: string; reason: string }>;\n\t/** Subset of `filesUnreadable`: `.zst` rollouts this Node runtime cannot decompress. */\n\tfilesZstdUnsupported: number;\n};\n\nexport function emptyScanStats(): ScanStats {\n\treturn {\n\t\tfilesFound: 0,\n\t\tfilesRead: 0,\n\t\tfilesSkippedByMtime: 0,\n\t\tfilesSkippedAsDuplicate: 0,\n\t\tfilesUnreadable: 0,\n\t\tfilesForeign: 0,\n\t\tforeignOriginators: new Map(),\n\t\tunreadableFiles: [],\n\t\tfilesZstdUnsupported: 0,\n\t};\n}\n","// The Codex CLI harness behind the seam (#66 decision 6, built in #67).\n\nimport { stat } from \"node:fs/promises\";\n\nimport { OPENAI_PRICING_TABLE_VERSION } from \"../shared/pricing.js\";\nimport type {\n\tHarnessAdapter,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"../types.js\";\nimport { createAggregate } from \"./analyzer.js\";\nimport { rolloutRoots, scan } from \"./scan.js\";\n\nexport const CODEX_HARNESS_NAME = \"codex\";\n\n/**\n * Codex's vendor-assigned tool surface, as observed in rollouts and pinned in\n * the Codex source (#65 §4). Same fail-closed mechanism as Claude's\n * BUILTIN_TOOLS: a literal set, never a pattern — an unknown-but-real\n * built-in withheld as a count is a small loss; an unknown-and-user-named\n * tool published verbatim is the leak this prevents. The last four are the\n * stable synthetic names the analyzer assigns to non-`function_call` items.\n *\n * Codex v1 publishes builtinTools and mcpServers ONLY (#66 decision 3):\n * slash commands verifiably never reach rollouts, and the skill/subagent\n * surfaces are unverified — those categories ship as empty arrays, absorbed\n * with no schema change if a later Codex version logs them.\n */\nexport const CODEX_BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"apply_patch\",\n\t\"exec_command\",\n\t\"grep_command\",\n\t\"list_dir\",\n\t\"read_file\",\n\t\"request_user_input\",\n\t\"shell\",\n\t\"unified_exec\",\n\t\"update_plan\",\n\t\"view_image\",\n\t\"write_stdin\",\n\t// synthetic names for non-function_call response items\n\t\"local_shell\",\n\t\"web_search\",\n\t\"tool_search\",\n]);\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport const codexAdapter: HarnessAdapter = {\n\tname: CODEX_HARNESS_NAME,\n\tbuiltinTools: CODEX_BUILTIN_TOOLS,\n\tpricingTableVersion: OPENAI_PRICING_TABLE_VERSION,\n\n\tasync detect(): Promise<boolean> {\n\t\tfor (const root of rolloutRoots()) {\n\t\t\tif (await exists(root)) return true;\n\t\t}\n\t\treturn false;\n\t},\n\n\tasync scan(opts: HarnessScanOptions): Promise<HarnessScan> {\n\t\tconst aggregate = createAggregate();\n\t\tconst stats = await scan(aggregate, {\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.onProgress ? { onProgress: opts.onProgress } : {}),\n\t\t});\n\t\treturn { aggregate, stats };\n\t},\n};\n","// Pure fold over parsed Codex CLI rollout lines. No I/O, no console.\n//\n// Wayfinder ticket #67 (map #60). Field semantics come from\n// docs/research/codex-session-log-anatomy-2026-08.md (#65) as locked by the\n// wire-format grilling #66. Every field is untrusted and optional: lines\n// arrive as `unknown` and are narrowed here.\n//\n// THE LOAD-BEARING SUBTLETY — Claude's cumulative gotcha, INVERTED.\n// Claude Code logs per-message usage that can repeat across snapshot records,\n// so its analyzer dedups by message id. Codex logs a `token_count` event whose\n// `total_token_usage` is the CUMULATIVE session sum — summing it across a\n// session's 20+ events overcounts by orders of magnitude. The rule locked in\n// #66: sum `last_token_usage` (the per-response delta) and never read the\n// totals. Deltas also carry the cached/non-cached split each response's cost\n// needs, which the cumulative figure re-counts every turn.\n//\n// Attribution: `token_count` events carry no model. Each delta is attributed\n// to the model of the nearest preceding `turn_context` in the same file.\n//\n// TokenCounts mapping (#66 decision 6): `cached_input_tokens` is a SUBSET of\n// `input_tokens`, so `input = input_tokens - cached_input`, `cacheRead =\n// cached_input`, and `cacheWrite = 0` — Codex reports no cache writes, and a\n// zero write prices correctly with zero pricing-code changes.\n\nimport {\n\taddModelUsage,\n\tasName,\n\tasNum,\n\tasObj,\n\tasStr,\n\tbump,\n\tcleanName,\n\tcountsTotal,\n\tcreateAggregate as createSharedAggregate,\n\ttype Obj,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\nimport {\n\tapiEquivalentCost,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"../shared/pricing.js\";\n\n/** Codex needs no response dedup bookkeeping — deltas count once by construction. */\nexport type Aggregate = SharedAggregate<never>;\n\nexport function createAggregate(): Aggregate {\n\treturn createSharedAggregate<never>();\n}\n\n/**\n * Per-file fold state. A rollout file is one session; the session id, CLI\n * version, cwd and current model are context lines that may sit BEFORE the\n * window opens, so they update state unconditionally and are only counted\n * when an in-window line lands (`noteActivity`).\n */\nexport type FileState = {\n\tsessionId: string | null;\n\tcliVersion: string | null;\n\tcwd: string | null;\n\t/** Pricing key of the nearest preceding `turn_context`. */\n\tmodelKey: string | null;\n\t/** True once any in-window line was counted for this file. */\n\tcounted: boolean;\n};\n\nexport function createFileState(): FileState {\n\treturn {\n\t\tsessionId: null,\n\t\tcliVersion: null,\n\t\tcwd: null,\n\t\tmodelKey: null,\n\t\tcounted: false,\n\t};\n}\n\n/**\n * Fold one parsed rollout line into the aggregate.\n *\n * `sinceMs` is applied HERE rather than in the scanner because context lines\n * (session_meta, turn_context) must update `state` even when they predate the\n * window — a session resumed today attributes today's deltas to a model named\n * last week.\n */\nexport function ingestLine(\n\tagg: Aggregate,\n\traw: unknown,\n\tstate: FileState,\n\tsinceMs?: number,\n): void {\n\tconst rec = asObj(raw);\n\tif (!rec) return;\n\tagg.records++;\n\n\tlet tsMs: number | null = null;\n\tconst timestamp = asStr(rec.timestamp);\n\tif (timestamp) {\n\t\tconst ts = Date.parse(timestamp);\n\t\tif (!Number.isNaN(ts)) tsMs = ts;\n\t}\n\tconst inWindow = sinceMs === undefined || (tsMs !== null && tsMs >= sinceMs);\n\n\tconst type = asStr(rec.type);\n\tconst payload = asObj(rec.payload);\n\n\tif (type === \"session_meta\" && payload) {\n\t\tstate.sessionId =\n\t\t\tasStr(payload.id) ?? asStr(payload.session_id) ?? state.sessionId;\n\t\tstate.cliVersion = asStr(payload.cli_version) ?? state.cliVersion;\n\t\tstate.cwd = asStr(payload.cwd) ?? state.cwd;\n\t} else if (type === \"turn_context\" && payload) {\n\t\tconst model = asName(payload.model);\n\t\tif (model) state.modelKey = normalizeModel(model);\n\t}\n\n\tif (!inWindow) return;\n\n\tif (tsMs !== null && timestamp) {\n\t\tagg.activeDays.add(timestamp.slice(0, 10));\n\t\tagg.firstTs = agg.firstTs === null ? tsMs : Math.min(agg.firstTs, tsMs);\n\t\tagg.lastTs = agg.lastTs === null ? tsMs : Math.max(agg.lastTs, tsMs);\n\t}\n\tnoteActivity(agg, state);\n\n\tif (type === \"event_msg\" && payload) ingestEvent(agg, payload, state, tsMs);\n\telse if (type === \"response_item\" && payload) ingestItem(agg, payload);\n}\n\n/** Count the file's session/version/cwd once, on its first in-window line. */\nfunction noteActivity(agg: Aggregate, state: FileState): void {\n\tif (state.counted) return;\n\tstate.counted = true;\n\tif (state.sessionId) agg.sessions.add(state.sessionId);\n\tif (state.cliVersion) agg.ccVersions.add(cleanName(state.cliVersion));\n\t// Counted, never published — same standing non-goal as Claude project dirs.\n\tagg.projectDirs.add(state.cwd ?? \"(unknown)\");\n}\n\n// ---------------------------------------------------------------------------\n// Usage — token_count deltas\n// ---------------------------------------------------------------------------\n\nfunction ingestEvent(\n\tagg: Aggregate,\n\tpayload: Obj,\n\tstate: FileState,\n\ttsMs: number | null,\n): void {\n\tif (asStr(payload.type) !== \"token_count\") return;\n\tconst info = asObj(payload.info);\n\tconst last = info ? asObj(info.last_token_usage) : null;\n\tif (!last) return;\n\n\tconst inputTotal = asNum(last.input_tokens);\n\tconst cached = Math.min(asNum(last.cached_input_tokens), inputTotal);\n\tconst counts: TokenCounts = {\n\t\tinput: inputTotal - cached,\n\t\toutput: asNum(last.output_tokens),\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: 0,\n\t\tcacheRead: cached,\n\t};\n\tconst total = countsTotal(counts);\n\t// A zero delta is a rate-limit-only refresh, not a response.\n\tif (total === 0) return;\n\n\tif (tsMs === null) agg.untimestampedResponses++;\n\tagg.distinctResponses++;\n\n\tconst modelKey = state.modelKey ?? \"(unknown)\";\n\taddModelUsage(\n\t\tagg,\n\t\tmodelKey,\n\t\tcounts,\n\t\tapiEquivalentCost(modelKey, counts, tsMs),\n\t);\n\t// Codex rollouts carry no sidechain flag; everything is the main thread,\n\t// which keeps `subagentShare` an honest 0 rather than a guess.\n\tagg.mainTokens += total;\n}\n\n// ---------------------------------------------------------------------------\n// Inventory — response_item tool calls\n// ---------------------------------------------------------------------------\n\n/**\n * MCP tools reach the model as `<server>__<tool>` (MCP_TOOL_NAME_DELIMITER in\n * the Codex source); split on the FIRST `__` to recover the server. Over-long\n * names get a hash suffix on the TOOL side, so the server segment survives.\n */\nfunction ingestCall(agg: Aggregate, name: string, callId: string | null): void {\n\tif (callId) {\n\t\tif (agg.toolCallDedup.has(callId)) return;\n\t\tagg.toolCallDedup.add(callId);\n\t}\n\tconst sep = name.indexOf(\"__\");\n\tif (sep > 0) {\n\t\tbump(agg.mcpServerCalls, cleanName(name.slice(0, sep)));\n\t\tbump(agg.mcpToolCalls, cleanName(name));\n\t\treturn;\n\t}\n\tbump(agg.toolCalls, cleanName(name));\n}\n\nfunction ingestItem(agg: Aggregate, payload: Obj): void {\n\tconst type = asStr(payload.type);\n\tif (type === \"function_call\" || type === \"custom_tool_call\") {\n\t\tconst name = asName(payload.name);\n\t\tif (!name) return;\n\t\tingestCall(agg, name, asStr(payload.call_id) ?? asStr(payload.id));\n\t\treturn;\n\t}\n\t// Non-function tool items publish under stable synthetic names that live in\n\t// CODEX_BUILTIN_TOOLS, so they survive the fail-closed filter.\n\tif (type === \"local_shell_call\") {\n\t\tingestCall(agg, \"local_shell\", asStr(payload.call_id) ?? asStr(payload.id));\n\t} else if (type === \"web_search_call\") {\n\t\tagg.webSearchRequests++;\n\t\tingestCall(agg, \"web_search\", asStr(payload.id));\n\t} else if (type === \"tool_search_call\") {\n\t\tingestCall(agg, \"tool_search\", asStr(payload.id));\n\t}\n}\n\n/**\n * Static MCP inventory from `~/.codex/config.toml` (#66 decision 3): a\n * configured server the window never called still exists. Zero-count entries\n * ride into the inventory (callShare 0) without inventing calls.\n */\nexport function noteConfiguredMcpServers(\n\tagg: Aggregate,\n\tserverNames: Iterable<string>,\n): void {\n\tfor (const raw of serverNames) {\n\t\tconst name = cleanName(raw);\n\t\tif (!agg.mcpServerCalls.has(name)) agg.mcpServerCalls.set(name, 0);\n\t}\n}\n","// I/O shell around the pure Codex analyzer: find rollout files, stream JSONL\n// (plain or zstd), hand each parsed line to ingestLine. Nothing leaves this\n// machine.\n//\n// Wayfinder ticket #67 (map #60), semantics from #65/#66.\n//\n// STANDING NON-GOAL (locked in #13): raw transcripts, prompts, absolute paths,\n// and repo names never leave the machine. `~/.codex/history.jsonl` holds raw\n// prompt text and is NEVER opened here; read errors are swallowed rather than\n// thrown, because the error object carries the absolute path.\n\nimport { type Dirent, readFileSync } from \"node:fs\";\nimport { readdir, realpath, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport * as zlib from \"node:zlib\";\n\nimport { parse as parseToml } from \"smol-toml\";\n\nimport { asObj, asStr } from \"../shared/aggregate.js\";\nimport { emptyScanStats, type ScanStats } from \"../shared/window.js\";\nimport {\n\ttype Aggregate,\n\tcreateFileState,\n\tingestLine,\n\tnoteConfiguredMcpServers,\n} from \"./analyzer.js\";\n\n/** `$CODEX_HOME` honored, `~/.codex` the default — mirrors the Codex source. */\nexport function codexHome(): string {\n\treturn process.env.CODEX_HOME || path.join(homedir(), \".codex\");\n}\n\n/**\n * Only `sessions/` is read. `archived_sessions/` is deliberately excluded: an\n * archived session was removed from the user's working set, and the rolling\n * window makes old ones irrelevant anyway. `history.jsonl` is raw prompts and\n * is out of bounds entirely.\n */\nexport function rolloutRoots(): string[] {\n\treturn [path.join(codexHome(), \"sessions\")];\n}\n\nconst ROLLOUT_RE = /^rollout-.*\\.jsonl(\\.zst)?$/;\n\n/** Recursive rollout walk — the YYYY/MM/DD nesting is real. */\nasync function* walkRollouts(dir: string): AsyncGenerator<string> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const e of entries) {\n\t\tconst full = path.join(dir, e.name);\n\t\tif (e.isDirectory()) yield* walkRollouts(full);\n\t\telse if (e.isFile() && ROLLOUT_RE.test(e.name)) yield full;\n\t}\n}\n\n/**\n * zstd support landed in node:zlib after the CLI's floor (`engines: >=18`),\n * so it is feature-detected. On an old runtime a `.zst` rollout counts as\n * unreadable — a visible coverage figure, never a silent skip.\n */\nconst zstdDecompress: ((buf: Buffer) => Buffer) | null =\n\ttypeof (zlib as { zstdDecompressSync?: unknown }).zstdDecompressSync ===\n\t\"function\"\n\t\t? (buf) =>\n\t\t\t\t(\n\t\t\t\t\tzlib as unknown as { zstdDecompressSync: (b: Buffer) => Buffer }\n\t\t\t\t).zstdDecompressSync(buf)\n\t\t: null;\n\nexport type ScanOptions = {\n\t/** Only count records with a timestamp at or after this epoch ms. */\n\tsinceMs?: number;\n\tonProgress?: (files: number) => void;\n\t/** Override the discovered roots. Tests only. */\n\troots?: string[];\n\t/** Override the config.toml path. Tests only. */\n\tconfigFile?: string;\n\t/** Override the file reader. Tests only. */\n\treadFileImpl?: (file: string) => Buffer | string;\n};\n\nexport async function scan(\n\tagg: Aggregate,\n\topts: ScanOptions = {},\n): Promise<ScanStats> {\n\tconst stats: ScanStats = emptyScanStats();\n\tconst visited = new Set<string>();\n\n\tfor (const root of opts.roots ?? rolloutRoots()) {\n\t\tif (!(await exists(root))) continue;\n\t\tfor await (const file of walkRollouts(root)) {\n\t\t\tstats.filesFound++;\n\n\t\t\tlet resolved: string;\n\t\t\ttry {\n\t\t\t\tresolved = await realpath(file);\n\t\t\t} catch {\n\t\t\t\tresolved = file;\n\t\t\t}\n\t\t\t// Dedup key = resolved path with `.zst` stripped. Codex's compression\n\t\t\t// worker leaves `foo.jsonl` and `foo.jsonl.zst` coexisting for a moment\n\t\t\t// (rename before unlink, #73 §4) — one session, two names. Keying on\n\t\t\t// the stem makes the second listing a duplicate, not a double count.\n\t\t\tconst dedupKey = resolved.endsWith(\".zst\")\n\t\t\t\t? resolved.slice(0, -\".zst\".length)\n\t\t\t\t: resolved;\n\t\t\tif (visited.has(dedupKey)) {\n\t\t\t\tstats.filesSkippedAsDuplicate++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvisited.add(dedupKey);\n\n\t\t\t// Rollouts are append-only and chronological, so a file untouched since\n\t\t\t// the window opened cannot hold an in-window record.\n\t\t\tif (opts.sinceMs !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tconst st = await stat(file);\n\t\t\t\t\tif (st.mtimeMs < opts.sinceMs) {\n\t\t\t\t\t\tstats.filesSkippedByMtime++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t/* unreadable stat — fall through and try to read it */\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tagg.files++;\n\t\t\tstats.filesRead++;\n\t\t\tif (opts.onProgress && agg.files % 200 === 0) opts.onProgress(agg.files);\n\t\t\tconst outcome = ingestWithRetry(agg, file, opts);\n\t\t\tif (!outcome.ok) {\n\t\t\t\t// Never rethrown: the error object carries the absolute path. The\n\t\t\t\t// stats keep a relative path and a bare error class instead (#75).\n\t\t\t\tstats.filesUnreadable++;\n\t\t\t\tstats.filesRead--;\n\t\t\t\tif (outcome.reason === \"zstd-unsupported\") stats.filesZstdUnsupported++;\n\t\t\t\tstats.unreadableFiles.push({\n\t\t\t\t\tpath: path.relative(root, file),\n\t\t\t\t\treason: outcome.reason,\n\t\t\t\t});\n\t\t\t} else if (!outcome.genuine) {\n\t\t\t\t// Fingerprint failure (#73): another tool wrote this file. Its usage\n\t\t\t\t// stayed out of the aggregate entirely.\n\t\t\t\tstats.filesForeign++;\n\t\t\t\tstats.filesRead--;\n\t\t\t\tconst seen = stats.foreignOriginators.get(outcome.originator) ?? 0;\n\t\t\t\tstats.foreignOriginators.set(outcome.originator, seen + 1);\n\t\t\t}\n\t\t}\n\t}\n\n\treadConfiguredMcpServers(agg, opts.configFile);\n\treturn stats;\n}\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\ntype IngestOutcome =\n\t| { ok: true; genuine: true }\n\t| { ok: true; genuine: false; originator: string }\n\t| { ok: false; reason: string };\n\n/**\n * A read failure classified WITHOUT the error object's message or stack —\n * both carry the absolute path, which never leaves this module. `code` is a\n * bare class name (`ENOENT`, `EACCES`, `zstd-unsupported`, `zstd-corrupt`).\n */\nfunction errorClass(e: unknown): string {\n\tconst code = (e as { code?: unknown } | null)?.code;\n\tif (typeof code === \"string\" && code.length > 0) return code;\n\treturn e instanceof Error ? e.constructor.name : \"unknown\";\n}\n\nconst readError = (reason: string): Error =>\n\tObject.assign(new Error(reason), { code: reason });\n\n/**\n * The compression race (#73 §4): codex's background worker compresses a\n * rollout to `.zst` and then unlinks the plain `.jsonl`, so a file listed by\n * the walk can be gone at read time. Mirror codex's own reader: on `ENOENT`,\n * try the `.zst` sibling once before counting the file unreadable.\n */\nfunction ingestWithRetry(\n\tagg: Aggregate,\n\tfile: string,\n\topts: ScanOptions,\n): IngestOutcome {\n\ttry {\n\t\treturn ingestFile(agg, file, opts);\n\t} catch (e) {\n\t\tif (errorClass(e) === \"ENOENT\" && !file.endsWith(\".zst\")) {\n\t\t\ttry {\n\t\t\t\treturn ingestFile(agg, `${file}.zst`, opts);\n\t\t\t} catch (e2) {\n\t\t\t\treturn { ok: false, reason: errorClass(e2) };\n\t\t\t}\n\t\t}\n\t\treturn { ok: false, reason: errorClass(e) };\n\t}\n}\n\n/**\n * Whole-file read rather than a stream: a `.zst` rollout must be decompressed\n * as one buffer anyway, and rollout files are single sessions — megabytes,\n * not gigabytes. The lines are parsed BEFORE any of them folds into the\n * aggregate, because the fingerprint verdict (#73) arrives only at end of\n * file: a foreign file must leave the aggregate untouched.\n */\nfunction ingestFile(\n\tagg: Aggregate,\n\tfile: string,\n\topts: ScanOptions,\n): IngestOutcome {\n\tconst readFile = opts.readFileImpl ?? readFileSync;\n\tlet text: string;\n\tif (file.endsWith(\".zst\")) {\n\t\tif (zstdDecompress === null) throw readError(\"zstd-unsupported\");\n\t\tconst raw = readFile(file);\n\t\ttry {\n\t\t\ttext = zstdDecompress(\n\t\t\t\tBuffer.isBuffer(raw) ? raw : Buffer.from(raw),\n\t\t\t).toString(\"utf8\");\n\t\t} catch {\n\t\t\tthrow readError(\"zstd-corrupt\");\n\t\t}\n\t} else {\n\t\ttext = readFile(file).toString(\"utf8\");\n\t}\n\n\tconst records: unknown[] = [];\n\tlet nonEmptyLines = 0;\n\tlet parseErrors = 0;\n\tfor (const line of text.split(\"\\n\")) {\n\t\tif (!line) continue;\n\t\tnonEmptyLines++;\n\t\ttry {\n\t\t\trecords.push(JSON.parse(line));\n\t\t} catch {\n\t\t\tparseErrors++;\n\t\t}\n\t}\n\n\tconst verdict = classifyRollout(records);\n\tif (!verdict.genuine) return { ok: true, ...verdict };\n\n\tagg.lines += nonEmptyLines;\n\tagg.parseErrors += parseErrors;\n\tconst state = createFileState();\n\tfor (const rec of records) ingestLine(agg, rec, state, opts.sinceMs);\n\treturn { ok: true, genuine: true };\n}\n\n/**\n * The genuine-rollout fingerprint (#73, source-pinned at rust-v0.146.0): the\n * codex-rs recorder always writes `session_meta` first, and every real user\n * turn persists a `turn_context` before any usage lands. A file whose first\n * parsed line is not `session_meta`, or that carries a `token_count` with no\n * preceding `turn_context`, was not written by Codex CLI. Negative by\n * construction — it detects \"not genuine\", never \"written by tool X\"; the\n * originator label is diagnostic only.\n */\nfunction classifyRollout(\n\trecords: readonly unknown[],\n): { genuine: true } | { genuine: false; originator: string } {\n\tlet originator: string | null = null;\n\tlet sawTurnContext = false;\n\tlet genuine = records.length > 0;\n\tfor (const [i, raw] of records.entries()) {\n\t\tconst rec = asObj(raw);\n\t\tconst type = rec ? asStr(rec.type) : null;\n\t\tconst payload = rec ? asObj(rec.payload) : null;\n\t\tif (i === 0 && type !== \"session_meta\") genuine = false;\n\t\tif (type === \"session_meta\" && payload && originator === null) {\n\t\t\toriginator = asStr(payload.originator);\n\t\t} else if (type === \"turn_context\") {\n\t\t\tsawTurnContext = true;\n\t\t} else if (\n\t\t\ttype === \"event_msg\" &&\n\t\t\tpayload &&\n\t\t\tasStr(payload.type) === \"token_count\" &&\n\t\t\t!sawTurnContext\n\t\t) {\n\t\t\tgenuine = false;\n\t\t}\n\t}\n\tif (genuine) return { genuine: true };\n\treturn { genuine: false, originator: originator ?? \"(none)\" };\n}\n\n/**\n * The static half of the MCP inventory (#66 decision 3): `[mcp_servers.*]`\n * in `~/.codex/config.toml`. Unreadable or absent config is silence, not an\n * error — the observed half stands on its own.\n */\nfunction readConfiguredMcpServers(agg: Aggregate, configFile?: string): void {\n\tconst file = configFile ?? path.join(codexHome(), \"config.toml\");\n\tlet names: string[] = [];\n\ttry {\n\t\tconst parsed = parseToml(readFileSync(file, \"utf8\"));\n\t\tconst servers = parsed.mcp_servers;\n\t\tif (servers && typeof servers === \"object\" && !Array.isArray(servers)) {\n\t\t\tnames = Object.keys(servers);\n\t\t}\n\t} catch {\n\t\treturn;\n\t}\n\tnoteConfiguredMcpServers(agg, names);\n}\n","// The wire payload builder — the only thing in this module that decides what\n// leaves the machine.\n//\n// Wayfinder ticket #37 (map #29). Shape fixed by the wire-format grilling #33;\n// nothing here is open design.\n//\n// Two invariants this file is responsible for:\n// 1. FAIL-CLOSED NAMES. Every freeform name is matched against an allowlist\n// before it can reach the payload; unmatched names publish only as\n// per-category counts (#33 decisions 2-4). Model ids are the sole exempt\n// class (decision 3) and are charset/length sanitized instead.\n// 2. COST IS ABSENT, NOT ZEROED. With `publishCost` off, the cost fields are\n// not in the payload at all (#33 decision 11) — there is nothing to\n// \"reveal\" server-side, because nothing was transmitted.\n\nimport {\n\ttype Aggregate,\n\tcleanName,\n\ttype Finalized,\n\tfinalize,\n\ttype ModelRow,\n} from \"./aggregate.js\";\nimport {\n\ttype Atom,\n\tfilterAtoms,\n\ttype KeptPrivateAtom,\n\tNAME_CATEGORIES,\n\ttype NameCategory,\n\ttype SyncConfig,\n} from \"./allowlist.js\";\nimport { baseModelId } from \"./pricing.js\";\nimport { type ScanStats, windowStartMs } from \"./window.js\";\n\nexport const SCHEMA_VERSION = 1;\n\nexport type PayloadModel = {\n\t/** Vendor-assigned id, sanitized. `catalogSlug` is resolved SERVER-side at read time. */\n\tid: string;\n\ttokenShare: number;\n\ttokens: {\n\t\tinput: number;\n\t\toutput: number;\n\t\tcacheWrite: number;\n\t\tcacheRead: number;\n\t};\n\tapiEquivalentUSD?: number;\n};\n\nexport type PayloadAtom = { name: string; callShare: number };\n\nexport type PayloadInventory = {\n\tbuiltinTools: PayloadAtom[];\n\tmcpServers: PayloadAtom[];\n\tskills: PayloadAtom[];\n\tsubagents: PayloadAtom[];\n\tslashCommands: PayloadAtom[];\n\t/** DISTINCT names withheld per category, so the gap in the shares is explained. */\n\twithheld: {\n\t\tbuiltinTools: number;\n\t\tmcpServers: number;\n\t\tskills: number;\n\t\tsubagents: number;\n\t\tslashCommands: number;\n\t};\n};\n\nexport type MeasuredPayload = {\n\tschemaVersion: number;\n\t/** Client clock. The server stamps its own `receivedAt` (#33 decision 6). */\n\tcapturedAt: number;\n\twindow: { days: number; from: string; to: string };\n\tharness: { name: string; version: string | null };\n\t/** `null` when `publishCost` is off — no cost was computed into the payload. */\n\tpricingTable: string | null;\n\tactivity: {\n\t\tsessions: number;\n\t\tactiveDays: number;\n\t\t/** COUNT only. Project directory names are munged absolute paths and never travel. */\n\t\tprojects: number;\n\t\ttotalTokens: number;\n\t\tcacheHitShare: number;\n\t\tsubagentShare: number;\n\t};\n\tmodels: PayloadModel[];\n\tinventory: PayloadInventory;\n\tcoverage: {\n\t\tfilesScanned: number;\n\t\tfilesUnreadable: number;\n\t\tlinesParsed: number;\n\t\tlinesFailed: number;\n\t};\n\texcludedTokens: { unpriced: number; synthetic: number };\n};\n\n// ---------------------------------------------------------------------------\n// Sanitization\n// ---------------------------------------------------------------------------\n\n/**\n * Model ids are exempt from the allowlist (#33 decision 3) precisely because\n * they are vendor-assigned: on the day a new Claude model ships, fail-closing it\n * would make its tokens silently vanish from every sync and understate cost with\n * no visible cause. Exempt is not unchecked, though — the id still becomes a\n * database key and a rendered string, so charset and length are bounded here.\n */\nconst MODEL_ID_UNSAFE_RE = /[^A-Za-z0-9._:-]+/g;\nconst MODEL_ID_MAX = 64;\n\nexport function sanitizeModelId(id: string): string {\n\tconst collapsed = cleanName(id)\n\t\t.replace(MODEL_ID_UNSAFE_RE, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\");\n\tif (collapsed.length === 0) return \"unknown\";\n\treturn collapsed.length > MODEL_ID_MAX\n\t\t? collapsed.slice(0, MODEL_ID_MAX)\n\t\t: collapsed;\n}\n\nconst round4 = (n: number): number => Math.round(n * 10_000) / 10_000;\nconst round2 = (n: number): number => Math.round(n * 100) / 100;\nconst utcDate = (ms: number): string => new Date(ms).toISOString().slice(0, 10);\n\n// ---------------------------------------------------------------------------\n// Inventory\n// ---------------------------------------------------------------------------\n\nconst toAtoms = (pairs: ReadonlyArray<readonly [string, number]>): Atom[] =>\n\tpairs.map(([name, count]) => ({ name, count }));\n\n/**\n * Shares are computed over ALL observed calls, including withheld ones.\n *\n * Renormalizing over only the allowlisted atoms would make the published shares\n * sum to 1.0 and read as a complete inventory — a withheld MCP server carrying\n * 90% of the calls would leave no trace. Keeping the true denominator means the\n * shares sum to less than 1 exactly when something was withheld, and the\n * `withheld` counts say how many things.\n */\nfunction buildCategory(\n\tobserved: ReadonlyArray<readonly [string, number]>,\n\tcurated: ReadonlySet<string>,\n\toptIns: readonly string[],\n\tdenominator: number,\n): { atoms: PayloadAtom[]; withheld: number; keptPrivate: KeptPrivateAtom[] } {\n\t// The union is where #42 decision 1 lands: a name publishes if it is curated\n\t// OR the owner ticked it. Filtering itself is unchanged — still client-side,\n\t// still fail-closed, still before the send. What moves is who judged the name.\n\tconst publishable = new Set([...curated, ...optIns]);\n\tconst {\n\t\tallowed: kept,\n\t\tkeptPrivate,\n\t\twithheld,\n\t} = filterAtoms(toAtoms(observed), { publishable, curated });\n\treturn {\n\t\tatoms: kept.map((a) => ({\n\t\t\tname: a.name,\n\t\t\tcallShare: denominator ? round4(a.count / denominator) : 0,\n\t\t})),\n\t\twithheld,\n\t\tkeptPrivate,\n\t};\n}\n\nconst sumCounts = (pairs: ReadonlyArray<readonly [string, number]>): number => {\n\tlet n = 0;\n\tfor (const [, c] of pairs) n += c;\n\treturn n;\n};\n\n// ---------------------------------------------------------------------------\n// Models\n// ---------------------------------------------------------------------------\n\ntype ModelGroup = {\n\tid: string;\n\ttotalTokens: number;\n\tinput: number;\n\toutput: number;\n\tcacheWrite: number;\n\tcacheRead: number;\n\tcostUSD: number;\n\tunpricedTokens: number;\n\tanyUnpriceable: boolean;\n};\n\n/**\n * Collapse the analyzer's pricing keys into vendor-assigned ids.\n *\n * The analyzer prices fast mode under a synthetic `claude-opus-5#fast` key\n * because it bills at a different rate ($10/$50 vs $5/$25). That suffix is OURS,\n * not the vendor's, so publishing it would hand the server an id that cannot\n * resolve against the models catalog — the exact silent-disappearance failure\n * decision 3 exists to prevent. The rows are therefore merged back onto the base\n * id here. Cost stays exact because it was already accumulated per response at\n * the fast rate; what is lost is the fast-mode share itself, which the payload\n * has no field for and which is a candidate for a later schema bump.\n */\nfunction groupModels(rows: readonly ModelRow[]): ModelGroup[] {\n\tconst groups = new Map<string, ModelGroup>();\n\tfor (const r of rows) {\n\t\tconst id = sanitizeModelId(baseModelId(r.modelKey));\n\t\tlet g = groups.get(id);\n\t\tif (!g) {\n\t\t\tg = {\n\t\t\t\tid,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcostUSD: 0,\n\t\t\t\tunpricedTokens: 0,\n\t\t\t\tanyUnpriceable: false,\n\t\t\t};\n\t\t\tgroups.set(id, g);\n\t\t}\n\t\tg.totalTokens += r.totalTokens;\n\t\tg.input += r.tokens.input;\n\t\tg.output += r.tokens.output;\n\t\tg.cacheWrite +=\n\t\t\tr.tokens.cacheWrite5m +\n\t\t\tr.tokens.cacheWrite1h +\n\t\t\tr.tokens.cacheWriteUnsplit;\n\t\tg.cacheRead += r.tokens.cacheRead;\n\t\tg.costUSD += r.costUSD ?? 0;\n\t\tg.unpricedTokens += r.unpricedTokens;\n\t\tif (r.costUSD === null) g.anyUnpriceable = true;\n\t}\n\treturn [...groups.values()].sort(\n\t\t(a, b) => b.totalTokens - a.totalTokens || a.id.localeCompare(b.id),\n\t);\n}\n\nfunction buildModels(\n\trows: readonly ModelRow[],\n\ttotalTokens: number,\n\tpublishCost: boolean,\n): PayloadModel[] {\n\treturn groupModels(rows).map((g) => {\n\t\tconst model: PayloadModel = {\n\t\t\tid: g.id,\n\t\t\ttokenShare: totalTokens ? round4(g.totalTokens / totalTokens) : 0,\n\t\t\ttokens: {\n\t\t\t\tinput: g.input,\n\t\t\t\toutput: g.output,\n\t\t\t\tcacheWrite: g.cacheWrite,\n\t\t\t\tcacheRead: g.cacheRead,\n\t\t\t},\n\t\t};\n\t\t// Absent, not zero: a partially-priced model reporting a dollar figure\n\t\t// would understate without saying so. `excludedTokens.unpriced` carries\n\t\t// the tokens that were left out.\n\t\tif (publishCost && !g.anyUnpriceable && g.unpricedTokens === 0) {\n\t\t\tmodel.apiEquivalentUSD = round2(g.costUSD);\n\t\t}\n\t\treturn model;\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Build\n// ---------------------------------------------------------------------------\n\nexport type BuildPayloadInput = {\n\taggregate: Aggregate;\n\tstats: ScanStats;\n\tsyncConfig: SyncConfig;\n\t/** Client clock, epoch ms. The same value used to derive the scan window. */\n\tnow: number;\n\twindowDays: number;\n\t/** The adapter's payload discriminator, e.g. `\"claude-code\"` (#66). */\n\tharnessName: string;\n\t/** The adapter's fail-closed vendor tool set (#66 decision 3). */\n\tbuiltinTools: ReadonlySet<string>;\n\t/** The adapter's pinned price-table id, stamped only when cost publishes. */\n\tpricingTableVersion: string;\n};\n\nexport type BuiltPayload = {\n\tpayload: MeasuredPayload;\n\t/** The same numbers unfiltered, for the local report and the approve gate. */\n\tfinalized: Finalized;\n\t/**\n\t * Every observed name that will NOT publish, by category — the gate's review\n\t * list (#42 decision 1, wired in #44).\n\t *\n\t * This is the one thing here that is deliberately NOT in the payload. It is\n\t * the list of names the user has not agreed to publish, so it stays on the\n\t * machine; the payload carries only the per-category COUNT.\n\t */\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>;\n};\n\nexport function buildPayload(input: BuildPayloadInput): BuiltPayload {\n\tconst {\n\t\taggregate: agg,\n\t\tstats,\n\t\tsyncConfig,\n\t\tnow,\n\t\twindowDays,\n\t\tharnessName,\n\t\tbuiltinTools,\n\t\tpricingTableVersion,\n\t} = input;\n\tconst finalized = finalize(agg);\n\tconst { publishCost, allowlist, optIns } = syncConfig;\n\n\tconst fromMs = windowStartMs(now, windowDays);\n\tconst from = utcDate(fromMs);\n\tconst to = utcDate(now);\n\n\t// Counted against the reported window rather than taken from the aggregate's\n\t// size: a clock-skewed, imported, or restored transcript dated in the future\n\t// would otherwise push activeDays past `windowDays`, printing an impossible\n\t// value under a payload that claims to be deterministic.\n\tlet activeDays = 0;\n\tfor (const d of agg.activeDays) if (d >= from && d <= to) activeDays++;\n\n\tconst totalToolCalls = finalized.totalToolCalls;\n\tconst builtins = buildCategory(\n\t\tfinalized.tools,\n\t\tbuiltinTools,\n\t\toptIns.builtinTools,\n\t\ttotalToolCalls,\n\t);\n\tconst mcp = buildCategory(\n\t\tfinalized.mcpServers,\n\t\tnew Set(allowlist.mcpServers),\n\t\toptIns.mcpServers,\n\t\tsumCounts(finalized.mcpServers),\n\t);\n\tconst skills = buildCategory(\n\t\tfinalized.skills,\n\t\tnew Set(allowlist.skills),\n\t\toptIns.skills,\n\t\tsumCounts(finalized.skills),\n\t);\n\tconst subagents = buildCategory(\n\t\tfinalized.subagents,\n\t\tnew Set(allowlist.subagents),\n\t\toptIns.subagents,\n\t\tsumCounts(finalized.subagents),\n\t);\n\tconst slash = buildCategory(\n\t\tfinalized.slashCommands,\n\t\tnew Set(allowlist.slashCommands),\n\t\toptIns.slashCommands,\n\t\tsumCounts(finalized.slashCommands),\n\t);\n\n\tconst payload: MeasuredPayload = {\n\t\tschemaVersion: SCHEMA_VERSION,\n\t\tcapturedAt: now,\n\t\twindow: { days: windowDays, from, to },\n\t\tharness: {\n\t\t\tname: harnessName,\n\t\t\tversion:\n\t\t\t\tfinalized.harnessVersion === null\n\t\t\t\t\t? null\n\t\t\t\t\t: sanitizeModelId(finalized.harnessVersion),\n\t\t},\n\t\tpricingTable: publishCost ? pricingTableVersion : null,\n\t\tactivity: {\n\t\t\tsessions: finalized.sessions,\n\t\t\tactiveDays,\n\t\t\tprojects: finalized.projects,\n\t\t\ttotalTokens: finalized.totalTokens,\n\t\t\tcacheHitShare: round4(finalized.cacheHitShare),\n\t\t\tsubagentShare: round4(finalized.sidechainShare),\n\t\t},\n\t\tmodels: buildModels(finalized.models, finalized.totalTokens, publishCost),\n\t\tinventory: {\n\t\t\tbuiltinTools: builtins.atoms,\n\t\t\tmcpServers: mcp.atoms,\n\t\t\tskills: skills.atoms,\n\t\t\tsubagents: subagents.atoms,\n\t\t\tslashCommands: slash.atoms,\n\t\t\twithheld: {\n\t\t\t\tbuiltinTools: builtins.withheld,\n\t\t\t\tmcpServers: mcp.withheld,\n\t\t\t\tskills: skills.withheld,\n\t\t\t\tsubagents: subagents.withheld,\n\t\t\t\tslashCommands: slash.withheld,\n\t\t\t},\n\t\t},\n\t\tcoverage: {\n\t\t\tfilesScanned: stats.filesRead,\n\t\t\tfilesUnreadable: stats.filesUnreadable,\n\t\t\tlinesParsed: agg.lines - agg.parseErrors,\n\t\t\tlinesFailed: agg.parseErrors,\n\t\t},\n\t\texcludedTokens: {\n\t\t\tunpriced: finalized.unpricedTokens,\n\t\t\tsynthetic: agg.syntheticTokens,\n\t\t},\n\t};\n\n\treturn {\n\t\tpayload,\n\t\tfinalized,\n\t\tkeptPrivate: {\n\t\t\tbuiltinTools: builtins.keptPrivate,\n\t\t\tmcpServers: mcp.keptPrivate,\n\t\t\tskills: skills.keptPrivate,\n\t\t\tsubagents: subagents.keptPrivate,\n\t\t\tslashCommands: slash.keptPrivate,\n\t\t},\n\t};\n}\n\n/**\n * What `POST /api/cli/sync` takes: one sealed payload PER DETECTED HARNESS,\n * one unsealed half shared across them (#66 decision 5). The batch is atomic\n * server-side, so two harnesses cannot wipe each other's staged names — which\n * is what two sequential per-harness publishes would have done, because the\n * staged list is a whole-list replace per stack.\n */\nexport type SyncBody = {\n\tpayloads: MeasuredPayload[];\n\tkeptPrivate?: Record<NameCategory, KeptPrivateAtom[]>;\n\t/**\n\t * The machine's standing auto-sync opt-in (#78). Not measurement and not a\n\t * name — it is the one bit of local state the backend cannot otherwise see,\n\t * and `auto_sync_enabled` has nothing to fire on without it.\n\t *\n\t * It rides BESIDE the payloads, never inside one: the payload validator is\n\t * closed, and that closedness is the privacy claim.\n\t */\n\tautoSync?: { enabled: boolean; frequencyHours: number };\n};\n\n/**\n * Union the per-harness kept-private lists into the one list the wire carries.\n *\n * One list, not one per harness, because consent is per NAME (#66 decision 5):\n * the owner ticks \"alp-river\", not \"alp-river as seen by Codex\". Counts merge\n * by (category, name); the group survives from whichever harness saw it first.\n */\nexport function mergeKeptPrivate(\n\thalves: ReadonlyArray<Record<NameCategory, KeptPrivateAtom[]>>,\n): Record<NameCategory, KeptPrivateAtom[]> {\n\tconst out = {} as Record<NameCategory, KeptPrivateAtom[]>;\n\tfor (const category of NAME_CATEGORIES) {\n\t\tconst merged = new Map<string, KeptPrivateAtom>();\n\t\tfor (const half of halves) {\n\t\t\tfor (const atom of half[category]) {\n\t\t\t\tconst held = merged.get(atom.name);\n\t\t\t\tif (held) held.count += atom.count;\n\t\t\t\telse merged.set(atom.name, { ...atom });\n\t\t\t}\n\t\t}\n\t\tout[category] = [...merged.values()].sort(\n\t\t\t(a, b) => b.count - a.count || a.name.localeCompare(b.name),\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * Assemble the request body from the built payloads, one per detected harness.\n *\n * The two halves ride in ONE request (#48): a second call would let them drift\n * against a newer snapshot. They stay SEPARATE objects because the payload's\n * validator is closed and rejects any extra key — that closedness is the privacy\n * claim, so a kept-private name may sit beside the payloads and never inside one.\n *\n * The switch is read from the sync config the server just served. Off — or a\n * config the machine could not fetch, which reads as off — sends the payloads\n * alone and the names stay on the machine.\n */\nexport function buildSyncBody(\n\tbuilt: readonly BuiltPayload[],\n\tsyncConfig: SyncConfig,\n\tautoSync?: { enabled: boolean; frequencyHours: number },\n): SyncBody {\n\tconst payloads = built.map((b) => b.payload);\n\tconst base: SyncBody = autoSync ? { payloads, autoSync } : { payloads };\n\tif (!syncConfig.reviewKeptPrivate) return base;\n\treturn {\n\t\t...base,\n\t\tkeptPrivate: mergeKeptPrivate(built.map((b) => b.keptPrivate)),\n\t};\n}\n","// Local transcript analysis -> the measured-layer wire payload.\n//\n// Wayfinder ticket #37 (map #29), reshaped around the adapter seam by #67\n// (map #60). Everything here runs on the user's machine; only the payloads\n// returned by `buildPayload` are ever candidates to leave it, and only after\n// the approve gate the send channel owns (ticket #41).\n//\n// Typical use:\n//\n// const now = Date.now();\n// const { config } = await loadSyncConfig({ baseUrl });\n// for (const adapter of await detectedAdapters()) {\n// const { aggregate, stats } = await adapter.scan({\n// sinceMs: windowStartMs(now, DEFAULT_WINDOW_DAYS),\n// });\n// const built = buildPayload({\n// aggregate, stats, syncConfig: config, now,\n// windowDays: DEFAULT_WINDOW_DAYS,\n// harnessName: adapter.name,\n// builtinTools: adapter.builtinTools,\n// pricingTableVersion: adapter.pricingTableVersion,\n// });\n// }\n\nimport { claudeAdapter } from \"./claude/adapter.js\";\nimport { codexAdapter } from \"./codex/adapter.js\";\nimport type { HarnessAdapter } from \"./types.js\";\n\nexport { CLAUDE_HARNESS_NAME, claudeAdapter } from \"./claude/adapter.js\";\nexport {\n\ttype Aggregate as ClaudeAggregate,\n\tcreateAggregate,\n\ttype IngestContext,\n\tingestRecord,\n} from \"./claude/analyzer.js\";\nexport { type ScanOptions, scan, transcriptRoots } from \"./claude/scan.js\";\nexport { CODEX_HARNESS_NAME, codexAdapter } from \"./codex/adapter.js\";\nexport {\n\ttype Aggregate,\n\tcleanName,\n\ttype Finalized,\n\tfinalize,\n\tisDisplaySafeName,\n\ttype ModelRow,\n\ttype ModelUsage,\n\tnewestVersion,\n} from \"./shared/aggregate.js\";\nexport {\n\ttype Atom,\n\tBUILTIN_TOOLS,\n\tBUNDLED_SYNC_CONFIG,\n\ttype CuratedAllowlist,\n\tEMPTY_OPT_INS,\n\ttype FilteredAtoms,\n\ttype FilterSets,\n\tfilterAtoms,\n\ttype KeptPrivateAtom,\n\ttype LoadedSyncConfig,\n\tloadSyncConfig,\n\tNAME_CATEGORIES,\n\ttype NameCategory,\n\ttype OptInNames,\n\tpluginGroup,\n\ttype SyncConfig,\n\ttype SyncConfigSource,\n} from \"./shared/allowlist.js\";\nexport { BUNDLED_CURATED_ALLOWLIST } from \"./shared/bundled-allowlist.js\";\nexport {\n\ttype BuildPayloadInput,\n\ttype BuiltPayload,\n\tbuildPayload,\n\tbuildSyncBody,\n\ttype MeasuredPayload,\n\tmergeKeptPrivate,\n\ttype PayloadAtom,\n\ttype PayloadInventory,\n\ttype PayloadModel,\n\tSCHEMA_VERSION,\n\ttype SyncBody,\n\tsanitizeModelId,\n} from \"./shared/payload.js\";\nexport {\n\tapiEquivalentCost,\n\tbaseModelId,\n\tCACHE_READ_MULTIPLIER,\n\tCACHE_WRITE_1H_MULTIPLIER,\n\tCACHE_WRITE_5M_MULTIPLIER,\n\tisPricedModel,\n\tnormalizeModel,\n\tOPENAI_PRICING_TABLE_VERSION,\n\tPRICING_TABLE_VERSION,\n\ttype PricePeriod,\n\tpriceAt,\n\tSONNET_5_INTRO_ENDS_MS,\n\ttype TokenCounts,\n} from \"./shared/pricing.js\";\nexport {\n\tDEFAULT_WINDOW_DAYS,\n\ttype ScanStats,\n\twindowStartMs,\n} from \"./shared/window.js\";\nexport type {\n\tHarnessAdapter,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"./types.js\";\n\n/**\n * Every harness this build can read, in the order their payloads publish.\n * Registration order is also display order at the gate, so Claude Code — the\n * documented default — stays first.\n */\nexport const HARNESS_ADAPTERS: readonly HarnessAdapter[] = [\n\tclaudeAdapter,\n\tcodexAdapter,\n];\n\n/** The adapters whose log roots exist on this machine. */\nexport async function detectedAdapters(): Promise<HarnessAdapter[]> {\n\tconst out: HarnessAdapter[] = [];\n\tfor (const adapter of HARNESS_ADAPTERS) {\n\t\tif (await adapter.detect()) out.push(adapter);\n\t}\n\treturn out;\n}\n","// The approve gate's two beats, as text.\n//\n// Wayfinder ticket #41 (map #29), shape fixed by the spike #35 and the copy\n// locked in #48. Beat one is the FULL summary, printed as ordinary scrollable\n// transcript output. Beat two is the SHORT elicitation message — it must stay\n// short, or `Accept` falls below the fold and the gate times out (#35, 1H).\n//\n// Everything here derives from the exact bytes that will be sent (`body`),\n// plus the local-only kept-private list that deliberately never enters them.\n// Nothing in this file is accepted as a caller-supplied argument beside the\n// payload — the spike promoted that from a caution to a demonstrated property.\n\nimport type {\n\tKeptPrivateAtom,\n\tNameCategory,\n\tSyncConfig,\n\tSyncConfigSource,\n} from \"../harness/shared/allowlist.js\";\nimport { NAME_CATEGORIES } from \"../harness/shared/allowlist.js\";\nimport type { MeasuredPayload, SyncBody } from \"../harness/shared/payload.js\";\nimport type { ScanStats } from \"../harness/shared/window.js\";\n\nexport type GateContext = {\n\t/** The exact request body a publish would send. */\n\tbody: SyncBody;\n\t/** The local-only review list — never inside any payload (#44). */\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>;\n\tconfig: SyncConfig;\n\tsource: SyncConfigSource;\n\t/** Web origin for the URLs the gate prints, e.g. https://aistack.to */\n\tbaseUrl: string;\n\t/**\n\t * Per-harness scan stats, keyed by harness name — the LOCAL-ONLY detail\n\t * behind the payload's bare coverage counts (#75): unreadable file names,\n\t * error classes, foreign-file originators. Like `keptPrivate`, it rides\n\t * beside the body and never inside it.\n\t */\n\tscanStats?: Record<string, ScanStats>;\n};\n\n// ---------------------------------------------------------------------------\n// Formatting\n// ---------------------------------------------------------------------------\n\n/** `4.27B`, `40.7M`, `216k`, `950` — three significant digits, like #40. */\nexport function fmtTokens(n: number): string {\n\tconst sig = (v: number): string => {\n\t\tconst s = v.toPrecision(3);\n\t\treturn s.includes(\".\") ? s.replace(/\\.?0+$/, \"\") : s;\n\t};\n\tif (n >= 1e9) return `${sig(n / 1e9)}B`;\n\tif (n >= 1e6) return `${sig(n / 1e6)}M`;\n\tif (n >= 1e3) return `${sig(n / 1e3)}k`;\n\treturn String(n);\n}\n\n/** `≈$5,840` — whole dollars; the ≈ and \"at API prices\" wording are #37's. */\nexport function fmtUSD(n: number): string {\n\treturn `≈$${Math.round(n).toLocaleString(\"en-US\")}`;\n}\n\nconst fmtPct = (share: number): string => `${(share * 100).toFixed(1)}%`;\n\n/**\n * The dollar figure the gate names, or `null` when none may render.\n *\n * Mirrors the public display's rule (#46): a dollar figure never renders\n * without its pricing table. Summing only the models that carry the field\n * matches what actually goes up — an unpriceable model publishes tokens, not\n * dollars.\n */\nexport function totalUSD(payload: MeasuredPayload): number | null {\n\tif (payload.pricingTable === null) return null;\n\tlet sum = 0;\n\tlet any = false;\n\tfor (const m of payload.models) {\n\t\tif (m.apiEquivalentUSD !== undefined) {\n\t\t\tsum += m.apiEquivalentUSD;\n\t\t\tany = true;\n\t\t}\n\t}\n\treturn any ? sum : null;\n}\n\n/** DISTINCT kept-private names, from the send bytes (`inventory.withheld`). */\nexport function withheldCount(payload: MeasuredPayload): number {\n\tconst w = payload.inventory.withheld;\n\treturn (\n\t\tw.builtinTools + w.mcpServers + w.skills + w.subagents + w.slashCommands\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Beat two — the elicitation message. Copy locked in #48; keep it SHORT.\n// ---------------------------------------------------------------------------\n\nexport function buildGateDialog(ctx: GateContext): string {\n\tconst { payloads, keptPrivate } = ctx.body;\n\tconst tokens = payloads.reduce((a, p) => a + p.activity.totalTokens, 0);\n\tconst usds = payloads\n\t\t.map((p) => totalUSD(p))\n\t\t.filter((u): u is number => u !== null);\n\tconst usd = usds.length > 0 ? usds.reduce((a, b) => a + b, 0) : null;\n\tconst days = payloads[0]?.window.days ?? 0;\n\tconst facts = [\n\t\t`${fmtTokens(tokens)} tokens`,\n\t\t`${days} days`,\n\t\t...(usd === null ? [] : [fmtUSD(usd)]),\n\t].join(\" · \");\n\n\tconst n = payloads.reduce((a, p) => a + withheldCount(p), 0);\n\tconst lines = [`Publish to aistack? ${facts}`];\n\tif (n > 0) {\n\t\tlines.push(\n\t\t\tkeptPrivate === undefined\n\t\t\t\t? `${n} name${n === 1 ? \"\" : \"s\"} stay${n === 1 ? \"s\" : \"\"} on this machine`\n\t\t\t\t: `${n} name${n === 1 ? \"\" : \"s\"} go${n === 1 ? \"es\" : \"\"} up for you to review`,\n\t\t);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Beat one — the full summary, transcript output.\n// ---------------------------------------------------------------------------\n\nconst CATEGORY_LABEL: Record<NameCategory, string> = {\n\tbuiltinTools: \"tools\",\n\tmcpServers: \"mcp\",\n\tskills: \"skills\",\n\tsubagents: \"agents\",\n\tslashCommands: \"commands\",\n};\n\n/** Kept-private rows for the gate: one row per group, then singles (#48). */\nexport function keptPrivateRows(\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>,\n): Array<{ label: string; names: number }> {\n\tconst groups = new Map<string, number>();\n\tconst singles: string[] = [];\n\tfor (const category of NAME_CATEGORIES) {\n\t\tfor (const atom of keptPrivate[category]) {\n\t\t\tif (atom.group === null) singles.push(atom.name);\n\t\t\telse groups.set(atom.group, (groups.get(atom.group) ?? 0) + 1);\n\t\t}\n\t}\n\tconst rows = [...groups].map(([label, names]) => ({ label, names }));\n\tfor (const name of singles) rows.push({ label: name, names: 1 });\n\trows.sort((a, b) => b.names - a.names || a.label.localeCompare(b.label));\n\treturn rows;\n}\n\nconst KEPT_PRIVATE_ROWS_SHOWN = 6;\n\n/** Display name for a harness discriminator the gate prints. */\nexport function harnessLabel(name: string): string {\n\tif (name === \"claude-code\") return \"Claude Code\";\n\tif (name === \"codex\") return \"Codex\";\n\treturn name;\n}\n\n/** How many unreadable files get named before the list truncates. */\nconst UNREADABLE_FILES_SHOWN = 5;\n\n/**\n * The local-only lines behind the bare coverage counts (#75). Everything here\n * stays on this machine: relative paths, error classes, and originator names\n * never enter the payload.\n */\nexport function scanNoteLines(stats: ScanStats, label: string): string[] {\n\tconst out: string[] = [];\n\tconst shown = stats.unreadableFiles.slice(0, UNREADABLE_FILES_SHOWN);\n\tfor (const f of shown) {\n\t\tout.push(` ${f.path} (${f.reason})`);\n\t}\n\tif (stats.unreadableFiles.length > shown.length) {\n\t\tout.push(\n\t\t\t` ...${stats.unreadableFiles.length - shown.length} more`,\n\t\t);\n\t}\n\tif (stats.filesZstdUnsupported > 0) {\n\t\tout.push(\n\t\t\t` ${stats.filesZstdUnsupported} compressed rollout${stats.filesZstdUnsupported === 1 ? \"\" : \"s\"} need Node 22.15 or newer`,\n\t\t);\n\t}\n\tif (stats.filesForeign > 0) {\n\t\tconst origins = [...stats.foreignOriginators]\n\t\t\t.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n\t\t\t.map(([name, n]) => (n > 1 ? `${name} ×${n}` : name))\n\t\t\t.join(\", \");\n\t\tout.push(\n\t\t\t`skipped ${stats.filesForeign} file${stats.filesForeign === 1 ? \"\" : \"s\"} not written by ${label} — left out (originators: ${origins})`,\n\t\t);\n\t}\n\treturn out;\n}\n\n/** One harness's payload block: window, activity, cost, models, inventory. */\nfunction payloadBlock(\n\tpayload: MeasuredPayload,\n\tshowHeader: boolean,\n\tstats?: ScanStats,\n): string[] {\n\tconst out: string[] = [];\n\tif (showHeader) {\n\t\tout.push(\n\t\t\t`— ${harnessLabel(payload.harness.name)}${payload.harness.version ? ` ${payload.harness.version}` : \"\"}`,\n\t\t);\n\t}\n\tout.push(\n\t\t`window ${payload.window.days} days · ${payload.window.from} → ${payload.window.to}`,\n\t);\n\tout.push(\n\t\t`activity ${payload.activity.sessions} sessions · ${payload.activity.activeDays} active days · ${fmtTokens(payload.activity.totalTokens)} tokens`,\n\t);\n\tconst usd = totalUSD(payload);\n\tout.push(\n\t\tusd === null\n\t\t\t? \"cost not published\"\n\t\t\t: `cost ${fmtUSD(usd)} at API prices`,\n\t);\n\n\t// Coverage is silent when clean; a degraded scan is named as a floor (#40).\n\tconst cov = payload.coverage;\n\tif (cov.filesUnreadable > 0 || cov.linesFailed > 0) {\n\t\tout.push(\n\t\t\t`coverage ${cov.filesUnreadable} files unreadable · ${cov.linesFailed} lines failed — this reading is a floor`,\n\t\t);\n\t}\n\t// Local-only detail behind those counts (#75): file names, error classes,\n\t// and the foreign-file line. Printed, never sent.\n\tif (stats) {\n\t\tout.push(...scanNoteLines(stats, harnessLabel(payload.harness.name)));\n\t}\n\n\tout.push(\"\");\n\tout.push(\"models\");\n\tfor (const m of payload.models) {\n\t\tconst dollars =\n\t\t\tusd !== null && m.apiEquivalentUSD !== undefined\n\t\t\t\t? ` ${fmtUSD(m.apiEquivalentUSD)}`\n\t\t\t\t: \"\";\n\t\tout.push(` ${m.id.padEnd(28)} ${fmtPct(m.tokenShare)}${dollars}`);\n\t}\n\n\tout.push(\"\");\n\tout.push(\"what publishes\");\n\tfor (const category of NAME_CATEGORIES) {\n\t\tconst atoms = payload.inventory[category];\n\t\tif (atoms.length === 0) continue;\n\t\tconst names = atoms.map((a) => a.name).join(\", \");\n\t\tout.push(` ${CATEGORY_LABEL[category].padEnd(9)} ${names}`);\n\t}\n\treturn out;\n}\n\nexport function buildGateSummary(ctx: GateContext): string {\n\tconst { body, keptPrivate, config, source, baseUrl } = ctx;\n\tconst { payloads } = body;\n\tconst host = baseUrl.replace(/^https?:\\/\\//, \"\");\n\tconst out: string[] = [];\n\n\tout.push(\"from your machine — sync preview\");\n\tout.push(\"\");\n\n\tif (config.stack === null) {\n\t\tout.push(\"to (no linked stack — publish is unavailable)\");\n\t} else {\n\t\tout.push(\n\t\t\t`to ${config.stack.name} · ${host}/stacks/${config.stack.slug}`,\n\t\t);\n\t}\n\n\t// One block per detected harness. With a single harness the header line is\n\t// dropped, so the single-harness preview reads exactly as it always did.\n\tfor (const payload of payloads) {\n\t\tconst stats = ctx.scanStats?.[payload.harness.name];\n\t\tout.push(...payloadBlock(payload, payloads.length > 1, stats));\n\t\tout.push(\"\");\n\t}\n\tif (out[out.length - 1] === \"\") out.pop();\n\n\tconst n = payloads.reduce((a, p) => a + withheldCount(p), 0);\n\tif (n > 0) {\n\t\tout.push(\"\");\n\t\tout.push(`kept private: ${n} name${n === 1 ? \"\" : \"s\"}`);\n\t\tconst rows = keptPrivateRows(keptPrivate);\n\t\tconst shown = rows.slice(0, KEPT_PRIVATE_ROWS_SHOWN);\n\t\tconst width = Math.max(...shown.map((r) => r.label.length));\n\t\tfor (const row of shown) {\n\t\t\tout.push(` ${row.label.padEnd(width)} ${row.names}`);\n\t\t}\n\t\tif (rows.length > shown.length) {\n\t\t\tout.push(` ...${rows.length - shown.length} more`);\n\t\t}\n\t\t// #48: beat one names the switch before the first upload, and points at\n\t\t// the changes page. Both lines are the locked copy, verbatim or near it.\n\t\tif (body.keptPrivate !== undefined && config.stack !== null) {\n\t\t\tout.push(` publish them at ${host}/stacks/${config.stack.slug}/changes`);\n\t\t\tout.push(\n\t\t\t\t\" (they go up for you to review — turn off: Review kept-private names, on your stack)\",\n\t\t\t);\n\t\t} else {\n\t\t\tout.push(\" they stay on this machine\");\n\t\t}\n\t}\n\n\t// Named at the gate because it is in the bytes (#78). It is not measurement\n\t// and not a name, but the rule is that the preview describes what goes, so a\n\t// field nobody can see in the preview does not get to ride along.\n\tif (body.autoSync !== undefined) {\n\t\tout.push(\"\");\n\t\tout.push(\n\t\t\t`auto-sync ${body.autoSync.enabled ? `on, about every ${body.autoSync.frequencyHours}h` : \"off\"}`,\n\t\t);\n\t}\n\n\tif (source === \"bundled\") {\n\t\tout.push(\"\");\n\t\tout.push(\n\t\t\t\"! could not fetch your settings from aistack — using the bundled list.\",\n\t\t);\n\t\tout.push(\n\t\t\t\" This publishes less: no cost, no ticked names, nothing staged for review.\",\n\t\t);\n\t}\n\n\treturn out.join(\"\\n\");\n}\n","// The local stdio MCP server — the send channel picked by the spike #35.\n//\n// Wayfinder ticket #41 (map #29). Two tools, two beats:\n//\n// sync_preview — scans locally, stages the exact send bytes, returns the\n// full summary as ordinary transcript output (beat one).\n// sync_publish — takes the stage id, raises a SHORT `elicitation/create`\n// with an ENUM field (beat two), and sends only on\n// `decision: \"publish\"`.\n//\n// Why elicitation and not `requiresUserInteraction`: the spike showed the\n// permission dialog can be silenced forever with one click and writes a grant\n// broader than the sentence shown, while an elicitation is raised INSIDE the\n// call — there is no string a model can spell to route around it, and no\n// \"don't ask again\" exists for it. The enum widget is the working one; the\n// boolean widget is dead in 2.1.220 and must never ship.\n//\n// Fail-closed, by construction: ESC, a timeout, a headless auto-cancel, an\n// error reply, or a client that never declared the elicitation capability all\n// resolve to \"nothing was sent\". The model's arguments count for nothing —\n// the only path to a send runs through the user's own keystrokes.\n//\n// Hand-rolled JSON-RPC over stdio, zero dependencies, structured so tests can\n// drive `handle()` directly and capture every outbound frame.\n\nimport { type SyncPublishResult, syncPublish } from \"../api.js\";\nimport { type StageDeps, type StagedSend, stageSync } from \"./stage.js\";\n\nconst SERVER_NAME = \"aistack\";\nconst SERVER_VERSION = \"0.3.0\";\n\n/** How long a staged preview stays publishable. Stale bytes must re-preview. */\nexport const STAGE_TTL_MS = 10 * 60 * 1000;\n\n/**\n * How long the gate waits for the human. Deliberately WELL past the harness's\n * own 120 s tool timeout (#35, 1H measured 92 s for a one-line answer): the\n * server must never be the first to give up. On expiry it resolves as cancel.\n */\nexport const ELICIT_TIMEOUT_MS = 10 * 60 * 1000;\n\nconst PREVIEW_TOOL = {\n\tname: \"sync_preview\",\n\tdescription:\n\t\t\"Scan local agent transcripts (Claude Code, Codex) and stage a measured-usage snapshot for aistack. \" +\n\t\t\"Returns the full preview of exactly what would publish. \" +\n\t\t\"Show the returned text to the user VERBATIM — it is the review surface. Nothing is sent.\",\n\tinputSchema: { type: \"object\", properties: {} },\n\tannotations: {\n\t\ttitle: \"aistack — preview sync (sends nothing)\",\n\t\treadOnlyHint: true,\n\t\topenWorldHint: true,\n\t},\n};\n\nconst PUBLISH_TOOL = {\n\tname: \"sync_publish\",\n\tdescription:\n\t\t\"Publish the staged aistack snapshot named by preview_id. \" +\n\t\t\"Asks the user for confirmation during the call; only their explicit choice sends anything. \" +\n\t\t\"Call sync_preview first and show its output.\",\n\tinputSchema: {\n\t\ttype: \"object\",\n\t\tproperties: {\n\t\t\tpreview_id: {\n\t\t\t\ttype: \"string\",\n\t\t\t\tdescription: \"The `preview id` line from sync_preview's output.\",\n\t\t\t},\n\t\t},\n\t\trequired: [\"preview_id\"],\n\t},\n\tannotations: {\n\t\ttitle: \"aistack — publish measured usage (asks the user first)\",\n\t\tdestructiveHint: false,\n\t\topenWorldHint: true,\n\t},\n};\n\ntype JsonRpcMessage = {\n\tjsonrpc?: string;\n\tid?: string | number;\n\tmethod?: string;\n\tparams?: Record<string, unknown> | undefined;\n\tresult?: unknown;\n\terror?: unknown;\n};\n\nexport type SyncServerDeps = {\n\tbaseUrl: string;\n\tstageImpl?: (deps: StageDeps) => Promise<StagedSend>;\n\tpublishImpl?: (token: string, bodyJson: string) => Promise<SyncPublishResult>;\n\tnow?: () => number;\n\telicitTimeoutMs?: number;\n\t/** Diagnostics only. NEVER stdout — that would corrupt the protocol. */\n\tlog?: (line: string) => void;\n};\n\nexport type SyncServer = {\n\thandle: (msg: JsonRpcMessage) => void;\n\t/** Test seam: the staged send, if any. */\n\tstaged: () => StagedSend | null;\n};\n\nconst textResult = (text: string, isError = false) => ({\n\tcontent: [{ type: \"text\", text }],\n\t...(isError ? { isError: true } : {}),\n});\n\nexport function createSyncServer(\n\tdeps: SyncServerDeps,\n\tsend: (msg: JsonRpcMessage) => void,\n): SyncServer {\n\tconst now = deps.now ?? Date.now;\n\tconst stage = deps.stageImpl ?? stageSync;\n\tconst publish = deps.publishImpl ?? syncPublish;\n\tconst log = deps.log ?? (() => {});\n\tconst elicitTimeoutMs = deps.elicitTimeoutMs ?? ELICIT_TIMEOUT_MS;\n\n\tlet clientSupportsElicitation = false;\n\tlet staged: StagedSend | null = null;\n\tlet nextRequestId = 1;\n\tconst pending = new Map<string, (reply: JsonRpcMessage | null) => void>();\n\n\tconst ok = (id: string | number | undefined, result: unknown) =>\n\t\tsend({ jsonrpc: \"2.0\", id, result });\n\tconst err = (\n\t\tid: string | number | undefined,\n\t\tcode: number,\n\t\tmessage: string,\n\t) => send({ jsonrpc: \"2.0\", id, error: { code, message } });\n\n\t/** Ask the client something; `null` reply means the gate timed out. */\n\tconst request = (\n\t\tmethod: string,\n\t\tparams: Record<string, unknown>,\n\t\tonReply: (reply: JsonRpcMessage | null) => void,\n\t) => {\n\t\tconst id = `aistack-${nextRequestId++}`;\n\t\tpending.set(id, onReply);\n\t\tsend({ jsonrpc: \"2.0\", id, method, params });\n\t\tconst timer = setTimeout(() => {\n\t\t\tif (pending.delete(id)) onReply(null);\n\t\t}, elicitTimeoutMs);\n\t\t(timer as { unref?: () => void }).unref?.();\n\t};\n\n\tconst runPreview = async (id: string | number | undefined) => {\n\t\ttry {\n\t\t\tstaged = await stage({ baseUrl: deps.baseUrl, now });\n\t\t} catch (e) {\n\t\t\tstaged = null;\n\t\t\tconst message = e instanceof Error ? e.message : String(e);\n\t\t\treturn ok(id, textResult(`Preview failed: ${message}`, true));\n\t\t}\n\t\tconst lines = [staged.summary, \"\"];\n\t\tif (staged.blockedReason === null) {\n\t\t\tlines.push(`preview id: ${staged.id}`);\n\t\t\tlines.push(\n\t\t\t\t\"To publish, call sync_publish with this preview id. The user confirms in a dialog during that call.\",\n\t\t\t);\n\t\t} else {\n\t\t\tlines.push(`publish unavailable: ${staged.blockedReason}`);\n\t\t}\n\t\treturn ok(id, textResult(lines.join(\"\\n\")));\n\t};\n\n\tconst runPublish = (\n\t\tid: string | number | undefined,\n\t\targs: Record<string, unknown> | undefined,\n\t) => {\n\t\t// Every refusal below is fail-closed: no dialog was shown, nothing sent.\n\t\tif (!clientSupportsElicitation) {\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: this Claude Code version did not declare the elicitation capability, \" +\n\t\t\t\t\t\t\"so the approve dialog cannot be shown. The gate never degrades silently — \" +\n\t\t\t\t\t\t\"update Claude Code and try again.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tconst previewId = args?.preview_id;\n\t\tif (staged === null) {\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: nothing is staged. Run sync_preview first and show its output to the user.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (typeof previewId !== \"string\" || previewId !== staged.id) {\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: preview_id does not match the staged preview. Run sync_preview again.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (staged.blockedReason !== null) {\n\t\t\treturn ok(id, textResult(`Not published: ${staged.blockedReason}`, true));\n\t\t}\n\t\tif (now() - staged.stagedAt > STAGE_TTL_MS) {\n\t\t\tstaged = null;\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: the staged preview is older than 10 minutes. Run sync_preview again so the user reviews current bytes.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tconst approvedStage = staged;\n\t\tlog(`elicitation raised for stage ${approvedStage.id}`);\n\t\trequest(\n\t\t\t\"elicitation/create\",\n\t\t\t{\n\t\t\t\tmessage: approvedStage.dialog,\n\t\t\t\trequestedSchema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tdecision: {\n\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t// The enum widget is the one that works (#35, 1G). Never a boolean.\n\t\t\t\t\t\t\tenum: [\"publish\", \"cancel\"],\n\t\t\t\t\t\t\tdescription: \"Publish the snapshot described above?\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"decision\"],\n\t\t\t\t},\n\t\t\t},\n\t\t\t(reply) => {\n\t\t\t\tconst result = reply?.result as\n\t\t\t\t\t| { action?: string; content?: { decision?: string } }\n\t\t\t\t\t| undefined;\n\t\t\t\tconst approved =\n\t\t\t\t\tresult?.action === \"accept\" &&\n\t\t\t\t\tresult?.content?.decision === \"publish\";\n\t\t\t\tif (!approved) {\n\t\t\t\t\tconst outcome =\n\t\t\t\t\t\treply === null ? \"timed out\" : (result?.action ?? \"error\");\n\t\t\t\t\tlog(`elicitation resolved without consent: ${outcome}`);\n\t\t\t\t\treturn ok(\n\t\t\t\t\t\tid,\n\t\t\t\t\t\ttextResult(\n\t\t\t\t\t\t\t`Not published: the confirmation was not accepted (${outcome}). Nothing left this machine.`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlog(`consent received, sending stage ${approvedStage.id}`);\n\t\t\t\tpublish(approvedStage.token as string, approvedStage.bodyJson).then(\n\t\t\t\t\t(res) => {\n\t\t\t\t\t\tif (staged?.id === approvedStage.id) staged = null;\n\t\t\t\t\t\tconst lines = [\n\t\t\t\t\t\t\t`Published. Snapshot received at ${new Date(res.receivedAt).toISOString()}.`,\n\t\t\t\t\t\t\tres.url,\n\t\t\t\t\t\t];\n\t\t\t\t\t\tconst kp = approvedStage.body.keptPrivate;\n\t\t\t\t\t\tif (res.keptPrivate.refused && kp !== undefined) {\n\t\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t\t\"Note: the kept-private names were refused by the server — the review switch is off there now. They stayed on this machine.\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else if (res.keptPrivate.stored > 0) {\n\t\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t\t`${res.keptPrivate.stored} kept-private names went up for your review at ${res.url}/changes`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tok(id, textResult(lines.join(\"\\n\")));\n\t\t\t\t\t},\n\t\t\t\t\t(e) => {\n\t\t\t\t\t\tconst message = e instanceof Error ? e.message : String(e);\n\t\t\t\t\t\tok(\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\ttextResult(`Publish failed after consent: ${message}`, true),\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t},\n\t\t);\n\t};\n\n\tconst handle = (msg: JsonRpcMessage) => {\n\t\tconst { id, method, params } = msg;\n\n\t\t// A reply to something we asked, not a new request.\n\t\tif (method === undefined && id !== undefined && pending.has(String(id))) {\n\t\t\tconst onReply = pending.get(String(id));\n\t\t\tpending.delete(String(id));\n\t\t\tonReply?.(msg);\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (method) {\n\t\t\tcase \"initialize\": {\n\t\t\t\tconst capabilities =\n\t\t\t\t\t(params?.capabilities as Record<string, unknown> | undefined) ?? {};\n\t\t\t\tclientSupportsElicitation = \"elicitation\" in capabilities;\n\t\t\t\tlog(\n\t\t\t\t\t`initialize: elicitation ${clientSupportsElicitation ? \"declared\" : \"ABSENT\"}`,\n\t\t\t\t);\n\t\t\t\treturn ok(id, {\n\t\t\t\t\tprotocolVersion:\n\t\t\t\t\t\t(params?.protocolVersion as string | undefined) ?? \"2025-06-18\",\n\t\t\t\t\tcapabilities: { tools: { listChanged: false } },\n\t\t\t\t\tserverInfo: { name: SERVER_NAME, version: SERVER_VERSION },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"ping\":\n\t\t\t\treturn ok(id, {});\n\n\t\t\tcase \"tools/list\":\n\t\t\t\treturn ok(id, { tools: [PREVIEW_TOOL, PUBLISH_TOOL] });\n\n\t\t\tcase \"tools/call\": {\n\t\t\t\tconst name = params?.name;\n\t\t\t\tconst args = params?.arguments as Record<string, unknown> | undefined;\n\t\t\t\tif (name === \"sync_preview\") return void runPreview(id);\n\t\t\t\tif (name === \"sync_publish\") return runPublish(id, args);\n\t\t\t\treturn err(id, -32602, `Unknown tool: ${String(name)}`);\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tif (method?.startsWith(\"notifications/\")) return;\n\t\t\t\tif (method !== undefined)\n\t\t\t\t\treturn err(id, -32601, `Method not found: ${method}`);\n\t\t}\n\t};\n\n\treturn { handle, staged: () => staged };\n}\n\n/** Wire the server to real stdio. Never returns; the harness owns the process. */\nexport function runStdioSyncServer(deps: SyncServerDeps): void {\n\tconst server = createSyncServer(deps, (msg) => {\n\t\tprocess.stdout.write(`${JSON.stringify(msg)}\\n`);\n\t});\n\tlet buffer = \"\";\n\tprocess.stdin.setEncoding(\"utf8\");\n\tprocess.stdin.on(\"data\", (chunk: string) => {\n\t\tbuffer += chunk;\n\t\tlet nl = buffer.indexOf(\"\\n\");\n\t\twhile (nl !== -1) {\n\t\t\tconst line = buffer.slice(0, nl).trim();\n\t\t\tbuffer = buffer.slice(nl + 1);\n\t\t\tif (line) {\n\t\t\t\ttry {\n\t\t\t\t\tserver.handle(JSON.parse(line));\n\t\t\t\t} catch (e) {\n\t\t\t\t\tdeps.log?.(`parse error: ${String(e)}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\tnl = buffer.indexOf(\"\\n\");\n\t\t}\n\t});\n}\n"],"mappings":"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","names":["path","p","dirname","dirname","dirname","path","existsSync","readFileSync","homedir","join","path","existsSync","readFileSync","homedir","join","path","readJson","existsSync","readFileSync","homedir","join","path","p","readJson","existsSync","readFileSync","join","homedir","existsSync","readdirSync","readFileSync","homedir","join","stat","intro","outro","intro","outro","existsSync","homedir","dirname","join","p","join","homedir","dirname","existsSync","intro","outro","existsSync","mkdirSync","readFileSync","writeFileSync","dirname","join","p","intro","join","existsSync","readFileSync","confirm","dirname","mkdirSync","writeFileSync","outro","p","intro","outro","p","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","dirname","join","p","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","dirname","join","isOurs","mkdirSync","readFileSync","writeFileSync","homedir","dirname","join","createHash","stat","p","p","createAggregate","homedir","homedir","p","exists","p","stat","createAggregate","stat","createAggregate","readFileSync","readdir","realpath","stat","homedir","path","parseToml","codexHome","path","homedir","readdir","scan","exists","realpath","stat","p","ingestFile","readFileSync","parseToml","exists","p","stat","createAggregate","scan","p","lines","createHash","join","homedir","mkdirSync","dirname","lines","readFileSync","writeFileSync","failure","intro","outro","lines","log","request","lines"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/version.ts","../src/api.ts","../src/commands/collect.ts","../src/classifier.ts","../src/stableKey.ts","../src/config.ts","../src/git.ts","../src/github-repo.ts","../src/hooks.ts","../src/mcp.ts","../src/plugins.ts","../src/scanner.ts","../src/theme.ts","../src/commands/connect.ts","../../pricing/src/index.ts","../src/harness/shared/aggregate.ts","../src/harness/shared/bundled-allowlist.ts","../src/harness/shared/allowlist.ts","../src/harness/shared/recency.ts","../src/harness/claude/analyzer.ts","../src/harness/claude/scan.ts","../src/harness/shared/window.ts","../src/harness/claude/adapter.ts","../src/harness/codex/analyzer.ts","../src/harness/codex/scan.ts","../src/harness/codex/adapter.ts","../src/harness/opencode/analyzer.ts","../src/harness/opencode/scan.ts","../src/harness/opencode/adapter.ts","../src/harness/pi/analyzer.ts","../src/harness/pi/scan.ts","../src/harness/pi/adapter.ts","../src/harness/shared/payload.ts","../src/harness/index.ts","../src/commands/create.ts","../src/commands/login.ts","../src/commands/sync.ts","../src/autosync/codexHook.ts","../src/autosync/optin.ts","../src/autosync/hook.ts","../src/autosync/run.ts","../src/sync/stage.ts","../src/sync/summary.ts","../src/sync/server.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { BASE_URL } from \"./api.js\";\nimport { collectCommand } from \"./commands/collect.js\";\nimport { connectCommand } from \"./commands/connect.js\";\nimport { createCommand } from \"./commands/create.js\";\nimport { loginCommand } from \"./commands/login.js\";\nimport { syncCommand } from \"./commands/sync.js\";\nimport { runStdioSyncServer } from \"./sync/server.js\";\nimport { CLI_VERSION } from \"./version.js\";\n\nconst program = new Command();\n\nprogram\n\t.name(\"aistack\")\n\t.description(\"Measure and share your AI stack from your terminal\")\n\t.version(CLI_VERSION);\n\nprogram\n\t.command(\"login\")\n\t.description(\"Authenticate with AI Stack\")\n\t.action(loginCommand);\n\nprogram\n\t.command(\"collect\")\n\t.description(\"Scan and upload AI config files from your project\")\n\t.option(\"--no-global\", \"Exclude global config files (~/.claude, etc.)\")\n\t.action((options) => collectCommand({ global: options.global ?? true }));\n\nprogram\n\t.command(\"create\")\n\t.description(\"Download and write your stack's AI config files\")\n\t.action(createCommand);\n\nprogram\n\t.command(\"mcp\")\n\t.description(\n\t\t\"Run the aistack MCP server on stdio (sync preview + gated publish)\",\n\t)\n\t.action(() => {\n\t\t// stdout belongs to the protocol. Diagnostics go to stderr only.\n\t\trunStdioSyncServer({\n\t\t\tbaseUrl: BASE_URL,\n\t\t\tlog: (line) => process.stderr.write(`[aistack-mcp] ${line}\\n`),\n\t\t});\n\t});\n\n// The documented default sync surface (#56): terminal-first, TTY gate.\nprogram\n\t.command(\"sync\")\n\t.description(\"Scan, preview, and publish measured usage (rolling 30 days)\")\n\t.option(\n\t\t\"--auto [state]\",\n\t\t\"silent background sync; 'on' asks your stack for the permission and installs the SessionStart hooks, 'off' revokes both\",\n\t)\n\t.option(\n\t\t\"--every <hours>\",\n\t\t\"with --auto on: hours between auto-syncs (default 24)\",\n\t)\n\t.action((options) => syncCommand(options));\n\nprogram\n\t.command(\"connect\")\n\t.description(\"Install the in-session sync surface (MCP server + Skill)\")\n\t.argument(\"<harness>\", 'the harness to connect (\"claude\")')\n\t.action(connectCommand);\n\nprogram.parse();\n","// The one place the CLI's version string lives.\n//\n// `tsup` replaces `__AISTACK_CLI_VERSION__` at build time with the version in\n// `package.json`, so a release cannot ship a stale number. The fallback covers\n// running from source (tests, `tsx src/index.ts`), where no define happens.\ndeclare const __AISTACK_CLI_VERSION__: string | undefined;\n\nexport const CLI_VERSION: string =\n\ttypeof __AISTACK_CLI_VERSION__ === \"string\"\n\t\t? __AISTACK_CLI_VERSION__\n\t\t: \"0.0.0-dev\";\n","import { CLI_VERSION } from \"./version.js\";\n\nexport const BASE_URL = process.env.AISTACK_URL || \"https://aistack.to\";\n\nasync function request(\n\tpath: string,\n\toptions: RequestInit = {},\n): Promise<Response> {\n\treturn fetch(`${BASE_URL}${path}`, {\n\t\t...options,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t...options.headers,\n\t\t},\n\t});\n}\n\nfunction authHeaders(token: string): HeadersInit {\n\treturn { Authorization: `Bearer ${token}` };\n}\n\n/**\n * Turn the two statuses #52 introduced into sentences.\n *\n * A bare `429` tells the user nothing they can act on, and a bare `403` reads\n * like a bug rather than a machine that is no longer allowed to do this. Every\n * other status keeps its number, because the number is all we know about it.\n */\nfunction failure(what: string, res: Response): Error {\n\tif (res.status === 429) {\n\t\tconst retry = res.headers.get(\"Retry-After\");\n\t\treturn new Error(\n\t\t\tretry\n\t\t\t\t? `${what}: too many requests. Try again in ${retry} seconds.`\n\t\t\t\t: `${what}: too many requests. Try again in a minute.`,\n\t\t);\n\t}\n\tif (res.status === 403) {\n\t\treturn new Error(\n\t\t\t`${what}: this machine is not allowed to do that. Run \\`aistack login\\` again to re-link it.`,\n\t\t);\n\t}\n\treturn new Error(`${what}: ${res.status}`);\n}\n\n/**\n * Open a device-code session.\n *\n * `machineName` is a PROPOSAL, not a fact: the approval page renders it in an\n * editable field, so the user sees the string before it is stored and can\n * overwrite or clear it. That is why the hostname may be sent automatically —\n * the consent happens in the browser, a moment later, with the string on screen.\n */\nexport async function authStart(machineName?: string): Promise<{\n\tsecretId: string;\n\tuserCode: string;\n\tauthUrl: string;\n}> {\n\tconst res = await request(\"/api/cli/auth/start\", {\n\t\tmethod: \"POST\",\n\t\t// `cliVersion` rides along so `cli_login_completed` can report which\n\t\t// version linked the machine (#78). The server carries it on the pending\n\t\t// session and reads it at the token exchange.\n\t\tbody: JSON.stringify(\n\t\t\tmachineName\n\t\t\t\t? { machineName, cliVersion: CLI_VERSION }\n\t\t\t\t: { cliVersion: CLI_VERSION },\n\t\t),\n\t});\n\tif (!res.ok) throw failure(\"Auth start failed\", res);\n\treturn res.json();\n}\n\nexport async function authPoll(\n\tsecretId: string,\n): Promise<{ status: string; token?: string; userId?: string }> {\n\tconst res = await request(\n\t\t`/api/cli/auth/poll?secretId=${encodeURIComponent(secretId)}`,\n\t);\n\tif (!res.ok) throw failure(\"Auth poll failed\", res);\n\treturn res.json();\n}\n\nexport async function stackCollect(\n\ttoken: string,\n\tdata: { resources: Resource[] },\n): Promise<{ slug: string; shortId: string; url: string }> {\n\tconst res = await request(\"/api/cli/stacks/collect\", {\n\t\tmethod: \"POST\",\n\t\theaders: authHeaders(token),\n\t\tbody: JSON.stringify(data),\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (!res.ok) {\n\t\tthrow new Error(await formatHttpError(res, \"Collect failed\"));\n\t}\n\treturn res.json();\n}\n\nasync function formatHttpError(res: Response, label: string): Promise<string> {\n\tconst prefix = `${label}: ${res.status} ${res.statusText || \"\"}`.trim();\n\tconst text = await res.text().catch(() => \"\");\n\tif (!text) return prefix;\n\ttry {\n\t\tconst body = JSON.parse(text) as { error?: string; message?: string };\n\t\tconst detail = body.error || body.message;\n\t\tif (detail) return `${prefix} — ${detail}`;\n\t} catch {}\n\tconst snippet = text.trim().slice(0, 500);\n\treturn snippet ? `${prefix} — ${snippet}` : prefix;\n}\n\nexport type SyncPublishResult = {\n\treceivedAt: number;\n\tstackSlug: string;\n\turl: string;\n\tkeptPrivate: { stored: number; refused: boolean };\n};\n\n/**\n * Publish one approved snapshot.\n *\n * Takes the staged body as an ALREADY-SERIALIZED string: the bytes the user\n * approved at the gate are the bytes on the wire, with no re-serialization\n * step between them (#35's binding constraint, #41).\n */\nexport async function syncPublish(\n\ttoken: string,\n\tbodyJson: string,\n): Promise<SyncPublishResult> {\n\tconst res = await request(\"/api/cli/sync\", {\n\t\tmethod: \"POST\",\n\t\theaders: authHeaders(token),\n\t\tbody: bodyJson,\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (res.status === 403 || res.status === 429)\n\t\tthrow failure(\"Sync failed\", res);\n\tif (!res.ok) {\n\t\tthrow new Error(await formatHttpError(res, \"Sync failed\"));\n\t}\n\treturn res.json();\n}\n\nexport type AutoSyncSetResult = {\n\tautoSync: { enabled: boolean; frequencyHours: number };\n\tlastAutoSyncAt: number | null;\n};\n\n/**\n * Set the auto-sync permission on the stack this machine is linked to (#103).\n *\n * The destination is the stack bound to the BEARER, exactly like a publish —\n * the body says what the permission is, never whose it is. The frequency goes\n * out only when the flag goes on: off keeps no schedule, and sending a number\n * with it would overwrite the interval the owner picked for the next enable.\n */\nexport async function setAutoSync(\n\ttoken: string,\n\tflag: { enabled: boolean; frequencyHours?: number },\n): Promise<AutoSyncSetResult> {\n\tconst res = await request(\"/api/cli/auto-sync\", {\n\t\tmethod: \"POST\",\n\t\theaders: authHeaders(token),\n\t\tbody: JSON.stringify(\n\t\t\tflag.enabled && flag.frequencyHours !== undefined\n\t\t\t\t? { enabled: true, frequencyHours: flag.frequencyHours }\n\t\t\t\t: { enabled: flag.enabled },\n\t\t),\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (res.status === 403 || res.status === 429)\n\t\tthrow failure(\"Auto-sync update failed\", res);\n\tif (!res.ok) {\n\t\tthrow new Error(await formatHttpError(res, \"Auto-sync update failed\"));\n\t}\n\treturn res.json();\n}\n\nexport async function stackGet(token: string): Promise<StackData | null> {\n\tconst res = await request(\"/api/cli/stacks\", {\n\t\theaders: authHeaders(token),\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (res.status === 404) return null;\n\tif (!res.ok) throw failure(\"Stack fetch failed\", res);\n\treturn res.json();\n}\n\n// Types used across the CLI\nexport interface ResourceFile {\n\tname: string;\n\tcontent: string;\n\tpath?: string;\n\ttags?: string[];\n}\n\nexport interface Resource {\n\ttype: string;\n\tname: string;\n\tdescription?: string;\n\tgroup: string;\n\tstableKey: string;\n\tfiles?: ResourceFile[];\n\tupstream?: {\n\t\trepoUrl: string;\n\t\tpath?: string;\n\t\tlicense?: string;\n\t\tstars?: number;\n\t\tlastCommitSha?: string;\n\t\tlastSyncAt?: number;\n\t};\n\tpkg?: {\n\t\tregistry: \"npm\" | \"pypi\" | \"oci\" | \"url\";\n\t\tid: string;\n\t\tversion?: string;\n\t\ttransport?: \"stdio\" | \"http\" | \"sse\";\n\t};\n}\n\nexport interface StackData {\n\tname: string;\n\tslug: string;\n\tshortId: string;\n\tresources: Resource[];\n}\n","import * as p from \"@clack/prompts\";\nimport { type Resource, stackCollect, stackGet } from \"../api.js\";\nimport { classify } from \"../classifier.js\";\nimport { getExcludedPaths, getToken, saveExcludedPaths } from \"../config.js\";\nimport { buildRepoLinkResource, detectRepoUrl } from \"../git.js\";\nimport { repoNameFromCanonical } from \"../github-repo.js\";\nimport { detectHooks } from \"../hooks.js\";\nimport { detectMcpServers } from \"../mcp.js\";\nimport { detectInstalledPlugins } from \"../plugins.js\";\nimport { type ScannedFile, scanGlobal, scanLocal } from \"../scanner.js\";\nimport {\n\tbold,\n\tdim,\n\tdivider,\n\tintro,\n\tlime,\n\tlimeBold,\n\tlines,\n\toutro,\n\toutroCancel,\n\toutroError,\n\toutroSkipped,\n\tred,\n\tsection,\n\tyellow,\n} from \"../theme.js\";\n\n// Sentinel selection key for the detected repo link. NUL-prefixed so it can\n// never collide with a real ScannedFile.relativePath, letting links ride the\n// existing excluded[] selection/persistence model with no config.ts changes.\nconst REPO_LINK_KEY = \"\\0repo-link\";\n\n/**\n * A non-file resource surfaced during collect — the repo link, an installed\n * plugin, etc. Toggleable and persisted exactly like a scanned file, keyed by\n * its sentinel. Everything attaches to the single stack (global) server-side.\n */\ninterface DetectedLink {\n\tkey: string;\n\tresource: Resource;\n\tlabel: string;\n}\n\nexport async function collectCommand(options: { global: boolean }) {\n\tintro(\"collect\");\n\n\tconst token = getToken();\n\tif (!token) {\n\t\tp.log.error(\n\t\t\t`Not authenticated. Run ${limeBold(\"npx @use-aistack/cli login\")} first.`,\n\t\t);\n\t\toutroError(\"not authenticated\");\n\t\tprocess.exit(1);\n\t}\n\n\tconst cwd = process.cwd();\n\tconst savedExcluded = getExcludedPaths(cwd);\n\n\tconst s = p.spinner();\n\ts.start(\"Scanning...\");\n\n\tconst localFiles = scanLocal(cwd);\n\tconst globalFiles = options.global ? scanGlobal() : [];\n\ts.stop(\"Scan complete\");\n\n\tif (localFiles.length === 0 && globalFiles.length === 0) {\n\t\tp.log.warn(\"No AI configuration files found.\");\n\t\toutroSkipped(\"nothing to collect\");\n\t\treturn;\n\t}\n\n\t// Apply saved exclusions\n\tconst allFiles = [...localFiles, ...globalFiles];\n\tlet selectedFiles = allFiles.filter(\n\t\t(f) => !savedExcluded.includes(f.relativePath),\n\t);\n\tlet excluded = allFiles.filter((f) => savedExcluded.includes(f.relativePath));\n\n\t// Show file counts\n\tp.log.info(\n\t\t`${lime(String(selectedFiles.length))} included${excluded.length > 0 ? ` · ${dim(String(excluded.length) + \" excluded\")}` : \"\"}`,\n\t);\n\n\t// Detect non-file links: the repo this lives in, installed Claude Code\n\t// plugins, MCP servers, hooks. Each is toggleable and included by default\n\t// unless previously deselected. All graceful no-ops.\n\tconst detectedLinks: DetectedLink[] = [];\n\tconst repoUrl = detectRepoUrl(cwd);\n\tif (repoUrl) {\n\t\tdetectedLinks.push({\n\t\t\tkey: REPO_LINK_KEY,\n\t\t\tresource: buildRepoLinkResource(repoUrl),\n\t\t\tlabel: `repo · ${repoNameFromCanonical(repoUrl)}`,\n\t\t});\n\t}\n\tfor (const resource of detectInstalledPlugins()) {\n\t\tdetectedLinks.push({\n\t\t\tkey: `\\0plugin:${resource.stableKey}`,\n\t\t\tresource,\n\t\t\tlabel: `plugin · ${resource.name}`,\n\t\t});\n\t}\n\tfor (const resource of detectMcpServers(cwd)) {\n\t\tdetectedLinks.push({\n\t\t\tkey: `\\0mcp:${resource.stableKey}`,\n\t\t\tresource,\n\t\t\tlabel: `mcp · ${resource.name}`,\n\t\t});\n\t}\n\tfor (const resource of detectHooks(cwd)) {\n\t\tdetectedLinks.push({\n\t\t\tkey: `\\0hook:${resource.stableKey}`,\n\t\t\tresource,\n\t\t\tlabel: `hook · ${resource.name}`,\n\t\t});\n\t}\n\n\tconst includedLinks = new Set(\n\t\tdetectedLinks\n\t\t\t.filter((l) => !savedExcluded.includes(l.key))\n\t\t\t.map((l) => l.key),\n\t);\n\tconst withLinks = (base: Resource[]): Resource[] => [\n\t\t...base,\n\t\t...detectedLinks\n\t\t\t.filter((l) => includedLinks.has(l.key))\n\t\t\t.map((l) => l.resource),\n\t];\n\n\t// Classify selected files\n\tlet allResources = withLinks(classify(selectedFiles));\n\n\t// Fetch the existing stack and diff against its resources.\n\tlet existingStack: Awaited<ReturnType<typeof stackGet>> = null;\n\ttry {\n\t\texistingStack = await stackGet(token);\n\t} catch (err) {\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\toutroError(\"error\");\n\t\tprocess.exit(1);\n\t}\n\n\t// Show file list or diff\n\tif (existingStack) {\n\t\tconst diff = diffResources(allResources, existingStack.resources);\n\t\tconst changeCount = diff.added + diff.changed + diff.removed;\n\n\t\tif (changeCount === 0) {\n\t\t\tp.log.info(\"No changes since last collect.\");\n\t\t\toutroSkipped(\"nothing to upload\");\n\t\t\treturn;\n\t\t}\n\n\t\tdivider();\n\t\tsection(\"changes\");\n\t\tlines(\n\t\t\tdiff.details.map((f) => {\n\t\t\t\tif (f.status === \"added\") return lime(`+ ${f.name}`);\n\t\t\t\tif (f.status === \"changed\") return yellow(`~ ${f.name}`);\n\t\t\t\treturn red(`- ${f.name}`);\n\t\t\t}),\n\t\t);\n\t\tif (diff.unchanged > 0) {\n\t\t\tlines([dim(`${diff.unchanged} unchanged`)]);\n\t\t}\n\t\tdivider();\n\t} else {\n\t\tconst local = selectedFiles.filter((f) => f.source === \"local\");\n\t\tconst global = selectedFiles.filter((f) => f.source === \"global\");\n\n\t\tif (local.length > 0) {\n\t\t\tp.log.step(`${bold(\"LOCAL\")} ${dim(String(local.length))}`);\n\t\t\tdivider();\n\t\t\tfor (const [type, files] of groupByType(local)) {\n\t\t\t\tlines([`${lime(type.toUpperCase())} ${dim(`${files.length}`)}`]);\n\t\t\t\tlines(files.map((f) => dim(` ${f.relativePath}`)));\n\t\t\t}\n\t\t\tdivider();\n\t\t}\n\t\tif (global.length > 0) {\n\t\t\tp.log.step(`${bold(\"GLOBAL\")} ${dim(String(global.length))}`);\n\t\t\tdivider();\n\t\t\tfor (const [type, files] of groupByType(global)) {\n\t\t\t\tlines([`${lime(type.toUpperCase())} ${dim(`${files.length}`)}`]);\n\t\t\t\tlines(files.map((f) => dim(` ${f.relativePath}`)));\n\t\t\t}\n\t\t\tdivider();\n\t\t}\n\t\tconst shownLinks = detectedLinks.filter((l) => includedLinks.has(l.key));\n\t\tif (shownLinks.length > 0) {\n\t\t\tp.log.step(`${bold(\"LINKS\")} ${dim(String(shownLinks.length))}`);\n\t\t\tdivider();\n\t\t\tlines(shownLinks.map((l) => dim(` ${l.label}`)));\n\t\t\tdivider();\n\t\t}\n\t}\n\n\t// Action: upload, customize, or cancel\n\tconst action = await p.select({\n\t\tmessage: existingStack\n\t\t\t? \"Upload changes?\"\n\t\t\t: `Upload ${bold(String(selectedFiles.length))} files to your stack?`,\n\t\toptions: [\n\t\t\t{ value: \"upload\", label: \"Upload\" },\n\t\t\t{ value: \"customize\", label: \"Select files\" },\n\t\t\t{ value: \"cancel\", label: \"Cancel\" },\n\t\t],\n\t});\n\n\tif (p.isCancel(action) || action === \"cancel\") {\n\t\toutroCancel();\n\t\tprocess.exit(0);\n\t}\n\n\tif (action === \"customize\") {\n\t\tconst linkOptions = detectedLinks.map((l) => ({\n\t\t\tvalue: l.key,\n\t\t\tlabel: l.label,\n\t\t\thint: \"link\",\n\t\t}));\n\t\tconst selected = await p.multiselect({\n\t\t\tmessage: \"Select files to include:\",\n\t\t\toptions: [\n\t\t\t\t...linkOptions,\n\t\t\t\t...allFiles.map((f) => ({\n\t\t\t\t\tvalue: f.relativePath,\n\t\t\t\t\tlabel: f.relativePath,\n\t\t\t\t\thint: `${f.type}${f.source === \"global\" ? \" · global\" : \"\"}`,\n\t\t\t\t})),\n\t\t\t],\n\t\t\tinitialValues: [\n\t\t\t\t...detectedLinks\n\t\t\t\t\t.filter((l) => includedLinks.has(l.key))\n\t\t\t\t\t.map((l) => l.key),\n\t\t\t\t...selectedFiles.map((f) => f.relativePath),\n\t\t\t],\n\t\t});\n\n\t\tif (p.isCancel(selected)) {\n\t\t\toutroCancel();\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\tconst selectedSet = new Set(selected as string[]);\n\t\tselectedFiles = allFiles.filter((f) => selectedSet.has(f.relativePath));\n\t\texcluded = allFiles.filter((f) => !selectedSet.has(f.relativePath));\n\t\tincludedLinks.clear();\n\t\tfor (const l of detectedLinks) {\n\t\t\tif (selectedSet.has(l.key)) includedLinks.add(l.key);\n\t\t}\n\t\tallResources = withLinks(classify(selectedFiles));\n\n\t\tif (selectedFiles.length === 0 && includedLinks.size === 0) {\n\t\t\tp.log.warn(\"No files selected.\");\n\t\t\toutroSkipped(\"nothing to collect\");\n\t\t\tprocess.exit(0);\n\t\t}\n\t}\n\n\ts.start(\"Uploading...\");\n\ttry {\n\t\tconst result = await stackCollect(token, { resources: allResources });\n\t\ts.stop(lime(\"Uploaded\"));\n\t\tconst excludedKeys = excluded.map((f) => f.relativePath);\n\t\tfor (const l of detectedLinks) {\n\t\t\tif (!includedLinks.has(l.key)) excludedKeys.push(l.key);\n\t\t}\n\t\tsaveExcludedPaths(cwd, excludedKeys);\n\t\tp.log.success(dim(result.url));\n\t\toutro(lime(\"done\"));\n\t} catch (err) {\n\t\ts.stop(\"Upload failed\");\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\toutroError(\"upload failed\");\n\t\tprocess.exit(1);\n\t}\n}\n\nconst TYPE_ORDER = [\n\t\"config\",\n\t\"prompt\",\n\t\"rule\",\n\t\"command\",\n\t\"skill\",\n\t\"subagent\",\n\t\"mcp\",\n\t\"hook\",\n\t\"custom\",\n];\n\nfunction groupByType(files: ScannedFile[]): Map<string, ScannedFile[]> {\n\tconst map = new Map<string, ScannedFile[]>();\n\tfor (const f of files) {\n\t\tconst existing = map.get(f.type) ?? [];\n\t\texisting.push(f);\n\t\tmap.set(f.type, existing);\n\t}\n\tconst sorted = new Map<string, ScannedFile[]>();\n\tfor (const type of TYPE_ORDER) {\n\t\tconst group = map.get(type);\n\t\tif (group) sorted.set(type, group);\n\t}\n\tfor (const [type, group] of map) {\n\t\tif (!sorted.has(type)) sorted.set(type, group);\n\t}\n\treturn sorted;\n}\n\ninterface DiffResult {\n\tadded: number;\n\tchanged: number;\n\tremoved: number;\n\tunchanged: number;\n\tdetails: Array<{ name: string; status: \"added\" | \"changed\" | \"removed\" }>;\n}\n\nexport function diffResources(\n\tcurrent: Resource[],\n\texisting: Resource[],\n): DiffResult {\n\tconst existingMap = new Map<string, string>();\n\tfor (const item of existing) {\n\t\tfor (const file of item.files ?? []) {\n\t\t\texistingMap.set(file.path ?? file.name, file.content);\n\t\t}\n\t}\n\n\tconst currentMap = new Map<string, string>();\n\tfor (const item of current) {\n\t\tfor (const file of item.files ?? []) {\n\t\t\tcurrentMap.set(file.path ?? file.name, file.content);\n\t\t}\n\t}\n\n\tconst details: DiffResult[\"details\"] = [];\n\tlet added = 0;\n\tlet changed = 0;\n\tlet unchanged = 0;\n\n\tfor (const [key, content] of currentMap) {\n\t\tconst prev = existingMap.get(key);\n\t\tif (prev === undefined) {\n\t\t\tadded++;\n\t\t\tdetails.push({ name: key, status: \"added\" });\n\t\t} else if (prev !== content) {\n\t\t\tchanged++;\n\t\t\tdetails.push({ name: key, status: \"changed\" });\n\t\t} else {\n\t\t\tunchanged++;\n\t\t}\n\t}\n\n\tlet removed = 0;\n\tfor (const key of existingMap.keys()) {\n\t\tif (!currentMap.has(key)) {\n\t\t\tremoved++;\n\t\t\tdetails.push({ name: key, status: \"removed\" });\n\t\t}\n\t}\n\n\t// Linked resources (GitHub repos AND package refs like MCP servers) carry no\n\t// files, so the file maps above can't see them. Diff them by stableKey —\n\t// unique for both `linked:<repo>:<path>` and `linked:pkg:<registry>:<id>` —\n\t// otherwise a link-only change is invisible and collect wrongly reports\n\t// \"nothing to upload\".\n\tconst linkLabel = (item: Resource): string => {\n\t\tif (item.upstream)\n\t\t\treturn `link: ${repoNameFromCanonical(item.upstream.repoUrl)}`;\n\t\tif (item.pkg) return `link: ${item.pkg.id}`;\n\t\treturn `link: ${item.name}`;\n\t};\n\tconst linkMap = (items: Resource[]): Map<string, Resource> => {\n\t\tconst map = new Map<string, Resource>();\n\t\tfor (const item of items) {\n\t\t\tif ((item.upstream || item.pkg) && !item.files?.length) {\n\t\t\t\tmap.set(item.stableKey, item);\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t};\n\tconst existingLinks = linkMap(existing);\n\tconst currentLinks = linkMap(current);\n\tfor (const [key, item] of currentLinks) {\n\t\tif (!existingLinks.has(key)) {\n\t\t\tadded++;\n\t\t\tdetails.push({ name: linkLabel(item), status: \"added\" });\n\t\t}\n\t}\n\tfor (const [key, item] of existingLinks) {\n\t\tif (!currentLinks.has(key)) {\n\t\t\tremoved++;\n\t\t\tdetails.push({ name: linkLabel(item), status: \"removed\" });\n\t\t}\n\t}\n\n\treturn { added, changed, removed, unchanged, details };\n}\n","import { basename, dirname } from \"node:path\";\nimport type { Resource } from \"./api.js\";\nimport type { ScannedFile } from \"./scanner.js\";\nimport { computeStableKey } from \"./stableKey.js\";\n\nexport function classify(files: ScannedFile[]): Resource[] {\n\t// Group by {group, source, type, containing directory}\n\tconst groups = new Map<string, ScannedFile[]>();\n\tconst singletons: ScannedFile[] = [];\n\n\tconst singletonRoots = new Set([\n\t\t\".\",\n\t\t\"~\",\n\t\t\"~/.claude\",\n\t\t\"~/.cursor\",\n\t\t\"~/.continue\",\n\t\t\".claude\",\n\t\t\".cursor\",\n\t\t\".github\",\n\t]);\n\n\tfor (const file of files) {\n\t\tconst dir = dirname(file.relativePath);\n\t\tconst isSingleton = singletonRoots.has(dir);\n\n\t\tif (isSingleton) {\n\t\t\tsingletons.push(file);\n\t\t} else {\n\t\t\tconst key = `${file.group}:${file.source}:${file.type}:${dir}`;\n\t\t\tconst existing = groups.get(key) ?? [];\n\t\t\texisting.push(file);\n\t\t\tgroups.set(key, existing);\n\t\t}\n\t}\n\n\tconst items: Resource[] = [];\n\n\t// Singletons: one Resource per file\n\tfor (const file of singletons) {\n\t\tconst relPath = file.relativePath\n\t\t\t.replace(/^~\\/\\.[^/]+\\//, \"\")\n\t\t\t.replace(/^\\.[^/]+\\//, \"\");\n\t\titems.push({\n\t\t\ttype: file.type,\n\t\t\tname: file.relativePath,\n\t\t\tgroup: file.group,\n\t\t\tstableKey: computeStableKey(file.group, file.type, relPath),\n\t\t\tfiles: [\n\t\t\t\t{\n\t\t\t\t\tname: basename(file.relativePath),\n\t\t\t\t\tcontent: file.content,\n\t\t\t\t\tpath: file.relativePath,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n\n\t// Groups: one Resource per directory group\n\tfor (const [, groupFiles] of groups) {\n\t\tconst first = groupFiles[0];\n\t\tconst dir = dirname(first.relativePath);\n\t\tconst relPath = dir\n\t\t\t.replace(/^~\\/\\.claude\\//, \"\")\n\t\t\t.replace(/^\\.claude\\//, \"\")\n\t\t\t.replace(/^~\\/\\.cursor\\//, \"\")\n\t\t\t.replace(/^\\.cursor\\//, \"\");\n\t\tconst typeLabel =\n\t\t\tfirst.type === \"subagent\" ? \"subagents\" : `${first.type}s`;\n\n\t\titems.push({\n\t\t\ttype: first.type,\n\t\t\tname: dir,\n\t\t\tdescription: `${groupFiles.length} ${typeLabel}`,\n\t\t\tgroup: first.group,\n\t\t\tstableKey: computeStableKey(first.group, first.type, relPath),\n\t\t\tfiles: groupFiles.map((f) => ({\n\t\t\t\tname: basename(f.relativePath),\n\t\t\t\tcontent: f.content,\n\t\t\t\tpath: f.relativePath,\n\t\t\t})),\n\t\t});\n\t}\n\n\treturn items;\n}\n","export function computeStableKey(\n\tgroup: string,\n\ttype: string,\n\trelPath: string,\n): string {\n\treturn `${group}:${type}:${relPath}`;\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { BASE_URL } from \"./api.js\";\n\nconst CONFIG_DIR = join(homedir(), \".config\", \"aistack\");\nconst CREDENTIALS_FILE = join(CONFIG_DIR, \"credentials.json\");\n\ninterface ServerCredentials {\n\ttoken: string;\n\tuserId?: string;\n}\n\n/**\n * Credentials keyed by server URL (#61). Since hash-at-rest (#52) the server\n * stores only a hash, so this file holds the only plaintext copy of each\n * token. The old flat `{token, userId}` form let a localhost login overwrite\n * the prod token, which was unrecoverable. The map keeps one entry per server.\n */\ninterface CredentialsFile {\n\tservers: Record<string, ServerCredentials>;\n}\n\n/** Where a legacy flat token is assumed to come from. */\nconst DEFAULT_SERVER_URL = \"https://aistack.to\";\n\n/**\n * Read the credentials file and lift the legacy flat form into the map.\n *\n * A legacy token carries no record of which server issued it. It is assigned\n * to the default prod URL, not the caller's current server: every real flat\n * token came from prod, and keying it under a localhost caller would put it\n * exactly where the next localhost login overwrites it.\n */\nfunction readCredentials(file: string): {\n\tdata: CredentialsFile;\n\tlegacy: boolean;\n} {\n\tconst empty = { data: { servers: {} }, legacy: false };\n\tif (!existsSync(file)) return empty;\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\tif (!raw || typeof raw !== \"object\") return empty;\n\t\tif (raw.servers && typeof raw.servers === \"object\") {\n\t\t\treturn {\n\t\t\t\tdata: { servers: raw.servers as Record<string, ServerCredentials> },\n\t\t\t\tlegacy: false,\n\t\t\t};\n\t\t}\n\t\tif (typeof raw.token === \"string\" && raw.token) {\n\t\t\treturn {\n\t\t\t\tdata: {\n\t\t\t\t\tservers: {\n\t\t\t\t\t\t[DEFAULT_SERVER_URL]: { token: raw.token, userId: raw.userId },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tlegacy: true,\n\t\t\t};\n\t\t}\n\t\t// A cleared legacy file is `{}` — empty, but safe to rewrite.\n\t\treturn { data: { servers: {} }, legacy: true };\n\t} catch {\n\t\t// Do not rewrite an unreadable file. It may still hold a token that a\n\t\t// human can recover, and this file holds the only plaintext copy.\n\t\treturn empty;\n\t}\n}\n\nfunction writeCredentials(file: string, data: CredentialsFile): void {\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, JSON.stringify(data, null, 2));\n}\n\nexport function getToken(\n\tserverUrl: string = BASE_URL,\n\tfile: string = CREDENTIALS_FILE,\n): string | null {\n\tconst { data, legacy } = readCredentials(file);\n\tif (legacy) writeCredentials(file, data);\n\treturn data.servers[serverUrl]?.token ?? null;\n}\n\nexport function saveToken(\n\ttoken: string,\n\tuserId?: string,\n\tserverUrl: string = BASE_URL,\n\tfile: string = CREDENTIALS_FILE,\n): void {\n\tconst { data } = readCredentials(file);\n\tdata.servers[serverUrl] = { token, userId };\n\twriteCredentials(file, data);\n}\n\n/** Remove only the current server's entry. Other servers keep their tokens. */\nexport function clearToken(\n\tserverUrl: string = BASE_URL,\n\tfile: string = CREDENTIALS_FILE,\n): void {\n\tif (!existsSync(file)) return;\n\tconst { data } = readCredentials(file);\n\tdelete data.servers[serverUrl];\n\twriteCredentials(file, data);\n}\n\nconst SETTINGS_FILE = join(CONFIG_DIR, \"settings.json\");\n\n/**\n * Machine-local switches (#56). A separate file from credentials.json so a\n * login overwrite never resets an answered upsell, and clearing settings never\n * touches the token.\n */\nexport interface AutoSyncConfig {\n\t/** The standing opt-in. `sync --auto` publishes nothing when false. */\n\tenabled: boolean;\n\t/** Minimum hours between auto-sync attempts. Default 24. */\n\tfrequencyHours: number;\n}\n\n/** Bookkeeping the `sync --auto` runs write. Separate from the opt-in. */\nexport interface AutoSyncState {\n\t/** Epoch ms of the last attempt (success or failure). The freshness gate. */\n\tlastRunAt?: number;\n\tlastSuccessAt?: number;\n\t/** One line about the last run, shown on the next interactive sync. */\n\tlastResult?: string;\n\tconsecutiveFailures?: number;\n\t/** The 3-failure systemMessage went out. Reset on success. */\n\tfailureWarned?: boolean;\n}\n\nexport const DEFAULT_FREQUENCY_HOURS = 24;\n\nexport interface Settings {\n\t/** The post-sync connect-claude upsell was answered (either way). */\n\tconnectClaudeAnswered?: boolean;\n\t/** The post-sync auto-sync ask was answered (either way). */\n\tautoSyncAnswered?: boolean;\n\tautoSync?: AutoSyncConfig;\n\tautoSyncState?: AutoSyncState;\n}\n\nexport function getSettings(file: string = SETTINGS_FILE): Settings {\n\tif (!existsSync(file)) return {};\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\treturn raw && typeof raw === \"object\" ? (raw as Settings) : {};\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nexport function saveSettings(\n\tpatch: Partial<Settings>,\n\tfile: string = SETTINGS_FILE,\n): void {\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(\n\t\tfile,\n\t\tJSON.stringify({ ...getSettings(file), ...patch }, null, 2),\n\t);\n}\n\nconst PROJECTS_FILE = join(CONFIG_DIR, \"projects.json\");\n\ninterface ProjectEntry {\n\texcluded?: string[];\n}\n\ninterface ProjectsData {\n\t[directory: string]: ProjectEntry;\n}\n\nfunction readProjects(): ProjectsData {\n\tif (!existsSync(PROJECTS_FILE)) return {};\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(PROJECTS_FILE, \"utf-8\"));\n\t\t// Tolerate legacy entries: string values (oldest) and objects that still\n\t\t// carry a `name` field. Only `excluded` is read going forward.\n\t\tconst data: ProjectsData = {};\n\t\tfor (const [key, value] of Object.entries(raw)) {\n\t\t\tif (typeof value === \"string\") {\n\t\t\t\tdata[key] = {};\n\t\t\t} else if (value && typeof value === \"object\") {\n\t\t\t\tconst excluded = (value as { excluded?: string[] }).excluded;\n\t\t\t\tdata[key] = Array.isArray(excluded) ? { excluded } : {};\n\t\t\t}\n\t\t}\n\t\treturn data;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction writeProjects(data: ProjectsData): void {\n\tmkdirSync(CONFIG_DIR, { recursive: true });\n\twriteFileSync(PROJECTS_FILE, JSON.stringify(data, null, 2));\n}\n\nexport function getExcludedPaths(directory: string): string[] {\n\treturn readProjects()[directory]?.excluded ?? [];\n}\n\nexport function saveExcludedPaths(directory: string, excluded: string[]): void {\n\tconst data = readProjects();\n\tdata[directory] = {\n\t\texcluded: excluded.length > 0 ? excluded : undefined,\n\t};\n\twriteProjects(data);\n}\n","import { execFileSync } from \"node:child_process\";\nimport type { Resource } from \"./api.js\";\nimport {\n\tcanonicalizeRepoUrl,\n\tnormalizeUpstreamPath,\n\trepoNameFromCanonical,\n} from \"./github-repo.js\";\n\n/**\n * Returns the raw `origin` remote URL for a working directory, or null when it\n * can't be determined. Injectable so `detectRepoUrl` stays unit-testable\n * without spawning git.\n */\nexport type GitRemoteRunner = (cwd: string) => string | null;\n\nexport const defaultGitRemoteRunner: GitRemoteRunner = (cwd) => {\n\ttry {\n\t\t// argv form (no shell) — git walks up to the repo root itself, and\n\t\t// stderr is swallowed so \"not a git repository\" never leaks into the\n\t\t// CLI's output. git missing / no repo / no origin all throw → null.\n\t\treturn execFileSync(\"git\", [\"-C\", cwd, \"remote\", \"get-url\", \"origin\"], {\n\t\t\tencoding: \"utf-8\",\n\t\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t\t}).trim();\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n/**\n * Detect the canonical GitHub repo URL for `cwd`, or null. Non-GitHub origins\n * (GitLab, Bitbucket, …) canonicalize to null, so this is a graceful no-op\n * outside of GitHub repos.\n */\nexport function detectRepoUrl(\n\tcwd: string,\n\trun: GitRemoteRunner = defaultGitRemoteRunner,\n): string | null {\n\tconst raw = run(cwd);\n\tif (!raw) return null;\n\treturn canonicalizeRepoUrl(raw);\n}\n\nexport interface LinkSpec {\n\t/** Canonical GitHub URL (https://github.com/owner/repo). */\n\tcanonical: string;\n\t/** Optional subpath within the repo. */\n\tpath?: string;\n\tname: string;\n\ttype: string;\n\tgroup: string;\n\t/** Optional pinned commit, stored as upstream.lastCommitSha. */\n\tsha?: string;\n}\n\n/**\n * Build a linked-resource payload. Mirrors the web `linkResource` mutation: no\n * files (upstream presence is the storage discriminator) and the exact\n * `linked:${canonical}:${normPath}` stableKey so the web unlink UI — which\n * matches by stableKey — recognizes it. `path`/`lastCommitSha` are omitted when\n * empty so the by_upstream dedup index matches at both write and query.\n */\nexport function buildLinkResource(spec: LinkSpec): Resource {\n\tconst normPath = normalizeUpstreamPath(spec.path);\n\treturn {\n\t\ttype: spec.type,\n\t\tname: spec.name,\n\t\tgroup: spec.group,\n\t\tstableKey: `linked:${spec.canonical}:${normPath}`,\n\t\tupstream: {\n\t\t\trepoUrl: spec.canonical,\n\t\t\t...(normPath ? { path: normPath } : {}),\n\t\t\t...(spec.sha ? { lastCommitSha: spec.sha } : {}),\n\t\t},\n\t};\n}\n\n/** The repo this project lives in: a GitHub link (stack-owned server-side). */\nexport function buildRepoLinkResource(canonical: string): Resource {\n\treturn buildLinkResource({\n\t\tcanonical,\n\t\tname: repoNameFromCanonical(canonical),\n\t\ttype: \"custom\",\n\t\tgroup: \"generic\",\n\t});\n}\n","/**\n * Trimmed copy of `src/lib/github-repo.ts` — the CANONICAL parser, whose\n * `github-repo.test.ts` is the canonical test table. Copied (not imported)\n * because the CLI ships as an independent npm package and its tsconfig\n * (`rootDir: \"src\"` + `declaration: true`) forbids cross-rootDir imports.\n * Keep these functions in sync with the canonical source.\n *\n * Only the pieces the CLI needs are included: `parseRepo`,\n * `canonicalizeRepoUrl`, `repoNameFromCanonical`, and `normalizeUpstreamPath`\n * (a null from `canonicalizeRepoUrl` is the CLI's graceful-skip signal, so\n * `isGithubRepoUrl` is intentionally omitted).\n */\n\nfunction isGithubHost(host: string): boolean {\n\tconst h = host.toLowerCase();\n\treturn h === \"github.com\" || h === \"www.github.com\";\n}\n\nexport function parseRepo(\n\tinput: string,\n): { owner: string; repo: string } | null {\n\tconst trimmed = input.trim();\n\tif (!trimmed) return null;\n\n\t// SCP-like form `git@host:owner/repo`: take the host and the path after the\n\t// colon. Otherwise strip the scheme, then split the leading host off the path.\n\tconst scpMatch = trimmed.match(/^[^@]+@([^:]+):(.+)$/);\n\tlet host: string;\n\tlet withoutHost: string;\n\tif (scpMatch) {\n\t\thost = scpMatch[1];\n\t\twithoutHost = scpMatch[2];\n\t} else {\n\t\tconst withoutScheme = trimmed.replace(/^[a-z]+:\\/\\//i, \"\");\n\t\tconst slash = withoutScheme.indexOf(\"/\");\n\t\tif (slash === -1) return null;\n\t\thost = withoutScheme.slice(0, slash);\n\t\twithoutHost = withoutScheme.slice(slash + 1);\n\t}\n\tif (!isGithubHost(host)) return null;\n\n\t// Drop any query string or anchor, then split into path segments.\n\tconst pathPart = withoutHost.replace(/[?#].*$/, \"\");\n\tconst segments = pathPart.split(\"/\").filter(Boolean);\n\n\tconst owner = segments[0];\n\tconst repo = segments[1]?.replace(/\\.git$/, \"\");\n\tif (!owner || !repo) return null;\n\n\treturn { owner: owner.toLowerCase(), repo: repo.toLowerCase() };\n}\n\nexport function canonicalizeRepoUrl(input: string): string | null {\n\tconst parsed = parseRepo(input);\n\tif (!parsed) return null;\n\treturn `https://github.com/${parsed.owner}/${parsed.repo}`;\n}\n\nexport function repoNameFromCanonical(canonical: string): string {\n\treturn parseRepo(canonical)?.repo ?? \"\";\n}\n\nexport function normalizeUpstreamPath(path: string | undefined): string {\n\tif (!path) return \"\";\n\treturn path.split(\"/\").filter(Boolean).join(\"/\");\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { Resource } from \"./api.js\";\n\n/**\n * Extract hooks defined inline in Claude Code settings as discrete `hook`\n * resources — one per event (PreToolUse, PostToolUse, …). Without this they\n * only ride inside the collected settings.json config blob and never surface as\n * first-class hooks.\n *\n * These are HOSTED resources (the event's config block is the content), so they\n * participate in the normal file-based diff. They intentionally duplicate data\n * also present in the settings.json resource; the distinct `hooks:` stableKeys\n * mean no collision, and first-class visibility was the deliberate tradeoff.\n */\n\ninterface SettingsFile {\n\thooks?: Record<string, unknown>;\n}\n\nfunction readJson<T>(path: string): T | null {\n\ttry {\n\t\tif (!existsSync(path)) return null;\n\t\treturn JSON.parse(readFileSync(path, \"utf-8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction hooksFrom(\n\tpath: string,\n\tsource: \"local\" | \"global\",\n\tout: Resource[],\n\tseen: Set<string>,\n) {\n\tconst hooks = readJson<SettingsFile>(path)?.hooks;\n\tif (!hooks || typeof hooks !== \"object\") return;\n\tfor (const [event, config] of Object.entries(hooks)) {\n\t\tconst stableKey = `hooks:${source}:${event}`;\n\t\tif (seen.has(stableKey)) continue;\n\t\tseen.add(stableKey);\n\t\tout.push({\n\t\t\ttype: \"hook\",\n\t\t\tname: event,\n\t\t\tgroup: \"claude-code\",\n\t\t\tstableKey,\n\t\t\tfiles: [\n\t\t\t\t{\n\t\t\t\t\tname: `${event}.json`,\n\t\t\t\t\tcontent: JSON.stringify(config, null, 2),\n\t\t\t\t\tpath: `hooks/${event}.json`,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n}\n\n/**\n * Detect inline hooks from project settings (`.claude/settings.json` +\n * `.claude/settings.local.json`) and global settings (`~/.claude/settings.json`).\n * Project settings win over the `.local` override on the same event.\n */\nexport function detectHooks(cwd: string, home: string = homedir()): Resource[] {\n\tconst out: Resource[] = [];\n\tconst seen = new Set<string>();\n\thooksFrom(join(cwd, \".claude\", \"settings.json\"), \"local\", out, seen);\n\thooksFrom(join(cwd, \".claude\", \"settings.local.json\"), \"local\", out, seen);\n\thooksFrom(join(home, \".claude\", \"settings.json\"), \"global\", out, seen);\n\treturn out;\n}\n","import { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join } from \"node:path\";\nimport { parse as parseToml } from \"smol-toml\";\nimport { parse as parseYaml } from \"yaml\";\nimport type { Resource } from \"./api.js\";\n\n/**\n * Detect configured MCP servers and resolve each to a `pkg` reference (its\n * package identity), parsed from the launch `command`/`args` — npm/PyPI/OCI for\n * stdio servers, or a URL for remote (http/sse) servers. `env` is intentionally\n * dropped (it carries secrets), so this is a safer representation than uploading\n * the raw config file.\n */\n\nexport interface McpServerConfig {\n\tcommand?: string;\n\targs?: string[];\n\ttype?: string;\n\ttransport?: string;\n\turl?: string;\n}\n\nexport interface PkgRef {\n\tregistry: \"npm\" | \"pypi\" | \"oci\" | \"url\";\n\tid: string;\n\tversion?: string;\n\ttransport?: \"stdio\" | \"http\" | \"sse\";\n}\n\nfunction commandName(cmd: string): string {\n\treturn cmd.replace(/\\\\/g, \"/\").split(\"/\").pop() ?? cmd;\n}\n\n/** Split an npm/PyPI spec into id + version, handling scoped npm (@scope/n@v). */\nfunction splitVersion(spec: string): { id: string; version?: string } {\n\tconst at = spec.indexOf(\"@\", spec.startsWith(\"@\") ? 1 : 0);\n\tif (at <= 0) return { id: spec };\n\treturn { id: spec.slice(0, at), version: spec.slice(at + 1) || undefined };\n}\n\n/** First arg that isn't a flag (and isn't in `skip`). */\nfunction firstPositional(args: string[], skip = 0): string | undefined {\n\tfor (const a of args.slice(skip)) {\n\t\tif (!a.startsWith(\"-\")) return a;\n\t}\n\treturn undefined;\n}\n\n// docker/podman flags that consume the following token (so it isn't the image).\nconst CONTAINER_VALUE_FLAGS = new Set([\n\t\"-e\",\n\t\"--env\",\n\t\"-v\",\n\t\"--volume\",\n\t\"-p\",\n\t\"--publish\",\n\t\"-w\",\n\t\"--workdir\",\n\t\"--name\",\n\t\"--mount\",\n\t\"--network\",\n\t\"-u\",\n\t\"--user\",\n\t\"-l\",\n\t\"--label\",\n]);\n\nfunction containerImage(args: string[]): string | undefined {\n\tconst runIdx = args.indexOf(\"run\");\n\tconst rest = runIdx >= 0 ? args.slice(runIdx + 1) : args;\n\tfor (let i = 0; i < rest.length; i++) {\n\t\tconst a = rest[i];\n\t\tif (a.startsWith(\"-\")) {\n\t\t\tif (CONTAINER_VALUE_FLAGS.has(a) && !a.includes(\"=\")) i++;\n\t\t\tcontinue;\n\t\t}\n\t\treturn a; // first positional after `run` is the image\n\t}\n\treturn undefined;\n}\n\n/** Split a container image ref into id + tag (ignoring a registry host:port). */\nfunction splitImageTag(image: string): { id: string; version?: string } {\n\tconst colon = image.lastIndexOf(\":\");\n\tif (colon > 0 && !image.slice(colon + 1).includes(\"/\")) {\n\t\treturn { id: image.slice(0, colon), version: image.slice(colon + 1) };\n\t}\n\treturn { id: image };\n}\n\n/** Parse a single MCP server config into a package reference, or null. */\nexport function parseMcpPackage(server: McpServerConfig): PkgRef | null {\n\t// Remote server: a URL endpoint (http/sse).\n\tif (server.url) {\n\t\tconst t = (server.type ?? server.transport ?? \"\").toLowerCase();\n\t\treturn {\n\t\t\tregistry: \"url\",\n\t\t\tid: server.url,\n\t\t\ttransport: t === \"sse\" ? \"sse\" : \"http\",\n\t\t};\n\t}\n\n\tconst command = server.command ? commandName(server.command) : \"\";\n\tif (!command) return null;\n\tconst args = server.args ?? [];\n\n\t// npm-family runners.\n\tif (command === \"npx\" || command === \"bunx\" || command === \"pnpx\") {\n\t\tconst spec = firstPositional(args);\n\t\treturn spec\n\t\t\t? { registry: \"npm\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif ((command === \"pnpm\" || command === \"yarn\") && args[0] === \"dlx\") {\n\t\tconst spec = firstPositional(args, 1);\n\t\treturn spec\n\t\t\t? { registry: \"npm\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\n\t// Python-family runners.\n\tif (command === \"uvx\") {\n\t\tconst spec = firstPositional(args);\n\t\treturn spec\n\t\t\t? { registry: \"pypi\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif (command === \"pipx\" && args[0] === \"run\") {\n\t\tconst spec = firstPositional(args, 1);\n\t\treturn spec\n\t\t\t? { registry: \"pypi\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif (command === \"uv\" && args[0] === \"tool\" && args[1] === \"run\") {\n\t\tconst spec = firstPositional(args, 2);\n\t\treturn spec\n\t\t\t? { registry: \"pypi\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif (/^python[0-9.]*$/.test(command)) {\n\t\tconst i = args.indexOf(\"-m\");\n\t\tconst mod = i >= 0 ? args[i + 1] : undefined;\n\t\treturn mod ? { registry: \"pypi\", id: mod, transport: \"stdio\" } : null;\n\t}\n\n\t// Container runners.\n\tif (command === \"docker\" || command === \"podman\") {\n\t\tconst image = containerImage(args);\n\t\tif (!image) return null;\n\t\treturn { registry: \"oci\", ...splitImageTag(image), transport: \"stdio\" };\n\t}\n\n\t// node/deno/bun running a local script, or an unknown command → skip.\n\treturn null;\n}\n\n/** Build a `type:\"mcp\"` linked resource from a parsed package reference. */\nexport function buildMcpResource(\n\tname: string,\n\tgroup: string,\n\tpkg: PkgRef,\n): Resource {\n\treturn {\n\t\ttype: \"mcp\",\n\t\tname,\n\t\tgroup,\n\t\tstableKey: `linked:pkg:${pkg.registry}:${pkg.id}`,\n\t\tpkg,\n\t};\n}\n\nfunction readText(path: string): string | null {\n\ttry {\n\t\tif (!existsSync(path)) return null;\n\t\treturn readFileSync(path, \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction readParsed<T>(\n\tpath: string,\n\tparse: (raw: string) => unknown,\n): T | null {\n\tconst raw = readText(path);\n\tif (raw === null) return null;\n\ttry {\n\t\treturn parse(raw) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction readJson<T>(path: string): T | null {\n\treturn readParsed<T>(path, JSON.parse);\n}\nfunction readYaml<T>(path: string): T | null {\n\treturn readParsed<T>(path, parseYaml);\n}\nfunction readToml<T>(path: string): T | null {\n\treturn readParsed<T>(path, parseToml);\n}\n\ntype ServerMap = Record<string, McpServerConfig> | undefined;\ninterface McpFile {\n\tmcpServers?: ServerMap;\n\tservers?: ServerMap; // VS Code uses `servers`\n}\ninterface ClaudeJson {\n\tmcpServers?: ServerMap;\n\tprojects?: Record<string, { mcpServers?: ServerMap }>;\n}\n// Continue uses a LIST under mcpServers; Codex (TOML) uses `mcp_servers`.\ninterface ContinueYaml {\n\tmcpServers?: Array<{ name?: string } & McpServerConfig>;\n}\ninterface CodexToml {\n\tmcp_servers?: Record<string, McpServerConfig>;\n}\n\n/** Normalize Continue's list form to the common name→config map. */\nfunction continueListToMap(file: ContinueYaml | null): ServerMap {\n\tif (!file?.mcpServers?.length) return undefined;\n\tconst map: Record<string, McpServerConfig> = {};\n\tfile.mcpServers.forEach((s, i) => {\n\t\tmap[s.name ?? `server-${i}`] = s;\n\t});\n\treturn map;\n}\n\n/** Per-OS VS Code globalStorage bases (+ Code-OSS/VSCodium variants). */\nfunction vscodeGlobalStorageBases(home: string): string[] {\n\tconst apps = [\"Code\", \"Code - OSS\", \"VSCodium\"];\n\tlet root: string;\n\tif (platform() === \"darwin\") {\n\t\troot = join(home, \"Library\", \"Application Support\");\n\t} else if (platform() === \"win32\") {\n\t\troot = process.env.APPDATA ?? join(home, \"AppData\", \"Roaming\");\n\t} else {\n\t\troot = process.env.XDG_CONFIG_HOME ?? join(home, \".config\");\n\t}\n\treturn apps.map((app) => join(root, app, \"User\", \"globalStorage\"));\n}\n\n/**\n * Read MCP server configs across the known tool locations and return one\n * `type:\"mcp\"` pkg-link resource per server, deduped by package identity.\n * Project-level configs win over global ones on the same identity.\n */\nexport function detectMcpServers(\n\tcwd: string,\n\thome: string = homedir(),\n): Resource[] {\n\tconst out: Resource[] = [];\n\tconst seen = new Set<string>();\n\tconst add = (servers: ServerMap, group: string) => {\n\t\tfor (const [name, cfg] of Object.entries(servers ?? {})) {\n\t\t\tconst pkg = parseMcpPackage(cfg);\n\t\t\tif (!pkg) continue;\n\t\t\tconst resource = buildMcpResource(name, group, pkg);\n\t\t\tif (seen.has(resource.stableKey)) continue;\n\t\t\tseen.add(resource.stableKey);\n\t\t\tout.push(resource);\n\t\t}\n\t};\n\n\t// Project configs first (so they win dedup over global).\n\tadd(readJson<McpFile>(join(cwd, \".mcp.json\"))?.mcpServers, \"claude-code\");\n\tadd(readJson<McpFile>(join(cwd, \"mcp.json\"))?.mcpServers, \"generic\");\n\tadd(\n\t\treadJson<McpFile>(join(cwd, \".cursor\", \"mcp.json\"))?.mcpServers,\n\t\t\"cursor\",\n\t);\n\tadd(readJson<McpFile>(join(cwd, \".vscode\", \"mcp.json\"))?.servers, \"generic\");\n\tadd(\n\t\treadJson<McpFile>(join(cwd, \"claude_desktop_config.json\"))?.mcpServers,\n\t\t\"claude-desktop\",\n\t);\n\n\t// Continue (project): a list per YAML file under .continue/mcpServers/.\n\tfor (const file of listYamlFiles(join(cwd, \".continue\", \"mcpServers\"))) {\n\t\tadd(continueListToMap(readYaml<ContinueYaml>(file)), \"continue\");\n\t}\n\t// Roo (project).\n\tadd(readJson<McpFile>(join(cwd, \".roo\", \"mcp.json\"))?.mcpServers, \"roo\");\n\n\t// --- Global / stack-scoped: user-level tool configs ---\n\tconst claudeJson = readJson<ClaudeJson>(join(home, \".claude.json\"));\n\tadd(claudeJson?.projects?.[cwd]?.mcpServers, \"claude-code\");\n\tadd(claudeJson?.mcpServers, \"claude-code\");\n\tadd(\n\t\treadJson<McpFile>(join(home, \".cursor\", \"mcp.json\"))?.mcpServers,\n\t\t\"cursor\",\n\t);\n\t// Windsurf (global only).\n\tadd(\n\t\treadJson<McpFile>(join(home, \".codeium\", \"windsurf\", \"mcp_config.json\"))\n\t\t\t?.mcpServers,\n\t\t\"windsurf\",\n\t);\n\t// Cline + Roo: VS Code extension globalStorage (OS-specific base).\n\tfor (const base of vscodeGlobalStorageBases(home)) {\n\t\tadd(\n\t\t\treadJson<McpFile>(\n\t\t\t\tjoin(\n\t\t\t\t\tbase,\n\t\t\t\t\t\"saoudrizwan.claude-dev\",\n\t\t\t\t\t\"settings\",\n\t\t\t\t\t\"cline_mcp_settings.json\",\n\t\t\t\t),\n\t\t\t)?.mcpServers,\n\t\t\t\"cline\",\n\t\t);\n\t\tadd(\n\t\t\treadJson<McpFile>(\n\t\t\t\tjoin(\n\t\t\t\t\tbase,\n\t\t\t\t\t\"rooveterinaryinc.roo-cline\",\n\t\t\t\t\t\"settings\",\n\t\t\t\t\t\"mcp_settings.json\",\n\t\t\t\t),\n\t\t\t)?.mcpServers,\n\t\t\t\"roo\",\n\t\t);\n\t}\n\t// Continue (global).\n\tfor (const file of listYamlFiles(join(home, \".continue\", \"mcpServers\"))) {\n\t\tadd(continueListToMap(readYaml<ContinueYaml>(file)), \"continue\");\n\t}\n\t// Gemini CLI (global).\n\tadd(\n\t\treadJson<McpFile>(join(home, \".gemini\", \"settings.json\"))?.mcpServers,\n\t\t\"gemini\",\n\t);\n\t// Codex CLI (global, TOML).\n\tadd(\n\t\treadToml<CodexToml>(join(home, \".codex\", \"config.toml\"))?.mcp_servers,\n\t\t\"codex\",\n\t);\n\n\treturn out;\n}\n\n/** List `*.yaml`/`*.yml` files in a directory (empty if absent). */\nfunction listYamlFiles(dir: string): string[] {\n\ttry {\n\t\treturn readdirSync(dir)\n\t\t\t.filter((f) => f.endsWith(\".yaml\") || f.endsWith(\".yml\"))\n\t\t\t.map((f) => join(dir, f));\n\t} catch {\n\t\treturn [];\n\t}\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { Resource } from \"./api.js\";\nimport { buildLinkResource } from \"./git.js\";\nimport { canonicalizeRepoUrl } from \"./github-repo.js\";\n\n/**\n * Detect installed Claude Code plugins from the on-disk registry and resolve\n * each to a GitHub link pointing at its TRUE upstream source (not the\n * aggregator marketplace), attached at stack scope (the user's toolchain).\n *\n * Registry shape (~/.claude/plugins/):\n * installed_plugins.json → { plugins: { \"<name>@<marketplace>\": [{ gitCommitSha, version, ... }] } }\n * known_marketplaces.json → { \"<marketplace>\": { source: { repo }, installLocation } }\n * <installLocation>/.claude-plugin/marketplace.json → { plugins: [{ name, source, ... }] }\n */\n\ninterface InstalledEntry {\n\tscope?: string;\n\tversion?: string;\n\tgitCommitSha?: string;\n}\n\nexport interface InstalledPlugins {\n\tplugins?: Record<string, InstalledEntry[]>;\n}\n\nexport interface KnownMarketplace {\n\tsource?: { source?: string; repo?: string; url?: string };\n\tinstallLocation?: string;\n}\n\nexport type KnownMarketplaces = Record<string, KnownMarketplace>;\n\n/** A plugin's source in marketplace.json — polymorphic. */\ntype PluginSource =\n\t| string\n\t| {\n\t\t\tsource?: string;\n\t\t\turl?: string;\n\t\t\tpath?: string;\n\t\t\tref?: string;\n\t\t\tsha?: string;\n\t };\n\ninterface ManifestPlugin {\n\tname: string;\n\tsource?: PluginSource;\n\trepository?: string;\n\thomepage?: string;\n}\n\nexport interface Manifest {\n\tplugins?: ManifestPlugin[];\n}\n\nfunction marketplaceRepoUrl(mp: KnownMarketplace | undefined): string | null {\n\tconst src = mp?.source;\n\tif (!src) return null;\n\tif (src.repo) return `https://github.com/${src.repo}`;\n\treturn src.url ?? null;\n}\n\n/** Resolve a plugin entry's source to a repo URL (+ optional subpath / sha). */\nfunction resolveSource(\n\tentry: ManifestPlugin,\n\tmpRepoUrl: string | null,\n): { url: string; path?: string; sha?: string } | null {\n\tconst src = entry.source;\n\tif (typeof src === \"string\") {\n\t\tif (!mpRepoUrl) return null;\n\t\tconst path = src.replace(/^\\.\\//, \"\").replace(/\\/+$/, \"\");\n\t\treturn { url: mpRepoUrl, path: path || undefined };\n\t}\n\tif (src && typeof src === \"object\" && src.url) {\n\t\treturn { url: src.url, path: src.path, sha: src.sha };\n\t}\n\tconst fallback = entry.repository ?? entry.homepage ?? mpRepoUrl;\n\treturn fallback ? { url: fallback } : null;\n}\n\n/** Pure: map the parsed registry + manifests to plugin link resources. */\nexport function resolvePluginLinks(\n\tinstalled: InstalledPlugins,\n\tmarketplaces: KnownMarketplaces,\n\tmanifests: Record<string, Manifest>,\n): Resource[] {\n\tconst out: Resource[] = [];\n\tfor (const [key, entries] of Object.entries(installed.plugins ?? {})) {\n\t\tconst at = key.lastIndexOf(\"@\");\n\t\tif (at <= 0) continue;\n\t\tconst pluginName = key.slice(0, at);\n\t\tconst marketplace = key.slice(at + 1);\n\n\t\tconst mpRepoUrl = marketplaceRepoUrl(marketplaces[marketplace]);\n\t\tconst entry = manifests[marketplace]?.plugins?.find(\n\t\t\t(p) => p.name === pluginName,\n\t\t);\n\t\tif (!entry) continue;\n\n\t\tconst resolved = resolveSource(entry, mpRepoUrl);\n\t\tif (!resolved) continue;\n\n\t\tconst canonical = canonicalizeRepoUrl(resolved.url);\n\t\tif (!canonical) continue; // non-GitHub source → skip (graceful)\n\n\t\tout.push(\n\t\t\tbuildLinkResource({\n\t\t\t\tcanonical,\n\t\t\t\tpath: resolved.path,\n\t\t\t\tname: pluginName,\n\t\t\t\ttype: \"plugin\",\n\t\t\t\tgroup: \"claude-code\",\n\t\t\t\tsha: resolved.sha ?? entries[0]?.gitCommitSha,\n\t\t\t}),\n\t\t);\n\t}\n\treturn out;\n}\n\nfunction readJson<T>(path: string): T | null {\n\ttry {\n\t\tif (!existsSync(path)) return null;\n\t\treturn JSON.parse(readFileSync(path, \"utf-8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** IO wrapper: read the registry + manifests, return plugin link resources. */\nexport function detectInstalledPlugins(\n\tpluginsDir: string = join(homedir(), \".claude\", \"plugins\"),\n): Resource[] {\n\tconst installed = readJson<InstalledPlugins>(\n\t\tjoin(pluginsDir, \"installed_plugins.json\"),\n\t);\n\tif (!installed?.plugins) return [];\n\n\tconst marketplaces =\n\t\treadJson<KnownMarketplaces>(join(pluginsDir, \"known_marketplaces.json\")) ??\n\t\t{};\n\n\tconst manifests: Record<string, Manifest> = {};\n\tfor (const key of Object.keys(installed.plugins)) {\n\t\tconst mp = key.slice(key.lastIndexOf(\"@\") + 1);\n\t\tif (!mp || manifests[mp]) continue;\n\t\tconst installLocation =\n\t\t\tmarketplaces[mp]?.installLocation ?? join(pluginsDir, \"marketplaces\", mp);\n\t\tconst manifest = readJson<Manifest>(\n\t\t\tjoin(installLocation, \".claude-plugin\", \"marketplace.json\"),\n\t\t);\n\t\tif (manifest) manifests[mp] = manifest;\n\t}\n\n\treturn resolvePluginLinks(installed, marketplaces, manifests);\n}\n","import { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, relative } from \"node:path\";\nimport ignore from \"ignore\";\n\nexport type FileType =\n\t| \"rule\"\n\t| \"mcp\"\n\t| \"skill\"\n\t| \"command\"\n\t| \"prompt\"\n\t| \"hook\"\n\t| \"subagent\"\n\t| \"config\"\n\t| \"custom\";\n\nexport interface ScannedFile {\n\tpath: string;\n\trelativePath: string;\n\tcontent: string;\n\ttype: FileType;\n\tsource: \"local\" | \"global\";\n\tgroup: string;\n}\n\nconst MAX_FILE_SIZE = 100 * 1024; // 100KB\n\ninterface FilePattern {\n\tpath: string;\n\ttype: FileType;\n\tgroup: string;\n}\n\nconst LOCAL_PATTERNS: FilePattern[] = [\n\t// Rules\n\t{ path: \"CLAUDE.md\", type: \"rule\", group: \"claude-code\" },\n\t{ path: \"AGENTS.md\", type: \"rule\", group: \"claude-code\" },\n\t{ path: \"GEMINI.md\", type: \"rule\", group: \"gemini\" },\n\t{ path: \".cursorrules\", type: \"rule\", group: \"cursor\" },\n\t{ path: \".windsurfrules\", type: \"rule\", group: \"windsurf\" },\n\t{ path: \".clinerules\", type: \"rule\", group: \"cline\" },\n\t{ path: \".roorules\", type: \"rule\", group: \"roo\" },\n\t{ path: \".github/copilot-instructions.md\", type: \"rule\", group: \"copilot\" },\n\t// MCP servers are detected separately as pkg-reference links (see mcp.ts) —\n\t// their config files are intentionally NOT collected as content here (which\n\t// would also upload `env` secrets).\n\t// Config\n\t{ path: \".aider.conf.yml\", type: \"config\", group: \"aider\" },\n\t{ path: \".continue/config.json\", type: \"config\", group: \"continue\" },\n\t{ path: \".continue/config.yaml\", type: \"config\", group: \"continue\" },\n\t{ path: \".claude/settings.json\", type: \"config\", group: \"claude-code\" },\n\t{\n\t\tpath: \".claude/settings.local.json\",\n\t\ttype: \"config\",\n\t\tgroup: \"claude-code\",\n\t},\n\t// Prompts\n\t{ path: \"system-prompt.md\", type: \"prompt\", group: \"generic\" },\n];\n\nconst LOCAL_DIR_PATTERNS: { dir: string; type: FileType; group: string }[] = [\n\t{ dir: \".cursor/rules\", type: \"rule\", group: \"cursor\" },\n\t{ dir: \".clinerules\", type: \"rule\", group: \"cline\" },\n\t{ dir: \".windsurf/rules\", type: \"rule\", group: \"windsurf\" },\n\t{ dir: \".roo/rules\", type: \"rule\", group: \"roo\" },\n\t{ dir: \".github/instructions\", type: \"rule\", group: \"copilot\" },\n\t{ dir: \".github/prompts\", type: \"prompt\", group: \"copilot\" },\n\t{ dir: \".claude/commands\", type: \"command\", group: \"claude-code\" },\n\t{ dir: \".claude/agents\", type: \"subagent\", group: \"claude-code\" },\n\t{ dir: \".claude/hooks\", type: \"hook\", group: \"claude-code\" },\n\t{ dir: \"prompts\", type: \"prompt\", group: \"generic\" },\n\t{ dir: \".ai\", type: \"custom\", group: \"generic\" },\n];\n\nfunction loadGitignore(cwd: string): ReturnType<typeof ignore> {\n\tconst ig = ignore();\n\tconst gitignorePath = join(cwd, \".gitignore\");\n\tif (existsSync(gitignorePath)) {\n\t\tig.add(readFileSync(gitignorePath, \"utf-8\"));\n\t}\n\tig.add([\"node_modules\", \".git\", \"dist\", \"build\", \".next\", \".output\"]);\n\treturn ig;\n}\n\nfunction readFileSafe(filePath: string): string | null {\n\ttry {\n\t\tconst stat = statSync(filePath);\n\t\tif (stat.size > MAX_FILE_SIZE) return null;\n\t\treturn readFileSync(filePath, \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction walkDir(dir: string, maxDepth = 3, currentDepth = 0): string[] {\n\tif (currentDepth >= maxDepth || !existsSync(dir)) return [];\n\tconst results: string[] = [];\n\ttry {\n\t\tfor (const entry of readdirSync(dir, { withFileTypes: true })) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\t\t\tif (entry.isFile()) {\n\t\t\t\tresults.push(fullPath);\n\t\t\t} else if (entry.isDirectory()) {\n\t\t\t\tresults.push(...walkDir(fullPath, maxDepth, currentDepth + 1));\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* permission errors, etc */\n\t}\n\treturn results;\n}\n\nexport function scanLocal(cwd: string): ScannedFile[] {\n\tconst ig = loadGitignore(cwd);\n\tconst results: ScannedFile[] = [];\n\n\tfor (const pattern of LOCAL_PATTERNS) {\n\t\tconst filePath = join(cwd, pattern.path);\n\t\tconst content = readFileSafe(filePath);\n\t\tif (content !== null) {\n\t\t\tconst rel = relative(cwd, filePath);\n\t\t\tif (!ig.ignores(rel)) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: rel,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype: pattern.type,\n\t\t\t\t\tsource: \"local\",\n\t\t\t\t\tgroup: pattern.group,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const { dir, type, group } of LOCAL_DIR_PATTERNS) {\n\t\tconst dirPath = join(cwd, dir);\n\t\tconst files = walkDir(dirPath);\n\t\tfor (const filePath of files) {\n\t\t\tconst rel = relative(cwd, filePath);\n\t\t\tif (ig.ignores(rel)) continue;\n\t\t\tconst content = readFileSafe(filePath);\n\t\t\tif (content !== null) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: rel,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype,\n\t\t\t\t\tsource: \"local\",\n\t\t\t\t\tgroup,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// Scan for skill directories (dirs with SKILL.md, 3 levels deep)\n\ttry {\n\t\tfor (const entry of readdirSync(cwd, { withFileTypes: true }).filter((e) =>\n\t\t\te.isDirectory(),\n\t\t)) {\n\t\t\tif (ig.ignores(entry.name + \"/\")) continue;\n\t\t\tscanSkillDirs(join(cwd, entry.name), cwd, ig, results, 1);\n\t\t}\n\t} catch {\n\t\t/* permission errors */\n\t}\n\n\treturn results;\n}\n\nfunction scanSkillDirs(\n\tdir: string,\n\tcwd: string,\n\tig: ReturnType<typeof ignore>,\n\tresults: ScannedFile[],\n\tdepth: number,\n) {\n\tif (depth > 3) return;\n\tconst skillMd = join(dir, \"SKILL.md\");\n\tif (existsSync(skillMd)) {\n\t\tconst files = walkDir(dir, 1);\n\t\tfor (const filePath of files) {\n\t\t\tconst rel = relative(cwd, filePath);\n\t\t\tif (ig.ignores(rel)) continue;\n\t\t\tconst content = readFileSafe(filePath);\n\t\t\tif (content !== null) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: rel,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype: \"skill\",\n\t\t\t\t\tsource: \"local\",\n\t\t\t\t\tgroup: \"generic\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\ttry {\n\t\tfor (const entry of readdirSync(dir, { withFileTypes: true })) {\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tconst rel = relative(cwd, join(dir, entry.name));\n\t\t\t\tif (!ig.ignores(rel + \"/\")) {\n\t\t\t\t\tscanSkillDirs(join(dir, entry.name), cwd, ig, results, depth + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* permission errors */\n\t}\n}\n\nexport function scanGlobal(): ScannedFile[] {\n\tconst home = homedir();\n\tconst results: ScannedFile[] = [];\n\n\tconst globalPatterns: FilePattern[] = [\n\t\t{ path: \".claude/CLAUDE.md\", type: \"rule\", group: \"claude-code\" },\n\t\t{ path: \".claude/settings.json\", type: \"config\", group: \"claude-code\" },\n\t\t{ path: \".continue/config.json\", type: \"config\", group: \"continue\" },\n\t\t{ path: \".continue/config.yaml\", type: \"config\", group: \"continue\" },\n\t\t{ path: \".aider.conf.yml\", type: \"config\", group: \"aider\" },\n\t\t{ path: \".gemini/GEMINI.md\", type: \"rule\", group: \"gemini\" },\n\t\t{ path: \".gemini/settings.json\", type: \"config\", group: \"gemini\" },\n\t\t{ path: \".codex/config.toml\", type: \"config\", group: \"codex\" },\n\t\t{\n\t\t\tpath: \".codeium/windsurf/memories/global_rules.md\",\n\t\t\ttype: \"rule\",\n\t\t\tgroup: \"windsurf\",\n\t\t},\n\t];\n\n\tfor (const pattern of globalPatterns) {\n\t\tconst filePath = join(home, pattern.path);\n\t\tconst content = readFileSafe(filePath);\n\t\tif (content !== null) {\n\t\t\tresults.push({\n\t\t\t\tpath: filePath,\n\t\t\t\trelativePath: `~/${pattern.path}`,\n\t\t\t\tcontent,\n\t\t\t\ttype: pattern.type,\n\t\t\t\tsource: \"global\",\n\t\t\t\tgroup: pattern.group,\n\t\t\t});\n\t\t}\n\t}\n\n\tconst globalDirs: { dir: string; type: FileType; group: string }[] = [\n\t\t{ dir: \".claude/commands\", type: \"command\", group: \"claude-code\" },\n\t\t{ dir: \".claude/agents\", type: \"subagent\", group: \"claude-code\" },\n\t\t{ dir: \".claude/hooks\", type: \"hook\", group: \"claude-code\" },\n\t\t{ dir: \".cursor/rules\", type: \"rule\", group: \"cursor\" },\n\t];\n\n\tfor (const { dir, type, group } of globalDirs) {\n\t\tconst dirPath = join(home, dir);\n\t\tconst files = walkDir(dirPath, 2);\n\t\tfor (const filePath of files) {\n\t\t\tconst content = readFileSafe(filePath);\n\t\t\tif (content !== null) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: `~/${relative(home, filePath)}`,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype,\n\t\t\t\t\tsource: \"global\",\n\t\t\t\t\tgroup,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// Global skills: ~/.claude/skills/<name>/SKILL.md (+ supporting files). Not\n\t// covered by globalDirs since each skill is its own dir keyed by SKILL.md.\n\tconst skillsRoot = join(home, \".claude\", \"skills\");\n\ttry {\n\t\tfor (const entry of readdirSync(skillsRoot, { withFileTypes: true })) {\n\t\t\tif (!entry.isDirectory()) continue;\n\t\t\tconst skillDir = join(skillsRoot, entry.name);\n\t\t\tif (!existsSync(join(skillDir, \"SKILL.md\"))) continue;\n\t\t\tfor (const filePath of walkDir(skillDir, 2)) {\n\t\t\t\tconst content = readFileSafe(filePath);\n\t\t\t\tif (content !== null) {\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\tpath: filePath,\n\t\t\t\t\t\trelativePath: `~/${relative(home, filePath)}`,\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t\ttype: \"skill\",\n\t\t\t\t\t\tsource: \"global\",\n\t\t\t\t\t\tgroup: \"claude-code\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* skills dir absent / permission errors */\n\t}\n\n\treturn results;\n}\n","import * as p from \"@clack/prompts\";\n\nconst esc = (code: string) => `\\x1b[${code}m`;\nconst reset = esc(\"0\");\n\nconst LIME = \"163;230;53\";\nconst BLACK = \"0;0;0\";\nconst YELLOW = \"250;204;21\";\nconst RED = \"248;113;113\";\nconst MUTED = \"120;120;120\";\n\nexport const lime = (s: string) => `${esc(`38;2;${LIME}`)}${s}${reset}`;\nexport const limeBold = (s: string) =>\n\t`${esc(\"1\")}${esc(`38;2;${LIME}`)}${s}${reset}`;\nexport const bgLime = (s: string) =>\n\t`${esc(`48;2;${LIME}`)}${esc(`38;2;${BLACK}`)}${s}${reset}`;\nexport const yellow = (s: string) => `${esc(`38;2;${YELLOW}`)}${s}${reset}`;\nexport const red = (s: string) => `${esc(`38;2;${RED}`)}${s}${reset}`;\nexport const dim = (s: string) => `${esc(`38;2;${MUTED}`)}${s}${reset}`;\nexport const bold = (s: string) => `${esc(\"1\")}${s}${reset}`;\n\n// ■ logo square in lime + AISTACK in bold on lime bg\nexport const banner = (cmd: string) =>\n\t`${lime(\"■\")} ${bgLime(` AISTACK `)} ${bold(cmd.toUpperCase())}`;\n\n// Compact line with clack-style bar\nconst BAR = `${esc(`38;2;${MUTED}`)}│${reset}`;\n\nexport function lines(items: string[]) {\n\tfor (const item of items) {\n\t\tconsole.log(`${BAR} ${item}`);\n\t}\n}\n\nexport function section(label: string, count?: number) {\n\tconsole.log(`${BAR}`);\n\tconst countStr = count !== undefined ? ` ${dim(String(count))}` : \"\";\n\tconsole.log(`${BAR} ${bold(label.toUpperCase())}${countStr}`);\n}\n\nexport function divider() {\n\tconsole.log(`${BAR} ${dim(\"─\".repeat(40))}`);\n}\n\nexport function intro(cmd: string) {\n\tconsole.log();\n\tp.intro(banner(cmd));\n}\n\nexport function outro(msg: string) {\n\tp.outro(msg);\n\tconsole.log();\n}\n\nexport function outroError(msg: string) {\n\tp.outro(red(msg));\n\tconsole.log();\n}\n\nexport function outroCancel(msg = \"cancelled\") {\n\tp.cancel(dim(msg));\n\tconsole.log();\n}\n\nexport function outroSkipped(msg: string) {\n\tp.outro(dim(msg));\n\tconsole.log();\n}\n","// `aistack connect claude` — the opt-in in-session sync surface (#56, #57).\n//\n// Installs BOTH halves or NEITHER: the user-scoped MCP server registration and\n// the Skill copy travel together, because the Skill drives `sync_preview` /\n// `sync_publish` and has nothing to do without the server (#56 decision 3).\n// The harness argument leaves room for `connect codex` later without a rename.\n\nimport { spawnSync } from \"node:child_process\";\nimport { cpSync, existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as p from \"@clack/prompts\";\nimport { getSettings, saveSettings } from \"../config.js\";\nimport { claudeAdapter, detectionSinceMs } from \"../harness/index.js\";\nimport {\n\tdim,\n\tintro,\n\tlimeBold,\n\toutro,\n\toutroError,\n\toutroSkipped,\n} from \"../theme.js\";\n\n/** The documented manual install line, printed when we cannot run it. */\nexport const MANUAL_MCP_ADD =\n\t\"claude mcp add --scope user aistack -- npx -y @use-aistack/cli mcp\";\n\nconst MCP_ADD_ARGS = [\n\t\"mcp\",\n\t\"add\",\n\t\"--scope\",\n\t\"user\",\n\t\"aistack\",\n\t\"--\",\n\t\"npx\",\n\t\"-y\",\n\t\"@use-aistack/cli\",\n\t\"mcp\",\n];\n\nconst MCP_REMOVE_ARGS = [\"mcp\", \"remove\", \"--scope\", \"user\", \"aistack\"];\n\nexport const SKILL_DEST = join(homedir(), \".claude\", \"skills\", \"aistack-sync\");\n\nexport interface RunResult {\n\t/** The binary was not found on PATH. */\n\tnotFound: boolean;\n\tstatus: number | null;\n\toutput: string;\n}\n\nexport type Runner = (args: string[]) => RunResult;\n\nfunction runClaude(args: string[]): RunResult {\n\tconst r = spawnSync(\"claude\", args, { encoding: \"utf-8\" });\n\tconst notFound =\n\t\tr.error !== undefined &&\n\t\t(r.error as NodeJS.ErrnoException).code === \"ENOENT\";\n\treturn {\n\t\tnotFound,\n\t\tstatus: r.status,\n\t\toutput: `${r.stdout ?? \"\"}${r.stderr ?? \"\"}`,\n\t};\n}\n\n/** Is the `claude` binary reachable? Cheap check used to skip the upsell. */\nexport function claudeOnPath(run: Runner = runClaude): boolean {\n\treturn !run([\"--version\"]).notFound;\n}\n\n/**\n * The bundled Skill directory, resolved relative to this module. In the\n * published package that is `<pkg>/skills/aistack-sync` next to `dist/`; in\n * dev it is two levels up from `src/commands/`. Walking up covers both.\n */\nexport function findSkillSource(\n\tfromDir: string = dirname(fileURLToPath(import.meta.url)),\n): string | null {\n\tlet dir = fromDir;\n\tfor (let i = 0; i < 4; i++) {\n\t\tconst candidate = join(dir, \"skills\", \"aistack-sync\");\n\t\tif (existsSync(join(candidate, \"SKILL.md\"))) return candidate;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) break;\n\t\tdir = parent;\n\t}\n\treturn null;\n}\n\nexport interface ConnectOutcome {\n\tok: boolean;\n\tmessage: string;\n}\n\n/**\n * Install the server registration, then the Skill. If the Skill copy fails\n * after a fresh registration, the registration is rolled back — both halves\n * or neither.\n */\nexport function installClaudeConnect(\n\trun: Runner = runClaude,\n\tcopySkill: (src: string, dest: string) => void = (src, dest) =>\n\t\tcpSync(src, dest, { recursive: true }),\n): ConnectOutcome {\n\tconst source = findSkillSource();\n\tif (source === null) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage:\n\t\t\t\t\"this install is missing its bundled Skill (skills/aistack-sync) — nothing was installed\",\n\t\t};\n\t}\n\n\tconst add = run(MCP_ADD_ARGS);\n\tif (add.notFound) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `claude was not found on PATH — nothing was installed. Manual install:\\n${MANUAL_MCP_ADD}`,\n\t\t};\n\t}\n\tconst alreadyRegistered =\n\t\tadd.status !== 0 && add.output.includes(\"already exists\");\n\tif (add.status !== 0 && !alreadyRegistered) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `claude mcp add failed — nothing was installed.\\n${add.output.trim()}`,\n\t\t};\n\t}\n\n\ttry {\n\t\tcopySkill(source, SKILL_DEST);\n\t} catch (e) {\n\t\t// Both halves or neither: a fresh registration without its Skill is\n\t\t// rolled back. A pre-existing registration is left as it was.\n\t\tif (!alreadyRegistered) run(MCP_REMOVE_ARGS);\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `copying the Skill to ${SKILL_DEST} failed — the MCP registration was ${\n\t\t\t\talreadyRegistered ? \"left as it was\" : \"rolled back\"\n\t\t\t}.\\n${e instanceof Error ? e.message : String(e)}`,\n\t\t};\n\t}\n\n\treturn {\n\t\tok: true,\n\t\tmessage: `Installed. Say ${limeBold('\"sync my stack\"')} in any Claude Code session.`,\n\t};\n}\n\nexport async function connectCommand(harness: string): Promise<void> {\n\tintro(\"connect\");\n\n\tif (harness !== \"claude\") {\n\t\toutroError(`unknown harness \"${harness}\" — supported: claude`);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tif (!claudeOnPath()) {\n\t\tp.log.warn(\n\t\t\t`claude was not found on PATH. Manual install:\\n${dim(MANUAL_MCP_ADD)}\\nplus copy skills/aistack-sync from this package to ${dim(SKILL_DEST)}`,\n\t\t);\n\t\toutroSkipped(\"nothing was installed\");\n\t\treturn;\n\t}\n\n\tconst result = installClaudeConnect();\n\tif (!result.ok) {\n\t\toutroError(result.message);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\tp.log.success(result.message);\n\toutro(\"done\");\n}\n\nexport interface UpsellDeps {\n\t/** Override the Claude activity check. Tests only. */\n\tclaudeActiveImpl?: () => Promise<boolean>;\n\t/** Override the PATH check. Tests only. */\n\tclaudeOnPathImpl?: () => boolean;\n\t/** Override the settings file. Tests only. */\n\tsettingsFile?: string;\n}\n\n/** Has Claude Code written a transcript inside the sync window? */\nexport function claudeRecentlyActive(): Promise<boolean> {\n\treturn claudeAdapter.detect({ sinceMs: detectionSinceMs() });\n}\n\n/**\n * The post-sync upsell (#56 decision 2), asked once per machine. Any explicit\n * answer persists to ~/.config/aistack/settings.json; ctrl-C is not an answer\n * and the question returns on the next sync.\n *\n * Two gates, both silent. The offer needs a Claude Code the user actually runs\n * (#101) — a months-old install asked a Codex-only user to connect a harness\n * they had left behind, which is what opened #100. It also needs `claude` on\n * PATH, because that binary is what installs the MCP server.\n */\nexport async function offerConnectUpsell(deps: UpsellDeps = {}): Promise<void> {\n\tif (getSettings(deps.settingsFile).connectClaudeAnswered === true) return;\n\tif (!(await (deps.claudeActiveImpl ?? claudeRecentlyActive)())) return;\n\tif (!(deps.claudeOnPathImpl ?? claudeOnPath)()) return;\n\n\tconst answer = await p.select({\n\t\tmessage: \"Sync from inside Claude Code too?\",\n\t\toptions: [\n\t\t\t{\n\t\t\t\tvalue: \"later\",\n\t\t\t\tlabel: \"Not now\",\n\t\t\t\thint: \"this question will not come back\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalue: \"install\",\n\t\t\t\tlabel: \"Install\",\n\t\t\t\thint: \"adds the aistack MCP server + Skill to Claude Code\",\n\t\t\t},\n\t\t],\n\t\tinitialValue: \"later\",\n\t});\n\n\tif (p.isCancel(answer)) return;\n\tsaveSettings({ connectClaudeAnswered: true }, deps.settingsFile);\n\n\tif (answer === \"install\") {\n\t\tconst result = installClaudeConnect();\n\t\tif (result.ok) {\n\t\t\tp.log.success(result.message);\n\t\t} else {\n\t\t\tp.log.error(result.message);\n\t\t}\n\t\treturn;\n\t}\n\n\tp.log.message(\n\t\t`If you change your mind: ${limeBold(\"npx @use-aistack/cli connect claude\")}`,\n\t);\n}\n","// Time-aware pinned price table for API-equivalent cost.\n//\n// Wayfinder ticket #37 (map #29), decision 8 of the wire-format grilling #33.\n// Moved out of the CLI by ticket #93 (map #76).\n//\n// WHY THIS IS A PACKAGE AND NOT A CLI FILE\n// Two programs price the same tokens. The CLI prices each response at ingest,\n// where the per-response timestamp still exists. The backend re-prices a\n// published snapshot at READ time, to fill the gaps a stale CLI table left\n// behind — one day of table drift published a stack at $14,764 when the same\n// tokens are worth at least $167,331 (#93). Two copies of a price table drift\n// against each other by construction, so there is one copy and both import it.\n//\n// WHY THIS IS A LIST OF PERIODS AND NOT A FLAT MAP\n// A published \"API-equivalent cost\" covers a rolling 30-day window, and a\n// window can straddle a repricing. On 2026-09-05 the window covers Aug 6 →\n// Sep 5, but `claude-sonnet-5`'s introductory rate ends Aug 31 — so 25 days\n// price at $2/$10 and 5 days at $3/$15. A flat table misprices one side or the\n// other for a month after every repricing, which breaks the honesty tenet the\n// measured layer is built on.\n//\n// So each model's price is a list of effective-from ranges, and every response\n// is priced at the rate in effect at ITS OWN timestamp. Cost therefore has to\n// accumulate at ingest (see analyzer.ts) — summing tokens per model and pricing\n// once at the end cannot express a mid-window rate change.\n//\n// Sources: Anthropic public list prices as of 2026-07-25 (cache multipliers\n// from https://platform.claude.com/docs/en/build-with-claude/prompt-caching:\n// 5m cache write = 1.25x input, 1h cache write = 2x input, read = 0.1x input)\n// and OpenAI public list prices as of 2026-08-02\n// (https://developers.openai.com/api/docs/pricing — cached input is 10% of\n// input, the same multiplier `cacheRead` already uses; Codex reports no cache\n// writes, so the write multipliers never fire for OpenAI rows).\n// Google public list prices as of 2026-08-09\n// (https://ai.google.dev/gemini-api/docs/pricing) were added by ticket #123.\n//\n// A read-time estimate cites the table of the model it priced, which is why the\n// table id sits on the rate rather than beside it.\n//\n// WHY A KEY CAN CARRY A PROVIDER\n// Claude Code and Codex each speak to one vendor, so a bare model id names a\n// rate without ambiguity. opencode and pi-mono route many providers, and two of\n// them RE-SERVE another vendor's models under that vendor's own slug: the\n// opencode-zen gateway and github-copilot both emit `gemini-3-pro-preview`, at\n// prices no list page states (ticket #122). Pricing a re-served model at the\n// vendor's list rate would invent a figure and could overstate, which the\n// lower-bound tenet forbids.\n//\n// So a multi-provider adapter keys its rows `provider:model`, and only a\n// provider this table maps to a vendor reaches that vendor's rates. Everything\n// else — gateways, unknown providers — holds no rate and lands in\n// `unpricedTokens`, which is the honest outcome and needs no new machinery.\n//\n// The separator is `:` and not `/` on purpose: `sanitizeModelId` in the CLI\n// payload rewrites `/` to `-`, so a slash-keyed id would reach the backend as a\n// different string than the one this table holds, and the read-time re-pricer\n// would miss it.\n//\n// A LOCAL MODEL IS FREE, WHICH IS NOT THE SAME FACT AS UNPRICED\n// `ollama:qwen3-coder` costs nothing per token. `openrouter:qwen3-coder` costs\n// something this table cannot name. Both used to look identical — no row, no\n// rate, tokens excluded from coverage — so a local run read as a hole in the\n// table. Local providers now hold a real zero rate, cited as\n// `LOCAL_PRICING_TABLE_VERSION`. Their tokens count as covered and add $0.\n\nexport const PRICING_TABLE_VERSION = \"anthropic-list-2026-07-25\";\nexport const OPENAI_PRICING_TABLE_VERSION = \"openai-list-2026-08-02\";\nexport const GOOGLE_PRICING_TABLE_VERSION = \"google-list-2026-08-09\";\n/**\n * The citation for a model that runs on the user's own machine. It is not a\n * vendor list — it is the statement that no per-token charge exists.\n */\nexport const LOCAL_PRICING_TABLE_VERSION = \"local-no-charge\";\n\nexport const CACHE_WRITE_5M_MULTIPLIER = 1.25;\nexport const CACHE_WRITE_1H_MULTIPLIER = 2.0;\nexport const CACHE_READ_MULTIPLIER = 0.1;\n\n/** The separator between a provider id and a model id in a pricing key. */\nexport const PROVIDER_SEPARATOR = \":\";\n\n/**\n * End of the `claude-sonnet-5` introductory rate. Anthropic documents it as \"in\n * effect through 2026-08-31\", so the post-intro period opens at the following\n * UTC midnight.\n *\n * The boundary is approximated in UTC because the announcement names a date,\n * not a timezone. A response written within a few hours of the boundary can\n * therefore be priced on the wrong side of it — worth a handful of cents on a\n * single day, and the alternative (guessing US/Pacific) is no more defensible.\n */\nexport const SONNET_5_INTRO_ENDS_MS = Date.UTC(2026, 8, 1); // 2026-09-01T00:00:00Z\n\n/** USD per million tokens, valid over `[from, to)`. */\nexport type PricePeriod = {\n\t/** Inclusive lower bound, epoch ms. `null` = since the model existed. */\n\tfrom: number | null;\n\t/** Exclusive upper bound, epoch ms. `null` = still in effect. */\n\tto: number | null;\n\tinput: number;\n\toutput: number;\n};\n\n/**\n * How a vendor charges for cache traffic, as multipliers on its input rate.\n *\n * Anthropic is the shape the constants above describe. Google charges the\n * standard input rate to write a cache and bills storage by the hour instead,\n * so a Google cache write must NOT carry Anthropic's 1.25x — that would\n * overstate, and every estimate here is a lower bound by construction. The\n * hourly storage fee is real spend this table cannot see, which keeps the\n * Google figure below the true one rather than above it.\n */\nexport type CacheMultipliers = {\n\twrite5m: number;\n\twrite1h: number;\n\tread: number;\n};\n\nconst DEFAULT_CACHE_MULTIPLIERS: CacheMultipliers = {\n\twrite5m: CACHE_WRITE_5M_MULTIPLIER,\n\twrite1h: CACHE_WRITE_1H_MULTIPLIER,\n\tread: CACHE_READ_MULTIPLIER,\n};\n\n/** Cached input is 10% of input; a write is charged as plain input. */\nconst GOOGLE_CACHE_MULTIPLIERS: CacheMultipliers = {\n\twrite5m: 1.0,\n\twrite1h: 1.0,\n\tread: 0.1,\n};\n\nconst FREE_CACHE_MULTIPLIERS: CacheMultipliers = {\n\twrite5m: 0,\n\twrite1h: 0,\n\tread: 0,\n};\n\n/**\n * Who sets the rate. A provider only reaches a vendor's rows when it IS that\n * vendor — a gateway re-serving the same model is a different price.\n */\ntype Vendor = \"anthropic\" | \"openai\" | \"google\" | \"local\";\n\n/** One model's rates, plus the table that cites them. */\ntype PriceEntry = {\n\tvendor: Vendor;\n\t/** The citation printed next to any dollar figure these rates produce. */\n\ttable: string;\n\tperiods: PricePeriod[];\n\tcache: CacheMultipliers;\n};\n\nconst anthropic = (periods: PricePeriod[]): PriceEntry => ({\n\tvendor: \"anthropic\",\n\ttable: PRICING_TABLE_VERSION,\n\tperiods,\n\tcache: DEFAULT_CACHE_MULTIPLIERS,\n});\nconst openai = (periods: PricePeriod[]): PriceEntry => ({\n\tvendor: \"openai\",\n\ttable: OPENAI_PRICING_TABLE_VERSION,\n\tperiods,\n\tcache: DEFAULT_CACHE_MULTIPLIERS,\n});\nconst google = (periods: PricePeriod[]): PriceEntry => ({\n\tvendor: \"google\",\n\ttable: GOOGLE_PRICING_TABLE_VERSION,\n\tperiods,\n\tcache: GOOGLE_CACHE_MULTIPLIERS,\n});\nconst flat = (input: number, output: number): PricePeriod[] => [\n\t{ from: null, to: null, input, output },\n];\n\n/**\n * The rate for anything served off the user's own hardware: zero, and citable.\n * One entry covers every local model id, because the fact does not depend on\n * which weights were loaded.\n */\nconst FREE: PriceEntry = {\n\tvendor: \"local\",\n\ttable: LOCAL_PRICING_TABLE_VERSION,\n\tperiods: flat(0, 0),\n\tcache: FREE_CACHE_MULTIPLIERS,\n};\n\n/**\n * Providers that ARE the vendor, so their rows price at that vendor's list.\n *\n * Deliberately absent, and each absence is a decision from ticket #122's\n * measurements: `opencode` (the opencode-zen gateway — `glm-4.7-free`,\n * `kimi-k2.5-free` and the unbranded `big-pickle` have no public list price),\n * `github-copilot` (re-serves other vendors' slugs at its own terms),\n * `openrouter`, `azure`, `bedrock` and `vercel-ai-gateway` (resellers).\n * A provider joins this map when a harness is measured emitting it, never\n * speculatively — an unused rate is a maintenance liability on a table that is\n * updated by hand.\n */\nconst PROVIDER_VENDOR: Record<string, Vendor> = {\n\tanthropic: \"anthropic\",\n\topenai: \"openai\",\n\tgoogle: \"google\",\n};\n\n/**\n * Providers that run the model on this machine. No API call, no per-token\n * charge. Ids as opencode and pi-mono spell them.\n */\nconst LOCAL_PROVIDERS = new Set([\n\t\"ollama\",\n\t\"lmstudio\",\n\t\"llama.cpp\",\n\t\"llamacpp\",\n\t\"local\",\n]);\n\n/**\n * Only rates we can actually cite are encoded. Inventing historical periods to\n * make the table look complete would fabricate cost for old records, so every\n * model with one known rate gets one open-ended period.\n */\nconst PRICES: Record<string, PriceEntry> = {\n\t\"claude-fable-5\": anthropic(flat(10, 50)),\n\t\"claude-mythos-5\": anthropic(flat(10, 50)),\n\t\"claude-opus-5\": anthropic(flat(5, 25)),\n\t\"claude-opus-4-8\": anthropic(flat(5, 25)),\n\t\"claude-opus-4-7\": anthropic(flat(5, 25)),\n\t\"claude-opus-4-6\": anthropic(flat(5, 25)),\n\t\"claude-sonnet-5\": anthropic([\n\t\t{ from: null, to: SONNET_5_INTRO_ENDS_MS, input: 2, output: 10 },\n\t\t{ from: SONNET_5_INTRO_ENDS_MS, to: null, input: 3, output: 15 },\n\t]),\n\t\"claude-sonnet-4-6\": anthropic(flat(3, 15)),\n\t\"claude-haiku-4-5\": anthropic(flat(1, 5)),\n\t// Fast mode (research preview) — Claude API only, Opus 5 / Opus 4.8 only.\n\t// Opus 4.7 fast mode was removed, so there is deliberately no 4-7 entry.\n\t\"claude-opus-5#fast\": anthropic(flat(10, 50)),\n\t\"claude-opus-4-8#fast\": anthropic(flat(10, 50)),\n\t// OpenAI (Codex) — standard-context tier (<272K; observed context window is\n\t// 258,400).\n\t\"gpt-5.5\": openai(flat(5, 30)),\n\t\"gpt-5.4\": openai(flat(2.5, 15)),\n\t\"gpt-5.4-mini\": openai(flat(0.75, 4.5)),\n\t\"gpt-5.3-codex\": openai(flat(1.75, 14)),\n\t// The gpt-5.6 family launched 2026-07-29; Terra and Luna were repriced on\n\t// 2026-07-30 (-20% / -80%). The one-day launch rates are not on the list\n\t// page and are NOT encoded — a July-29 Terra/Luna record underprices for\n\t// one day rather than carrying a rate we cannot cite (#72).\n\t\"gpt-5.6-sol\": openai(flat(5, 30)),\n\t\"gpt-5.6-terra\": openai(flat(2, 12)),\n\t\"gpt-5.6-luna\": openai(flat(0.2, 1.2)),\n\t// NOT on OpenAI's list page — an internal Codex routing label with no\n\t// official price (openai/codex#20981). Rate is the aggregator consensus\n\t// ($2.50 / $15.00), scoped in explicitly by ticket #72 because it carries\n\t// real token volume in Codex rollouts.\n\t\"codex-auto-review\": openai(flat(2.5, 15)),\n\t// Google (opencode, pi-mono) — Standard tier. Where a model is\n\t// context-tiered, the <=200K rate is encoded, exactly as the OpenAI rows\n\t// encode the standard-context tier: the payload carries no per-response\n\t// context length, so the cheaper side keeps the figure a lower bound. Only\n\t// the Pro and Flash families are here — image, TTS, embedding, Live and\n\t// robotics models are not what a coding harness selects.\n\t\"gemini-3.1-pro-preview\": google(flat(2, 12)),\n\t\"gemini-3.6-flash\": google(flat(1.5, 7.5)),\n\t\"gemini-3.5-flash\": google(flat(1.5, 9)),\n\t\"gemini-3-flash-preview\": google(flat(0.5, 3)),\n\t\"gemini-2.5-pro\": google(flat(1.25, 10)),\n\t\"gemini-2.5-flash\": google(flat(0.3, 2.5)),\n\t// RETIRED from Google's list page by 2026-08-09, and still the largest\n\t// single block of Google tokens measured in #122 (10.9M, 20.5% of that\n\t// machine's total). Encoded at its launch rate on the same footing as\n\t// `codex-auto-review`: real volume, a rate we can name, and a note saying\n\t// where it came from. Announcement rate, <=200K tier: $2.00 / $12.00.\n\t\"gemini-3-pro-preview\": google(flat(2, 12)),\n\t// A real Anthropic model with no row until #123. Measured in #122 as\n\t// `claude-opus-4-5-20251101`, which the dated-suffix rule strips to this key.\n\t// Same $5/$25 as the rest of the Opus 4 line on the 2026-07-25 list.\n\t\"claude-opus-4-5\": anthropic(flat(5, 25)),\n};\n\n/**\n * Split a pricing key into its provider and its model part.\n *\n * A key with no separator has no provider, which is what the single-vendor\n * adapters produce and how every id published before ticket #123 is shaped.\n * The split is on the FIRST separator only, so an id that carries its own colon\n * (`ollama:llama3.2:3b`) keeps it.\n */\nexport function splitModelKey(modelKey: string): {\n\tprovider: string | null;\n\tmodel: string;\n} {\n\tconst at = modelKey.indexOf(PROVIDER_SEPARATOR);\n\tif (at === -1) return { provider: null, model: modelKey };\n\treturn {\n\t\tprovider: modelKey.slice(0, at),\n\t\tmodel: modelKey.slice(at + PROVIDER_SEPARATOR.length),\n\t};\n}\n\n/**\n * Build the pricing key a multi-provider harness reports under. Pass the\n * harness's own provider id verbatim; this table decides what it means.\n */\nexport function modelKeyFor(provider: string, model: string): string {\n\treturn `${provider}${PROVIDER_SEPARATOR}${model}`;\n}\n\n/**\n * The rates that apply to a pricing key, or `null` when none can be cited.\n *\n * This is the single lookup every exported function goes through, so the\n * provider rule is stated once.\n */\nfunction entryFor(modelKey: string): PriceEntry | null {\n\tconst { provider, model } = splitModelKey(modelKey);\n\tif (provider === null) return PRICES[model] ?? null;\n\tif (LOCAL_PROVIDERS.has(provider)) return FREE;\n\tconst vendor = PROVIDER_VENDOR[provider];\n\tif (!vendor) return null;\n\tconst entry = PRICES[model];\n\t// A gateway that names a vendor's model does not get the vendor's rate, and\n\t// a vendor that names a model from a table we do not hold gets nothing.\n\treturn entry && entry.vendor === vendor ? entry : null;\n}\n\nexport type TokenCounts = {\n\tinput: number;\n\toutput: number;\n\tcacheWrite5m: number;\n\tcacheWrite1h: number;\n\t/** `cache_creation_input_tokens` not covered by the TTL breakdown; priced at the 5m rate. */\n\tcacheWriteUnsplit: number;\n\tcacheRead: number;\n};\n\n/**\n * Normalize an observed `message.model` into a pricing key. Handles the\n * dated-suffix variants (`claude-haiku-4-5-20251001`). The `#fast` suffix is\n * appended by the caller from `usage.speed`. A `provider:` prefix passes\n * through untouched, so a caller can normalize a composed key.\n */\nexport function normalizeModel(model: string): string {\n\tconst { provider, model: bare } = splitModelKey(model);\n\tconst [base, suffix] = bare.split(\"#\");\n\tconst stripped = base.replace(/-\\d{8}$/, \"\");\n\tconst normalized = suffix ? `${stripped}#${suffix}` : stripped;\n\treturn provider === null ? normalized : modelKeyFor(provider, normalized);\n}\n\n/**\n * Drop the analyzer's synthetic `#fast` suffix, leaving the id the payload\n * publishes.\n *\n * A `provider:` prefix SURVIVES this, and that is deliberate: the provider is\n * what tells `google:gemini-3-pro-preview` from\n * `github-copilot:gemini-3-pro-preview`, and the backend re-pricer needs that\n * difference at read time. Use `vendorModelId` where a human or the models\n * catalog needs the plain vendor id.\n */\nexport function baseModelId(modelKey: string): string {\n\treturn modelKey.split(\"#\")[0];\n}\n\n/**\n * The vendor-assigned id alone — no provider prefix, no `#fast`. This is the id\n * to show a reader and to match against the models catalog.\n */\nexport function vendorModelId(modelKey: string): string {\n\treturn splitModelKey(baseModelId(modelKey)).model;\n}\n\n/**\n * How this model's vendor charges for cache traffic. Falls back to the\n * Anthropic-shaped constants for a model with no rate, so a caller that prices\n * an unknown model still gets a defined shape rather than a crash.\n */\nexport function cacheMultipliersFor(modelKey: string): CacheMultipliers {\n\treturn entryFor(modelKey)?.cache ?? DEFAULT_CACHE_MULTIPLIERS;\n}\n\n/**\n * True when this key names a model that runs locally and therefore costs\n * nothing per token. A caller printing dollars uses this to say \"free\", never\n * \"unknown\".\n */\nexport function isLocalModel(modelKey: string): boolean {\n\treturn entryFor(modelKey)?.vendor === \"local\";\n}\n\n/**\n * The rate in effect for `modelKey` at `atMs`, or `null` when the model is\n * unknown or the timestamp predates every period we can cite.\n *\n * A `null` timestamp also yields `null`: a record with no parseable timestamp\n * cannot be priced time-awarely, and inventing a price for it (say, today's)\n * would silently attribute the wrong rate. Its tokens surface as unpriced.\n */\nexport function priceAt(\n\tmodelKey: string,\n\tatMs: number | null,\n): PricePeriod | null {\n\tif (atMs === null) return null;\n\tconst entry = entryFor(modelKey);\n\tif (!entry) return null;\n\tfor (const p of entry.periods) {\n\t\tif ((p.from === null || atMs >= p.from) && (p.to === null || atMs < p.to)) {\n\t\t\treturn p;\n\t\t}\n\t}\n\treturn null;\n}\n\n/** True when we hold at least one citable rate for this model, at any time. */\nexport function isPricedModel(modelKey: string): boolean {\n\treturn entryFor(modelKey) !== null;\n}\n\n/**\n * The table id that cites this model's rates, or `null` when it has none.\n *\n * The citation belongs to the rate, not to the harness that reported it: a\n * read-time estimate is cited by the table it was drawn from, and one stack can\n * carry Anthropic and OpenAI rows at once.\n */\nexport function pricingTableFor(modelKey: string): string | null {\n\treturn entryFor(modelKey)?.table ?? null;\n}\n\n/**\n * Every rate that applies to `modelKey` anywhere inside `[fromMs, toMs]`.\n *\n * This is the read-time counterpart of `priceAt`. A published snapshot has no\n * per-response timestamps left — it carries one merged token total over a\n * window — so a re-pricer cannot ask \"what did this response cost\". It can only\n * ask \"which rates could this window have paid\", and then choose. Returns an\n * empty list for an unknown model and for a window that closes before the\n * model's first citable rate opens.\n */\nexport function pricePeriodsInWindow(\n\tmodelKey: string,\n\tfromMs: number,\n\ttoMs: number,\n): PricePeriod[] {\n\tconst entry = entryFor(modelKey);\n\tif (!entry) return [];\n\treturn entry.periods.filter(\n\t\t(p) =>\n\t\t\t(p.from === null || p.from <= toMs) && (p.to === null || p.to > fromMs),\n\t);\n}\n\n/**\n * Cost of one response's tokens at the rate in effect at its own timestamp.\n * Returns `null` when no rate applies — the caller must surface that as\n * unpriced tokens rather than zeroing it.\n */\nexport function apiEquivalentCost(\n\tmodelKey: string,\n\tt: TokenCounts,\n\tatMs: number | null,\n): number | null {\n\tconst p = priceAt(modelKey, atMs);\n\tif (!p) return null;\n\tconst c = cacheMultipliersFor(modelKey);\n\tconst M = 1_000_000;\n\treturn (\n\t\t(t.input * p.input +\n\t\t\tt.output * p.output +\n\t\t\t(t.cacheWrite5m + t.cacheWriteUnsplit) * p.input * c.write5m +\n\t\t\tt.cacheWrite1h * p.input * c.write1h +\n\t\t\tt.cacheRead * p.input * c.read) /\n\t\tM\n\t);\n}\n","// Harness-agnostic aggregate machinery: the fold target every adapter fills,\n// and the finalize step that turns it into display-ready rows.\n//\n// Extracted from the Claude analyzer by ticket #67 (map #60) so the Codex\n// adapter can reuse the same totals, name hygiene, and model rows without\n// inheriting Claude's record-dedup logic. Everything here is pure — no I/O,\n// no console.\n\nimport { isPricedModel, type TokenCounts } from \"@aistack/pricing\";\n\n// ---------------------------------------------------------------------------\n// Narrowing helpers — records are untrusted external JSON\n// ---------------------------------------------------------------------------\n\nexport type Obj = Record<string, unknown>;\n\nexport const asObj = (v: unknown): Obj | null =>\n\ttypeof v === \"object\" && v !== null && !Array.isArray(v) ? (v as Obj) : null;\nexport const asStr = (v: unknown): string | null =>\n\ttypeof v === \"string\" && v.length > 0 ? v : null;\nexport const asNum = (v: unknown): number =>\n\ttypeof v === \"number\" && Number.isFinite(v) ? v : 0;\nexport const asArr = (v: unknown): unknown[] => (Array.isArray(v) ? v : []);\n\n/**\n * Every name that becomes a Map key or leaves this module goes through here.\n *\n * These are user-chosen strings (skill names, MCP servers, subagent types,\n * slash commands, model ids) and a hostile one is a real vector: control\n * characters move a terminal cursor, and an unterminated bidi override (U+202E)\n * reorders the rest of the rendered line — including the count and percentage\n * printed beside the name. Both survive `JSON.stringify`, which escapes C0 but\n * not bidi. See CVE-2021-42574 (\"Trojan Source\").\n *\n * Sanitizing at ingest rather than at print means the guarantee travels with\n * the module: `finalize()`'s output is safe for any consumer, not just the\n * renderer that happens to sit in front of it today.\n */\nconst NAME_UNSAFE_RE =\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: stripping them is the point\n\t/[\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u061c\\u200b-\\u200f\\u2028-\\u202e\\u2060-\\u2064\\u2066-\\u2069\\ufeff]/g;\nconst NAME_MAX = 64;\n\nexport function cleanName(s: string): string {\n\tconst stripped = s.replace(NAME_UNSAFE_RE, \"�\").trim();\n\tif (stripped.length === 0) return \"(unnamed)\";\n\treturn stripped.length > NAME_MAX\n\t\t? `${stripped.slice(0, NAME_MAX - 1)}…`\n\t\t: stripped;\n}\n\n/**\n * The same bar as `cleanName`, asked as a question.\n *\n * Used on names arriving from the NETWORK — the per-stack opt-ins the sync\n * config carries (#44). Those are the user's own strings, so the curated list's\n * conventional charset is the wrong bar: parentheses, accents and CJK are all\n * legitimate names someone runs. What is refused is what cannot be rendered\n * safely, which is exactly what `cleanName` strips on the way in.\n */\nexport function isDisplaySafeName(s: string): boolean {\n\tif (s.length === 0 || s.trim().length === 0) return false;\n\tif (s.length > NAME_MAX) return false;\n\t// A `g`-flagged regex carries `lastIndex` across `.test` calls, so this uses\n\t// a fresh non-global copy rather than the shared literal.\n\treturn !new RegExp(NAME_UNSAFE_RE.source).test(s);\n}\n\n/** `asStr` for anything that will be used as a name. */\nexport const asName = (v: unknown): string | null => {\n\tconst s = asStr(v);\n\treturn s === null ? null : cleanName(s);\n};\n\n// ---------------------------------------------------------------------------\n// Aggregate\n// ---------------------------------------------------------------------------\n\nexport type ModelUsage = TokenCounts & {\n\tmessages: number;\n\t/**\n\t * API-equivalent cost accumulated per response at that response's own rate\n\t * (#33 decision 8). Not derivable from the token totals above once a window\n\t * straddles a repricing.\n\t */\n\tcostUSD: number;\n\t/** Tokens whose own timestamp had no citable rate. Surfaced, never zeroed. */\n\tunpricedTokens: number;\n};\n\n/**\n * The fold target. `Seen` is the adapter's own dedup bookkeeping type —\n * Claude keys responses by `message.id`, Codex needs none — kept generic so\n * the shared shape does not import any one harness's record semantics.\n */\nexport type Aggregate<Seen = unknown> = {\n\t// provenance / scan health\n\tfiles: number;\n\tlines: number;\n\tparseErrors: number;\n\trecords: number;\n\tassistantRecords: number;\n\t/** Distinct API responses actually counted. */\n\tdistinctResponses: number;\n\t/** Extra records of a response already counted (same message.id AND requestId). */\n\tcontinuationsFolded: number;\n\t/** Same message.id under a NEW requestId — a genuine replay (e.g. /btw sidechain). */\n\trealReplaysFolded: number;\n\t/** Times a later record superseded an earlier one because it carried a larger total. */\n\tsupersededByLarger: number;\n\t/** Assistant records with no message.id — counted without dedup protection. */\n\tunkeyedResponses: number;\n\tsyntheticRecords: number;\n\tsyntheticTokens: number;\n\ttoolBlocksWithoutId: number;\n\t/** Responses whose first attempt ran on a different model (#33 decision 9). */\n\tfallbackAttempts: number;\n\tuntypedMirrors: number;\n\t/** Records with no parseable timestamp — cannot be priced time-awarely. */\n\tuntimestampedResponses: number;\n\tprojectDirs: Set<string>; // held only to count — names never leave this module\n\tccVersions: Set<string>;\n\tmirroredIterationTypes: Map<string, number>;\n\n\t// tokens\n\tbyModel: Map<string, ModelUsage>;\n\tsidechainTokens: number;\n\tmainTokens: number;\n\n\t// activity\n\tsessions: Set<string>;\n\tactiveDays: Set<string>; // UTC YYYY-MM-DD\n\tfirstTs: number | null;\n\tlastTs: number | null;\n\n\t// tools / skills / mcp / agents\n\ttoolCalls: Map<string, number>;\n\tskillCalls: Map<string, number>;\n\tmcpServerCalls: Map<string, number>;\n\tmcpToolCalls: Map<string, number>;\n\tsubagentCalls: Map<string, number>;\n\tslashCommands: Map<string, number>;\n\ttoolCallDedup: Set<string>;\n\n\t// content-block shape\n\tthinkingBlocks: number;\n\ttextBlocks: number;\n\twebSearchRequests: number;\n\twebFetchRequests: number;\n\n\t// adapter-owned dedup bookkeeping\n\tseen: Map<string, Seen>;\n};\n\nexport function createAggregate<Seen = unknown>(): Aggregate<Seen> {\n\treturn {\n\t\tfiles: 0,\n\t\tlines: 0,\n\t\tparseErrors: 0,\n\t\trecords: 0,\n\t\tassistantRecords: 0,\n\t\tdistinctResponses: 0,\n\t\tcontinuationsFolded: 0,\n\t\trealReplaysFolded: 0,\n\t\tsupersededByLarger: 0,\n\t\tunkeyedResponses: 0,\n\t\tsyntheticRecords: 0,\n\t\tsyntheticTokens: 0,\n\t\ttoolBlocksWithoutId: 0,\n\t\tfallbackAttempts: 0,\n\t\tuntypedMirrors: 0,\n\t\tuntimestampedResponses: 0,\n\t\tprojectDirs: new Set(),\n\t\tccVersions: new Set(),\n\t\tmirroredIterationTypes: new Map(),\n\t\tbyModel: new Map(),\n\t\tsidechainTokens: 0,\n\t\tmainTokens: 0,\n\t\tsessions: new Set(),\n\t\tactiveDays: new Set(),\n\t\tfirstTs: null,\n\t\tlastTs: null,\n\t\ttoolCalls: new Map(),\n\t\tskillCalls: new Map(),\n\t\tmcpServerCalls: new Map(),\n\t\tmcpToolCalls: new Map(),\n\t\tsubagentCalls: new Map(),\n\t\tslashCommands: new Map(),\n\t\ttoolCallDedup: new Set(),\n\t\tthinkingBlocks: 0,\n\t\ttextBlocks: 0,\n\t\twebSearchRequests: 0,\n\t\twebFetchRequests: 0,\n\t\tseen: new Map(),\n\t};\n}\n\nexport const bump = (m: Map<string, number>, k: string, n = 1) =>\n\tm.set(k, (m.get(k) ?? 0) + n);\n\nexport function emptyUsage(): ModelUsage {\n\treturn {\n\t\tinput: 0,\n\t\toutput: 0,\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: 0,\n\t\tcacheRead: 0,\n\t\tmessages: 0,\n\t\tcostUSD: 0,\n\t\tunpricedTokens: 0,\n\t};\n}\n\nexport const countsTotal = (t: TokenCounts): number =>\n\tt.input +\n\tt.output +\n\tt.cacheWrite5m +\n\tt.cacheWrite1h +\n\tt.cacheWriteUnsplit +\n\tt.cacheRead;\n\n/**\n * Fold one priced usage delta into the per-model totals. The Claude adapter\n * has its own apply/retract pair (dedup can un-count a response); an adapter\n * whose records are already deltas — Codex — adds through here.\n */\nexport function addModelUsage(\n\tagg: Aggregate<never> | Aggregate<unknown>,\n\tmodelKey: string,\n\tcounts: TokenCounts,\n\tcostUSD: number | null,\n\tmessages = 1,\n): void {\n\tlet m = agg.byModel.get(modelKey);\n\tif (!m) {\n\t\tm = emptyUsage();\n\t\tagg.byModel.set(modelKey, m);\n\t}\n\tm.messages += messages;\n\tm.input += counts.input;\n\tm.output += counts.output;\n\tm.cacheWrite5m += counts.cacheWrite5m;\n\tm.cacheWrite1h += counts.cacheWrite1h;\n\tm.cacheWriteUnsplit += counts.cacheWriteUnsplit;\n\tm.cacheRead += counts.cacheRead;\n\tif (costUSD === null) m.unpricedTokens += countsTotal(counts);\n\telse m.costUSD += costUSD;\n}\n\n// ---------------------------------------------------------------------------\n// Finalize — the shape the wire payload is derived from\n// ---------------------------------------------------------------------------\n\nexport type ModelRow = {\n\t/** Pricing key: normalized vendor id, plus `#fast` when speed was fast. */\n\tmodelKey: string;\n\ttokens: TokenCounts;\n\ttotalTokens: number;\n\tmessages: number;\n\tshare: number;\n\t/** Accumulated at each response's own rate. `null` when nothing was priced. */\n\tcostUSD: number | null;\n\t/** Tokens inside this row that no rate covered. */\n\tunpricedTokens: number;\n};\n\nexport type Finalized = {\n\tmodels: ModelRow[];\n\ttotalTokens: number;\n\ttotalCostUSD: number;\n\tunpricedModels: string[];\n\tunpricedTokens: number;\n\tcacheHitShare: number;\n\tsidechainShare: number;\n\tactiveDays: number;\n\tfirstTs: number | null;\n\tlastTs: number | null;\n\tsessions: number;\n\tprojects: number;\n\ttools: Array<[string, number]>;\n\tskills: Array<[string, number]>;\n\tmcpServers: Array<[string, number]>;\n\tsubagents: Array<[string, number]>;\n\tslashCommands: Array<[string, number]>;\n\ttotalToolCalls: number;\n\t/** Newest harness version observed, or null when none was recorded. */\n\tharnessVersion: string | null;\n};\n\nfunction buildModelRows(agg: Aggregate): {\n\trows: ModelRow[];\n\ttotalTokens: number;\n\ttotalCostUSD: number;\n\tunpricedModels: string[];\n\tunpricedTokens: number;\n} {\n\tconst rows: ModelRow[] = [];\n\tlet totalTokens = 0;\n\tlet totalCostUSD = 0;\n\tconst unpricedModels: string[] = [];\n\tlet unpricedTokens = 0;\n\n\tfor (const [modelKey, u] of agg.byModel) {\n\t\tconst tokens: TokenCounts = {\n\t\t\tinput: u.input,\n\t\t\toutput: u.output,\n\t\t\tcacheWrite5m: u.cacheWrite5m,\n\t\t\tcacheWrite1h: u.cacheWrite1h,\n\t\t\tcacheWriteUnsplit: u.cacheWriteUnsplit,\n\t\t\tcacheRead: u.cacheRead,\n\t\t};\n\t\tconst sum = countsTotal(tokens);\n\t\ttotalTokens += sum;\n\t\tif (u.unpricedTokens > 0) {\n\t\t\tunpricedModels.push(modelKey);\n\t\t\tunpricedTokens += u.unpricedTokens;\n\t\t}\n\t\ttotalCostUSD += u.costUSD;\n\t\trows.push({\n\t\t\tmodelKey,\n\t\t\ttokens,\n\t\t\ttotalTokens: sum,\n\t\t\tmessages: u.messages,\n\t\t\tshare: 0,\n\t\t\t// A model we hold no rate for at all reports null rather than $0.00,\n\t\t\t// so \"we can't price this\" never reads as \"this was free\".\n\t\t\tcostUSD: isPricedModel(modelKey) ? u.costUSD : null,\n\t\t\tunpricedTokens: u.unpricedTokens,\n\t\t});\n\t}\n\tfor (const r of rows) r.share = totalTokens ? r.totalTokens / totalTokens : 0;\n\trows.sort(\n\t\t(a, b) =>\n\t\t\tb.totalTokens - a.totalTokens || a.modelKey.localeCompare(b.modelKey),\n\t);\n\treturn { rows, totalTokens, totalCostUSD, unpricedModels, unpricedTokens };\n}\n\nfunction computeCacheHitShare(rows: ModelRow[]): number {\n\tlet cacheRead = 0;\n\tlet inputClass = 0;\n\tfor (const r of rows) {\n\t\tcacheRead += r.tokens.cacheRead;\n\t\tinputClass +=\n\t\t\tr.tokens.input +\n\t\t\tr.tokens.cacheRead +\n\t\t\tr.tokens.cacheWrite5m +\n\t\t\tr.tokens.cacheWrite1h +\n\t\t\tr.tokens.cacheWriteUnsplit;\n\t}\n\treturn inputClass ? cacheRead / inputClass : 0;\n}\n\n/**\n * Newest observed harness version, compared numerically per dotted segment\n * so `2.1.9` doesn't sort above `2.1.220`.\n */\nexport function newestVersion(versions: Iterable<string>): string | null {\n\tlet best: string | null = null;\n\tlet bestParts: number[] = [];\n\tfor (const v of versions) {\n\t\tconst parts = v.split(\".\").map((p) => Number.parseInt(p, 10));\n\t\tif (parts.some((n) => !Number.isFinite(n))) continue;\n\t\tif (best === null || compareParts(parts, bestParts) > 0) {\n\t\t\tbest = v;\n\t\t\tbestParts = parts;\n\t\t}\n\t}\n\treturn best;\n}\n\nfunction compareParts(a: number[], b: number[]): number {\n\tconst len = Math.max(a.length, b.length);\n\tfor (let i = 0; i < len; i++) {\n\t\tconst d = (a[i] ?? 0) - (b[i] ?? 0);\n\t\tif (d !== 0) return d;\n\t}\n\treturn 0;\n}\n\nexport function finalize(agg: Aggregate): Finalized {\n\tconst { rows, totalTokens, totalCostUSD, unpricedModels, unpricedTokens } =\n\t\tbuildModelRows(agg);\n\n\tconst byCount = (m: Map<string, number>): Array<[string, number]> =>\n\t\t[...m.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));\n\n\tlet totalToolCalls = 0;\n\tfor (const v of agg.toolCalls.values()) totalToolCalls += v;\n\tfor (const v of agg.mcpToolCalls.values()) totalToolCalls += v;\n\n\tconst sideTotal = agg.sidechainTokens + agg.mainTokens;\n\n\treturn {\n\t\tmodels: rows,\n\t\ttotalTokens,\n\t\ttotalCostUSD,\n\t\tunpricedModels,\n\t\tunpricedTokens,\n\t\tcacheHitShare: computeCacheHitShare(rows),\n\t\tsidechainShare: sideTotal ? agg.sidechainTokens / sideTotal : 0,\n\t\tactiveDays: agg.activeDays.size,\n\t\tfirstTs: agg.firstTs,\n\t\tlastTs: agg.lastTs,\n\t\tsessions: agg.sessions.size,\n\t\tprojects: agg.projectDirs.size,\n\t\ttools: byCount(agg.toolCalls),\n\t\tskills: byCount(agg.skillCalls),\n\t\tmcpServers: byCount(agg.mcpServerCalls),\n\t\tsubagents: byCount(agg.subagentCalls),\n\t\tslashCommands: byCount(agg.slashCommands),\n\t\ttotalToolCalls,\n\t\tharnessVersion: newestVersion(agg.ccVersions),\n\t};\n}\n","// The bundled curated allowlist — the fallback copy for `/api/sync-config`.\n//\n// Wayfinder ticket #37 (map #29), decision 4 of the wire-format grilling #33.\n//\n// WHAT BELONGS HERE, AND WHY IT IS SHORT\n// These four classes of name are user-chosen. A Skill called `acme-q3-pricing`,\n// an MCP server called `internal-billing`, a subagent called `client-migration`\n// — each is a real leak, and none of them is distinguishable from a public name\n// by shape.\n//\n// THE BAR (grilling #42): a name qualifies if the STRING carries no private\n// information no matter who typed it. That is a property of the string, not of\n// the user and not of the artifact.\n//\n// The bar is deliberately NOT \"the name identifies a public artifact, so\n// publishing it reveals nothing the user hasn't already published\". That was the\n// original wording and it is wrong: `stripe` is on this list, and publishing it\n// plainly does reveal something the user never published — that they use Stripe.\n// It cannot be the harm, because revealing what you use is the entire product.\n// The harm is narrower: strings drawn from the user's private vocabulary, which\n// leak a relationship (an employer, a client, a codename) rather than a\n// preference. `stripe` and `filesystem` are safe even for someone who named\n// their own server that by coincidence.\n//\n// Three sources meet that bar:\n// 1. Claude Code's own built-in subagent types and slash commands (vendor-\n// assigned, same class as a built-in tool name).\n// 2. Skills that ship with Claude Code itself.\n// 3. MCP servers with a public, documented, first-party endpoint.\n//\n// WHY THIS LIST DOES NOT NEED TO BE LONG (#42 decision 1)\n// It is no longer the only road to publishing a name. The approve gate offers\n// every kept-private name as an explicit, default-off tick, and the tick set\n// comes back down with the rest of the sync config. This list only exists to\n// spare a user from ticking boxes nobody would think twice about — so it can\n// stay strict, and every user-chosen name goes through the person who knows\n// whether it is a secret.\n//\n// The author's own `alp-river:*` plugin is deliberately NOT seeded, even though\n// it is genuinely published. This list is GLOBAL: seeding it would publish those\n// names for every user who installs the plugin without any of them ticking\n// anything, and an author adding their own names to the default everyone else\n// inherits is what would make the list untrustworthy for every other entry.\n//\n// `/api/sync-config` (ticket #38) serves the AUTHORITATIVE list. This copy only\n// covers the case where that endpoint can't be reached, which for an installed\n// user is permanent if the plugin never auto-updates. Growing the curated list\n// is server-side work; adding entries here only helps the offline case.\n\nimport type { CuratedAllowlist } from \"./allowlist.js\";\n\n/** Claude Code's own subagent types. Vendor-assigned, not user-chosen. */\nconst BUILTIN_SUBAGENTS = [\n\t\"(default)\",\n\t\"claude\",\n\t\"claude-code-guide\",\n\t\"Explore\",\n\t\"fork\",\n\t\"general-purpose\",\n\t\"Plan\",\n\t\"statusline-setup\",\n] as const;\n\n/** Skills bundled with Claude Code. */\nconst BUILTIN_SKILLS = [\n\t\"artifact-capabilities\",\n\t\"artifact-design\",\n\t\"claude-api\",\n\t\"code-review\",\n\t\"codebase-design\",\n\t\"dataviz\",\n\t\"diagnosing-bugs\",\n\t\"domain-modeling\",\n\t\"fewer-permission-prompts\",\n\t\"grilling\",\n\t\"init\",\n\t\"keybindings-help\",\n\t\"loop\",\n\t\"prototype\",\n\t\"research\",\n\t\"review\",\n\t\"run\",\n\t\"schedule\",\n\t\"security-review\",\n\t\"simplify\",\n\t\"tdd\",\n\t\"update-config\",\n] as const;\n\n/** Claude Code's own slash commands. */\nconst BUILTIN_SLASH_COMMANDS = [\n\t\"add-dir\",\n\t\"agents\",\n\t\"bug\",\n\t\"clear\",\n\t\"compact\",\n\t\"config\",\n\t\"context\",\n\t\"cost\",\n\t\"doctor\",\n\t\"effort\",\n\t\"exit\",\n\t\"export\",\n\t\"fast\",\n\t\"help\",\n\t\"hooks\",\n\t\"ide\",\n\t\"init\",\n\t\"login\",\n\t\"logout\",\n\t\"mcp\",\n\t\"memory\",\n\t\"model\",\n\t\"output-style\",\n\t\"permissions\",\n\t\"plugin\",\n\t\"privacy-settings\",\n\t\"release-notes\",\n\t\"resume\",\n\t\"review\",\n\t\"rewind\",\n\t\"security-review\",\n\t\"status\",\n\t\"statusline\",\n\t\"terminal-setup\",\n\t\"todos\",\n\t\"upgrade\",\n\t\"usage\",\n\t\"vim\",\n\t\"workflows\",\n] as const;\n\n/**\n * MCP servers with a public first-party endpoint.\n *\n * Matched against the server segment the analyzer parses out of an\n * `mcp__<server>__<tool>` name, which is the LOCAL alias the user configured —\n * so this only fires when the user kept the conventional name. A renamed server\n * is kept private, which is the correct direction to fail.\n *\n * ONE normalization applies first (#42 decision 5): a server provided by a\n * plugin is observed as `plugin_<plugin>_<server>`, a string Claude Code\n * generates rather than one the user typed. Strip that wrapper before matching,\n * and publish the NORMALIZED name. The safety property is that normalization can\n * only ever emit a string already on this list — a non-matching inner segment\n * emits nothing and the raw name falls through to the gate's review list — so a\n * bug here is bounded by an already-vetted set. If the upstream convention\n * changes, matching reverts to keeping names private: a fail-safe regression.\n */\nconst PUBLIC_MCP_SERVERS = [\n\t\"chrome-devtools\",\n\t\"context7\",\n\t\"deepwiki\",\n\t\"figma\",\n\t\"filesystem\",\n\t\"git\",\n\t\"github\",\n\t\"huggingface\",\n\t\"ide\",\n\t\"linear\",\n\t\"notion\",\n\t\"playwright\",\n\t\"puppeteer\",\n\t\"sentry\",\n\t\"slack\",\n\t\"stripe\",\n] as const;\n\nexport const BUNDLED_CURATED_ALLOWLIST: CuratedAllowlist = {\n\tmcpServers: PUBLIC_MCP_SERVERS,\n\tskills: BUILTIN_SKILLS,\n\tsubagents: BUILTIN_SUBAGENTS,\n\tslashCommands: BUILTIN_SLASH_COMMANDS,\n};\n","// Fail-closed name filtering for the measured layer.\n//\n// Wayfinder ticket #37 (map #29), decisions 2-4 of the wire-format grilling #33.\n//\n// THE INVERSION THIS FILE EXISTS TO PERFORM\n// The prototype's `toolCalls` map was a catch-all: anything that wasn't an\n// `mcp__*` tool, a Skill, or an Agent fell THROUGH into it, and from there into\n// the payload. That is denylist-shaped — a tool name nobody anticipated\n// publishes by default. Here a name publishes only if it matches a known list,\n// and everything else is withheld and published as a per-category count.\n//\n// Two classes of name, two mechanisms:\n// - Built-in Claude Code tool names are VENDOR-assigned and enumerable, so\n// they match a hardcoded literal set (BUILTIN_TOOLS below).\n// - MCP servers / Skills / subagents / slash commands are USER-chosen and can\n// carry a client name, a project codename, or an internal system's name.\n// They match a curated list fetched from aistack, with the bundled copy\n// below as the fallback.\n//\n// Model ids are exempt from all of this — see decision 3 and payload.ts.\n//\n// WHY FETCHED AND NOT ONLY BUNDLED (decision 4)\n// Third-party marketplace plugin auto-update defaults to OFF, and a\n// `plugin.json` whose `version` isn't bumped ships nothing. A bundled-only list\n// is, for an installed user, frozen forever — a Skill that becomes public next\n// month would never publish. The filtering itself still runs client-side:\n// fail-closed only means something if it happens before the send.\n\nimport { isDisplaySafeName } from \"./aggregate.js\";\nimport { BUNDLED_CURATED_ALLOWLIST } from \"./bundled-allowlist.js\";\n\n/**\n * Every built-in tool Claude Code can emit as a `tool_use` block name.\n *\n * Deliberately a literal set and not a pattern: a pattern is a denylist wearing\n * a hat. Grounded in the observed corpus (22 distinct names across 235,961\n * records) plus the documented tool surface, including tools that are deferred\n * or unavailable in most sessions — an unknown-but-real built-in withheld as a\n * count is a small loss; an unknown-and-user-named tool published verbatim is\n * the leak this whole file prevents.\n *\n * `Task` is the pre-rename spelling of `Agent`; the analyzer folds it into\n * `Agent` at ingest, so it is here only to make the set self-documenting.\n */\nexport const BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"Agent\",\n\t\"Artifact\",\n\t\"AskUserQuestion\",\n\t\"Bash\",\n\t\"BashOutput\",\n\t\"CronCreate\",\n\t\"CronDelete\",\n\t\"CronList\",\n\t\"DesignSync\",\n\t\"Edit\",\n\t\"EndConversation\",\n\t\"EnterPlanMode\",\n\t\"EnterWorktree\",\n\t\"ExitPlanMode\",\n\t\"ExitWorktree\",\n\t\"Glob\",\n\t\"Grep\",\n\t\"KillBash\",\n\t\"KillShell\",\n\t\"ListMcpResourcesTool\",\n\t\"LS\",\n\t\"Monitor\",\n\t\"MultiEdit\",\n\t\"NotebookEdit\",\n\t\"NotebookRead\",\n\t\"PushNotification\",\n\t\"Read\",\n\t\"ReadMcpResourceDirTool\",\n\t\"ReadMcpResourceTool\",\n\t\"RemoteTrigger\",\n\t\"ReportFindings\",\n\t\"ScheduleWakeup\",\n\t\"SendMessage\",\n\t\"SendUserFile\",\n\t\"Skill\",\n\t\"SlashCommand\",\n\t\"Task\",\n\t\"TaskCreate\",\n\t\"TaskGet\",\n\t\"TaskList\",\n\t\"TaskOutput\",\n\t\"TaskStop\",\n\t\"TaskUpdate\",\n\t\"TodoWrite\",\n\t\"ToolSearch\",\n\t\"WebFetch\",\n\t\"WebSearch\",\n\t\"Workflow\",\n\t\"Write\",\n]);\n\n/** The four user-chosen atom classes that need the curated list. */\nexport type CuratedAllowlist = {\n\tmcpServers: readonly string[];\n\tskills: readonly string[];\n\tsubagents: readonly string[];\n\tslashCommands: readonly string[];\n};\n\n/** The five inventory classes the payload carries. */\nexport const NAME_CATEGORIES = [\n\t\"builtinTools\",\n\t\"mcpServers\",\n\t\"skills\",\n\t\"subagents\",\n\t\"slashCommands\",\n] as const;\n\nexport type NameCategory = (typeof NAME_CATEGORIES)[number];\n\n/**\n * Names this stack's owner has explicitly ticked for publication (#42\n * decision 1), served per-stack by the authenticated half of `/api/sync-config`.\n *\n * The curated list is a convenience default, not the coverage mechanism: every\n * user-chosen name class is unbounded and unenumerable, so a hand-curated list\n * can only ever be a rounding error against the real population. Coverage comes\n * from here — from the person who knows which of their names are secret.\n *\n * `builtinTools` is included for symmetry even though that class is\n * vendor-assigned: a built-in this version of the client has never heard of is\n * kept private like anything else, and the owner can tick it.\n */\nexport type OptInNames = Record<NameCategory, readonly string[]>;\n\nexport const EMPTY_OPT_INS: OptInNames = {\n\tbuiltinTools: [],\n\tmcpServers: [],\n\tskills: [],\n\tsubagents: [],\n\tslashCommands: [],\n};\n\nexport type SyncConfig = {\n\tallowlist: CuratedAllowlist;\n\t/**\n\t * Stack-level cost preference (decision 11). When false the payload omits\n\t * cost entirely rather than zeroing it — see payload.ts.\n\t */\n\tpublishCost: boolean;\n\t/** Per-stack ticked names, unioned into the allowlist before filtering. */\n\toptIns: OptInNames;\n\t/**\n\t * Whether this stack stages its kept-private names on the web so the owner\n\t * can tick them there (#48). Off means the machine sends the payload alone\n\t * and the names never leave it.\n\t */\n\treviewKeptPrivate: boolean;\n\t/**\n\t * The stack the bearer token is bound to — where a publish would land.\n\t *\n\t * The approve gate must name its destination BEFORE the send (#33\n\t * decision 7, #41), and beat one points at `/stacks/{slug}/changes` (#48),\n\t * so both ride on the authenticated half of the config fetch. `null` when\n\t * the fetch was anonymous, failed, or the token resolved no stack — and a\n\t * gate that cannot name its destination must not publish.\n\t */\n\tstack: { name: string; slug: string } | null;\n\t/**\n\t * The auto-sync permission the STACK holds (#102, read by #103).\n\t *\n\t * Three states, not two, and the third is the whole point. `null` means no\n\t * owner has ever decided, and that is the one state a machine's local opt-in\n\t * may still seed. `{ enabled: false }` means the owner said no, and\n\t * `sync --auto` publishes nothing on this machine until they say otherwise.\n\t *\n\t * `frequencyHours` is absent when the value could not be read — see\n\t * `readAutoSync`.\n\t */\n\tautoSync: AutoSyncPermission | null;\n};\n\n/** What the stack allows, as the CLI reads it off the wire. */\nexport type AutoSyncPermission = {\n\tenabled: boolean;\n\tfrequencyHours?: number;\n};\n\n/**\n * Used when `/api/sync-config` can't be reached.\n *\n * `publishCost: false` is deliberate. The toggle is a stack-level preference we\n * do not hold locally, and the fail-closed default for a preference we can't\n * read is the one that transmits less. A user whose fetch failed sees cost\n * missing from the gate and can retry; the reverse — publishing cost the stack\n * had opted out of — is not recoverable, because the snapshot is immutable.\n */\nexport const BUNDLED_SYNC_CONFIG: SyncConfig = {\n\tallowlist: BUNDLED_CURATED_ALLOWLIST,\n\tpublishCost: false,\n\t// Empty for the same reason, and it is the load-bearing half of #42\n\t// decision 2: a failed config fetch reverts every ticked name to\n\t// kept-private. Losing the network publishes LESS, never more.\n\toptIns: EMPTY_OPT_INS,\n\t// Same direction again (#48): a machine that cannot read the switch does not\n\t// upload the names it is holding back. The default is ON server-side, so this\n\t// costs the owner one retry and never costs them a name.\n\treviewKeptPrivate: false,\n\t// No fetch, no destination — and the gate refuses to publish without one.\n\tstack: null,\n\t// No fetch, no permission either. This costs nothing on its own: `stack` is\n\t// null in the same breath, so the stage blocks before any publish.\n\tautoSync: null,\n};\n\n// ---------------------------------------------------------------------------\n// Fetch\n// ---------------------------------------------------------------------------\n\nconst SYNC_CONFIG_PATH = \"/api/sync-config\";\nconst FETCH_TIMEOUT_MS = 5_000;\n\nexport type SyncConfigSource = \"fetched\" | \"bundled\";\n\nexport type LoadedSyncConfig = {\n\tconfig: SyncConfig;\n\tsource: SyncConfigSource;\n\t/** Present when the fetch failed and the bundled copy was used. */\n\terror?: string;\n};\n\n/**\n * A name arriving from the network is no more trusted than one from a\n * transcript. Names are matched by exact equality, so a hostile list can widen\n * what publishes but can never smuggle a wildcard — and the approve gate\n * renders every name that will publish, which is what defuses that residual\n * trust (decision 4). Charset and length are still bounded so a pathological\n * entry can't reach a terminal or a database column.\n */\nconst CURATED_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9 ._:@/-]{0,63}$/;\n\nfunction readNameList(v: unknown): string[] {\n\tif (!Array.isArray(v)) return [];\n\tconst out: string[] = [];\n\tfor (const item of v) {\n\t\tif (typeof item === \"string\" && CURATED_NAME_RE.test(item)) out.push(item);\n\t}\n\treturn out;\n}\n\n/**\n * Opt-ins are read against a LOOSER bar than the curated list.\n *\n * A curated entry is ours and conventional, so the tight charset costs nothing.\n * An opt-in is the user's own name — `(default)`, an accented word, a CJK skill\n * — and dropping it here would silently un-tick a decision they made at the\n * gate. The bar that survives is the one that matters for a string we print and\n * store: no control characters, no bidi overrides, bounded length.\n */\nfunction readOptInList(v: unknown): string[] {\n\tif (!Array.isArray(v)) return [];\n\tconst out: string[] = [];\n\tfor (const item of v) {\n\t\tif (typeof item === \"string\" && isDisplaySafeName(item)) out.push(item);\n\t}\n\treturn out;\n}\n\nfunction readOptIns(v: unknown): OptInNames {\n\tif (typeof v !== \"object\" || v === null || Array.isArray(v))\n\t\treturn EMPTY_OPT_INS;\n\tconst obj = v as Record<string, unknown>;\n\treturn {\n\t\tbuiltinTools: readOptInList(obj.builtinTools),\n\t\tmcpServers: readOptInList(obj.mcpServers),\n\t\tskills: readOptInList(obj.skills),\n\t\tsubagents: readOptInList(obj.subagents),\n\t\tslashCommands: readOptInList(obj.slashCommands),\n\t};\n}\n\n/**\n * A slug becomes a URL path segment the gate prints, so it gets the tightest\n * bar of any string here. The name is display text and gets `isDisplaySafeName`.\n */\nconst STACK_SLUG_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;\n\nfunction readStack(v: unknown): SyncConfig[\"stack\"] {\n\tif (typeof v !== \"object\" || v === null || Array.isArray(v)) return null;\n\tconst obj = v as Record<string, unknown>;\n\tif (typeof obj.name !== \"string\" || !isDisplaySafeName(obj.name)) return null;\n\tif (typeof obj.slug !== \"string\" || !STACK_SLUG_RE.test(obj.slug))\n\t\treturn null;\n\treturn { name: obj.name, slug: obj.slug };\n}\n\n/**\n * Read the stack's auto-sync permission (#103).\n *\n * ABSENT AND OFF ARE DIFFERENT ANSWERS. Absent — the key is missing or null —\n * means no owner has decided, and only that lets a local flag seed the server.\n * A value that is PRESENT but unreadable is not that state: a permission the\n * machine cannot read is a permission it does not hold, so it reads as off.\n * The frequency is left out there rather than guessed, because off keeps no\n * schedule and a made-up number would outlive the garbled value that caused it.\n */\nfunction readAutoSync(v: unknown): AutoSyncPermission | null {\n\tif (v === undefined || v === null) return null;\n\tif (typeof v !== \"object\" || Array.isArray(v)) return { enabled: false };\n\tconst obj = v as Record<string, unknown>;\n\tif (typeof obj.enabled !== \"boolean\") return { enabled: false };\n\tif (\n\t\ttypeof obj.frequencyHours !== \"number\" ||\n\t\t!Number.isFinite(obj.frequencyHours)\n\t)\n\t\treturn { enabled: false };\n\treturn { enabled: obj.enabled, frequencyHours: obj.frequencyHours };\n}\n\nfunction readSyncConfig(raw: unknown): SyncConfig | null {\n\tif (typeof raw !== \"object\" || raw === null || Array.isArray(raw))\n\t\treturn null;\n\tconst obj = raw as Record<string, unknown>;\n\tconst listRaw = obj.allowlist;\n\tif (typeof listRaw !== \"object\" || listRaw === null) return null;\n\tconst list = listRaw as Record<string, unknown>;\n\treturn {\n\t\tallowlist: {\n\t\t\tmcpServers: readNameList(list.mcpServers),\n\t\t\tskills: readNameList(list.skills),\n\t\t\tsubagents: readNameList(list.subagents),\n\t\t\tslashCommands: readNameList(list.slashCommands),\n\t\t},\n\t\t// Anything other than an explicit `true` fails closed.\n\t\tpublishCost: obj.publishCost === true,\n\t\t// Absent means \"no stack resolved\" — an anonymous fetch, or a token bound\n\t\t// to nothing. Both fail closed to publishing no user-chosen names.\n\t\toptIns: readOptIns(obj.optIns),\n\t\t// Anything other than an explicit `true` keeps the names on the machine.\n\t\treviewKeptPrivate: obj.reviewKeptPrivate === true,\n\t\tstack: readStack(obj.stack),\n\t\tautoSync: readAutoSync(obj.autoSync),\n\t};\n}\n\n/**\n * Fetch the curated allowlist and the cost preference, falling back to the\n * bundled copy on any failure. Never throws — an unreachable aistack must not\n * prevent a local analysis from running, it must only narrow what could publish.\n */\nexport async function loadSyncConfig(opts: {\n\tbaseUrl: string;\n\t/**\n\t * Bearer for the authenticated half: `publishCost`, `optIns`,\n\t * `reviewKeptPrivate` and the destination stack. Absent, the server answers\n\t * with the anonymous fail-closed body — same allowlist, everything else off.\n\t */\n\ttoken?: string;\n\tfetchImpl?: typeof fetch;\n\ttimeoutMs?: number;\n}): Promise<LoadedSyncConfig> {\n\tconst doFetch = opts.fetchImpl ?? fetch;\n\ttry {\n\t\tconst res = await doFetch(`${opts.baseUrl}${SYNC_CONFIG_PATH}`, {\n\t\t\tsignal: AbortSignal.timeout(opts.timeoutMs ?? FETCH_TIMEOUT_MS),\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/json\",\n\t\t\t\t...(opts.token ? { Authorization: `Bearer ${opts.token}` } : {}),\n\t\t\t},\n\t\t});\n\t\tif (!res.ok) {\n\t\t\treturn {\n\t\t\t\tconfig: BUNDLED_SYNC_CONFIG,\n\t\t\t\tsource: \"bundled\",\n\t\t\t\terror: `sync-config returned ${res.status}`,\n\t\t\t};\n\t\t}\n\t\tconst parsed = readSyncConfig(await res.json());\n\t\tif (!parsed) {\n\t\t\treturn {\n\t\t\t\tconfig: BUNDLED_SYNC_CONFIG,\n\t\t\t\tsource: \"bundled\",\n\t\t\t\terror: \"sync-config response was not the expected shape\",\n\t\t\t};\n\t\t}\n\t\treturn { config: parsed, source: \"fetched\" };\n\t} catch (err) {\n\t\treturn {\n\t\t\tconfig: BUNDLED_SYNC_CONFIG,\n\t\t\tsource: \"bundled\",\n\t\t\terror: err instanceof Error ? err.message : \"sync-config fetch failed\",\n\t\t};\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Filtering\n// ---------------------------------------------------------------------------\n\nexport type Atom = { name: string; count: number };\n\n/**\n * One observed name that will NOT publish, as the approve gate needs to render\n * it: the raw string, how often it ran, and the plugin it came from.\n *\n * Local only — this never enters the payload. It exists because the gate offers\n * every kept-private name as an explicit, default-off tick (#42 decision 1), and\n * it cannot offer what the analyzer does not hand back.\n */\nexport type KeptPrivateAtom = {\n\tname: string;\n\tcount: number;\n\t/** Plugin prefix, for the gate's grouped bulk tick. `null` when standalone. */\n\tgroup: string | null;\n};\n\nexport type FilteredAtoms = {\n\t/** Publishable names, ordered by count descending. */\n\tallowed: Atom[];\n\t/** The rest, with everything the gate needs to offer them as ticks. */\n\tkeptPrivate: KeptPrivateAtom[];\n\t/** How many DISTINCT names were kept private. */\n\twithheld: number;\n};\n\n/**\n * A server an MCP plugin provides is observed as `plugin_<plugin>_<server>`.\n *\n * That whole string is GENERATED by Claude Code — the user typed none of it —\n * which is a different class from a hand-edited `.mcp.json` alias. Strip the\n * wrapper before matching (#42 decision 5).\n *\n * The split takes the FIRST underscore-free segment as the plugin name. A plugin\n * whose own name carries an underscore therefore splits wrong, the inner segment\n * matches nothing, and the raw name stays kept private — the same direction\n * every other miss fails in.\n */\nconst PLUGIN_MCP_RE = /^plugin_([^_]+)_(.+)$/;\n\n/** `plugin:artifact` is the convention for a plugin's skills and subagents. */\nconst PLUGIN_PREFIX_RE = /^([^:\\s]+):(.+)$/;\n\n/**\n * The plugin a name came from, for the gate's grouped bulk tick.\n *\n * Grouping is a UI affordance only. What the gate STORES is every name in the\n * group, expanded (#42 decision 3): a stored `alp-river:*` would be a standing\n * grant to names that do not exist yet, and nobody can consent to a name they\n * have not thought of.\n */\nexport function pluginGroup(name: string): string | null {\n\treturn (\n\t\tPLUGIN_MCP_RE.exec(name)?.[1] ?? PLUGIN_PREFIX_RE.exec(name)?.[1] ?? null\n\t);\n}\n\nexport type FilterSets = {\n\t/** Curated list UNION this stack's opt-ins. A match here publishes verbatim. */\n\tpublishable: ReadonlySet<string>;\n\t/**\n\t * The curated list alone — the only target normalization may match.\n\t *\n\t * This is what makes the normalization safe to state in one line:\n\t * normalization can only ever emit a string that is already curated. The\n\t * blast radius of a bug in it is an already-vetted set, by construction.\n\t */\n\tcurated: ReadonlySet<string>;\n};\n\n/**\n * Resolve the name an atom would publish under, or `null` to keep it private.\n *\n * Raw match first, so a name the owner ticked publishes exactly as they saw it\n * at the gate. Only an unmatched name is normalized, and only against the\n * curated list.\n */\nfunction publishedName(name: string, sets: FilterSets): string | null {\n\tif (sets.publishable.has(name)) return name;\n\tconst inner = PLUGIN_MCP_RE.exec(name)?.[2];\n\tif (inner && sets.curated.has(inner)) return inner;\n\treturn null;\n}\n\n/**\n * Split observed atoms into what publishes and what stays on the machine.\n *\n * The withheld figure counts distinct names, not calls: it answers \"how much of\n * my inventory is not shown\", which is the honesty question, without leaking\n * how heavily any single kept-private thing is used.\n *\n * Counts are merged by PUBLISHED name, because normalization can map two\n * observed names onto one — a plugin-provided `chrome-devtools` and a directly\n * configured one both publish as `chrome-devtools`, and two rows with the same\n * name would double-count that server in the rendered inventory.\n */\nexport function filterAtoms(\n\tatoms: readonly Atom[],\n\tsets: FilterSets,\n): FilteredAtoms {\n\tconst merged = new Map<string, number>();\n\tconst keptPrivate: KeptPrivateAtom[] = [];\n\tfor (const atom of atoms) {\n\t\tconst published = publishedName(atom.name, sets);\n\t\tif (published === null) {\n\t\t\tkeptPrivate.push({\n\t\t\t\tname: atom.name,\n\t\t\t\tcount: atom.count,\n\t\t\t\tgroup: pluginGroup(atom.name),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tmerged.set(published, (merged.get(published) ?? 0) + atom.count);\n\t}\n\tconst allowed = [...merged].map(([name, count]) => ({ name, count }));\n\tallowed.sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));\n\tkeptPrivate.sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));\n\treturn { allowed, keptPrivate, withheld: keptPrivate.length };\n}\n","// \"Is this harness alive?\" — the detection primitive behind every adapter's\n// `detect()` (#101, decided in #100).\n//\n// A directory that merely exists proves nothing: a Claude Code install from\n// months ago leaves its transcript root behind forever, and detection keyed on\n// that root scanned it, published a dead snapshot next to a live one, and asked\n// its owner to connect a harness they had stopped using.\n//\n// So detection asks the same question the scan asks: did this harness write\n// anything inside the rolling window? It answers with `stat` calls only —\n// nothing is opened, nothing is parsed. A live harness answers on the first\n// recent file it meets, which is usually the first file it meets. A dead\n// harness pays a full walk of stats to say no, which is the cost the old\n// `exists()` check saved and the reason the answer was wrong.\n//\n// Directory mtimes cannot prune this walk. `claude/scan.ts` verified why:\n// appending to a file does not move its parent directory's mtime, so a resumed\n// session writes in-window records into a directory that looks untouched.\n\nimport type { Dirent, Stats } from \"node:fs\";\nimport { readdir, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport type RecencyOptions = {\n\t/** Override the directory reader. Tests only. */\n\treaddirImpl?: (dir: string) => Promise<Dirent[]>;\n\t/** Override the stat call. Tests only. */\n\tstatImpl?: (file: string) => Promise<Stats>;\n};\n\n/**\n * True when any file under `roots` whose basename passes `matches` was modified\n * at or after `sinceMs`. Unreadable directories and files are silence, not an\n * error — the same fail-quiet rule the scanners hold, and for the same reason:\n * the error object carries the absolute path.\n */\nexport async function hasRecentFile(\n\troots: readonly string[],\n\tmatches: (basename: string) => boolean,\n\tsinceMs: number,\n\topts: RecencyOptions = {},\n): Promise<boolean> {\n\tconst readDir =\n\t\topts.readdirImpl ??\n\t\t((dir: string) => readdir(dir, { withFileTypes: true }));\n\tconst statFile = opts.statImpl ?? stat;\n\n\tconst seen = new Set<string>();\n\tconst pending: string[] = [...roots];\n\twhile (pending.length > 0) {\n\t\tconst dir = pending.pop() as string;\n\t\tif (seen.has(dir)) continue;\n\t\tseen.add(dir);\n\n\t\tlet entries: Dirent[];\n\t\ttry {\n\t\t\tentries = await readDir(dir);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const e of entries) {\n\t\t\tconst full = path.join(dir, e.name);\n\t\t\tif (e.isDirectory()) {\n\t\t\t\tpending.push(full);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!e.isFile() || !matches(e.name)) continue;\n\t\t\ttry {\n\t\t\t\tconst st = await statFile(full);\n\t\t\t\tif (st.mtimeMs >= sinceMs) return true;\n\t\t\t} catch {\n\t\t\t\t/* unreadable file — it proves nothing either way */\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n","// Pure fold over parsed Claude Code transcript records. No I/O, no console.\n//\n// Wayfinder ticket #37 (map #29), productizing the #32 prototype. Field\n// semantics come from docs/research/claude-code-transcripts-2026-07.md (#30),\n// as corrected by #32 and #33. Every field is treated as untrusted and\n// optional: records arrive as `unknown` and are narrowed here.\n//\n// The shared aggregate/finalize machinery lives in ../shared/aggregate.ts\n// (#67); this file owns what is CLAUDE-specific — the record shapes, and the\n// response dedup below.\n//\n// THE LOAD-BEARING SUBTLETY — read before touching `ingestAssistant`.\n// Claude Code writes ONE API response as SEVERAL JSONL records: each carries a\n// distinct content block (thinking, then tool_use, then tool_use...) and a\n// *cumulative* `usage` snapshot that grows with each record. Measured on a real\n// corpus: 20,073 of 44,280 response groups have differing usage across their\n// records, 20,071 of them monotonically increasing.\n//\n// So there are three wrong ways to count and one right way:\n// - sum every record -> ~2x over\n// - keep the first record -> ~2.1x under\n// - keep the last record -> right, but relies on file order\n// - keep the largest total -> right, order-independent <- this\n// Keeping the largest total is also ccusage's documented rule\n// (`should_replace_deduped_entry`).\n//\n// THE SECOND SUBTLETY — cost accumulates HERE, not in `finalize`.\n// Decision 8 of #33 made pricing time-aware, so a response is priced at the\n// rate in effect at its own timestamp. Summing tokens per model and pricing\n// once at the end cannot express a mid-window rate change, so each response's\n// cost is computed as it is ingested and un-applied on replace.\n\nimport {\n\tapiEquivalentCost,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"@aistack/pricing\";\nimport {\n\tasArr,\n\tasName,\n\tasNum,\n\tasObj,\n\tasStr,\n\tbump,\n\tcleanName,\n\tcountsTotal,\n\tcreateAggregate as createSharedAggregate,\n\temptyUsage,\n\ttype Obj,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\n\n// Re-exported for the existing import sites (tests, stage, summary); the\n// definitions moved to ../shared/aggregate.ts in #67.\nexport {\n\tcleanName,\n\ttype Finalized,\n\tfinalize,\n\tisDisplaySafeName,\n\ttype ModelRow,\n\ttype ModelUsage,\n\tnewestVersion,\n} from \"../shared/aggregate.js\";\n\ntype Entry = {\n\tmodelKey: string;\n\tcounts: TokenCounts;\n\t/** `null` = no rate applied at this response's timestamp. */\n\tcostUSD: number | null;\n};\n\n/** One API response's full contribution, kept so it can be un-applied on replace. */\ntype Contribution = {\n\tentries: Entry[];\n\ttotal: number;\n\tsidechain: boolean;\n\twebSearch: number;\n\twebFetch: number;\n\t/** Iteration types that mirrored top-level usage, for the diagnostics line. */\n\tmirroredIterationTypes: Array<[string, number]>;\n\t/** Iterations naming a different model, attributed to that model (#33 dec. 9). */\n\tfallbackAttempts: number;\n\t/** Mirror-suspected iterations with no `model` field — skipped, not billed. */\n\tuntypedMirrors: number;\n};\n\ntype SeenEntry = { requestId: string | null; contribution: Contribution };\n\n/**\n * The Claude adapter's aggregate: the shared fold target, with `seen` keyed\n * by `message.id` holding this adapter's replay/continuation bookkeeping.\n */\nexport type Aggregate = SharedAggregate<SeenEntry>;\n\nexport function createAggregate(): Aggregate {\n\treturn createSharedAggregate<SeenEntry>();\n}\n\n// ---------------------------------------------------------------------------\n// Ingest\n// ---------------------------------------------------------------------------\n\nexport type IngestContext = { projectDir: string };\n\n/** Fold one parsed JSONL record into the aggregate. */\nexport function ingestRecord(\n\tagg: Aggregate,\n\traw: unknown,\n\tctx: IngestContext,\n): void {\n\tconst rec = asObj(raw);\n\tif (!rec) return;\n\n\tagg.records++;\n\tagg.projectDirs.add(ctx.projectDir);\n\n\tconst version = asStr(rec.version);\n\tif (version) agg.ccVersions.add(cleanName(version));\n\tconst sessionId = asStr(rec.sessionId);\n\tif (sessionId) agg.sessions.add(sessionId);\n\n\tlet tsMs: number | null = null;\n\tconst timestamp = asStr(rec.timestamp);\n\tif (timestamp) {\n\t\tconst ts = Date.parse(timestamp);\n\t\tif (!Number.isNaN(ts)) {\n\t\t\ttsMs = ts;\n\t\t\tagg.activeDays.add(timestamp.slice(0, 10));\n\t\t\tagg.firstTs = agg.firstTs === null ? ts : Math.min(agg.firstTs, ts);\n\t\t\tagg.lastTs = agg.lastTs === null ? ts : Math.max(agg.lastTs, ts);\n\t\t}\n\t}\n\n\tconst type = asStr(rec.type);\n\tif (type === \"assistant\") ingestAssistant(agg, rec, tsMs);\n\telse if (type === \"user\") ingestUser(agg, rec);\n}\n\nfunction ingestAssistant(agg: Aggregate, rec: Obj, tsMs: number | null): void {\n\tagg.assistantRecords++;\n\tconst msg = asObj(rec.message);\n\tif (!msg) return;\n\n\tconst messageId = asStr(msg.id);\n\tconst requestId = asStr(rec.requestId);\n\tconst existing = messageId === null ? undefined : agg.seen.get(messageId);\n\t// A genuine replay is the same message.id under a NEW requestId. Its records\n\t// repeat content already counted; a continuation's records do not.\n\tconst isReplay = existing !== undefined && existing.requestId !== requestId;\n\n\t// Content blocks are counted per RECORD, deliberately outside the token\n\t// fold: the records of ONE response carry disjoint blocks (verified across\n\t// 44,478 groups — zero overlap), so folding them would drop real blocks.\n\t// Replays are the exception and must be skipped, because `tool_use` has\n\t// `block.id` to dedup on but thinking/text blocks have no identity at all.\n\tif (!isReplay) ingestContentBlocks(agg, msg.content);\n\n\tconst usage = asObj(msg.usage);\n\tif (!usage) return;\n\n\tconst model = asName(msg.model) ?? \"(unknown)\";\n\t// `<synthetic>` is the harness's own pseudo-model for records it generates\n\t// itself. Not a tool the user chose — excluded from inventory and pricing,\n\t// but its tokens are surfaced rather than silently dropped.\n\tif (model.startsWith(\"<\")) {\n\t\tagg.syntheticRecords++;\n\t\tagg.syntheticTokens += countsTotal(readCounts(usage));\n\t\treturn;\n\t}\n\n\tif (tsMs === null) agg.untimestampedResponses++;\n\n\tconst sidechain = rec.isSidechain === true;\n\tconst contribution = buildContribution(usage, model, sidechain, tsMs);\n\n\tif (messageId === null) {\n\t\t// No dedup key available — count it and record that we were unprotected.\n\t\tagg.unkeyedResponses++;\n\t\tacceptContribution(agg, contribution);\n\t\treturn;\n\t}\n\n\tif (existing === undefined) {\n\t\tagg.distinctResponses++;\n\t\tacceptContribution(agg, contribution);\n\t\tagg.seen.set(messageId, { requestId, contribution });\n\t\treturn;\n\t}\n\n\tif (isReplay) agg.realReplaysFolded++;\n\telse agg.continuationsFolded++;\n\n\tif (!supersedes(contribution, existing.contribution)) return;\n\n\tagg.supersededByLarger++;\n\tretractContribution(agg, existing.contribution);\n\tacceptContribution(agg, contribution);\n\t// Keep the FIRST-seen requestId, not this record's. If a genuine replay wins\n\t// on tokens, overwriting it would make the replay's own later records compare\n\t// equal to the stored id, read as continuations, and get their thinking/text\n\t// blocks counted a second time — reopening exactly what the `isReplay` gate\n\t// above exists to close. (tool_use survives either way via `block.id`.)\n\tagg.seen.set(messageId, { requestId: existing.requestId, contribution });\n}\n\n/**\n * Apply a contribution and tally its diagnostics. Paired with\n * `retractContribution` so every per-response census stays per-RESPONSE rather\n * than per-record — these used to be bumped while merely *building* a\n * contribution, which counted every folded continuation too.\n */\nfunction acceptContribution(agg: Aggregate, c: Contribution): void {\n\tapplyContribution(agg, c, +1);\n}\n\nfunction retractContribution(agg: Aggregate, c: Contribution): void {\n\tapplyContribution(agg, c, -1);\n}\n\n/**\n * ccusage's collision rule: a non-sidechain copy beats a sidechain one;\n * otherwise the larger token total wins. Order-independent by construction,\n * so the result does not depend on filesystem traversal order.\n */\nfunction supersedes(next: Contribution, prev: Contribution): boolean {\n\tif (prev.sidechain !== next.sidechain)\n\t\treturn prev.sidechain && !next.sidechain;\n\treturn next.total > prev.total;\n}\n\nfunction readCounts(usage: Obj): TokenCounts {\n\tconst t: TokenCounts = {\n\t\tinput: asNum(usage.input_tokens),\n\t\toutput: asNum(usage.output_tokens),\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: 0,\n\t\tcacheRead: asNum(usage.cache_read_input_tokens),\n\t};\n\tconst cacheWriteTotal = asNum(usage.cache_creation_input_tokens);\n\tconst cc = asObj(usage.cache_creation);\n\tif (cc) {\n\t\tt.cacheWrite5m = asNum(cc.ephemeral_5m_input_tokens);\n\t\tt.cacheWrite1h = asNum(cc.ephemeral_1h_input_tokens);\n\t\tconst residual = cacheWriteTotal - (t.cacheWrite5m + t.cacheWrite1h);\n\t\tif (residual > 0) t.cacheWriteUnsplit = residual;\n\t} else {\n\t\tt.cacheWriteUnsplit = cacheWriteTotal;\n\t}\n\treturn t;\n}\n\n/** `usage.speed === \"fast\"` prices under a separate, higher rate. */\nfunction modelKeyFor(model: string, speed: string | null): string {\n\treturn normalizeModel(speed === \"fast\" ? `${model}#fast` : model);\n}\n\nfunction makeEntry(\n\tmodelKey: string,\n\tcounts: TokenCounts,\n\ttsMs: number | null,\n): Entry {\n\treturn {\n\t\tmodelKey,\n\t\tcounts,\n\t\tcostUSD: apiEquivalentCost(modelKey, counts, tsMs),\n\t};\n}\n\nfunction buildContribution(\n\tusage: Obj,\n\tmodel: string,\n\tsidechain: boolean,\n\ttsMs: number | null,\n): Contribution {\n\tconst modelKey = modelKeyFor(model, asStr(usage.speed));\n\tconst entries: Entry[] = [makeEntry(modelKey, readCounts(usage), tsMs)];\n\tconst mirrored = new Map<string, number>();\n\tlet fallbackAttempts = 0;\n\tlet untypedMirrors = 0;\n\n\tfor (const rawIt of asArr(usage.iterations)) {\n\t\tconst it = asObj(rawIt);\n\t\tif (!it) continue;\n\t\tconst itType = asName(it.type) ?? \"(untyped)\";\n\t\tconst itModel = asName(it.model);\n\t\tconst itKey =\n\t\t\titModel === null ? null : modelKeyFor(itModel, asStr(it.speed));\n\n\t\t// Advisor iterations are a genuinely separate billed call under their own\n\t\t// model, never a mirror of top-level usage (ccusage prices them apart).\n\t\tif (itType === \"advisor_message\") {\n\t\t\tentries.push(makeEntry(itKey ?? modelKey, readCounts(it), tsMs));\n\t\t\tcontinue;\n\t\t}\n\n\t\t// #33 decision 9, SHARPENED — read the whole comment before touching this.\n\t\t//\n\t\t// The prototype skipped EVERY `type: \"message\"` iteration as a mirror of\n\t\t// top-level usage, which was correct by luck rather than construction: a\n\t\t// real `fallback_message` record showed top-level usage equal to the\n\t\t// fallback iteration EXACTLY, while a sibling `type: message` iteration\n\t\t// named a DIFFERENT model and carried tokens recorded nowhere else. So\n\t\t// `message.model` is already the serving model, and the mirror test is the\n\t\t// MODEL, not the type.\n\t\t//\n\t\t// #33 phrased the fix as \"skip it only when `iter.model === message.model`\".\n\t\t// Taken literally that is a ~2x overcount, because the corpus says the\n\t\t// `model` field is almost never there: of 63,638 non-advisor iterations,\n\t\t// 63,634 carry NO `model` at all — and all 63,634 are byte-exact mirrors of\n\t\t// their record's top-level usage (measured: zero differ). They carry 7.24\n\t\t// BILLION tokens, nearly double the corpus total, so attributing them as\n\t\t// separate entries would roughly double both tokens and cost. Only 8\n\t\t// iterations name a model: 4 matching (the `fallback_message` entries) and\n\t\t// 4 differing (the real first attempts).\n\t\t//\n\t\t// So the operative rule is: SKIP UNLESS THE ITERATION NAMES A DIFFERENT\n\t\t// MODEL. Absent is treated as matching — mis-attributing is a double-bill,\n\t\t// skipping is at worst an undercount, and the measurement above says it is\n\t\t// not even that.\n\t\tif (itKey === null) {\n\t\t\tuntypedMirrors++;\n\t\t\tbump(mirrored, itType);\n\t\t\tcontinue;\n\t\t}\n\t\tif (itKey === modelKey) {\n\t\t\tbump(mirrored, itType);\n\t\t\tcontinue;\n\t\t}\n\t\tentries.push(makeEntry(itKey, readCounts(it), tsMs));\n\t\tfallbackAttempts++;\n\t}\n\n\tconst serverTools = asObj(usage.server_tool_use);\n\treturn {\n\t\tentries,\n\t\ttotal: entries.reduce((a, e) => a + countsTotal(e.counts), 0),\n\t\tsidechain,\n\t\twebSearch: serverTools ? asNum(serverTools.web_search_requests) : 0,\n\t\twebFetch: serverTools ? asNum(serverTools.web_fetch_requests) : 0,\n\t\tmirroredIterationTypes: [...mirrored],\n\t\tfallbackAttempts,\n\t\tuntypedMirrors,\n\t};\n}\n\n/** Add (sign +1) or remove (sign -1) a response's contribution from the totals. */\nfunction applyContribution(\n\tagg: Aggregate,\n\tc: Contribution,\n\tsign: 1 | -1,\n): void {\n\tc.entries.forEach(({ modelKey, counts, costUSD }, i) => {\n\t\tlet m = agg.byModel.get(modelKey);\n\t\tif (!m) {\n\t\t\tm = emptyUsage();\n\t\t\tagg.byModel.set(modelKey, m);\n\t\t}\n\t\t// One response is one message, even when a fallback attempt or an advisor\n\t\t// iteration attributes tokens to a second model — counting per entry would\n\t\t// inflate the response total past distinctResponses.\n\t\tif (i === 0) m.messages += sign;\n\t\tm.input += sign * counts.input;\n\t\tm.output += sign * counts.output;\n\t\tm.cacheWrite5m += sign * counts.cacheWrite5m;\n\t\tm.cacheWrite1h += sign * counts.cacheWrite1h;\n\t\tm.cacheWriteUnsplit += sign * counts.cacheWriteUnsplit;\n\t\tm.cacheRead += sign * counts.cacheRead;\n\t\tif (costUSD === null) m.unpricedTokens += sign * countsTotal(counts);\n\t\telse m.costUSD += sign * costUSD;\n\t});\n\tif (c.sidechain) agg.sidechainTokens += sign * c.total;\n\telse agg.mainTokens += sign * c.total;\n\tagg.webSearchRequests += sign * c.webSearch;\n\tagg.webFetchRequests += sign * c.webFetch;\n\tagg.fallbackAttempts += sign * c.fallbackAttempts;\n\tagg.untypedMirrors += sign * c.untypedMirrors;\n\tfor (const [type, count] of c.mirroredIterationTypes) {\n\t\tbump(agg.mirroredIterationTypes, type, sign * count);\n\t}\n}\n\nfunction ingestContentBlocks(agg: Aggregate, content: unknown): void {\n\tfor (const rawBlock of asArr(content)) {\n\t\tconst block = asObj(rawBlock);\n\t\tif (!block) continue;\n\t\tconst type = asStr(block.type);\n\t\tif (type === \"thinking\") agg.thinkingBlocks++;\n\t\telse if (type === \"text\") agg.textBlocks++;\n\t\telse if (type === \"tool_use\") ingestToolUse(agg, block);\n\t}\n}\n\nfunction ingestToolUse(agg: Aggregate, block: Obj): void {\n\tconst name = asName(block.name);\n\tif (!name) return;\n\n\t// `toolu_...` block ids are globally unique, which makes this key both\n\t// collision-proof and replay-proof without a record-level prefix. A block\n\t// with no id is skipped rather than folded under a name-only key, which\n\t// would silently collapse every call to that tool into one.\n\tconst blockId = asStr(block.id);\n\tif (!blockId) {\n\t\tagg.toolBlocksWithoutId++;\n\t\treturn;\n\t}\n\tif (agg.toolCallDedup.has(blockId)) return;\n\tagg.toolCallDedup.add(blockId);\n\n\tconst input = asObj(block.input) ?? {};\n\n\tif (name.startsWith(\"mcp__\")) {\n\t\tconst parts = name.slice(\"mcp__\".length).split(\"__\");\n\t\tbump(agg.mcpServerCalls, parts[0] || \"(unknown)\");\n\t\tbump(agg.mcpToolCalls, name);\n\t\treturn;\n\t}\n\tif (name === \"Skill\") {\n\t\tbump(agg.skillCalls, asName(input.skill) ?? \"(unnamed)\");\n\t\tbump(agg.toolCalls, \"Skill\");\n\t\treturn;\n\t}\n\t// `Task` is the pre-rename spelling of `Agent`.\n\tif (name === \"Agent\" || name === \"Task\") {\n\t\tbump(agg.subagentCalls, asName(input.subagent_type) ?? \"(default)\");\n\t\tbump(agg.toolCalls, \"Agent\");\n\t\treturn;\n\t}\n\tbump(agg.toolCalls, name);\n}\n\nconst SLASH_RE = /<command-name>\\/?([^<\\n\\r]{1,64})<\\/command-name>/g;\n\nfunction ingestUser(agg: Aggregate, rec: Obj): void {\n\tconst msg = asObj(rec.message);\n\tif (!msg) return;\n\tconst content = msg.content;\n\n\tlet text = \"\";\n\tif (typeof content === \"string\") text = content;\n\telse {\n\t\tfor (const rawBlock of asArr(content)) {\n\t\t\tconst block = asObj(rawBlock);\n\t\t\tif (!block) continue;\n\t\t\tif (asStr(block.type) === \"text\") text += asStr(block.text) ?? \"\";\n\t\t}\n\t}\n\tif (!text.includes(\"<command-name>\")) return;\n\n\t// `matchAll` over `exec` in a loop: the regex is module-level and `g`-flagged,\n\t// so an `exec` loop carries a shared `lastIndex` that a forgotten reset turns\n\t// into records being skipped at random.\n\tfor (const match of text.matchAll(SLASH_RE)) {\n\t\tbump(agg.slashCommands, cleanName(match[1]));\n\t}\n}\n","// I/O shell around the pure analyzer: find transcript roots, stream JSONL, hand\n// each parsed record to ingestRecord. Nothing leaves this machine.\n//\n// Wayfinder ticket #37 (map #29).\n//\n// STANDING NON-GOAL (locked in #13): raw transcripts, prompts, absolute paths,\n// and repo names never leave the machine. Two consequences visible here:\n// project directories are counted but their names never escape this module, and\n// read errors are swallowed rather than thrown, because the error object carries\n// the absolute path and the munged project directory.\n\nimport { createReadStream, type Dirent } from \"node:fs\";\nimport { readdir, realpath, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport readline from \"node:readline\";\n\nimport {\n\temptyScanStats,\n\ttype ScanStats,\n\twindowStartMs,\n} from \"../shared/window.js\";\nimport { type Aggregate, ingestRecord } from \"./analyzer.js\";\n\nexport { type ScanStats, windowStartMs };\n\n/** Discovery order mirrors ccusage's adapter: CLAUDE_CONFIG_DIR, then the defaults. */\nexport function transcriptRoots(): string[] {\n\tconst env = process.env.CLAUDE_CONFIG_DIR;\n\tif (env) {\n\t\treturn env\n\t\t\t.split(\",\")\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean)\n\t\t\t.map((s) => path.join(s, \"projects\"));\n\t}\n\tconst roots = [path.join(homedir(), \".claude\", \"projects\")];\n\tconst xdg = process.env.XDG_CONFIG_HOME ?? path.join(homedir(), \".config\");\n\troots.push(path.join(xdg, \"claude\", \"projects\"));\n\treturn roots;\n}\n\n/** What counts as a Claude Code transcript. Shared with `detect` (#101). */\nexport function isTranscriptFile(basename: string): boolean {\n\treturn basename.endsWith(\".jsonl\");\n}\n\n/** Recursive *.jsonl walk — the nested `<sessionId>/subagents/` layout is real. */\nasync function* walkJsonl(dir: string): AsyncGenerator<string> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const e of entries) {\n\t\tconst full = path.join(dir, e.name);\n\t\tif (e.isDirectory()) yield* walkJsonl(full);\n\t\telse if (e.isFile() && isTranscriptFile(e.name)) yield full;\n\t}\n}\n\nexport type ScanOptions = {\n\t/** Only ingest records with a timestamp at or after this epoch ms. */\n\tsinceMs?: number;\n\tonProgress?: (files: number) => void;\n\t/** Override the discovered roots. Tests only. */\n\troots?: string[];\n};\n\n/**\n * KNOWN PERFORMANCE FLOOR — measured, decided, deliberately not fixed.\n *\n * Enumeration walks every project directory and `realpath`+`stat`s every file\n * BEFORE the mtime filter can skip anything, so a narrow window still pays a\n * floor proportional to TOTAL history (~60 ms over 3,206 files today, growing\n * linearly). The obvious fix — prune whole project directories by directory\n * mtime — is UNSOUND, and this was verified rather than assumed: appending to a\n * file does not update its parent directory's mtime (only adding, removing, or\n * renaming an entry does). A session resumed with `--resume` appends to a\n * transcript created before the window opened, inside a directory whose mtime\n * never moves, so dir-mtime pruning would silently drop live in-window records\n * — a wrong number, which is worse than a slow one for a tool whose whole claim\n * is measured-not-claimed.\n *\n * The sound version is a persisted enumeration cache, which cuts against #33\n * decision 1 (a sync is a stateless snapshot-replace, no durable client scan\n * state). So: DEFERRED. 60 ms is two orders of magnitude under the ~3 s full\n * scan and invisible next to the send round-trip; it becomes worth revisiting\n * only when total history reaches a scale where the floor dominates.\n */\nexport async function scan(\n\tagg: Aggregate,\n\topts: ScanOptions = {},\n): Promise<ScanStats> {\n\tconst stats: ScanStats = emptyScanStats();\n\t// Roots can overlap (CLAUDE_CONFIG_DIR may repeat a dir; ~/.claude and\n\t// ~/.config/claude may be symlinked together). Without this guard the same\n\t// file is ingested twice and the record/line/block counters silently double.\n\tconst visited = new Set<string>();\n\n\tfor (const root of opts.roots ?? transcriptRoots()) {\n\t\tif (!(await exists(root))) continue;\n\t\tfor await (const file of walkJsonl(root)) {\n\t\t\tstats.filesFound++;\n\n\t\t\tlet resolved: string;\n\t\t\ttry {\n\t\t\t\tresolved = await realpath(file);\n\t\t\t} catch {\n\t\t\t\tresolved = file;\n\t\t\t}\n\t\t\tif (visited.has(resolved)) {\n\t\t\t\tstats.filesSkippedAsDuplicate++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvisited.add(resolved);\n\n\t\t\t// Transcripts are append-only and chronological, so a file untouched\n\t\t\t// since the window opened cannot hold an in-window record. This is what\n\t\t\t// makes a narrow window actually cheaper rather than merely narrower.\n\t\t\tif (opts.sinceMs !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tconst st = await stat(file);\n\t\t\t\t\tif (st.mtimeMs < opts.sinceMs) {\n\t\t\t\t\t\tstats.filesSkippedByMtime++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t/* unreadable stat — fall through and try to read it */\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Project dir = first path segment under projects/ (privacy-sensitive:\n\t\t\t// it is a munged absolute path, so it is only ever counted, never shown).\n\t\t\tconst rel = path.relative(root, file);\n\t\t\tconst projectDir = rel.split(path.sep)[0] ?? \"(root)\";\n\t\t\tagg.files++;\n\t\t\tstats.filesRead++;\n\t\t\tif (opts.onProgress && agg.files % 200 === 0) opts.onProgress(agg.files);\n\t\t\ttry {\n\t\t\t\tawait ingestFile(agg, file, projectDir, opts.sinceMs);\n\t\t\t} catch {\n\t\t\t\t// Swallow deliberately: the error object carries the absolute path.\n\t\t\t\tstats.filesUnreadable++;\n\t\t\t\tstats.filesRead--;\n\t\t\t}\n\t\t}\n\t}\n\treturn stats;\n}\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function ingestFile(\n\tagg: Aggregate,\n\tfile: string,\n\tprojectDir: string,\n\tsinceMs?: number,\n): Promise<void> {\n\tconst rl = readline.createInterface({\n\t\tinput: createReadStream(file, { encoding: \"utf8\" }),\n\t\tcrlfDelay: Number.POSITIVE_INFINITY,\n\t});\n\tfor await (const line of rl) {\n\t\tif (!line) continue;\n\t\tagg.lines++;\n\t\tlet rec: unknown;\n\t\ttry {\n\t\t\trec = JSON.parse(line);\n\t\t} catch {\n\t\t\tagg.parseErrors++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (sinceMs !== undefined) {\n\t\t\tconst ts =\n\t\t\t\trec &&\n\t\t\t\ttypeof rec === \"object\" &&\n\t\t\t\t\"timestamp\" in rec &&\n\t\t\t\ttypeof (rec as { timestamp?: unknown }).timestamp === \"string\"\n\t\t\t\t\t? Date.parse((rec as { timestamp: string }).timestamp)\n\t\t\t\t\t: Number.NaN;\n\t\t\tif (Number.isNaN(ts) || ts < sinceMs) continue;\n\t\t}\n\t\tingestRecord(agg, rec, { projectDir });\n\t}\n}\n","// The rolling window and the scan-health shape every adapter reports.\n// Shared so the payload builder and each harness scanner agree by import\n// rather than by convention (#67).\n\n/** Rolling window locked by the owner in the #32 prototype resolution. */\nexport const DEFAULT_WINDOW_DAYS = 30;\n\n/**\n * UTC midnight opening a rolling window of `days` calendar days ending on the\n * day containing `now` (inclusive). `days = 30` therefore spans today plus the\n * 29 preceding days.\n *\n * Defined once and shared by the scan filter and the payload's `window.from`, so\n * the reported window and the records actually counted cannot drift apart.\n */\nexport function windowStartMs(now: number, days: number): number {\n\tconst startOfToday = Date.UTC(\n\t\tnew Date(now).getUTCFullYear(),\n\t\tnew Date(now).getUTCMonth(),\n\t\tnew Date(now).getUTCDate(),\n\t);\n\treturn startOfToday - (days - 1) * 86_400_000;\n}\n\nexport type ScanStats = {\n\t/** Files found on disk before any window filter. */\n\tfilesFound: number;\n\t/** Files actually opened and read. */\n\tfilesRead: number;\n\t/** Files skipped because their mtime predates the window. */\n\tfilesSkippedByMtime: number;\n\t/** Files skipped because a resolved path was already scanned (overlapping roots). */\n\tfilesSkippedAsDuplicate: number;\n\t/**\n\t * Files that could not be read (permissions, or pruned mid-scan). Counted\n\t * rather than thrown: an unhandled read error would surface the absolute path\n\t * AND the munged project directory in the crash output, which is exactly what\n\t * this tool promises never to emit.\n\t */\n\tfilesUnreadable: number;\n\t/**\n\t * Files excluded because they fail the genuine-rollout fingerprint (#73):\n\t * another tool wrote them into the harness's log directory, so their usage\n\t * would distort the numbers. Codex sets this; Claude has no known impostors.\n\t */\n\tfilesForeign: number;\n\t/**\n\t * LOCAL-ONLY detail behind the counts above. `buildPayload` copies coverage\n\t * fields one by one, so nothing below can reach the wire.\n\t *\n\t * `session_meta.originator` values seen on foreign files, value → file count.\n\t */\n\tforeignOriginators: Map<string, number>;\n\t/** LOCAL-ONLY: one `{path relative to the scan root, error class}` per unreadable file. */\n\tunreadableFiles: Array<{ path: string; reason: string }>;\n\t/** Subset of `filesUnreadable`: `.zst` rollouts this Node runtime cannot decompress. */\n\tfilesZstdUnsupported: number;\n};\n\nexport function emptyScanStats(): ScanStats {\n\treturn {\n\t\tfilesFound: 0,\n\t\tfilesRead: 0,\n\t\tfilesSkippedByMtime: 0,\n\t\tfilesSkippedAsDuplicate: 0,\n\t\tfilesUnreadable: 0,\n\t\tfilesForeign: 0,\n\t\tforeignOriginators: new Map(),\n\t\tunreadableFiles: [],\n\t\tfilesZstdUnsupported: 0,\n\t};\n}\n","// The Claude Code harness behind the seam (#67). The parsing lives in\n// analyzer.ts/scan.ts, unchanged from the single-harness era; this file only\n// gives it the adapter shape.\n\nimport { BUILTIN_TOOLS } from \"../shared/allowlist.js\";\nimport { hasRecentFile } from \"../shared/recency.js\";\nimport type {\n\tHarnessAdapter,\n\tHarnessDetectOptions,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"../types.js\";\nimport { createAggregate } from \"./analyzer.js\";\nimport { isTranscriptFile, scan, transcriptRoots } from \"./scan.js\";\n\nexport const CLAUDE_HARNESS_NAME = \"claude-code\";\n\nexport const claudeAdapter: HarnessAdapter = {\n\tname: CLAUDE_HARNESS_NAME,\n\tbuiltinTools: BUILTIN_TOOLS,\n\n\tasync detect(opts: HarnessDetectOptions): Promise<boolean> {\n\t\treturn hasRecentFile(\n\t\t\topts.roots ?? transcriptRoots(),\n\t\t\tisTranscriptFile,\n\t\t\topts.sinceMs,\n\t\t);\n\t},\n\n\tasync scan(opts: HarnessScanOptions): Promise<HarnessScan> {\n\t\tconst aggregate = createAggregate();\n\t\tconst stats = await scan(aggregate, {\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.onProgress ? { onProgress: opts.onProgress } : {}),\n\t\t});\n\t\treturn { aggregate, stats };\n\t},\n};\n","// Pure fold over parsed Codex CLI rollout lines. No I/O, no console.\n//\n// Wayfinder ticket #67 (map #60). Field semantics come from\n// docs/research/codex-session-log-anatomy-2026-08.md (#65) as locked by the\n// wire-format grilling #66. Every field is untrusted and optional: lines\n// arrive as `unknown` and are narrowed here.\n//\n// THE LOAD-BEARING SUBTLETY — Claude's cumulative gotcha, INVERTED.\n// Claude Code logs per-message usage that can repeat across snapshot records,\n// so its analyzer dedups by message id. Codex logs a `token_count` event whose\n// `total_token_usage` is the CUMULATIVE session sum — summing it across a\n// session's 20+ events overcounts by orders of magnitude. The rule locked in\n// #66: sum `last_token_usage` (the per-response delta) and never read the\n// totals. Deltas also carry the cached/non-cached split each response's cost\n// needs, which the cumulative figure re-counts every turn.\n//\n// Attribution: `token_count` events carry no model. Each delta is attributed\n// to the model of the nearest preceding `turn_context` in the same file.\n//\n// TokenCounts mapping (#66 decision 6): `cached_input_tokens` is a SUBSET of\n// `input_tokens`, so `input = input_tokens - cached_input`, `cacheRead =\n// cached_input`, and `cacheWrite = 0` — Codex reports no cache writes, and a\n// zero write prices correctly with zero pricing-code changes.\n\nimport {\n\tapiEquivalentCost,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"@aistack/pricing\";\nimport {\n\taddModelUsage,\n\tasName,\n\tasNum,\n\tasObj,\n\tasStr,\n\tbump,\n\tcleanName,\n\tcountsTotal,\n\tcreateAggregate as createSharedAggregate,\n\ttype Obj,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\n\n/** Codex needs no response dedup bookkeeping — deltas count once by construction. */\nexport type Aggregate = SharedAggregate<never>;\n\nexport function createAggregate(): Aggregate {\n\treturn createSharedAggregate<never>();\n}\n\n/**\n * Per-file fold state. A rollout file is one session; the session id, CLI\n * version, cwd and current model are context lines that may sit BEFORE the\n * window opens, so they update state unconditionally and are only counted\n * when an in-window line lands (`noteActivity`).\n */\nexport type FileState = {\n\tsessionId: string | null;\n\tcliVersion: string | null;\n\tcwd: string | null;\n\t/** Pricing key of the nearest preceding `turn_context`. */\n\tmodelKey: string | null;\n\t/** True once any in-window line was counted for this file. */\n\tcounted: boolean;\n};\n\nexport function createFileState(): FileState {\n\treturn {\n\t\tsessionId: null,\n\t\tcliVersion: null,\n\t\tcwd: null,\n\t\tmodelKey: null,\n\t\tcounted: false,\n\t};\n}\n\n/**\n * Fold one parsed rollout line into the aggregate.\n *\n * `sinceMs` is applied HERE rather than in the scanner because context lines\n * (session_meta, turn_context) must update `state` even when they predate the\n * window — a session resumed today attributes today's deltas to a model named\n * last week.\n */\nexport function ingestLine(\n\tagg: Aggregate,\n\traw: unknown,\n\tstate: FileState,\n\tsinceMs?: number,\n): void {\n\tconst rec = asObj(raw);\n\tif (!rec) return;\n\tagg.records++;\n\n\tlet tsMs: number | null = null;\n\tconst timestamp = asStr(rec.timestamp);\n\tif (timestamp) {\n\t\tconst ts = Date.parse(timestamp);\n\t\tif (!Number.isNaN(ts)) tsMs = ts;\n\t}\n\tconst inWindow = sinceMs === undefined || (tsMs !== null && tsMs >= sinceMs);\n\n\tconst type = asStr(rec.type);\n\tconst payload = asObj(rec.payload);\n\n\tif (type === \"session_meta\" && payload) {\n\t\tstate.sessionId =\n\t\t\tasStr(payload.id) ?? asStr(payload.session_id) ?? state.sessionId;\n\t\tstate.cliVersion = asStr(payload.cli_version) ?? state.cliVersion;\n\t\tstate.cwd = asStr(payload.cwd) ?? state.cwd;\n\t} else if (type === \"turn_context\" && payload) {\n\t\tconst model = asName(payload.model);\n\t\tif (model) state.modelKey = normalizeModel(model);\n\t}\n\n\tif (!inWindow) return;\n\n\tif (tsMs !== null && timestamp) {\n\t\tagg.activeDays.add(timestamp.slice(0, 10));\n\t\tagg.firstTs = agg.firstTs === null ? tsMs : Math.min(agg.firstTs, tsMs);\n\t\tagg.lastTs = agg.lastTs === null ? tsMs : Math.max(agg.lastTs, tsMs);\n\t}\n\tnoteActivity(agg, state);\n\n\tif (type === \"event_msg\" && payload) ingestEvent(agg, payload, state, tsMs);\n\telse if (type === \"response_item\" && payload) ingestItem(agg, payload);\n}\n\n/** Count the file's session/version/cwd once, on its first in-window line. */\nfunction noteActivity(agg: Aggregate, state: FileState): void {\n\tif (state.counted) return;\n\tstate.counted = true;\n\tif (state.sessionId) agg.sessions.add(state.sessionId);\n\tif (state.cliVersion) agg.ccVersions.add(cleanName(state.cliVersion));\n\t// Counted, never published — same standing non-goal as Claude project dirs.\n\tagg.projectDirs.add(state.cwd ?? \"(unknown)\");\n}\n\n// ---------------------------------------------------------------------------\n// Usage — token_count deltas\n// ---------------------------------------------------------------------------\n\nfunction ingestEvent(\n\tagg: Aggregate,\n\tpayload: Obj,\n\tstate: FileState,\n\ttsMs: number | null,\n): void {\n\tif (asStr(payload.type) !== \"token_count\") return;\n\tconst info = asObj(payload.info);\n\tconst last = info ? asObj(info.last_token_usage) : null;\n\tif (!last) return;\n\n\tconst inputTotal = asNum(last.input_tokens);\n\tconst cached = Math.min(asNum(last.cached_input_tokens), inputTotal);\n\tconst counts: TokenCounts = {\n\t\tinput: inputTotal - cached,\n\t\toutput: asNum(last.output_tokens),\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: 0,\n\t\tcacheRead: cached,\n\t};\n\tconst total = countsTotal(counts);\n\t// A zero delta is a rate-limit-only refresh, not a response.\n\tif (total === 0) return;\n\n\tif (tsMs === null) agg.untimestampedResponses++;\n\tagg.distinctResponses++;\n\n\tconst modelKey = state.modelKey ?? \"(unknown)\";\n\taddModelUsage(\n\t\tagg,\n\t\tmodelKey,\n\t\tcounts,\n\t\tapiEquivalentCost(modelKey, counts, tsMs),\n\t);\n\t// Codex rollouts carry no sidechain flag; everything is the main thread,\n\t// which keeps `subagentShare` an honest 0 rather than a guess.\n\tagg.mainTokens += total;\n}\n\n// ---------------------------------------------------------------------------\n// Inventory — response_item tool calls\n// ---------------------------------------------------------------------------\n\n/**\n * MCP tools reach the model as `<server>__<tool>` (MCP_TOOL_NAME_DELIMITER in\n * the Codex source); split on the FIRST `__` to recover the server. Over-long\n * names get a hash suffix on the TOOL side, so the server segment survives.\n */\nfunction ingestCall(agg: Aggregate, name: string, callId: string | null): void {\n\tif (callId) {\n\t\tif (agg.toolCallDedup.has(callId)) return;\n\t\tagg.toolCallDedup.add(callId);\n\t}\n\tconst sep = name.indexOf(\"__\");\n\tif (sep > 0) {\n\t\tbump(agg.mcpServerCalls, cleanName(name.slice(0, sep)));\n\t\tbump(agg.mcpToolCalls, cleanName(name));\n\t\treturn;\n\t}\n\tbump(agg.toolCalls, cleanName(name));\n}\n\nfunction ingestItem(agg: Aggregate, payload: Obj): void {\n\tconst type = asStr(payload.type);\n\tif (type === \"function_call\" || type === \"custom_tool_call\") {\n\t\tconst name = asName(payload.name);\n\t\tif (!name) return;\n\t\tingestCall(agg, name, asStr(payload.call_id) ?? asStr(payload.id));\n\t\treturn;\n\t}\n\t// Non-function tool items publish under stable synthetic names that live in\n\t// CODEX_BUILTIN_TOOLS, so they survive the fail-closed filter.\n\tif (type === \"local_shell_call\") {\n\t\tingestCall(agg, \"local_shell\", asStr(payload.call_id) ?? asStr(payload.id));\n\t} else if (type === \"web_search_call\") {\n\t\tagg.webSearchRequests++;\n\t\tingestCall(agg, \"web_search\", asStr(payload.id));\n\t} else if (type === \"tool_search_call\") {\n\t\tingestCall(agg, \"tool_search\", asStr(payload.id));\n\t}\n}\n\n/**\n * Static MCP inventory from `~/.codex/config.toml` (#66 decision 3): a\n * configured server the window never called still exists. Zero-count entries\n * ride into the inventory (callShare 0) without inventing calls.\n */\nexport function noteConfiguredMcpServers(\n\tagg: Aggregate,\n\tserverNames: Iterable<string>,\n): void {\n\tfor (const raw of serverNames) {\n\t\tconst name = cleanName(raw);\n\t\tif (!agg.mcpServerCalls.has(name)) agg.mcpServerCalls.set(name, 0);\n\t}\n}\n","// I/O shell around the pure Codex analyzer: find rollout files, stream JSONL\n// (plain or zstd), hand each parsed line to ingestLine. Nothing leaves this\n// machine.\n//\n// Wayfinder ticket #67 (map #60), semantics from #65/#66.\n//\n// STANDING NON-GOAL (locked in #13): raw transcripts, prompts, absolute paths,\n// and repo names never leave the machine. `~/.codex/history.jsonl` holds raw\n// prompt text and is NEVER opened here; read errors are swallowed rather than\n// thrown, because the error object carries the absolute path.\n\nimport { type Dirent, readFileSync } from \"node:fs\";\nimport { readdir, realpath, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport * as zlib from \"node:zlib\";\n\nimport { parse as parseToml } from \"smol-toml\";\n\nimport { asObj, asStr } from \"../shared/aggregate.js\";\nimport { emptyScanStats, type ScanStats } from \"../shared/window.js\";\nimport {\n\ttype Aggregate,\n\tcreateFileState,\n\tingestLine,\n\tnoteConfiguredMcpServers,\n} from \"./analyzer.js\";\n\n/** `$CODEX_HOME` honored, `~/.codex` the default — mirrors the Codex source. */\nexport function codexHome(): string {\n\treturn process.env.CODEX_HOME || path.join(homedir(), \".codex\");\n}\n\n/**\n * Only `sessions/` is read. `archived_sessions/` is deliberately excluded: an\n * archived session was removed from the user's working set, and the rolling\n * window makes old ones irrelevant anyway. `history.jsonl` is raw prompts and\n * is out of bounds entirely.\n */\nexport function rolloutRoots(): string[] {\n\treturn [path.join(codexHome(), \"sessions\")];\n}\n\nconst ROLLOUT_RE = /^rollout-.*\\.jsonl(\\.zst)?$/;\n\n/** What counts as a Codex rollout. Shared with `detect` (#101). */\nexport function isRolloutFile(basename: string): boolean {\n\treturn ROLLOUT_RE.test(basename);\n}\n\n/** Recursive rollout walk — the YYYY/MM/DD nesting is real. */\nasync function* walkRollouts(dir: string): AsyncGenerator<string> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const e of entries) {\n\t\tconst full = path.join(dir, e.name);\n\t\tif (e.isDirectory()) yield* walkRollouts(full);\n\t\telse if (e.isFile() && isRolloutFile(e.name)) yield full;\n\t}\n}\n\n/**\n * zstd support landed in node:zlib after the CLI's floor (`engines: >=18`),\n * so it is feature-detected. On an old runtime a `.zst` rollout counts as\n * unreadable — a visible coverage figure, never a silent skip.\n */\nconst zstdDecompress: ((buf: Buffer) => Buffer) | null =\n\ttypeof (zlib as { zstdDecompressSync?: unknown }).zstdDecompressSync ===\n\t\"function\"\n\t\t? (buf) =>\n\t\t\t\t(\n\t\t\t\t\tzlib as unknown as { zstdDecompressSync: (b: Buffer) => Buffer }\n\t\t\t\t).zstdDecompressSync(buf)\n\t\t: null;\n\nexport type ScanOptions = {\n\t/** Only count records with a timestamp at or after this epoch ms. */\n\tsinceMs?: number;\n\tonProgress?: (files: number) => void;\n\t/** Override the discovered roots. Tests only. */\n\troots?: string[];\n\t/** Override the config.toml path. Tests only. */\n\tconfigFile?: string;\n\t/** Override the file reader. Tests only. */\n\treadFileImpl?: (file: string) => Buffer | string;\n};\n\nexport async function scan(\n\tagg: Aggregate,\n\topts: ScanOptions = {},\n): Promise<ScanStats> {\n\tconst stats: ScanStats = emptyScanStats();\n\tconst visited = new Set<string>();\n\n\tfor (const root of opts.roots ?? rolloutRoots()) {\n\t\tif (!(await exists(root))) continue;\n\t\tfor await (const file of walkRollouts(root)) {\n\t\t\tstats.filesFound++;\n\n\t\t\tlet resolved: string;\n\t\t\ttry {\n\t\t\t\tresolved = await realpath(file);\n\t\t\t} catch {\n\t\t\t\tresolved = file;\n\t\t\t}\n\t\t\t// Dedup key = resolved path with `.zst` stripped. Codex's compression\n\t\t\t// worker leaves `foo.jsonl` and `foo.jsonl.zst` coexisting for a moment\n\t\t\t// (rename before unlink, #73 §4) — one session, two names. Keying on\n\t\t\t// the stem makes the second listing a duplicate, not a double count.\n\t\t\tconst dedupKey = resolved.endsWith(\".zst\")\n\t\t\t\t? resolved.slice(0, -\".zst\".length)\n\t\t\t\t: resolved;\n\t\t\tif (visited.has(dedupKey)) {\n\t\t\t\tstats.filesSkippedAsDuplicate++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvisited.add(dedupKey);\n\n\t\t\t// Rollouts are append-only and chronological, so a file untouched since\n\t\t\t// the window opened cannot hold an in-window record.\n\t\t\tif (opts.sinceMs !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tconst st = await stat(file);\n\t\t\t\t\tif (st.mtimeMs < opts.sinceMs) {\n\t\t\t\t\t\tstats.filesSkippedByMtime++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t/* unreadable stat — fall through and try to read it */\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tagg.files++;\n\t\t\tstats.filesRead++;\n\t\t\tif (opts.onProgress && agg.files % 200 === 0) opts.onProgress(agg.files);\n\t\t\tconst outcome = ingestWithRetry(agg, file, opts);\n\t\t\tif (!outcome.ok) {\n\t\t\t\t// Never rethrown: the error object carries the absolute path. The\n\t\t\t\t// stats keep a relative path and a bare error class instead (#75).\n\t\t\t\tstats.filesUnreadable++;\n\t\t\t\tstats.filesRead--;\n\t\t\t\tif (outcome.reason === \"zstd-unsupported\") stats.filesZstdUnsupported++;\n\t\t\t\tstats.unreadableFiles.push({\n\t\t\t\t\tpath: path.relative(root, file),\n\t\t\t\t\treason: outcome.reason,\n\t\t\t\t});\n\t\t\t} else if (!outcome.genuine) {\n\t\t\t\t// Fingerprint failure (#73): another tool wrote this file. Its usage\n\t\t\t\t// stayed out of the aggregate entirely.\n\t\t\t\tstats.filesForeign++;\n\t\t\t\tstats.filesRead--;\n\t\t\t\tconst seen = stats.foreignOriginators.get(outcome.originator) ?? 0;\n\t\t\t\tstats.foreignOriginators.set(outcome.originator, seen + 1);\n\t\t\t}\n\t\t}\n\t}\n\n\treadConfiguredMcpServers(agg, opts.configFile);\n\treturn stats;\n}\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\ntype IngestOutcome =\n\t| { ok: true; genuine: true }\n\t| { ok: true; genuine: false; originator: string }\n\t| { ok: false; reason: string };\n\n/**\n * A read failure classified WITHOUT the error object's message or stack —\n * both carry the absolute path, which never leaves this module. `code` is a\n * bare class name (`ENOENT`, `EACCES`, `zstd-unsupported`, `zstd-corrupt`).\n */\nfunction errorClass(e: unknown): string {\n\tconst code = (e as { code?: unknown } | null)?.code;\n\tif (typeof code === \"string\" && code.length > 0) return code;\n\treturn e instanceof Error ? e.constructor.name : \"unknown\";\n}\n\nconst readError = (reason: string): Error =>\n\tObject.assign(new Error(reason), { code: reason });\n\n/**\n * The compression race (#73 §4): codex's background worker compresses a\n * rollout to `.zst` and then unlinks the plain `.jsonl`, so a file listed by\n * the walk can be gone at read time. Mirror codex's own reader: on `ENOENT`,\n * try the `.zst` sibling once before counting the file unreadable.\n */\nfunction ingestWithRetry(\n\tagg: Aggregate,\n\tfile: string,\n\topts: ScanOptions,\n): IngestOutcome {\n\ttry {\n\t\treturn ingestFile(agg, file, opts);\n\t} catch (e) {\n\t\tif (errorClass(e) === \"ENOENT\" && !file.endsWith(\".zst\")) {\n\t\t\ttry {\n\t\t\t\treturn ingestFile(agg, `${file}.zst`, opts);\n\t\t\t} catch (e2) {\n\t\t\t\treturn { ok: false, reason: errorClass(e2) };\n\t\t\t}\n\t\t}\n\t\treturn { ok: false, reason: errorClass(e) };\n\t}\n}\n\n/**\n * Whole-file read rather than a stream: a `.zst` rollout must be decompressed\n * as one buffer anyway, and rollout files are single sessions — megabytes,\n * not gigabytes. The lines are parsed BEFORE any of them folds into the\n * aggregate, because the fingerprint verdict (#73) arrives only at end of\n * file: a foreign file must leave the aggregate untouched.\n */\nfunction ingestFile(\n\tagg: Aggregate,\n\tfile: string,\n\topts: ScanOptions,\n): IngestOutcome {\n\tconst readFile = opts.readFileImpl ?? readFileSync;\n\tlet text: string;\n\tif (file.endsWith(\".zst\")) {\n\t\tif (zstdDecompress === null) throw readError(\"zstd-unsupported\");\n\t\tconst raw = readFile(file);\n\t\ttry {\n\t\t\ttext = zstdDecompress(\n\t\t\t\tBuffer.isBuffer(raw) ? raw : Buffer.from(raw),\n\t\t\t).toString(\"utf8\");\n\t\t} catch {\n\t\t\tthrow readError(\"zstd-corrupt\");\n\t\t}\n\t} else {\n\t\ttext = readFile(file).toString(\"utf8\");\n\t}\n\n\tconst records: unknown[] = [];\n\tlet nonEmptyLines = 0;\n\tlet parseErrors = 0;\n\tfor (const line of text.split(\"\\n\")) {\n\t\tif (!line) continue;\n\t\tnonEmptyLines++;\n\t\ttry {\n\t\t\trecords.push(JSON.parse(line));\n\t\t} catch {\n\t\t\tparseErrors++;\n\t\t}\n\t}\n\n\tconst verdict = classifyRollout(records);\n\tif (!verdict.genuine) return { ok: true, ...verdict };\n\n\tagg.lines += nonEmptyLines;\n\tagg.parseErrors += parseErrors;\n\tconst state = createFileState();\n\tfor (const rec of records) ingestLine(agg, rec, state, opts.sinceMs);\n\treturn { ok: true, genuine: true };\n}\n\n/**\n * The genuine-rollout fingerprint (#73, source-pinned at rust-v0.146.0): the\n * codex-rs recorder always writes `session_meta` first, and every real user\n * turn persists a `turn_context` before any usage lands. A file whose first\n * parsed line is not `session_meta`, or that carries a `token_count` with no\n * preceding `turn_context`, was not written by Codex CLI. Negative by\n * construction — it detects \"not genuine\", never \"written by tool X\"; the\n * originator label is diagnostic only.\n */\nfunction classifyRollout(\n\trecords: readonly unknown[],\n): { genuine: true } | { genuine: false; originator: string } {\n\tlet originator: string | null = null;\n\tlet sawTurnContext = false;\n\tlet genuine = records.length > 0;\n\tfor (const [i, raw] of records.entries()) {\n\t\tconst rec = asObj(raw);\n\t\tconst type = rec ? asStr(rec.type) : null;\n\t\tconst payload = rec ? asObj(rec.payload) : null;\n\t\tif (i === 0 && type !== \"session_meta\") genuine = false;\n\t\tif (type === \"session_meta\" && payload && originator === null) {\n\t\t\toriginator = asStr(payload.originator);\n\t\t} else if (type === \"turn_context\") {\n\t\t\tsawTurnContext = true;\n\t\t} else if (\n\t\t\ttype === \"event_msg\" &&\n\t\t\tpayload &&\n\t\t\tasStr(payload.type) === \"token_count\" &&\n\t\t\t!sawTurnContext\n\t\t) {\n\t\t\tgenuine = false;\n\t\t}\n\t}\n\tif (genuine) return { genuine: true };\n\treturn { genuine: false, originator: originator ?? \"(none)\" };\n}\n\n/**\n * The static half of the MCP inventory (#66 decision 3): `[mcp_servers.*]`\n * in `~/.codex/config.toml`. Unreadable or absent config is silence, not an\n * error — the observed half stands on its own.\n */\nfunction readConfiguredMcpServers(agg: Aggregate, configFile?: string): void {\n\tconst file = configFile ?? path.join(codexHome(), \"config.toml\");\n\tlet names: string[] = [];\n\ttry {\n\t\tconst parsed = parseToml(readFileSync(file, \"utf8\"));\n\t\tconst servers = parsed.mcp_servers;\n\t\tif (servers && typeof servers === \"object\" && !Array.isArray(servers)) {\n\t\t\tnames = Object.keys(servers);\n\t\t}\n\t} catch {\n\t\treturn;\n\t}\n\tnoteConfiguredMcpServers(agg, names);\n}\n","// The Codex CLI harness behind the seam (#66 decision 6, built in #67).\n\nimport { hasRecentFile } from \"../shared/recency.js\";\nimport type {\n\tHarnessAdapter,\n\tHarnessDetectOptions,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"../types.js\";\nimport { createAggregate } from \"./analyzer.js\";\nimport { isRolloutFile, rolloutRoots, scan } from \"./scan.js\";\n\nexport const CODEX_HARNESS_NAME = \"codex\";\n\n/**\n * Codex's vendor-assigned tool surface, as observed in rollouts and pinned in\n * the Codex source (#65 §4). Same fail-closed mechanism as Claude's\n * BUILTIN_TOOLS: a literal set, never a pattern — an unknown-but-real\n * built-in withheld as a count is a small loss; an unknown-and-user-named\n * tool published verbatim is the leak this prevents. The last four are the\n * stable synthetic names the analyzer assigns to non-`function_call` items.\n *\n * Codex v1 publishes builtinTools and mcpServers ONLY (#66 decision 3):\n * slash commands verifiably never reach rollouts, and the skill/subagent\n * surfaces are unverified — those categories ship as empty arrays, absorbed\n * with no schema change if a later Codex version logs them.\n */\nexport const CODEX_BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"apply_patch\",\n\t\"exec_command\",\n\t\"grep_command\",\n\t\"list_dir\",\n\t\"read_file\",\n\t\"request_user_input\",\n\t\"shell\",\n\t\"unified_exec\",\n\t\"update_plan\",\n\t\"view_image\",\n\t\"write_stdin\",\n\t// synthetic names for non-function_call response items\n\t\"local_shell\",\n\t\"web_search\",\n\t\"tool_search\",\n]);\n\nexport const codexAdapter: HarnessAdapter = {\n\tname: CODEX_HARNESS_NAME,\n\tbuiltinTools: CODEX_BUILTIN_TOOLS,\n\n\tasync detect(opts: HarnessDetectOptions): Promise<boolean> {\n\t\treturn hasRecentFile(\n\t\t\topts.roots ?? rolloutRoots(),\n\t\t\tisRolloutFile,\n\t\t\topts.sinceMs,\n\t\t);\n\t},\n\n\tasync scan(opts: HarnessScanOptions): Promise<HarnessScan> {\n\t\tconst aggregate = createAggregate();\n\t\tconst stats = await scan(aggregate, {\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.onProgress ? { onProgress: opts.onProgress } : {}),\n\t\t});\n\t\treturn { aggregate, stats };\n\t},\n};\n","// Pure fold over rows projected out of opencode's SQLite store. No I/O, no\n// console — the scanner owns the database and hands this module plain values.\n//\n// Wayfinder ticket #124 (map #121). Field semantics come from\n// docs/research/harness-adapters-2026-08.md (§opencode), keyed by #123's\n// binding rule: every pricing key carries the harness's own provider id\n// (`modelKeyFor(providerID, modelID)`), because one opencode install routes\n// several providers and a gateway re-serving a vendor's model must not price\n// at that vendor's list rate.\n//\n// THE LOAD-BEARING FACTS (research §2):\n// - the four token counters are DISJOINT deltas: input, output, cache.read,\n// cache.write map straight onto TokenCounts with no arithmetic;\n// - `tokens.total` is absent on some rows and wrong on Google rows — never\n// read it; `reasoning` is a subset of `output` for two vendors and\n// additive for one — never add it;\n// - opencode's own `cost` is 0.0 on every record (subscription/OAuth auth);\n// a zero is not a measurement, so cost comes from @aistack/pricing only.\n\nimport {\n\tapiEquivalentCost,\n\tmodelKeyFor,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"@aistack/pricing\";\nimport {\n\taddModelUsage,\n\tasNum,\n\tasStr,\n\tbump,\n\tcleanName,\n\tcreateAggregate as createSharedAggregate,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\n\n/**\n * opencode's vendor-assigned tool surface, as observed in the probe DB and\n * pinned against the opencode source. Same fail-closed mechanism as the other\n * adapters: a literal set, never a pattern. It doubles as the MCP guard —\n * MCP tool names are `sanitize(server) + \"_\" + sanitize(tool)` and built-in\n * names contain `_` too, so a name must miss this set AND carry a configured\n * server's prefix before any split is attempted.\n */\nexport const OPENCODE_BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"apply_patch\",\n\t\"bash\",\n\t\"edit\",\n\t\"glob\",\n\t\"grep\",\n\t\"list\",\n\t\"patch\",\n\t\"question\",\n\t\"read\",\n\t\"skill\",\n\t\"task\",\n\t\"todoread\",\n\t\"todowrite\",\n\t\"webfetch\",\n\t\"websearch\",\n\t\"write\",\n]);\n\n/** Message-id dedup lives in DbFoldState, not the aggregate's `seen`. */\nexport type Aggregate = SharedAggregate<never>;\n\nexport function createAggregate(): Aggregate {\n\treturn createSharedAggregate<never>();\n}\n\n/** What the scanner knows about one session row, named columns only. */\nexport type SessionInfo = {\n\tid: unknown;\n\tparentId: unknown;\n\tversion: unknown;\n};\n\n/**\n * Per-database fold state. Sessions are loaded up front (a message's session\n * may predate the window); message ids are deduped across the v1 and v2\n * tables because v2 is described in the opencode source as a projection —\n * reading a row from both would double count.\n */\nexport type DbFoldState = {\n\tsessions: Map<string, { parentId: string | null; version: string | null }>;\n\tseenMessageIds: Set<string>;\n\t/** Configured MCP server names, sanitized the way opencode builds tool names. */\n\tmcpServers: string[];\n};\n\nexport function createDbFoldState(): DbFoldState {\n\treturn { sessions: new Map(), seenMessageIds: new Set(), mcpServers: [] };\n}\n\nexport function noteSessions(\n\tstate: DbFoldState,\n\trows: Iterable<SessionInfo>,\n): void {\n\tfor (const row of rows) {\n\t\tconst id = asStr(row.id);\n\t\tif (!id) continue;\n\t\tstate.sessions.set(id, {\n\t\t\tparentId: asStr(row.parentId),\n\t\t\tversion: asStr(row.version),\n\t\t});\n\t}\n}\n\n/**\n * One projected message row. Every value is untrusted — `json_extract` returns\n * whatever the blob holds — so the fold narrows each field itself.\n */\nexport type MessageRow = {\n\tid: unknown;\n\tsessionId: unknown;\n\trole: unknown;\n\tproviderId: unknown;\n\tmodelId: unknown;\n\t/** `data.time.created`, epoch ms. */\n\ttsMs: unknown;\n\tinput: unknown;\n\toutput: unknown;\n\tcacheRead: unknown;\n\tcacheWrite: unknown;\n\tcwd: unknown;\n};\n\nexport function ingestMessageRow(\n\tagg: Aggregate,\n\tstate: DbFoldState,\n\trow: MessageRow,\n): void {\n\tagg.records++;\n\n\tconst id = asStr(row.id);\n\tif (id) {\n\t\tif (state.seenMessageIds.has(id)) return;\n\t\tstate.seenMessageIds.add(id);\n\t}\n\n\tconst tsMs =\n\t\ttypeof row.tsMs === \"number\" && Number.isFinite(row.tsMs) ? row.tsMs : null;\n\tif (tsMs !== null) {\n\t\tagg.activeDays.add(new Date(tsMs).toISOString().slice(0, 10));\n\t\tagg.firstTs = agg.firstTs === null ? tsMs : Math.min(agg.firstTs, tsMs);\n\t\tagg.lastTs = agg.lastTs === null ? tsMs : Math.max(agg.lastTs, tsMs);\n\t}\n\n\tconst sessionId = asStr(row.sessionId);\n\tconst session = sessionId ? state.sessions.get(sessionId) : undefined;\n\tif (sessionId) {\n\t\tagg.sessions.add(sessionId);\n\t\tif (session?.version) agg.ccVersions.add(cleanName(session.version));\n\t}\n\t// Counted, never published — same standing non-goal as Claude project dirs.\n\tconst cwd = asStr(row.cwd);\n\tif (cwd) agg.projectDirs.add(cwd);\n\n\tif (asStr(row.role) !== \"assistant\") return;\n\tagg.assistantRecords++;\n\tagg.distinctResponses++;\n\tif (tsMs === null) agg.untimestampedResponses++;\n\n\tconst counts: TokenCounts = {\n\t\tinput: asNum(row.input),\n\t\toutput: asNum(row.output),\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: asNum(row.cacheWrite),\n\t\tcacheRead: asNum(row.cacheRead),\n\t};\n\n\t// A session with a parent is a subagent's — an honest measurement here,\n\t// unlike Codex's structural 0 (research §inventory: 21.4% on the probe DB).\n\tconst total =\n\t\tcounts.input + counts.output + counts.cacheWriteUnsplit + counts.cacheRead;\n\tif (session?.parentId) agg.sidechainTokens += total;\n\telse agg.mainTokens += total;\n\n\tconst provider = asStr(row.providerId);\n\tconst model = asStr(row.modelId);\n\tconst modelKey =\n\t\tprovider && model\n\t\t\t? normalizeModel(modelKeyFor(provider, cleanName(model)))\n\t\t\t: \"(unknown)\";\n\taddModelUsage(\n\t\tagg,\n\t\tmodelKey,\n\t\tcounts,\n\t\tapiEquivalentCost(modelKey, counts, tsMs),\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Inventory — `part` rows of type \"tool\"\n// ---------------------------------------------------------------------------\n\n/**\n * opencode's own `sanitize` as applied when composing MCP tool names: the\n * observed names (`chrome-devtools_click`) show `-` surviving, so everything\n * outside the tool-name charset collapses to `_`.\n */\nconst sanitizeMcpName = (name: string): string =>\n\tname.replace(/[^A-Za-z0-9_-]/g, \"_\");\n\n/**\n * One projected `part` row. The scanner extracts these named paths and\n * nothing else — `part.data.state.output` holds full command output and never\n * materializes in JS.\n */\nexport type ToolPartRow = {\n\tid: unknown;\n\tpartType: unknown;\n\ttool: unknown;\n\tcallId: unknown;\n\t/** `$.state.input.name` — the skill tool's skill. */\n\tinputName: unknown;\n\t/** `$.state.input.subagent_type` — the task tool's agent. */\n\tsubagentType: unknown;\n};\n\nexport function ingestToolPart(\n\tagg: Aggregate,\n\tstate: DbFoldState,\n\trow: ToolPartRow,\n): void {\n\t// step-finish parts repeat the message's tokens and text parts carry prose;\n\t// only tool executions count (research §2).\n\tif (asStr(row.partType) !== \"tool\") return;\n\tconst rawName = asStr(row.tool);\n\tif (!rawName) return;\n\tconst name = cleanName(rawName);\n\n\tconst dedupKey = asStr(row.callId) ?? asStr(row.id);\n\tif (dedupKey) {\n\t\tif (agg.toolCallDedup.has(dedupKey)) return;\n\t\tagg.toolCallDedup.add(dedupKey);\n\t}\n\n\t// Fail-closed order (research §inventory): the literal built-in set first,\n\t// then configured MCP server prefixes, then a plain count the shared\n\t// payload filter withholds — never a split that invents a server name.\n\tif (OPENCODE_BUILTIN_TOOLS.has(name)) {\n\t\tbump(agg.toolCalls, name);\n\t\tif (name === \"skill\") {\n\t\t\tconst skill = asStr(row.inputName);\n\t\t\tif (skill) bump(agg.skillCalls, cleanName(skill));\n\t\t} else if (name === \"task\") {\n\t\t\tconst agent = asStr(row.subagentType);\n\t\t\tif (agent) bump(agg.subagentCalls, cleanName(agent));\n\t\t}\n\t\treturn;\n\t}\n\tfor (const server of state.mcpServers) {\n\t\tif (name.startsWith(`${server}_`)) {\n\t\t\tbump(agg.mcpServerCalls, server);\n\t\t\tbump(agg.mcpToolCalls, name);\n\t\t\treturn;\n\t\t}\n\t}\n\tbump(agg.toolCalls, name);\n}\n\n/**\n * Static MCP inventory from `~/.config/opencode/opencode.json` (JSONC — the\n * scanner owns the tolerant parse). A configured server the window never\n * called still exists: zero-count entries ride into the inventory. The\n * sanitized form is what tool-name prefixes are matched against, longest\n * first so `foo-bar` wins over `foo` for `foo-bar_tool`.\n */\nexport function noteConfiguredMcpServers(\n\tagg: Aggregate,\n\tstate: DbFoldState,\n\tserverNames: Iterable<string>,\n): void {\n\tfor (const raw of serverNames) {\n\t\tconst name = cleanName(sanitizeMcpName(raw));\n\t\tif (!agg.mcpServerCalls.has(name)) agg.mcpServerCalls.set(name, 0);\n\t\tif (!state.mcpServers.includes(name)) state.mcpServers.push(name);\n\t}\n\tstate.mcpServers.sort((a, b) => b.length - a.length);\n}\n","// I/O shell around the pure opencode analyzer: find `opencode*.db`, open it\n// read-only with node:sqlite, project NAMED COLUMNS through json_extract, and\n// hand plain values to the fold. Nothing leaves this machine.\n//\n// Wayfinder ticket #124 (map #121), semantics from\n// docs/research/harness-adapters-2026-08.md (§opencode).\n//\n// STANDING NON-GOAL (locked in #13): raw transcripts, prompts, absolute paths\n// and repo names never leave the machine. The SAME FILE this module opens\n// also holds `account.refresh_token`, `credential.value`,\n// `session_input.prompt` and full file contents in `session.summary_diffs`\n// and `part.data.state.output`. The rule that keeps them out: never\n// `SELECT *` — every query names its columns, and `part.data` reaches JS only\n// as four json_extract'ed scalars. Errors are swallowed, not thrown, because\n// a node:sqlite error message carries the DB path.\n\nimport { readFileSync } from \"node:fs\";\nimport { readdir, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\n\nimport { emptyScanStats, type ScanStats } from \"../shared/window.js\";\nimport {\n\ttype Aggregate,\n\tcreateDbFoldState,\n\ttype DbFoldState,\n\tingestMessageRow,\n\tingestToolPart,\n\tnoteConfiguredMcpServers,\n\tnoteSessions,\n} from \"./analyzer.js\";\n\n/**\n * Newest migration id this build understands, from the probe DB (research\n * §1). A DB migrated past it may hold the same table names with different\n * semantics, so it counts as UNREADABLE — a visible coverage figure — rather\n * than being read on the guess that nothing moved.\n */\nexport const OPENCODE_MIGRATION_CEILING = 20260622202450;\n\n/** `$XDG_DATA_HOME/opencode` or `~/.local/share/opencode` — opencode's own rule. */\nexport function opencodeDataDirs(): string[] {\n\tconst xdg = process.env.XDG_DATA_HOME;\n\tconst base = xdg || path.join(homedir(), \".local\", \"share\");\n\treturn [path.join(base, \"opencode\")];\n}\n\n/**\n * The store is `opencode.db` on release channels, `opencode-<channel>.db`\n * otherwise, and `$OPENCODE_DB` overrides both. WAL siblings (`-wal`,\n * `-shm`) are opened by SQLite itself, never listed as stores.\n */\nfunction isStoreFile(basename: string): boolean {\n\treturn basename === \"opencode.db\" || /^opencode-[^/]+\\.db$/.test(basename);\n}\n\nasync function dbFilesIn(root: string): Promise<string[]> {\n\tconst override = process.env.OPENCODE_DB;\n\tif (override) return [override];\n\ttry {\n\t\tconst entries = await readdir(root, { withFileTypes: true });\n\t\treturn entries\n\t\t\t.filter((e) => e.isFile() && isStoreFile(e.name))\n\t\t\t.map((e) => path.join(root, e.name))\n\t\t\t.sort();\n\t} catch {\n\t\treturn [];\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// node:sqlite, feature-detected\n// ---------------------------------------------------------------------------\n\ntype SqliteDb = {\n\tprepare(sql: string): {\n\t\tall(...params: unknown[]): Record<string, unknown>[];\n\t\tget(...params: unknown[]): Record<string, unknown> | undefined;\n\t};\n\tclose(): void;\n};\n\n/**\n * `node:sqlite` landed in Node 22.5 while this CLI's floor is 18, so it is\n * feature-detected in the shape of the codex scanner's zstd check. On a\n * runtime without it, a found DB counts as unreadable — a visible coverage\n * figure, never a silent skip.\n */\nasync function loadSqlite(): Promise<((file: string) => SqliteDb) | null> {\n\ttry {\n\t\tconst mod = (await import(\"node:sqlite\")) as {\n\t\t\tDatabaseSync: new (file: string, opts: { readOnly: boolean }) => SqliteDb;\n\t\t};\n\t\tif (typeof mod.DatabaseSync !== \"function\") return null;\n\t\treturn (file) => new mod.DatabaseSync(file, { readOnly: true });\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * A read failure classified WITHOUT the error object's message or stack —\n * both can carry the absolute DB path, which never leaves this module.\n */\nfunction errorClass(e: unknown): string {\n\tconst code = (e as { code?: unknown } | null)?.code;\n\tif (typeof code === \"string\" && code.length > 0) return code;\n\treturn e instanceof Error ? e.constructor.name : \"unknown\";\n}\n\nconst readError = (reason: string): Error =>\n\tObject.assign(new Error(reason), { code: reason });\n\n// ---------------------------------------------------------------------------\n// Scan\n// ---------------------------------------------------------------------------\n\nexport type ScanOptions = {\n\t/** Only count records with a timestamp at or after this epoch ms. */\n\tsinceMs?: number;\n\tonProgress?: (files: number) => void;\n\t/** Override the discovered data dirs. Tests only. */\n\troots?: string[];\n\t/** Override the opencode.json path. Tests only. */\n\tconfigFile?: string;\n};\n\nexport async function scan(\n\tagg: Aggregate,\n\topts: ScanOptions = {},\n): Promise<ScanStats> {\n\tconst stats: ScanStats = emptyScanStats();\n\tconst open = await loadSqlite();\n\tconst sinceMs = opts.sinceMs ?? 0;\n\n\tconst state = createDbFoldState();\n\treadConfiguredMcpServers(agg, state, opts.configFile);\n\n\tfor (const root of opts.roots ?? opencodeDataDirs()) {\n\t\tfor (const file of await dbFilesIn(root)) {\n\t\t\tstats.filesFound++;\n\t\t\tagg.files++;\n\t\t\ttry {\n\t\t\t\tif (open === null) throw readError(\"sqlite-unsupported\");\n\t\t\t\treadDb(agg, state, open, file, sinceMs);\n\t\t\t\tstats.filesRead++;\n\t\t\t} catch (e) {\n\t\t\t\tstats.filesUnreadable++;\n\t\t\t\tstats.unreadableFiles.push({\n\t\t\t\t\tpath: path.basename(file),\n\t\t\t\t\treason: errorClass(e),\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (opts.onProgress) opts.onProgress(stats.filesFound);\n\t\t}\n\t}\n\treturn stats;\n}\n\n/** Refuse a DB whose newest migration this build has never seen. */\nfunction checkMigrationCeiling(db: SqliteDb): void {\n\tlet newest: unknown;\n\ttry {\n\t\tnewest = db.prepare(\"select max(id) as id from migration\").get()?.id;\n\t} catch {\n\t\tthrow readError(\"schema-unversioned\");\n\t}\n\tconst prefix = Number.parseInt(String(newest ?? \"\"), 10);\n\tif (!Number.isFinite(prefix)) throw readError(\"schema-unversioned\");\n\tif (prefix > OPENCODE_MIGRATION_CEILING) throw readError(\"schema-too-new\");\n}\n\nfunction readDb(\n\tagg: Aggregate,\n\tstate: DbFoldState,\n\topen: (file: string) => SqliteDb,\n\tfile: string,\n\tsinceMs: number,\n): void {\n\tconst db = open(file);\n\ttry {\n\t\tcheckMigrationCeiling(db);\n\n\t\tnoteSessions(\n\t\t\tstate,\n\t\t\tdb\n\t\t\t\t.prepare(\"select id, parent_id, version from session\")\n\t\t\t\t.all()\n\t\t\t\t.map((r) => ({ id: r.id, parentId: r.parent_id, version: r.version })),\n\t\t);\n\n\t\t// v1 messages. `time.created` (epoch ms, in the blob) prices the\n\t\t// response; the indexed integer column runs the window filter and backs\n\t\t// a blob whose clock field is missing or malformed.\n\t\tconst v1 = db.prepare(\n\t\t\t`select id, session_id, time_created,\n\t\t\t\tjson_extract(data, '$.role') as role,\n\t\t\t\tjson_extract(data, '$.providerID') as provider_id,\n\t\t\t\tjson_extract(data, '$.modelID') as model_id,\n\t\t\t\tjson_extract(data, '$.time.created') as ts_ms,\n\t\t\t\tjson_extract(data, '$.tokens.input') as tok_input,\n\t\t\t\tjson_extract(data, '$.tokens.output') as tok_output,\n\t\t\t\tjson_extract(data, '$.tokens.cache.read') as tok_cache_read,\n\t\t\t\tjson_extract(data, '$.tokens.cache.write') as tok_cache_write,\n\t\t\t\tjson_extract(data, '$.path.cwd') as cwd\n\t\t\tfrom message where time_created >= ?`,\n\t\t);\n\t\tfor (const r of v1.all(sinceMs)) {\n\t\t\tagg.lines++;\n\t\t\tingestMessageRow(agg, state, {\n\t\t\t\tid: r.id,\n\t\t\t\tsessionId: r.session_id,\n\t\t\t\trole: r.role,\n\t\t\t\tproviderId: r.provider_id,\n\t\t\t\tmodelId: r.model_id,\n\t\t\t\ttsMs: pickTs(r.ts_ms, r.time_created),\n\t\t\t\tinput: r.tok_input,\n\t\t\t\toutput: r.tok_output,\n\t\t\t\tcacheRead: r.tok_cache_read,\n\t\t\t\tcacheWrite: r.tok_cache_write,\n\t\t\t\tcwd: r.cwd,\n\t\t\t});\n\t\t}\n\n\t\t// v2 (`session_message`) — the other live generation (research §1: which\n\t\t// one current opencode writes is unproven, so both are read and message\n\t\t// ids dedup across them). The assistant shape differs: `model: {id,\n\t\t// providerID}`, role in the `type` column.\n\t\tconst v2 = db.prepare(\n\t\t\t`select id, session_id, type, time_created,\n\t\t\t\tjson_extract(data, '$.model.providerID') as provider_id,\n\t\t\t\tjson_extract(data, '$.model.id') as model_id,\n\t\t\t\tjson_extract(data, '$.time.created') as ts_ms,\n\t\t\t\tjson_extract(data, '$.tokens.input') as tok_input,\n\t\t\t\tjson_extract(data, '$.tokens.output') as tok_output,\n\t\t\t\tjson_extract(data, '$.tokens.cache.read') as tok_cache_read,\n\t\t\t\tjson_extract(data, '$.tokens.cache.write') as tok_cache_write,\n\t\t\t\tjson_extract(data, '$.path.cwd') as cwd\n\t\t\tfrom session_message where time_created >= ?`,\n\t\t);\n\t\tfor (const r of v2.all(sinceMs)) {\n\t\t\tagg.lines++;\n\t\t\tingestMessageRow(agg, state, {\n\t\t\t\tid: r.id,\n\t\t\t\tsessionId: r.session_id,\n\t\t\t\trole: r.type,\n\t\t\t\tproviderId: r.provider_id,\n\t\t\t\tmodelId: r.model_id,\n\t\t\t\ttsMs: pickTs(r.ts_ms, r.time_created),\n\t\t\t\tinput: r.tok_input,\n\t\t\t\toutput: r.tok_output,\n\t\t\t\tcacheRead: r.tok_cache_read,\n\t\t\t\tcacheWrite: r.tok_cache_write,\n\t\t\t\tcwd: r.cwd,\n\t\t\t});\n\t\t}\n\n\t\t// v1 tool parts. Only these four scalar paths of `part.data` ever reach\n\t\t// JS — `$.state.output` holds full command output and stays in SQLite.\n\t\tconst parts = db.prepare(\n\t\t\t`select id,\n\t\t\t\tjson_extract(data, '$.type') as part_type,\n\t\t\t\tjson_extract(data, '$.tool') as tool,\n\t\t\t\tjson_extract(data, '$.callID') as call_id,\n\t\t\t\tjson_extract(data, '$.state.input.name') as input_name,\n\t\t\t\tjson_extract(data, '$.state.input.subagent_type') as subagent_type\n\t\t\tfrom part\n\t\t\twhere time_created >= ? and json_extract(data, '$.type') = 'tool'`,\n\t\t);\n\t\tfor (const r of parts.all(sinceMs)) {\n\t\t\tagg.lines++;\n\t\t\tingestToolPart(agg, state, {\n\t\t\t\tid: r.id,\n\t\t\t\tpartType: r.part_type,\n\t\t\t\ttool: r.tool,\n\t\t\t\tcallId: r.call_id,\n\t\t\t\tinputName: r.input_name,\n\t\t\t\tsubagentType: r.subagent_type,\n\t\t\t});\n\t\t}\n\n\t\t// v2 inline tool content, same named-scalar rule via json_each. The v2\n\t\t// content shape is unverified on any real machine, so a query error here\n\t\t// is tolerated — the tokens above are the load-bearing read.\n\t\ttry {\n\t\t\tconst v2parts = db.prepare(\n\t\t\t\t`select sm.id || ':' || je.key as id,\n\t\t\t\t\tjson_extract(je.value, '$.type') as part_type,\n\t\t\t\t\tjson_extract(je.value, '$.tool') as tool,\n\t\t\t\t\tjson_extract(je.value, '$.callID') as call_id,\n\t\t\t\t\tjson_extract(je.value, '$.state.input.name') as input_name,\n\t\t\t\t\tjson_extract(je.value, '$.state.input.subagent_type') as subagent_type\n\t\t\t\tfrom session_message sm, json_each(sm.data, '$.content') je\n\t\t\t\twhere sm.time_created >= ? and sm.type = 'assistant'`,\n\t\t\t);\n\t\t\tfor (const r of v2parts.all(sinceMs)) {\n\t\t\t\tingestToolPart(agg, state, {\n\t\t\t\t\tid: r.id,\n\t\t\t\t\tpartType: r.part_type,\n\t\t\t\t\ttool: r.tool,\n\t\t\t\t\tcallId: r.call_id,\n\t\t\t\t\tinputName: r.input_name,\n\t\t\t\t\tsubagentType: r.subagent_type,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t/* v2 content unreadable — the message tokens already counted */\n\t\t}\n\t} finally {\n\t\ttry {\n\t\t\tdb.close();\n\t\t} catch {\n\t\t\t/* already closed or never opened fully */\n\t\t}\n\t}\n}\n\n/** The blob's own clock when it is a finite number, else the indexed column. */\nfunction pickTs(jsonTs: unknown, columnTs: unknown): number | null {\n\tif (typeof jsonTs === \"number\" && Number.isFinite(jsonTs)) return jsonTs;\n\tif (typeof columnTs === \"number\" && Number.isFinite(columnTs))\n\t\treturn columnTs;\n\t// node:sqlite may hand integers back as bigint depending on flags.\n\tif (typeof columnTs === \"bigint\") return Number(columnTs);\n\tif (typeof jsonTs === \"bigint\") return Number(jsonTs);\n\treturn null;\n}\n\n// ---------------------------------------------------------------------------\n// Config — the static MCP inventory\n// ---------------------------------------------------------------------------\n\n/** `$XDG_CONFIG_HOME/opencode/opencode.json` or `~/.config/opencode/opencode.json`. */\nexport function opencodeConfigFile(): string {\n\tconst xdg = process.env.XDG_CONFIG_HOME;\n\tconst base = xdg || path.join(homedir(), \".config\");\n\treturn path.join(base, \"opencode\", \"opencode.json\");\n}\n\n/**\n * The real config is JSONC — comments and trailing commas (research\n * §inventory) — so `JSON.parse` alone throws on it. The strip below is\n * string-aware: a `//` inside a quoted URL survives. Any remaining parse\n * failure is silence, not an error: the observed half of the MCP inventory\n * stands on its own.\n */\nfunction readConfiguredMcpServers(\n\tagg: Aggregate,\n\tstate: DbFoldState,\n\tconfigFile?: string,\n): void {\n\tconst file = configFile ?? opencodeConfigFile();\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(stripJsonc(readFileSync(file, \"utf8\")));\n\t\tconst mcp = (parsed as { mcp?: unknown } | null)?.mcp;\n\t\tif (mcp && typeof mcp === \"object\" && !Array.isArray(mcp)) {\n\t\t\tnoteConfiguredMcpServers(agg, state, Object.keys(mcp));\n\t\t}\n\t} catch {\n\t\treturn;\n\t}\n}\n\nexport function stripJsonc(text: string): string {\n\tlet out = \"\";\n\tlet i = 0;\n\tlet inString = false;\n\twhile (i < text.length) {\n\t\tconst ch = text[i];\n\t\tconst next = text[i + 1];\n\t\tif (inString) {\n\t\t\tout += ch;\n\t\t\tif (ch === \"\\\\\") {\n\t\t\t\tout += next ?? \"\";\n\t\t\t\ti += 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ch === '\"') inString = false;\n\t\t\ti++;\n\t\t} else if (ch === '\"') {\n\t\t\tinString = true;\n\t\t\tout += ch;\n\t\t\ti++;\n\t\t} else if (ch === \"/\" && next === \"/\") {\n\t\t\twhile (i < text.length && text[i] !== \"\\n\") i++;\n\t\t} else if (ch === \"/\" && next === \"*\") {\n\t\t\ti += 2;\n\t\t\twhile (i < text.length && !(text[i] === \"*\" && text[i + 1] === \"/\")) i++;\n\t\t\ti += 2;\n\t\t} else {\n\t\t\tout += ch;\n\t\t\ti++;\n\t\t}\n\t}\n\t// Trailing commas: `, }` and `, ]` with any whitespace between.\n\treturn out.replace(/,(\\s*[}\\]])/g, \"$1\");\n}\n\n// ---------------------------------------------------------------------------\n// Detection\n// ---------------------------------------------------------------------------\n\n/**\n * Detection is a QUERY, not a stat walk (#101, research §6): every opencode\n * start — including `opencode --version` — touches the DB file, and the probe\n * machine showed a four-month gap between the file's mtime and the newest\n * real message. The indexed probe costs 0.02 ms.\n */\nexport async function detectOpencode(opts: {\n\tsinceMs: number;\n\troots?: string[];\n}): Promise<boolean> {\n\tconst open = await loadSqlite();\n\tif (open === null) return false;\n\n\tfor (const root of opts.roots ?? opencodeDataDirs()) {\n\t\tfor (const file of await dbFilesIn(root)) {\n\t\t\tif (!(await exists(file))) continue;\n\t\t\tlet db: SqliteDb | null = null;\n\t\t\ttry {\n\t\t\t\tdb = open(file);\n\t\t\t\tcheckMigrationCeiling(db);\n\t\t\t\tconst probe = (table: string) =>\n\t\t\t\t\tdb\n\t\t\t\t\t\t?.prepare(\n\t\t\t\t\t\t\t`select 1 as hit from ${table} where time_created >= ? limit 1`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.get(opts.sinceMs) !== undefined;\n\t\t\t\tif (probe(\"message\") || probe(\"session_message\")) return true;\n\t\t\t} catch {\n\t\t\t\t/* unreadable or foreign DB — not detection */\n\t\t\t} finally {\n\t\t\t\ttry {\n\t\t\t\t\tdb?.close();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ignore */\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n","// The opencode harness behind the seam (#66 decision 6) — wayfinder ticket\n// #124 (map #121).\n\nimport type {\n\tHarnessAdapter,\n\tHarnessDetectOptions,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"../types.js\";\nimport { createAggregate, OPENCODE_BUILTIN_TOOLS } from \"./analyzer.js\";\nimport { detectOpencode, scan } from \"./scan.js\";\n\nexport const OPENCODE_HARNESS_NAME = \"opencode\";\n\nexport { OPENCODE_BUILTIN_TOOLS } from \"./analyzer.js\";\n\nexport const opencodeAdapter: HarnessAdapter = {\n\tname: OPENCODE_HARNESS_NAME,\n\tbuiltinTools: OPENCODE_BUILTIN_TOOLS,\n\n\tasync detect(opts: HarnessDetectOptions): Promise<boolean> {\n\t\treturn detectOpencode({\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.roots ? { roots: opts.roots } : {}),\n\t\t});\n\t},\n\n\tasync scan(opts: HarnessScanOptions): Promise<HarnessScan> {\n\t\tconst aggregate = createAggregate();\n\t\tconst stats = await scan(aggregate, {\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.onProgress ? { onProgress: opts.onProgress } : {}),\n\t\t});\n\t\treturn { aggregate, stats };\n\t},\n};\n","// Pure fold over parsed pi session-file entries. No I/O, no console.\n//\n// Wayfinder ticket #126 (map #121). Field semantics come from\n// docs/research/harness-adapters-2026-08.md (§pi-mono), read off the vendor\n// doc set shipped in /opt/pi-coding-agent/docs and verified against the real\n// files in ~/.pi/agent/sessions. Every field is untrusted and optional: entries\n// arrive as `unknown` and are narrowed here.\n//\n// THE LOAD-BEARING FACTS (research §2-§3):\n// - `usage.input` already EXCLUDES cache traffic — no subtraction (Codex,\n// inverted); `reasoning` is a subset of `output` — never add it;\n// - `cacheWrite1h` is a SUBSET of `cacheWrite`, so the TTL split maps onto\n// TokenCounts exactly: pi is the only harness that hands the re-pricer\n// the split instead of a lower bound;\n// - /fork and /clone copy entries into a second file KEEPING entry ids, so\n// usage dedup is cross-file, and the 8-hex id alone collides at corpus\n// scale — the key is `${id}:${timestamp}:${totalTokens}`;\n// - `compaction.retainedTail` embeds assistant messages that already appear\n// as their own entries earlier in the same file — never descend into it;\n// - pi's own `usage.cost` is computed against an unpinned network-refreshed\n// table — uncitable, so cost comes from @aistack/pricing only.\n//\n// Pricing keys follow #123's binding rule: every row is keyed\n// `modelKeyFor(provider, model)` with pi's own provider id verbatim. Only\n// `anthropic`/`openai`/`google` reach vendor rates; a router-billed response\n// (`openrouter:anthropic/claude-opus-4.6`) stays unpriced, which is the safe\n// direction — it did not pay vendor list price.\n\nimport {\n\tapiEquivalentCost,\n\tmodelKeyFor,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"@aistack/pricing\";\nimport {\n\taddModelUsage,\n\tasArr,\n\tasName,\n\tasNum,\n\tasObj,\n\tasStr,\n\tbump,\n\tcountsTotal,\n\tcreateAggregate as createSharedAggregate,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\n\n/** Cross-file dedup lives in FoldState, not the aggregate's `seen`. */\nexport type Aggregate = SharedAggregate<never>;\n\nexport function createAggregate(): Aggregate {\n\treturn createSharedAggregate<never>();\n}\n\n/**\n * Scan-level fold state, shared across every file in one scan: /fork and\n * /clone duplicate entries into a second file keeping their ids, so the dedup\n * set cannot be per-file.\n */\nexport type FoldState = {\n\tseenUsage: Set<string>;\n};\n\nexport function createFoldState(): FoldState {\n\treturn { seenUsage: new Set() };\n}\n\n/** Per-file fold state. */\nexport type FileState = {\n\tsessionId: string | null;\n\tcwd: string | null;\n\t/** Pricing key of the nearest preceding assistant message or model_change. */\n\tmodelKey: string | null;\n\t/** True once any in-window entry was counted for this file. */\n\tcounted: boolean;\n};\n\nexport function createFileState(): FileState {\n\treturn { sessionId: null, cwd: null, modelKey: null, counted: false };\n}\n\n/**\n * Fold one parsed session-file entry into the aggregate.\n *\n * `sinceMs` is applied HERE rather than in the scanner because context entries\n * (the header, `model_change`) must update `state` even when they predate the\n * window — a session resumed today bills today's usage to a model named last\n * week. The window filter reads the entry's ISO timestamp first and falls back\n * to the message's Unix-ms timestamp, the same order pricing uses.\n */\nexport function ingestEntry(\n\tagg: Aggregate,\n\traw: unknown,\n\tstate: FileState,\n\tfold: FoldState,\n\tsinceMs?: number,\n): void {\n\tconst rec = asObj(raw);\n\tif (!rec) return;\n\tagg.records++;\n\n\tconst type = asStr(rec.type);\n\tconst message = type === \"message\" ? asObj(rec.message) : null;\n\tconst role = message ? asStr(message.role) : null;\n\n\t// Context updates happen regardless of the window.\n\tif (type === \"session\") {\n\t\tstate.sessionId = asStr(rec.id) ?? state.sessionId;\n\t\tstate.cwd = asStr(rec.cwd) ?? state.cwd;\n\t\treturn;\n\t}\n\tif (type === \"model_change\") {\n\t\tconst provider = asStr(rec.provider);\n\t\tconst model = asStr(rec.modelId);\n\t\tif (provider && model) state.modelKey = toPricingKey(provider, model);\n\t}\n\tif (role === \"assistant\" && message) {\n\t\tconst provider = asStr(message.provider);\n\t\tconst model = asStr(message.model);\n\t\tif (provider && model) state.modelKey = toPricingKey(provider, model);\n\t}\n\n\t// Entry-level ISO timestamp first, message-level Unix ms as the fallback.\n\tconst entryTs = Date.parse(asStr(rec.timestamp) ?? \"\");\n\tconst msgTs = message ? asNum(message.timestamp) : 0;\n\tconst tsMs = !Number.isNaN(entryTs) ? entryTs : msgTs > 0 ? msgTs : null;\n\n\tconst inWindow = sinceMs === undefined || (tsMs !== null && tsMs >= sinceMs);\n\tif (!inWindow) return;\n\n\tif (tsMs !== null) {\n\t\tagg.activeDays.add(new Date(tsMs).toISOString().slice(0, 10));\n\t\tagg.firstTs = agg.firstTs === null ? tsMs : Math.min(agg.firstTs, tsMs);\n\t\tagg.lastTs = agg.lastTs === null ? tsMs : Math.max(agg.lastTs, tsMs);\n\t}\n\tnoteActivity(agg, state);\n\n\tif (role === \"assistant\" && message) {\n\t\tagg.assistantRecords++;\n\t\t// `model` is what pi asked for, `responseModel` what the API says it\n\t\t// served. Routers make them differ, and then the rate for `model`\n\t\t// cannot be cited — the tokens surface as unpriced instead.\n\t\tconst served = asStr(message.responseModel);\n\t\tconst priceable = served === null || served === asStr(message.model);\n\t\tconst outcome = countUsage(\n\t\t\tagg,\n\t\t\tfold,\n\t\t\trec,\n\t\t\tmsgTs,\n\t\t\tmessage.usage,\n\t\t\tstate.modelKey,\n\t\t\ttsMs,\n\t\t\tpriceable,\n\t\t);\n\t\t// A /fork duplicate repeats the content blocks too; the call-id dedup\n\t\t// already covers tool calls, but the thinking/text tallies have no ids.\n\t\tif (outcome !== \"duplicate\") ingestContent(agg, message.content);\n\t} else if (role === \"toolResult\" && message) {\n\t\t// \"Nested LLM work performed by the tool\" — real spend, counted by\n\t\t// pi's own footer. No model of its own, so it bills to the model in\n\t\t// effect, the way Codex deltas bill to the nearest turn_context.\n\t\tcountUsage(agg, fold, rec, msgTs, message.usage, state.modelKey, tsMs);\n\t} else if (type === \"compaction\" || type === \"branch_summary\") {\n\t\t// Summary generation is real spend (optional `usage` on the entry). The\n\t\t// materialized `retainedTail` embeds assistant messages that already\n\t\t// appear as their own entries — deliberately never walked.\n\t\tcountUsage(agg, fold, rec, 0, rec.usage, state.modelKey, tsMs);\n\t}\n}\n\n/** Count the file's session and cwd once, on its first in-window entry. */\nfunction noteActivity(agg: Aggregate, state: FileState): void {\n\tif (state.counted) return;\n\tstate.counted = true;\n\tif (state.sessionId) agg.sessions.add(state.sessionId);\n\t// Counted, never published — same standing non-goal as Claude project dirs.\n\tagg.projectDirs.add(state.cwd ?? \"(unknown)\");\n}\n\n/** Fold one usage block into the totals, behind the cross-file dedup. */\nfunction countUsage(\n\tagg: Aggregate,\n\tfold: FoldState,\n\trec: Record<string, unknown>,\n\tmsgTsMs: number,\n\tusageRaw: unknown,\n\tmodelKey: string | null,\n\ttsMs: number | null,\n\tpriceable = true,\n): \"counted\" | \"duplicate\" | \"none\" {\n\tconst counts = readCounts(usageRaw);\n\tif (!counts) return \"none\";\n\tconst total = countsTotal(counts);\n\tif (total === 0) return \"none\";\n\n\t// /fork and /clone write the same entry into a second file with its id\n\t// intact, so dedup is cross-file. The 8-hex id alone has a real birthday\n\t// collision at corpus scale, so the timestamps and the token total ride\n\t// along — two genuinely different responses sharing an id stay two.\n\tconst id = asStr(rec.id);\n\tif (id) {\n\t\tconst key = `${id}:${asStr(rec.timestamp) ?? \"\"}:${msgTsMs}:${total}`;\n\t\tif (fold.seenUsage.has(key)) {\n\t\t\tagg.continuationsFolded++;\n\t\t\treturn \"duplicate\";\n\t\t}\n\t\tfold.seenUsage.add(key);\n\t} else {\n\t\tagg.unkeyedResponses++;\n\t}\n\n\tif (tsMs === null) agg.untimestampedResponses++;\n\tagg.distinctResponses++;\n\tconst key = modelKey ?? \"(unknown)\";\n\taddModelUsage(\n\t\tagg,\n\t\tkey,\n\t\tcounts,\n\t\tpriceable ? apiEquivalentCost(key, counts, tsMs) : null,\n\t);\n\t// pi has no subagents by vendor design — everything is the main thread,\n\t// which keeps `subagentShare` an honest 0 (same case as Codex).\n\tagg.mainTokens += total;\n\treturn \"counted\";\n}\n\n/**\n * Compose the pricing key from pi's own provider and model ids (#123's\n * binding rule). pi spells fast mode as an id SUFFIX (`claude-opus-5-fast`)\n * and has no `usage.speed` field, so the suffix is translated into the price\n * table's `#fast` marker here. A model genuinely named `-fast` by a provider\n * the table does not cover stays unpriced either way, so the translation\n * cannot invent a rate.\n */\nfunction toPricingKey(provider: string, model: string): string {\n\tconst fast = model.endsWith(\"-fast\");\n\tconst marked = fast ? `${model.slice(0, -\"-fast\".length)}#fast` : model;\n\treturn normalizeModel(modelKeyFor(provider, marked));\n}\n\n/**\n * Assistant content blocks: tool calls plus the thinking/text tallies.\n *\n * `bashExecution` entries are deliberately NOT counted as tool calls — they\n * are user-typed `!` commands, not something the model chose. A name outside\n * PI_BUILTIN_TOOLS is a user extension's tool; it stays a plain count and the\n * shared fail-closed payload filter withholds the name. pi has no MCP, no\n * subagents and no skill tool by vendor design, so those maps stay EMPTY —\n * absent from the payload, never zero (#40).\n */\nfunction ingestContent(agg: Aggregate, contentRaw: unknown): void {\n\tfor (const blockRaw of asArr(contentRaw)) {\n\t\tconst block = asObj(blockRaw);\n\t\tif (!block) continue;\n\t\tconst type = asStr(block.type);\n\t\tif (type === \"thinking\") {\n\t\t\tagg.thinkingBlocks++;\n\t\t} else if (type === \"text\") {\n\t\t\tagg.textBlocks++;\n\t\t} else if (type === \"toolCall\") {\n\t\t\tconst name = asName(block.name);\n\t\t\tif (!name) continue;\n\t\t\t// /fork duplicates keep call ids, so the shared dedup set makes the\n\t\t\t// copy a repeat rather than a double count.\n\t\t\tconst callId = asStr(block.id);\n\t\t\tif (callId) {\n\t\t\t\tif (agg.toolCallDedup.has(callId)) continue;\n\t\t\t\tagg.toolCallDedup.add(callId);\n\t\t\t} else {\n\t\t\t\tagg.toolBlocksWithoutId++;\n\t\t\t}\n\t\t\tbump(agg.toolCalls, name);\n\t\t}\n\t}\n}\n\n/** The Usage shape shared by assistant/toolResult messages and summary entries. */\nfunction readCounts(usageRaw: unknown): TokenCounts | null {\n\tconst u = asObj(usageRaw);\n\tif (!u) return null;\n\tconst cacheWrite = asNum(u.cacheWrite);\n\tconst split =\n\t\ttypeof u.cacheWrite1h === \"number\" && Number.isFinite(u.cacheWrite1h);\n\tconst cacheWrite1h = split\n\t\t? Math.min(Math.max(u.cacheWrite1h as number, 0), cacheWrite)\n\t\t: 0;\n\treturn {\n\t\t// Already exclusive of cache traffic — no subtraction (research §2).\n\t\tinput: asNum(u.input),\n\t\t// `reasoning` is a subset of `output` — never added.\n\t\toutput: asNum(u.output),\n\t\tcacheWrite5m: split ? cacheWrite - cacheWrite1h : 0,\n\t\tcacheWrite1h,\n\t\tcacheWriteUnsplit: split ? 0 : cacheWrite,\n\t\tcacheRead: asNum(u.cacheRead),\n\t};\n}\n","// I/O shell around the pure pi analyzer: find session files, stream JSONL,\n// hand each parsed entry to ingestEntry. Nothing leaves this machine.\n//\n// Wayfinder ticket #126 (map #121), semantics from\n// docs/research/harness-adapters-2026-08.md (§pi-mono).\n//\n// STANDING NON-GOAL (locked in #13): raw transcripts, prompts, absolute paths,\n// and repo names never leave the machine. pi's files are MORE sensitive than\n// the other harnesses': there is no separate history file, so raw prompts,\n// bashExecution output, base64 screenshots, provider error bodies and the\n// munged-absolute-path directory names all sit on the same lines the scanner\n// parses. The analyzer reads named fields only; the directory name is never\n// even counted (the header's `cwd` is, as an opaque key). Streaming line by\n// line keeps a pasted screenshot from pulling megabytes into memory.\n// `~/.pi/agent/auth.json` holds credentials — only `sessions/` is ever walked.\n// Read errors are swallowed, not thrown: the error object carries the path.\n\nimport { createReadStream, type Dirent } from \"node:fs\";\nimport { readdir, realpath, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport readline from \"node:readline\";\n\nimport { asNum, asObj, asStr } from \"../shared/aggregate.js\";\nimport { emptyScanStats, type ScanStats } from \"../shared/window.js\";\nimport {\n\ttype Aggregate,\n\tcreateFileState,\n\tcreateFoldState,\n\ttype FoldState,\n\tingestEntry,\n} from \"./analyzer.js\";\n\n/**\n * The newest session-format version this scanner understands. The vendor doc\n * states the ladder (v1 linear, v2 tree, v3 renamed hookMessage to custom);\n * none of the changes touched `usage`, `model` or `timestamp`, so v1-v3 all\n * read with one fold. A file ABOVE the ceiling may have reshaped those fields,\n * so it counts as unreadable — a visible coverage figure — rather than being\n * misread as zeros.\n */\nexport const MAX_SESSION_VERSION = 3;\n\n/** `PI_CODING_AGENT_DIR` honored, `~/.pi/agent` the default — mirrors pi. */\nexport function piAgentDir(): string {\n\treturn (\n\t\tprocess.env.PI_CODING_AGENT_DIR || path.join(homedir(), \".pi\", \"agent\")\n\t);\n}\n\n/**\n * Only `sessions/` is read — `auth.json` (credentials), `settings.json` and\n * the ACP session map live beside it and are out of bounds. A run started\n * with `--session-dir` writes outside every discoverable root and is\n * invisible, which is silence — the direction #40 permits.\n */\nexport function sessionRoots(): string[] {\n\tconst override = process.env.PI_CODING_AGENT_SESSION_DIR;\n\treturn [override || path.join(piAgentDir(), \"sessions\")];\n}\n\n/**\n * pi names every session file `<munged-ISO-start>_<uuid>.jsonl`\n * (`2026-07-23T16-54-00-149Z_019f8fe5-….jsonl`). Shared with `detect` (#101).\n */\nconst SESSION_FILE_RE =\n\t/^\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}-\\d{3}Z_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\.jsonl$/;\n\n/** What counts as a pi session file. */\nexport function isSessionFile(basename: string): boolean {\n\treturn SESSION_FILE_RE.test(basename);\n}\n\n/** Recursive walk — the real layout is two levels (one directory per cwd). */\nasync function* walkSessions(dir: string): AsyncGenerator<string> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const e of entries) {\n\t\tconst full = path.join(dir, e.name);\n\t\tif (e.isDirectory()) yield* walkSessions(full);\n\t\telse if (e.isFile() && isSessionFile(e.name)) yield full;\n\t}\n}\n\nexport type ScanOptions = {\n\t/** Only count entries with a timestamp at or after this epoch ms. */\n\tsinceMs?: number;\n\tonProgress?: (files: number) => void;\n\t/** Override the discovered roots. Tests only. */\n\troots?: string[];\n};\n\nexport async function scan(\n\tagg: Aggregate,\n\topts: ScanOptions = {},\n): Promise<ScanStats> {\n\tconst stats: ScanStats = emptyScanStats();\n\tconst visited = new Set<string>();\n\t// /fork and /clone duplicate entries ACROSS files, so the dedup state is\n\t// one per scan, not one per file.\n\tconst fold = createFoldState();\n\n\tfor (const root of opts.roots ?? sessionRoots()) {\n\t\tif (!(await exists(root))) continue;\n\t\tfor await (const file of walkSessions(root)) {\n\t\t\tstats.filesFound++;\n\n\t\t\tlet resolved: string;\n\t\t\ttry {\n\t\t\t\tresolved = await realpath(file);\n\t\t\t} catch {\n\t\t\t\tresolved = file;\n\t\t\t}\n\t\t\tif (visited.has(resolved)) {\n\t\t\t\tstats.filesSkippedAsDuplicate++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvisited.add(resolved);\n\n\t\t\t// Session files are append-only, so a file untouched since the window\n\t\t\t// opened cannot hold an in-window entry.\n\t\t\tif (opts.sinceMs !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tconst st = await stat(file);\n\t\t\t\t\tif (st.mtimeMs < opts.sinceMs) {\n\t\t\t\t\t\tstats.filesSkippedByMtime++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t/* unreadable stat — fall through and try to read it */\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tagg.files++;\n\t\t\tstats.filesRead++;\n\t\t\tif (opts.onProgress && agg.files % 200 === 0) opts.onProgress(agg.files);\n\t\t\ttry {\n\t\t\t\tconst verdict = await ingestFile(agg, fold, file, opts.sinceMs);\n\t\t\t\tif (verdict === \"foreign\") {\n\t\t\t\t\t// The first line is not a pi session header: another tool wrote\n\t\t\t\t\t// this file. Its usage stayed out of the aggregate entirely.\n\t\t\t\t\tstats.filesForeign++;\n\t\t\t\t\tstats.filesRead--;\n\t\t\t\t\tconst seen = stats.foreignOriginators.get(\"(no-pi-header)\") ?? 0;\n\t\t\t\t\tstats.foreignOriginators.set(\"(no-pi-header)\", seen + 1);\n\t\t\t\t} else if (verdict === \"version-too-new\") {\n\t\t\t\t\tstats.filesUnreadable++;\n\t\t\t\t\tstats.filesRead--;\n\t\t\t\t\tstats.unreadableFiles.push({\n\t\t\t\t\t\tpath: path.relative(root, file),\n\t\t\t\t\t\treason: \"version-too-new\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Swallow deliberately: the error object carries the absolute path.\n\t\t\t\tstats.filesUnreadable++;\n\t\t\t\tstats.filesRead--;\n\t\t\t\tstats.unreadableFiles.push({\n\t\t\t\t\tpath: path.relative(root, file),\n\t\t\t\t\treason: \"read-error\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\treturn stats;\n}\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Stream one session file through the fold. The vendor put the fingerprint on\n * line 1 — `{\"type\":\"session\"}` with a numeric `version` — so the verdict\n * lands before any usage is folded, and a foreign or too-new file leaves the\n * aggregate untouched by construction (no parse-then-fold buffering needed).\n */\nasync function ingestFile(\n\tagg: Aggregate,\n\tfold: FoldState,\n\tfile: string,\n\tsinceMs?: number,\n): Promise<\"ok\" | \"foreign\" | \"version-too-new\"> {\n\tconst rl = readline.createInterface({\n\t\tinput: createReadStream(file, { encoding: \"utf8\" }),\n\t\tcrlfDelay: Number.POSITIVE_INFINITY,\n\t});\n\tconst state = createFileState();\n\tlet first = true;\n\ttry {\n\t\tfor await (const line of rl) {\n\t\t\tif (!line) continue;\n\t\t\tagg.lines++;\n\t\t\tlet entry: unknown;\n\t\t\ttry {\n\t\t\t\tentry = JSON.parse(line);\n\t\t\t} catch {\n\t\t\t\tagg.parseErrors++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (first) {\n\t\t\t\tfirst = false;\n\t\t\t\tconst header = asObj(entry);\n\t\t\t\tconst version = header ? asNum(header.version) : 0;\n\t\t\t\tif (!header || asStr(header.type) !== \"session\" || version < 1) {\n\t\t\t\t\tagg.lines--;\n\t\t\t\t\treturn \"foreign\";\n\t\t\t\t}\n\t\t\t\tif (version > MAX_SESSION_VERSION) {\n\t\t\t\t\tagg.lines--;\n\t\t\t\t\treturn \"version-too-new\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tingestEntry(agg, entry, state, fold, sinceMs);\n\t\t}\n\t} finally {\n\t\trl.close();\n\t}\n\treturn \"ok\";\n}\n","// The pi coding agent behind the harness seam (#66 decision 6) — wayfinder\n// ticket #126 (map #121). The payload discriminator is the catalog slug,\n// `pi-mono` (the repo is earendil-works/pi-mono; the binary is `pi`).\n\nimport { hasRecentFile } from \"../shared/recency.js\";\nimport type {\n\tHarnessAdapter,\n\tHarnessDetectOptions,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"../types.js\";\nimport { createAggregate } from \"./analyzer.js\";\nimport { isSessionFile, scan, sessionRoots } from \"./scan.js\";\n\nexport const PI_HARNESS_NAME = \"pi-mono\";\n\n/**\n * pi's vendor-assigned tool surface — seven names, published in the vendor's\n * own docs (usage.md §tools). Same fail-closed mechanism as the other\n * adapters: a literal set, never a pattern. Everything outside it comes from\n * a user extension and publishes only as a per-category count. pi has no MCP,\n * no subagents and no skill tool by explicit vendor design, so those\n * categories stay absent rather than zero (#40).\n */\nexport const PI_BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"read\",\n\t\"bash\",\n\t\"edit\",\n\t\"write\",\n\t\"grep\",\n\t\"find\",\n\t\"ls\",\n]);\n\nexport const piAdapter: HarnessAdapter = {\n\tname: PI_HARNESS_NAME,\n\tbuiltinTools: PI_BUILTIN_TOOLS,\n\n\tasync detect(opts: HarnessDetectOptions): Promise<boolean> {\n\t\treturn hasRecentFile(\n\t\t\topts.roots ?? sessionRoots(),\n\t\t\tisSessionFile,\n\t\t\topts.sinceMs,\n\t\t);\n\t},\n\n\tasync scan(opts: HarnessScanOptions): Promise<HarnessScan> {\n\t\tconst aggregate = createAggregate();\n\t\tconst stats = await scan(aggregate, {\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.onProgress ? { onProgress: opts.onProgress } : {}),\n\t\t});\n\t\treturn { aggregate, stats };\n\t},\n};\n","// The wire payload builder — the only thing in this module that decides what\n// leaves the machine.\n//\n// Wayfinder ticket #37 (map #29). Shape fixed by the wire-format grilling #33;\n// nothing here is open design.\n//\n// Two invariants this file is responsible for:\n// 1. FAIL-CLOSED NAMES. Every freeform name is matched against an allowlist\n// before it can reach the payload; unmatched names publish only as\n// per-category counts (#33 decisions 2-4). Model ids are the sole exempt\n// class (decision 3) and are charset/length sanitized instead.\n// 2. COST IS ABSENT, NOT ZEROED. With `publishCost` off, the cost fields are\n// not in the payload at all (#33 decision 11) — there is nothing to\n// \"reveal\" server-side, because nothing was transmitted.\n\nimport { baseModelId, pricingTableFor } from \"@aistack/pricing\";\nimport {\n\ttype Aggregate,\n\tcleanName,\n\ttype Finalized,\n\tfinalize,\n\ttype ModelRow,\n} from \"./aggregate.js\";\nimport {\n\ttype Atom,\n\tfilterAtoms,\n\ttype KeptPrivateAtom,\n\tNAME_CATEGORIES,\n\ttype NameCategory,\n\ttype SyncConfig,\n} from \"./allowlist.js\";\nimport { type ScanStats, windowStartMs } from \"./window.js\";\n\nexport const SCHEMA_VERSION = 1;\n\nexport type PayloadModel = {\n\t/** Vendor-assigned id, sanitized. `catalogSlug` is resolved SERVER-side at read time. */\n\tid: string;\n\ttokenShare: number;\n\ttokens: {\n\t\tinput: number;\n\t\toutput: number;\n\t\tcacheWrite: number;\n\t\tcacheRead: number;\n\t};\n\tapiEquivalentUSD?: number;\n\t/**\n\t * The table that produced `apiEquivalentUSD` — present exactly when the\n\t * dollars are (#136). Per model, not per payload: one opencode payload mixes\n\t * vendors, so a single top-level id would cite one table for dollars drawn\n\t * from two.\n\t */\n\tpricingTable?: string;\n};\n\nexport type PayloadAtom = { name: string; callShare: number };\n\nexport type PayloadInventory = {\n\tbuiltinTools: PayloadAtom[];\n\tmcpServers: PayloadAtom[];\n\tskills: PayloadAtom[];\n\tsubagents: PayloadAtom[];\n\tslashCommands: PayloadAtom[];\n\t/** DISTINCT names withheld per category, so the gap in the shares is explained. */\n\twithheld: {\n\t\tbuiltinTools: number;\n\t\tmcpServers: number;\n\t\tskills: number;\n\t\tsubagents: number;\n\t\tslashCommands: number;\n\t};\n};\n\nexport type MeasuredPayload = {\n\tschemaVersion: number;\n\t/** Client clock. The server stamps its own `receivedAt` (#33 decision 6). */\n\tcapturedAt: number;\n\twindow: { days: number; from: string; to: string };\n\tharness: { name: string; version: string | null };\n\t/**\n\t * The one table the models' citations agree on, or `null` — when\n\t * `publishCost` is off, when nothing priced, and when a mixed-vendor payload\n\t * cites several tables (the per-model `pricingTable` fields carry the truth,\n\t * and joining them here would blow the server's 64-character name bound).\n\t */\n\tpricingTable: string | null;\n\tactivity: {\n\t\tsessions: number;\n\t\tactiveDays: number;\n\t\t/** COUNT only. Project directory names are munged absolute paths and never travel. */\n\t\tprojects: number;\n\t\ttotalTokens: number;\n\t\tcacheHitShare: number;\n\t\tsubagentShare: number;\n\t};\n\tmodels: PayloadModel[];\n\tinventory: PayloadInventory;\n\tcoverage: {\n\t\tfilesScanned: number;\n\t\tfilesUnreadable: number;\n\t\tlinesParsed: number;\n\t\tlinesFailed: number;\n\t};\n\texcludedTokens: { unpriced: number; synthetic: number };\n};\n\n// ---------------------------------------------------------------------------\n// Sanitization\n// ---------------------------------------------------------------------------\n\n/**\n * Model ids are exempt from the allowlist (#33 decision 3) precisely because\n * they are vendor-assigned: on the day a new Claude model ships, fail-closing it\n * would make its tokens silently vanish from every sync and understate cost with\n * no visible cause. Exempt is not unchecked, though — the id still becomes a\n * database key and a rendered string, so charset and length are bounded here.\n */\nconst MODEL_ID_UNSAFE_RE = /[^A-Za-z0-9._:-]+/g;\nconst MODEL_ID_MAX = 64;\n\nexport function sanitizeModelId(id: string): string {\n\tconst collapsed = cleanName(id)\n\t\t.replace(MODEL_ID_UNSAFE_RE, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\");\n\tif (collapsed.length === 0) return \"unknown\";\n\treturn collapsed.length > MODEL_ID_MAX\n\t\t? collapsed.slice(0, MODEL_ID_MAX)\n\t\t: collapsed;\n}\n\nconst round4 = (n: number): number => Math.round(n * 10_000) / 10_000;\nconst round2 = (n: number): number => Math.round(n * 100) / 100;\nconst utcDate = (ms: number): string => new Date(ms).toISOString().slice(0, 10);\n\n// ---------------------------------------------------------------------------\n// Inventory\n// ---------------------------------------------------------------------------\n\nconst toAtoms = (pairs: ReadonlyArray<readonly [string, number]>): Atom[] =>\n\tpairs.map(([name, count]) => ({ name, count }));\n\n/**\n * Shares are computed over ALL observed calls, including withheld ones.\n *\n * Renormalizing over only the allowlisted atoms would make the published shares\n * sum to 1.0 and read as a complete inventory — a withheld MCP server carrying\n * 90% of the calls would leave no trace. Keeping the true denominator means the\n * shares sum to less than 1 exactly when something was withheld, and the\n * `withheld` counts say how many things.\n */\nfunction buildCategory(\n\tobserved: ReadonlyArray<readonly [string, number]>,\n\tcurated: ReadonlySet<string>,\n\toptIns: readonly string[],\n\tdenominator: number,\n): { atoms: PayloadAtom[]; withheld: number; keptPrivate: KeptPrivateAtom[] } {\n\t// The union is where #42 decision 1 lands: a name publishes if it is curated\n\t// OR the owner ticked it. Filtering itself is unchanged — still client-side,\n\t// still fail-closed, still before the send. What moves is who judged the name.\n\tconst publishable = new Set([...curated, ...optIns]);\n\tconst {\n\t\tallowed: kept,\n\t\tkeptPrivate,\n\t\twithheld,\n\t} = filterAtoms(toAtoms(observed), { publishable, curated });\n\treturn {\n\t\tatoms: kept.map((a) => ({\n\t\t\tname: a.name,\n\t\t\tcallShare: denominator ? round4(a.count / denominator) : 0,\n\t\t})),\n\t\twithheld,\n\t\tkeptPrivate,\n\t};\n}\n\nconst sumCounts = (pairs: ReadonlyArray<readonly [string, number]>): number => {\n\tlet n = 0;\n\tfor (const [, c] of pairs) n += c;\n\treturn n;\n};\n\n// ---------------------------------------------------------------------------\n// Models\n// ---------------------------------------------------------------------------\n\ntype ModelGroup = {\n\tid: string;\n\ttotalTokens: number;\n\tinput: number;\n\toutput: number;\n\tcacheWrite: number;\n\tcacheRead: number;\n\tcostUSD: number;\n\tunpricedTokens: number;\n\tanyUnpriceable: boolean;\n\t/** The table citing this group's rates; every row shares it (one base model). */\n\ttable: string | null;\n};\n\n/**\n * Collapse the analyzer's pricing keys into vendor-assigned ids.\n *\n * The analyzer prices fast mode under a synthetic `claude-opus-5#fast` key\n * because it bills at a different rate ($10/$50 vs $5/$25). That suffix is OURS,\n * not the vendor's, so publishing it would hand the server an id that cannot\n * resolve against the models catalog — the exact silent-disappearance failure\n * decision 3 exists to prevent. The rows are therefore merged back onto the base\n * id here. Cost stays exact because it was already accumulated per response at\n * the fast rate; what is lost is the fast-mode share itself, which the payload\n * has no field for and which is a candidate for a later schema bump.\n */\nfunction groupModels(rows: readonly ModelRow[]): ModelGroup[] {\n\tconst groups = new Map<string, ModelGroup>();\n\tfor (const r of rows) {\n\t\tconst id = sanitizeModelId(baseModelId(r.modelKey));\n\t\tlet g = groups.get(id);\n\t\tif (!g) {\n\t\t\tg = {\n\t\t\t\tid,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcostUSD: 0,\n\t\t\t\tunpricedTokens: 0,\n\t\t\t\tanyUnpriceable: false,\n\t\t\t\ttable: null,\n\t\t\t};\n\t\t\tgroups.set(id, g);\n\t\t}\n\t\tg.table ??= pricingTableFor(r.modelKey);\n\t\tg.totalTokens += r.totalTokens;\n\t\tg.input += r.tokens.input;\n\t\tg.output += r.tokens.output;\n\t\tg.cacheWrite +=\n\t\t\tr.tokens.cacheWrite5m +\n\t\t\tr.tokens.cacheWrite1h +\n\t\t\tr.tokens.cacheWriteUnsplit;\n\t\tg.cacheRead += r.tokens.cacheRead;\n\t\tg.costUSD += r.costUSD ?? 0;\n\t\tg.unpricedTokens += r.unpricedTokens;\n\t\tif (r.costUSD === null) g.anyUnpriceable = true;\n\t}\n\treturn [...groups.values()].sort(\n\t\t(a, b) => b.totalTokens - a.totalTokens || a.id.localeCompare(b.id),\n\t);\n}\n\nfunction buildModels(\n\trows: readonly ModelRow[],\n\ttotalTokens: number,\n\tpublishCost: boolean,\n): PayloadModel[] {\n\treturn groupModels(rows).map((g) => {\n\t\tconst model: PayloadModel = {\n\t\t\tid: g.id,\n\t\t\ttokenShare: totalTokens ? round4(g.totalTokens / totalTokens) : 0,\n\t\t\ttokens: {\n\t\t\t\tinput: g.input,\n\t\t\t\toutput: g.output,\n\t\t\t\tcacheWrite: g.cacheWrite,\n\t\t\t\tcacheRead: g.cacheRead,\n\t\t\t},\n\t\t};\n\t\t// Absent, not zero: a partially-priced model reporting a dollar figure\n\t\t// would understate without saying so. `excludedTokens.unpriced` carries\n\t\t// the tokens that were left out. Dollars and their citation travel\n\t\t// together (#136) — a figure without its table may not render anywhere.\n\t\tif (\n\t\t\tpublishCost &&\n\t\t\t!g.anyUnpriceable &&\n\t\t\tg.unpricedTokens === 0 &&\n\t\t\tg.table !== null\n\t\t) {\n\t\t\tmodel.apiEquivalentUSD = round2(g.costUSD);\n\t\t\tmodel.pricingTable = g.table;\n\t\t}\n\t\treturn model;\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Build\n// ---------------------------------------------------------------------------\n\nexport type BuildPayloadInput = {\n\taggregate: Aggregate;\n\tstats: ScanStats;\n\tsyncConfig: SyncConfig;\n\t/** Client clock, epoch ms. The same value used to derive the scan window. */\n\tnow: number;\n\twindowDays: number;\n\t/** The adapter's payload discriminator, e.g. `\"claude-code\"` (#66). */\n\tharnessName: string;\n\t/** The adapter's fail-closed vendor tool set (#66 decision 3). */\n\tbuiltinTools: ReadonlySet<string>;\n};\n\nexport type BuiltPayload = {\n\tpayload: MeasuredPayload;\n\t/** The same numbers unfiltered, for the local report and the approve gate. */\n\tfinalized: Finalized;\n\t/**\n\t * Every observed name that will NOT publish, by category — the gate's review\n\t * list (#42 decision 1, wired in #44).\n\t *\n\t * This is the one thing here that is deliberately NOT in the payload. It is\n\t * the list of names the user has not agreed to publish, so it stays on the\n\t * machine; the payload carries only the per-category COUNT.\n\t */\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>;\n};\n\nexport function buildPayload(input: BuildPayloadInput): BuiltPayload {\n\tconst {\n\t\taggregate: agg,\n\t\tstats,\n\t\tsyncConfig,\n\t\tnow,\n\t\twindowDays,\n\t\tharnessName,\n\t\tbuiltinTools,\n\t} = input;\n\tconst finalized = finalize(agg);\n\tconst { publishCost, allowlist, optIns } = syncConfig;\n\n\tconst fromMs = windowStartMs(now, windowDays);\n\tconst from = utcDate(fromMs);\n\tconst to = utcDate(now);\n\n\t// Counted against the reported window rather than taken from the aggregate's\n\t// size: a clock-skewed, imported, or restored transcript dated in the future\n\t// would otherwise push activeDays past `windowDays`, printing an impossible\n\t// value under a payload that claims to be deterministic.\n\tlet activeDays = 0;\n\tfor (const d of agg.activeDays) if (d >= from && d <= to) activeDays++;\n\n\tconst totalToolCalls = finalized.totalToolCalls;\n\tconst builtins = buildCategory(\n\t\tfinalized.tools,\n\t\tbuiltinTools,\n\t\toptIns.builtinTools,\n\t\ttotalToolCalls,\n\t);\n\tconst mcp = buildCategory(\n\t\tfinalized.mcpServers,\n\t\tnew Set(allowlist.mcpServers),\n\t\toptIns.mcpServers,\n\t\tsumCounts(finalized.mcpServers),\n\t);\n\tconst skills = buildCategory(\n\t\tfinalized.skills,\n\t\tnew Set(allowlist.skills),\n\t\toptIns.skills,\n\t\tsumCounts(finalized.skills),\n\t);\n\tconst subagents = buildCategory(\n\t\tfinalized.subagents,\n\t\tnew Set(allowlist.subagents),\n\t\toptIns.subagents,\n\t\tsumCounts(finalized.subagents),\n\t);\n\tconst slash = buildCategory(\n\t\tfinalized.slashCommands,\n\t\tnew Set(allowlist.slashCommands),\n\t\toptIns.slashCommands,\n\t\tsumCounts(finalized.slashCommands),\n\t);\n\n\tconst models = buildModels(\n\t\tfinalized.models,\n\t\tfinalized.totalTokens,\n\t\tpublishCost,\n\t);\n\t// The citation lives on each model (#136). The top-level field survives for\n\t// readers of the old shape and states the one table everything agrees on —\n\t// never a false single citation over a mixed payload.\n\tconst citedTables = [\n\t\t...new Set(models.flatMap((m) => (m.pricingTable ? [m.pricingTable] : []))),\n\t];\n\n\tconst payload: MeasuredPayload = {\n\t\tschemaVersion: SCHEMA_VERSION,\n\t\tcapturedAt: now,\n\t\twindow: { days: windowDays, from, to },\n\t\tharness: {\n\t\t\tname: harnessName,\n\t\t\tversion:\n\t\t\t\tfinalized.harnessVersion === null\n\t\t\t\t\t? null\n\t\t\t\t\t: sanitizeModelId(finalized.harnessVersion),\n\t\t},\n\t\tpricingTable: citedTables.length === 1 ? citedTables[0] : null,\n\t\tactivity: {\n\t\t\tsessions: finalized.sessions,\n\t\t\tactiveDays,\n\t\t\tprojects: finalized.projects,\n\t\t\ttotalTokens: finalized.totalTokens,\n\t\t\tcacheHitShare: round4(finalized.cacheHitShare),\n\t\t\tsubagentShare: round4(finalized.sidechainShare),\n\t\t},\n\t\tmodels,\n\t\tinventory: {\n\t\t\tbuiltinTools: builtins.atoms,\n\t\t\tmcpServers: mcp.atoms,\n\t\t\tskills: skills.atoms,\n\t\t\tsubagents: subagents.atoms,\n\t\t\tslashCommands: slash.atoms,\n\t\t\twithheld: {\n\t\t\t\tbuiltinTools: builtins.withheld,\n\t\t\t\tmcpServers: mcp.withheld,\n\t\t\t\tskills: skills.withheld,\n\t\t\t\tsubagents: subagents.withheld,\n\t\t\t\tslashCommands: slash.withheld,\n\t\t\t},\n\t\t},\n\t\tcoverage: {\n\t\t\tfilesScanned: stats.filesRead,\n\t\t\tfilesUnreadable: stats.filesUnreadable,\n\t\t\tlinesParsed: agg.lines - agg.parseErrors,\n\t\t\tlinesFailed: agg.parseErrors,\n\t\t},\n\t\texcludedTokens: {\n\t\t\tunpriced: finalized.unpricedTokens,\n\t\t\tsynthetic: agg.syntheticTokens,\n\t\t},\n\t};\n\n\treturn {\n\t\tpayload,\n\t\tfinalized,\n\t\tkeptPrivate: {\n\t\t\tbuiltinTools: builtins.keptPrivate,\n\t\t\tmcpServers: mcp.keptPrivate,\n\t\t\tskills: skills.keptPrivate,\n\t\t\tsubagents: subagents.keptPrivate,\n\t\t\tslashCommands: slash.keptPrivate,\n\t\t},\n\t};\n}\n\n/**\n * What `POST /api/cli/sync` takes: one sealed payload PER DETECTED HARNESS,\n * one unsealed half shared across them (#66 decision 5). The batch is atomic\n * server-side, so two harnesses cannot wipe each other's staged names — which\n * is what two sequential per-harness publishes would have done, because the\n * staged list is a whole-list replace per stack.\n */\nexport type SyncBody = {\n\tpayloads: MeasuredPayload[];\n\tkeptPrivate?: Record<NameCategory, KeptPrivateAtom[]>;\n\t/**\n\t * The machine's standing auto-sync opt-in (#78). Not measurement and not a\n\t * name — it is the one bit of local state the backend cannot otherwise see,\n\t * and `auto_sync_enabled` has nothing to fire on without it.\n\t *\n\t * It rides BESIDE the payloads, never inside one: the payload validator is\n\t * closed, and that closedness is the privacy claim.\n\t */\n\tautoSync?: { enabled: boolean; frequencyHours: number };\n\t/**\n\t * How this sync fired (#102, sent by #103). `auto` means a SessionStart hook\n\t * ran it with nobody watching; `manual` means a human typed the command.\n\t *\n\t * The server stamps `lastAutoSyncAt` from it, so the web switch can tell\n\t * on-and-working from on-but-never-fired. It rides beside the payloads for\n\t * the same reason `autoSync` does: the payload validator is closed.\n\t */\n\ttrigger?: SyncTrigger;\n};\n\n/** The two ways a sync can fire. Absent on an old CLI, and that reads as manual. */\nexport type SyncTrigger = \"manual\" | \"auto\";\n\n/**\n * Union the per-harness kept-private lists into the one list the wire carries.\n *\n * One list, not one per harness, because consent is per NAME (#66 decision 5):\n * the owner ticks \"alp-river\", not \"alp-river as seen by Codex\". Counts merge\n * by (category, name); the group survives from whichever harness saw it first.\n */\nexport function mergeKeptPrivate(\n\thalves: ReadonlyArray<Record<NameCategory, KeptPrivateAtom[]>>,\n): Record<NameCategory, KeptPrivateAtom[]> {\n\tconst out = {} as Record<NameCategory, KeptPrivateAtom[]>;\n\tfor (const category of NAME_CATEGORIES) {\n\t\tconst merged = new Map<string, KeptPrivateAtom>();\n\t\tfor (const half of halves) {\n\t\t\tfor (const atom of half[category]) {\n\t\t\t\tconst held = merged.get(atom.name);\n\t\t\t\tif (held) held.count += atom.count;\n\t\t\t\telse merged.set(atom.name, { ...atom });\n\t\t\t}\n\t\t}\n\t\tout[category] = [...merged.values()].sort(\n\t\t\t(a, b) => b.count - a.count || a.name.localeCompare(b.name),\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * Assemble the request body from the built payloads, one per detected harness.\n *\n * The two halves ride in ONE request (#48): a second call would let them drift\n * against a newer snapshot. They stay SEPARATE objects because the payload's\n * validator is closed and rejects any extra key — that closedness is the privacy\n * claim, so a kept-private name may sit beside the payloads and never inside one.\n *\n * The switch is read from the sync config the server just served. Off — or a\n * config the machine could not fetch, which reads as off — sends the payloads\n * alone and the names stay on the machine.\n */\nexport function buildSyncBody(\n\tbuilt: readonly BuiltPayload[],\n\tsyncConfig: SyncConfig,\n\tautoSync?: { enabled: boolean; frequencyHours: number },\n\ttrigger: SyncTrigger = \"manual\",\n): SyncBody {\n\tconst payloads = built.map((b) => b.payload);\n\tconst base: SyncBody = autoSync\n\t\t? { payloads, autoSync, trigger }\n\t\t: { payloads, trigger };\n\tif (!syncConfig.reviewKeptPrivate) return base;\n\treturn {\n\t\t...base,\n\t\tkeptPrivate: mergeKeptPrivate(built.map((b) => b.keptPrivate)),\n\t};\n}\n","// Local transcript analysis -> the measured-layer wire payload.\n//\n// Wayfinder ticket #37 (map #29), reshaped around the adapter seam by #67\n// (map #60). Everything here runs on the user's machine; only the payloads\n// returned by `buildPayload` are ever candidates to leave it, and only after\n// the approve gate the send channel owns (ticket #41).\n//\n// Typical use:\n//\n// const now = Date.now();\n// const sinceMs = windowStartMs(now, DEFAULT_WINDOW_DAYS);\n// const { config } = await loadSyncConfig({ baseUrl });\n// for (const adapter of await detectedAdapters(sinceMs)) {\n// const { aggregate, stats } = await adapter.scan({ sinceMs });\n// const built = buildPayload({\n// aggregate, stats, syncConfig: config, now,\n// windowDays: DEFAULT_WINDOW_DAYS,\n// harnessName: adapter.name,\n// builtinTools: adapter.builtinTools,\n// });\n// }\n\nimport { CLAUDE_HARNESS_NAME, claudeAdapter } from \"./claude/adapter.js\";\nimport { CODEX_HARNESS_NAME, codexAdapter } from \"./codex/adapter.js\";\nimport { OPENCODE_HARNESS_NAME, opencodeAdapter } from \"./opencode/adapter.js\";\nimport { PI_HARNESS_NAME, piAdapter } from \"./pi/adapter.js\";\nimport { DEFAULT_WINDOW_DAYS, windowStartMs } from \"./shared/window.js\";\nimport type { HarnessAdapter } from \"./types.js\";\n\nexport {\n\tapiEquivalentCost,\n\tbaseModelId,\n\tCACHE_READ_MULTIPLIER,\n\tCACHE_WRITE_1H_MULTIPLIER,\n\tCACHE_WRITE_5M_MULTIPLIER,\n\ttype CacheMultipliers,\n\tcacheMultipliersFor,\n\tGOOGLE_PRICING_TABLE_VERSION,\n\tisLocalModel,\n\tisPricedModel,\n\tLOCAL_PRICING_TABLE_VERSION,\n\tmodelKeyFor,\n\tnormalizeModel,\n\tOPENAI_PRICING_TABLE_VERSION,\n\tPRICING_TABLE_VERSION,\n\tPROVIDER_SEPARATOR,\n\ttype PricePeriod,\n\tpriceAt,\n\tSONNET_5_INTRO_ENDS_MS,\n\tsplitModelKey,\n\ttype TokenCounts,\n\tvendorModelId,\n} from \"@aistack/pricing\";\nexport { CLAUDE_HARNESS_NAME, claudeAdapter } from \"./claude/adapter.js\";\nexport {\n\ttype Aggregate as ClaudeAggregate,\n\tcreateAggregate,\n\ttype IngestContext,\n\tingestRecord,\n} from \"./claude/analyzer.js\";\nexport { type ScanOptions, scan, transcriptRoots } from \"./claude/scan.js\";\nexport { CODEX_HARNESS_NAME, codexAdapter } from \"./codex/adapter.js\";\nexport {\n\tOPENCODE_BUILTIN_TOOLS,\n\tOPENCODE_HARNESS_NAME,\n\topencodeAdapter,\n} from \"./opencode/adapter.js\";\nexport {\n\tPI_BUILTIN_TOOLS,\n\tPI_HARNESS_NAME,\n\tpiAdapter,\n} from \"./pi/adapter.js\";\nexport {\n\ttype Aggregate,\n\tcleanName,\n\ttype Finalized,\n\tfinalize,\n\tisDisplaySafeName,\n\ttype ModelRow,\n\ttype ModelUsage,\n\tnewestVersion,\n} from \"./shared/aggregate.js\";\nexport {\n\ttype Atom,\n\ttype AutoSyncPermission,\n\tBUILTIN_TOOLS,\n\tBUNDLED_SYNC_CONFIG,\n\ttype CuratedAllowlist,\n\tEMPTY_OPT_INS,\n\ttype FilteredAtoms,\n\ttype FilterSets,\n\tfilterAtoms,\n\ttype KeptPrivateAtom,\n\ttype LoadedSyncConfig,\n\tloadSyncConfig,\n\tNAME_CATEGORIES,\n\ttype NameCategory,\n\ttype OptInNames,\n\tpluginGroup,\n\ttype SyncConfig,\n\ttype SyncConfigSource,\n} from \"./shared/allowlist.js\";\nexport { BUNDLED_CURATED_ALLOWLIST } from \"./shared/bundled-allowlist.js\";\nexport {\n\ttype BuildPayloadInput,\n\ttype BuiltPayload,\n\tbuildPayload,\n\tbuildSyncBody,\n\ttype MeasuredPayload,\n\tmergeKeptPrivate,\n\ttype PayloadAtom,\n\ttype PayloadInventory,\n\ttype PayloadModel,\n\tSCHEMA_VERSION,\n\ttype SyncBody,\n\tsanitizeModelId,\n} from \"./shared/payload.js\";\nexport { hasRecentFile } from \"./shared/recency.js\";\nexport {\n\tDEFAULT_WINDOW_DAYS,\n\ttype ScanStats,\n\twindowStartMs,\n} from \"./shared/window.js\";\nexport type {\n\tHarnessAdapter,\n\tHarnessDetectOptions,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"./types.js\";\n\n/**\n * Every harness this build can read, in the order their payloads publish.\n * Registration order is also display order at the gate, so Claude Code — the\n * documented default — stays first.\n */\nexport const HARNESS_ADAPTERS: readonly HarnessAdapter[] = [\n\tclaudeAdapter,\n\tcodexAdapter,\n\topencodeAdapter,\n\tpiAdapter,\n];\n\n/** Display name for a harness discriminator. One name per harness, defined here. */\nexport function harnessLabel(name: string): string {\n\tif (name === CLAUDE_HARNESS_NAME) return \"Claude Code\";\n\tif (name === CODEX_HARNESS_NAME) return \"Codex\";\n\t// These brands spell themselves lowercase, so the discriminator IS the\n\t// label — but each is an explicit row, so a rename cannot leak a raw slug.\n\tif (name === OPENCODE_HARNESS_NAME) return \"opencode\";\n\tif (name === PI_HARNESS_NAME) return \"pi-mono\";\n\treturn name;\n}\n\n/** The detected harnesses as one phrase: \"Claude Code or Codex\". */\nexport function harnessListLabel(adapters: readonly HarnessAdapter[]): string {\n\treturn adapters.map((a) => harnessLabel(a.name)).join(\" or \");\n}\n\n/**\n * The window every detection uses when the caller has no scan in hand. The\n * scan passes its own window start instead, which is the same number.\n */\nexport function detectionSinceMs(now: number = Date.now()): number {\n\treturn windowStartMs(now, DEFAULT_WINDOW_DAYS);\n}\n\n/**\n * The adapters that wrote a transcript inside the window — the machine's LIVE\n * harnesses (#101). A stale one is skipped everywhere this is read: it does not\n * scan, it does not publish, it earns no upsell and it gets no hook.\n */\nexport async function detectedAdapters(\n\tsinceMs: number = detectionSinceMs(),\n): Promise<HarnessAdapter[]> {\n\tconst out: HarnessAdapter[] = [];\n\tfor (const adapter of HARNESS_ADAPTERS) {\n\t\tif (await adapter.detect({ sinceMs })) out.push(adapter);\n\t}\n\treturn out;\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport { stackGet } from \"../api.js\";\nimport { getToken } from \"../config.js\";\nimport {\n\tbold,\n\tdim,\n\tdivider,\n\tintro,\n\tlime,\n\tlimeBold,\n\tlines,\n\toutro,\n\toutroCancel,\n\toutroError,\n\toutroSkipped,\n\tsection,\n\tyellow,\n} from \"../theme.js\";\n\nexport async function createCommand() {\n\tintro(\"create\");\n\n\tconst token = getToken();\n\tif (!token) {\n\t\tp.log.error(\n\t\t\t`Not authenticated. Run ${limeBold(\"npx @use-aistack/cli login\")} first.`,\n\t\t);\n\t\toutroError(\"not authenticated\");\n\t\tprocess.exit(1);\n\t}\n\n\tconst s = p.spinner();\n\ts.start(\"Fetching stack...\");\n\n\tlet stack: Awaited<ReturnType<typeof stackGet>>;\n\ttry {\n\t\tstack = await stackGet(token);\n\t\tif (!stack) {\n\t\t\ts.stop(\"Not found\");\n\t\t\tp.log.error(\"No stack found. Create a stack on aistack.to first.\");\n\t\t\toutroError(\"not found\");\n\t\t\tprocess.exit(1);\n\t\t}\n\t\ts.stop(bold(stack.name));\n\t} catch (err) {\n\t\ts.stop(\"Failed to fetch stack\");\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\toutroError(\"error\");\n\t\tprocess.exit(1);\n\t}\n\n\tconst localFiles: FileToWrite[] = [];\n\n\tfor (const item of stack.resources) {\n\t\tfor (const file of item.files ?? []) {\n\t\t\tlocalFiles.push({ path: file.path ?? file.name, content: file.content });\n\t\t}\n\t}\n\n\t// Linked resources have no files to write — surface them so they aren't\n\t// silently dropped on download (GitHub repos + package refs like MCP servers).\n\tconst linked = stack.resources.filter(\n\t\t(item) => (item.upstream || item.pkg) && !item.files?.length,\n\t);\n\tif (linked.length > 0) {\n\t\tsection(\"linked\", linked.length);\n\t\tlines([dim(\"view only\")]);\n\t\tlines(\n\t\t\tlinked.map((item) =>\n\t\t\t\tdim(\n\t\t\t\t\titem.upstream?.repoUrl ??\n\t\t\t\t\t\t(item.pkg ? `${item.pkg.registry}:${item.pkg.id}` : \"\"),\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t}\n\n\tif (localFiles.length === 0) {\n\t\tp.log.warn(\"No local files to write.\");\n\t\toutroSkipped(\"nothing to create\");\n\t\treturn;\n\t}\n\n\tconst cwd = process.cwd();\n\tconst toWrite: FileToWrite[] = [];\n\tconst skipped: { path: string; differs: boolean }[] = [];\n\n\tfor (const f of localFiles) {\n\t\tconst fullPath = join(cwd, f.path);\n\t\tif (existsSync(fullPath)) {\n\t\t\tconst existing = readFileSync(fullPath, \"utf-8\");\n\t\t\tskipped.push({ path: f.path, differs: existing !== f.content });\n\t\t} else {\n\t\t\ttoWrite.push(f);\n\t\t}\n\t}\n\n\tsection(\"local files\", localFiles.length);\n\tlines(toWrite.map((f) => lime(`+ ${f.path}`)));\n\tlines(\n\t\tskipped.map((f) =>\n\t\t\tf.differs\n\t\t\t\t? `${yellow(`= ${f.path}`)} ${dim(\"(differs)\")}`\n\t\t\t\t: dim(`= ${f.path} (identical)`),\n\t\t),\n\t);\n\n\tif (toWrite.length === 0) {\n\t\tdivider();\n\t\tp.log.info(\"All local files already exist.\");\n\t\toutroSkipped(\"nothing to write\");\n\t\treturn;\n\t}\n\n\tdivider();\n\n\tconst confirm = await p.confirm({\n\t\tmessage: `Write ${lime(String(toWrite.length))} new files? ${dim(`(${skipped.length} skipped)`)}`,\n\t});\n\n\tif (p.isCancel(confirm) || !confirm) {\n\t\toutroCancel();\n\t\tprocess.exit(0);\n\t}\n\n\tfor (const f of toWrite) {\n\t\tconst fullPath = join(cwd, f.path);\n\t\tconst dir = dirname(fullPath);\n\t\tmkdirSync(dir, { recursive: true });\n\t\twriteFileSync(fullPath, f.content);\n\t}\n\n\tp.log.success(\n\t\t`${lime(String(toWrite.length))} written, ${dim(String(skipped.length) + \" skipped\")}`,\n\t);\n\toutro(lime(\"done\"));\n}\n\ninterface FileToWrite {\n\tpath: string;\n\tcontent: string;\n}\n","import { hostname } from \"node:os\";\nimport * as p from \"@clack/prompts\";\nimport open from \"open\";\nimport { authPoll, authStart } from \"../api.js\";\nimport { saveToken } from \"../config.js\";\nimport { dim, intro, lime, limeBold, outro, outroError } from \"../theme.js\";\n\n/**\n * What to call this machine on the account's linked-machines list (#49).\n *\n * The hostname is only a proposal — the approval page shows it in an editable\n * field before anything is stored. Trimmed to the server's 64-character bound so\n * a long hostname is dropped by us rather than silently by the server, and\n * `.local` is stripped because mDNS suffixes carry no information for a reader.\n */\nexport function proposedMachineName(\n\tread: () => string = hostname,\n): string | undefined {\n\ttry {\n\t\tconst name = read()\n\t\t\t.trim()\n\t\t\t.replace(/\\.local$/i, \"\");\n\t\tif (!name || name.length > 64) return undefined;\n\t\treturn name;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * The device-auth flow itself, without intro/outro framing or process.exit —\n * so `sync` can run it inline on an unlinked machine (#74) and `login` stays\n * the standalone command. Logs its own progress and errors; returns whether a\n * token was saved.\n */\nexport async function performLogin(): Promise<boolean> {\n\tconst s = p.spinner();\n\ts.start(\"Starting authentication...\");\n\n\tlet session: Awaited<ReturnType<typeof authStart>>;\n\ttry {\n\t\tsession = await authStart(proposedMachineName());\n\t\ts.stop(\"Session created\");\n\t} catch (err) {\n\t\ts.stop(\"Failed to start authentication\");\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\treturn false;\n\t}\n\n\tp.log.info(`${dim(\"CODE\")} ${limeBold(session.userCode)}`);\n\tp.log.info(`${dim(\"OPEN\")} ${dim(session.authUrl)}`);\n\n\ttry {\n\t\tawait open(session.authUrl);\n\t} catch {\n\t\tp.log.warn(\n\t\t\t\"Could not open browser automatically. Please visit the URL above.\",\n\t\t);\n\t}\n\n\ts.start(\"Waiting for approval...\");\n\n\tconst maxAttempts = 36;\n\tfor (let i = 0; i < maxAttempts; i++) {\n\t\tawait new Promise((resolve) => setTimeout(resolve, 5000));\n\n\t\ttry {\n\t\t\tconst result = await authPoll(session.secretId);\n\n\t\t\tif (result.status === \"approved\" && result.token) {\n\t\t\t\ts.stop(lime(\"Authenticated\"));\n\t\t\t\tsaveToken(result.token, result.userId);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (result.status === \"expired\") {\n\t\t\t\ts.stop(\"Session expired\");\n\t\t\t\tp.log.error(\"Authentication session expired. Please try again.\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} catch (err) {\n\t\t\ts.stop(\"Error polling\");\n\t\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\ts.stop(\"Timed out\");\n\tp.log.error(\"Authentication timed out after 3 minutes. Please try again.\");\n\treturn false;\n}\n\nexport async function loginCommand() {\n\tintro(\"login\");\n\n\tif (!(await performLogin())) {\n\t\toutroError(\"error\");\n\t\tprocess.exit(1);\n\t}\n\n\tp.log.success(\n\t\t`Token saved. Run ${limeBold(\"npx @use-aistack/cli sync\")} to publish your usage.`,\n\t);\n\toutro(lime(\"done\"));\n}\n","// The documented default sync surface (#56, built by #55/#57).\n//\n// The MCP-free channel: a human types `aistack sync` in their own terminal,\n// so a real TTY exists and the gate can be a @clack/prompts select. Same\n// staged-bytes property as the MCP server (#41): the summary and the confirm\n// derive from the exact serialized `bodyJson`, and that string goes on the\n// wire byte-identical. One gate policy, two renderings.\n//\n// Fail-closed: ctrl-C, ESC, EOF, and a missing TTY all resolve to \"nothing\n// was sent\" before any network call.\n\nimport * as p from \"@clack/prompts\";\nimport { BASE_URL, syncPublish } from \"../api.js\";\nimport {\n\tCODEX_TRUST_INSTRUCTION,\n\tcodexAutoSyncHookInstalled,\n\tcodexHookTrusted,\n} from \"../autosync/codexHook.js\";\nimport {\n\tdisableAutoSync,\n\tenableAutoSync,\n\tsettleAutoSync,\n} from \"../autosync/optin.js\";\nimport { runAutoSync } from \"../autosync/run.js\";\nimport { DEFAULT_FREQUENCY_HOURS, getSettings, getToken } from \"../config.js\";\nimport { stageSync } from \"../sync/stage.js\";\nimport { dim, intro, lime, outro, outroCancel, outroError } from \"../theme.js\";\nimport { offerConnectUpsell } from \"./connect.js\";\nimport { performLogin } from \"./login.js\";\n\nexport interface SyncOptions {\n\t/** `--auto` → true, `--auto on` → \"on\", `--auto off` → \"off\". */\n\tauto?: boolean | string;\n\t/** `--every <hours>`, applied with `--auto on`. */\n\tevery?: string;\n}\n\nexport async function syncCommand(options: SyncOptions = {}): Promise<void> {\n\t// The silent path (#62): no TTY, no prompts, no upsells. Publishes only\n\t// under the standing opt-in and always exits 0 — the hook command's `||`\n\t// offline fallback must never fire on a mere sync failure.\n\tif (options.auto === true) {\n\t\tawait runAutoSync({ baseUrl: BASE_URL });\n\t\treturn;\n\t}\n\n\tif (options.auto === \"on\" || options.auto === \"off\") {\n\t\tintro(\"sync\");\n\t\tconst result =\n\t\t\toptions.auto === \"on\"\n\t\t\t\t? await enableAutoSync(\n\t\t\t\t\t\toptions.every\n\t\t\t\t\t\t\t? Number.parseInt(options.every, 10) || DEFAULT_FREQUENCY_HOURS\n\t\t\t\t\t\t\t: DEFAULT_FREQUENCY_HOURS,\n\t\t\t\t\t)\n\t\t\t\t: await disableAutoSync();\n\t\tif (result.ok) {\n\t\t\tp.log.success(result.message);\n\t\t\toutro(\"done\");\n\t\t} else {\n\t\t\toutroError(result.message);\n\t\t\tprocess.exitCode = 1;\n\t\t}\n\t\treturn;\n\t}\n\tif (options.auto !== undefined) {\n\t\tintro(\"sync\");\n\t\toutroError(`unknown --auto value \"${options.auto}\" — use on or off`);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tintro(\"sync\");\n\n\t// The interactive surface is where a silent failure becomes visible (#62):\n\t// report the last auto-sync outcome, whatever it was.\n\tconst lastAuto = getSettings().autoSyncState?.lastResult;\n\tif (lastAuto !== undefined) {\n\t\tp.log.message(dim(`auto-sync: ${lastAuto}`));\n\t}\n\n\t// The Codex hook does not run until the user trusts it via /hooks (#65 §6).\n\t// Repeat the one-time instruction while the hook is installed but the trust\n\t// hash is verifiably absent; an unreadable config stays silent.\n\tif (codexAutoSyncHookInstalled() && codexHookTrusted() === false) {\n\t\tp.log.warn(CODEX_TRUST_INSTRUCTION);\n\t}\n\n\t// The whole premise of this channel is a human at a terminal. A pipe or a\n\t// model-launched Bash call has no TTY, and a gate that cannot ask must not\n\t// send (#31) — refuse before scanning anything.\n\tif (!process.stdin.isTTY || !process.stdout.isTTY) {\n\t\toutroError(\"sync needs an interactive terminal — nothing was sent\");\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\t// An unlinked machine used to hard-block with \"run login first\" (#74). The\n\t// TTY gate above guarantees a human is present, so the device-auth browser\n\t// hop fits here — `sync` is the whole onboarding command.\n\tif (getToken() === null) {\n\t\tp.log.message(\n\t\t\t\"This machine is not linked to an aistack account yet — linking it first.\",\n\t\t);\n\t\tif (!(await performLogin())) {\n\t\t\toutroError(\"login failed — nothing was sent\");\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tconst s = p.spinner();\n\ts.start(\"Scanning local agent transcripts\");\n\tlet staged: Awaited<ReturnType<typeof stageSync>>;\n\ttry {\n\t\tstaged = await stageSync({ baseUrl: BASE_URL });\n\t} catch (e) {\n\t\ts.stop(\"Scan failed\");\n\t\toutroError(e instanceof Error ? e.message : String(e));\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\ts.stop(\"Scan complete\");\n\n\t// Beat one — the same full summary the MCP preview returns, verbatim,\n\t// printed behind the clack bar so it reads as one flow.\n\tp.log.message(staged.summary.split(\"\\n\").join(\"\\n\"));\n\n\tif (staged.blockedReason !== null) {\n\t\toutroError(staged.blockedReason);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\t// Beat two — the same short dialog text, as a select. The enum mirrors the\n\t// elicitation's {publish, cancel}; cancel is the initial value, so Enter\n\t// alone publishes nothing.\n\tconst decision = await p.select({\n\t\tmessage: staged.dialog.split(\"\\n\").join(dim(\" · \")),\n\t\toptions: [\n\t\t\t{ value: \"cancel\", label: \"Cancel\", hint: \"nothing leaves this machine\" },\n\t\t\t{ value: \"publish\", label: \"Publish\" },\n\t\t],\n\t\tinitialValue: \"cancel\",\n\t});\n\n\tif (p.isCancel(decision) || decision !== \"publish\") {\n\t\toutroCancel(\"nothing was sent\");\n\t\treturn;\n\t}\n\n\ts.start(\"Publishing\");\n\ttry {\n\t\tconst res = await syncPublish(staged.token as string, staged.bodyJson);\n\t\ts.stop(\"Published\");\n\t\tconst lines = [\n\t\t\t`Snapshot received at ${new Date(res.receivedAt).toISOString()}`,\n\t\t\tlime(res.url),\n\t\t];\n\t\tif (res.keptPrivate.refused && staged.body.keptPrivate !== undefined) {\n\t\t\tlines.push(\n\t\t\t\t\"Note: the kept-private names were refused by the server — the review switch is off there now. They stayed on this machine.\",\n\t\t\t);\n\t\t} else if (res.keptPrivate.stored > 0) {\n\t\t\tlines.push(\n\t\t\t\t`${res.keptPrivate.stored} kept-private names went up for your review at ${res.url}/changes`,\n\t\t\t);\n\t\t}\n\t\tp.log.message(lines.join(\"\\n\"));\n\t\t// EVERY interactive sync settles auto-sync against the stack's own\n\t\t// answer (#103): it reconciles the missing triggers when the switch is\n\t\t// on, keeps quiet when it is off, and asks only when nobody has decided.\n\t\t// This is what completes a web-first enable, and what gives a harness\n\t\t// adopted months later its trigger.\n\t\t//\n\t\t// At most one ask per sync (#62): the auto-sync opt-in is the primary\n\t\t// ask; the connect upsell yields and waits for a later sync.\n\t\tconst asked = await settleAutoSync(staged.config.autoSync);\n\t\tif (!asked) await offerConnectUpsell();\n\t\toutro(\"done\");\n\t} catch (e) {\n\t\ts.stop(\"Publish failed\");\n\t\toutroError(e instanceof Error ? e.message : String(e));\n\t\tprocess.exitCode = 1;\n\t}\n}\n","// The Codex half of the background trigger (#66 decision 4, built in #67):\n// a `SessionStart` hook in ~/.codex/hooks.json, matcher `startup` only.\n//\n// Two ways this differs from the Claude hook (hook.ts):\n//\n// 1. THE COMMAND SELF-DETACHES. Codex parses `async` but does not honor it —\n// the runner awaits the hook with a timeout and kill_on_drop (#65 §6).\n// So the command backgrounds the real work under `setsid nohup … &` and\n// exits 0 immediately; kill_on_drop kills only the already-exited shell.\n// 2. THE TRUST GATE. Codex pins each hook command's sha256 as a\n// `trusted_hash` in config.toml. An untrusted or CHANGED command silently\n// does not run, and only the user can trust it, via /hooks inside Codex.\n// That is why the command text uses `@latest` — the text (and therefore\n// the hash) stays stable across CLI updates — and why install prints the\n// one-time trust instruction.\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nimport type { HookResult } from \"./hook.js\";\n\nfunction codexHome(): string {\n\treturn process.env.CODEX_HOME || join(homedir(), \".codex\");\n}\n\nexport function codexHooksFile(): string {\n\treturn join(codexHome(), \"hooks.json\");\n}\n\n// `codexPresent()` lived here and keyed on $CODEX_HOME existing. #101 replaced\n// it with `codexAdapter.detect()`: a directory proves an install, and an\n// install is not a user. A fresh Codex with no sessions yet gets its hook from\n// the interactive sync that reconciles hooks (#103), one session later.\n\nexport function codexConfigFile(): string {\n\treturn join(codexHome(), \"config.toml\");\n}\n\n/**\n * EXACT quoting, settled here (#66 left it to this ticket): the outer layer is\n * a JSON string in hooks.json; Codex runs it through a shell, and the single\n * `sh -c '…'` wrapper makes the detach group unambiguous regardless of how\n * that outer shell tokenizes. No `||` fallback like the Claude command — the\n * fallback semantics live INSIDE the detached shell so the hook process itself\n * still exits instantly.\n *\n * DO NOT REFORMAT THIS STRING. Its sha256 is the trust hash; any byte change\n * un-trusts the hook on every machine until each user re-runs /hooks.\n */\nexport const CODEX_HOOK_COMMAND =\n\t\"sh -c 'setsid nohup sh -c \\\"npx -y @use-aistack/cli@latest sync --auto || npx -y --prefer-offline @use-aistack/cli sync --auto\\\" >/dev/null 2>&1 &'\";\n\ninterface HookEntry {\n\ttype: string;\n\tcommand?: string;\n\ttimeout?: number;\n}\n\ninterface HookMatcher {\n\tmatcher?: string;\n\thooks?: HookEntry[];\n}\n\ninterface CodexHooksJson {\n\thooks?: Record<string, HookMatcher[]>;\n\t[key: string]: unknown;\n}\n\n/** Recognize our hook across versions: package name plus the auto flag. */\nfunction isOurs(entry: HookEntry): boolean {\n\treturn (\n\t\ttypeof entry.command === \"string\" &&\n\t\tentry.command.includes(\"@use-aistack/cli\") &&\n\t\tentry.command.includes(\"sync --auto\")\n\t);\n}\n\nfunction readHooksJson(\n\tfile: string,\n): { settings: CodexHooksJson } | { error: string } {\n\tif (!existsSync(file)) return { settings: {} };\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\tif (raw && typeof raw === \"object\" && !Array.isArray(raw)) {\n\t\t\treturn { settings: raw as CodexHooksJson };\n\t\t}\n\t\treturn { error: `${file} does not hold a JSON object` };\n\t} catch {\n\t\t// Never rewrite a file we cannot parse — it is the user's Codex\n\t\t// configuration, and a rewrite would destroy whatever is in it.\n\t\treturn { error: `${file} is not valid JSON — fix it, then retry` };\n\t}\n}\n\n/** The instruction install prints; the interactive sync repeats it while untrusted. */\nexport const CODEX_TRUST_INSTRUCTION =\n\t\"Codex hook written — open Codex and run /hooks once to trust it, or it will not run.\";\n\n/**\n * Add the SessionStart auto-sync hook, matcher `startup` only (resume/clear/\n * compact would multiply runs; the freshness gate would drop them anyway).\n * Idempotent: an existing aistack entry is replaced, not duplicated.\n */\nexport function installCodexAutoSyncHook(\n\tfile: string = codexHooksFile(),\n): HookResult {\n\tconst read = readHooksJson(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst hooks = settings.hooks ?? {};\n\tconst sessionStart = Array.isArray(hooks.SessionStart)\n\t\t? hooks.SessionStart\n\t\t: [];\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tkept.push({\n\t\tmatcher: \"startup\",\n\t\thooks: [{ type: \"command\", command: CODEX_HOOK_COMMAND, timeout: 30 }],\n\t});\n\n\tsettings.hooks = { ...hooks, SessionStart: kept };\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: CODEX_TRUST_INSTRUCTION };\n}\n\n/**\n * Remove only our hook. Other hooks and events stay. A missing file or an\n * absent hook is success — the goal state already holds.\n */\nexport function removeCodexAutoSyncHook(\n\tfile: string = codexHooksFile(),\n): HookResult {\n\tif (!existsSync(file))\n\t\treturn { ok: true, message: \"no Codex hook to remove\" };\n\tconst read = readHooksJson(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst sessionStart = settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) {\n\t\treturn { ok: true, message: \"no Codex hook to remove\" };\n\t}\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tconst hooks = { ...settings.hooks };\n\tif (kept.length > 0) {\n\t\thooks.SessionStart = kept;\n\t} else {\n\t\tdelete hooks.SessionStart;\n\t}\n\tif (Object.keys(hooks).length > 0) {\n\t\tsettings.hooks = hooks;\n\t} else {\n\t\tdelete settings.hooks;\n\t}\n\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: `hook removed from ${file}` };\n}\n\n/** Is the Codex auto-sync hook present? */\nexport function codexAutoSyncHookInstalled(\n\tfile: string = codexHooksFile(),\n): boolean {\n\tconst read = readHooksJson(file);\n\tif (\"error\" in read) return false;\n\tconst sessionStart = read.settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) return false;\n\treturn sessionStart.some((m) => (m.hooks ?? []).some((h) => isOurs(h)));\n}\n\n/**\n * Best-effort trust check: Codex pins each trusted hook's sha256 under\n * `[hooks.state]` in config.toml, so the command's hash appearing anywhere in\n * that file reads as trusted. `null` when the file cannot be read — unknown,\n * not untrusted, so the caller does not nag on a parse quirk.\n */\nexport function codexHookTrusted(\n\tconfigFile: string = codexConfigFile(),\n): boolean | null {\n\tlet text: string;\n\ttry {\n\t\ttext = readFileSync(configFile, \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n\tconst hash = createHash(\"sha256\").update(CODEX_HOOK_COMMAND).digest(\"hex\");\n\treturn text.includes(hash);\n}\n","// The auto-sync opt-in (#62, map #60), narrowed to active harnesses by #101,\n// and moved onto the stack by #103.\n//\n// WHERE THE PERMISSION LIVES: on the stack, in Convex (#100 decision 2, #102).\n// The local flag and the SessionStart hooks are what this machine holds, and\n// neither of them grants anything — `sync --auto` asks the stack before it\n// publishes. So enable grants on the stack first, revoke revokes here first,\n// and `reconcileAutoSync` brings a machine in line with an answer the owner\n// gave somewhere else.\n//\n// This ask is the PRIMARY post-sync ask: it runs first, and the connect-claude\n// upsell yields to a later sync (at most one ask per sync, each asked once,\n// each persisted). Any explicit answer persists to\n// ~/.config/aistack/settings.json; ctrl-C is not an answer and the question\n// returns on the next sync. A stack that has already decided is never asked at\n// all — `settleAutoSync` reconciles it instead.\n\nimport * as p from \"@clack/prompts\";\nimport { setAutoSync } from \"../api.js\";\nimport {\n\tDEFAULT_FREQUENCY_HOURS,\n\tgetSettings,\n\tgetToken,\n\tsaveSettings,\n} from \"../config.js\";\nimport { CLAUDE_HARNESS_NAME } from \"../harness/claude/adapter.js\";\nimport { CODEX_HARNESS_NAME } from \"../harness/codex/adapter.js\";\nimport {\n\ttype AutoSyncPermission,\n\tDEFAULT_WINDOW_DAYS,\n\tdetectedAdapters,\n\tharnessLabel,\n\tharnessListLabel,\n} from \"../harness/index.js\";\nimport type { HarnessAdapter } from \"../harness/types.js\";\nimport { dim, limeBold } from \"../theme.js\";\nimport {\n\tcodexAutoSyncHookInstalled,\n\tinstallCodexAutoSyncHook,\n\tremoveCodexAutoSyncHook,\n} from \"./codexHook.js\";\nimport {\n\tautoSyncHookInstalled,\n\ttype HookResult,\n\tinstallAutoSyncHook,\n\tremoveAutoSyncHook,\n} from \"./hook.js\";\n\nexport interface EnableDeps {\n\tsettingsFile?: string;\n\tinstallHook?: () => HookResult;\n\tremoveHook?: () => HookResult;\n\tinstallCodexHook?: () => HookResult;\n\tremoveCodexHook?: () => HookResult;\n\t/** Is the Claude Code trigger already on this machine? */\n\thookInstalledImpl?: () => boolean;\n\t/** Is the Codex trigger already on this machine? */\n\tcodexHookInstalledImpl?: () => boolean;\n\t/** Override the detected harness set. Tests only. */\n\tdetectedImpl?: () => Promise<HarnessAdapter[]>;\n\tgetTokenImpl?: () => string | null;\n\tsetAutoSyncImpl?: typeof setAutoSync;\n}\n\n/** What an unlinked machine is told when it tries to grant the permission. */\nexport const NOT_LINKED =\n\t\"This machine is not linked to an aistack account, and the auto-sync permission lives on your stack. Run `npx @use-aistack/cli sync` first.\";\n\n/** The message when the machine has no active harness to trigger anything. */\nexport const NOTHING_TO_TRIGGER = `No Claude Code or Codex session on this machine in the last ${DEFAULT_WINDOW_DAYS} days, so nothing would trigger an auto-sync. Nothing was changed.`;\n\n/**\n * Turn auto-sync on: grant the permission on the STACK, then write the\n * SessionStart hooks — one per DETECTED harness (#101). A hook for a harness\n * whose last session predates the window is a trigger that will never fire, and\n * its install is the step that made a dead Claude Code install look alive.\n *\n * THE STACK IS ASKED FIRST (#103). The permission is the thing that lets a\n * publish happen, and the hooks are dumb local triggers for it — so a refusal\n * from aistack.to leaves the machine exactly as it was, with no hook running an\n * npx at every session start for a permission nobody granted.\n *\n * When a hook write then fails, the local flag is NOT persisted. The stack\n * shows on-but-never-fired, which is true, and the next interactive sync\n * reconciles the missing trigger.\n */\nexport async function enableAutoSync(\n\tfrequencyHours: number = DEFAULT_FREQUENCY_HOURS,\n\tdeps: EnableDeps = {},\n): Promise<HookResult> {\n\tconst detected = await (deps.detectedImpl ?? detectedAdapters)();\n\tif (detected.length === 0) {\n\t\treturn { ok: false, message: NOTHING_TO_TRIGGER };\n\t}\n\tconst names = new Set(detected.map((a) => a.name));\n\n\tconst token = (deps.getTokenImpl ?? getToken)();\n\tif (token === null) return { ok: false, message: NOT_LINKED };\n\ttry {\n\t\tawait (deps.setAutoSyncImpl ?? setAutoSync)(token, {\n\t\t\tenabled: true,\n\t\t\tfrequencyHours,\n\t\t});\n\t} catch (e) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `Auto-sync was not turned on: ${e instanceof Error ? e.message : String(e)}`,\n\t\t};\n\t}\n\n\tif (names.has(CLAUDE_HARNESS_NAME)) {\n\t\tconst result = (deps.installHook ?? installAutoSyncHook)();\n\t\tif (!result.ok) return result;\n\t}\n\n\tlet trustLine: string | null = null;\n\tif (names.has(CODEX_HARNESS_NAME)) {\n\t\tconst codexResult = (deps.installCodexHook ?? installCodexAutoSyncHook)();\n\t\tif (!codexResult.ok) return codexResult;\n\t\t// The one-time /hooks trust step (#65 §6) — repeated by the next\n\t\t// interactive sync while the hook stays untrusted.\n\t\ttrustLine = codexResult.message;\n\t}\n\n\tsaveSettings(\n\t\t{\n\t\t\tautoSyncAnswered: true,\n\t\t\tautoSync: { enabled: true, frequencyHours },\n\t\t},\n\t\tdeps.settingsFile,\n\t);\n\treturn {\n\t\tok: true,\n\t\tmessage: [\n\t\t\t`Auto-sync is on — about every ${frequencyHours}h when a ${harnessListLabel(detected)} session starts. Turn it off any time: npx @use-aistack/cli sync --auto off`,\n\t\t\t...(trustLine ? [trustLine] : []),\n\t\t].join(\"\\n\"),\n\t};\n}\n\n/**\n * Revoke: flip the local flag, remove the hooks, then take the permission off\n * the stack. The flag flips even when a hook file cannot be edited, because\n * `sync --auto` gates on it — a stale hook without the flag publishes nothing.\n *\n * THE LOCAL HALF RUNS FIRST, the mirror image of enable (#103). A revoke must\n * never be blocked by an unreachable network: this machine stops publishing the\n * moment the flag flips, whatever aistack.to says next. The server half is\n * still reported when it fails, because it is the half that reaches every OTHER\n * machine, and the owner can also use the switch on their stack page.\n *\n * Removal is unconditional, unlike install: a revoke must reach the hook of a\n * harness that has since gone quiet, and removing an absent hook is success.\n */\nexport async function disableAutoSync(\n\tdeps: EnableDeps = {},\n): Promise<HookResult> {\n\tconst settings = getSettings(deps.settingsFile);\n\tsaveSettings(\n\t\t{\n\t\t\tautoSyncAnswered: true,\n\t\t\tautoSync: {\n\t\t\t\tenabled: false,\n\t\t\t\tfrequencyHours:\n\t\t\t\t\tsettings.autoSync?.frequencyHours ?? DEFAULT_FREQUENCY_HOURS,\n\t\t\t},\n\t\t},\n\t\tdeps.settingsFile,\n\t);\n\tconst result = (deps.removeHook ?? removeAutoSyncHook)();\n\tconst codexResult = (deps.removeCodexHook ?? removeCodexAutoSyncHook)();\n\tconst failures = [result, codexResult]\n\t\t.filter((r) => !r.ok)\n\t\t.map((r) => r.message);\n\n\tconst token = (deps.getTokenImpl ?? getToken)();\n\tif (token !== null) {\n\t\ttry {\n\t\t\tawait (deps.setAutoSyncImpl ?? setAutoSync)(token, { enabled: false });\n\t\t} catch (e) {\n\t\t\tfailures.push(\n\t\t\t\t`aistack.to was not told (${e instanceof Error ? e.message : String(e)}) — your other machines keep the permission`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (failures.length > 0) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `Auto-sync is off on this machine (nothing will publish), but: ${failures.join(\"; \")}`,\n\t\t};\n\t}\n\treturn {\n\t\tok: true,\n\t\tmessage: \"Auto-sync is off. The hooks were removed.\",\n\t};\n}\n\n/**\n * Bring the machine in line with the permission the stack holds (#103).\n *\n * This is not an ask and never prompts. The owner already answered, on the web\n * switch or on another machine, and this is the machine catching up with them:\n *\n * - flag ON — install a trigger for every DETECTED harness that lacks one,\n * and mirror the flag locally so `sync --auto` passes its own\n * gate. That is what makes \"flip the web switch, run one sync\"\n * the whole enable story, and it is also what gives a harness\n * adopted months later its trigger.\n * - flag OFF — touch nothing. The revoke is already in force: `sync --auto`\n * asks the stack before it publishes, so a live hook on this\n * machine publishes nothing.\n * - ABSENT — touch nothing. Nobody has decided, and the post-sync ask still\n * owns that case.\n *\n * Returns the one line to print, or `null` when there was nothing to do.\n */\nexport async function reconcileAutoSync(\n\tpermission: AutoSyncPermission | null,\n\tdeps: EnableDeps = {},\n): Promise<HookResult | null> {\n\tif (permission?.enabled !== true) return null;\n\n\tconst detected = await (deps.detectedImpl ?? detectedAdapters)();\n\tif (detected.length === 0) return null;\n\tconst names = new Set(detected.map((a) => a.name));\n\n\tconst installed: string[] = [];\n\tconst failures: string[] = [];\n\tconst install = (\n\t\tharnessName: string,\n\t\tisInstalled: () => boolean,\n\t\twrite: () => HookResult,\n\t) => {\n\t\tif (!names.has(harnessName) || isInstalled()) return;\n\t\tconst result = write();\n\t\tif (result.ok) installed.push(harnessLabel(harnessName));\n\t\telse failures.push(result.message);\n\t};\n\n\tinstall(\n\t\tCLAUDE_HARNESS_NAME,\n\t\tdeps.hookInstalledImpl ?? autoSyncHookInstalled,\n\t\tdeps.installHook ?? installAutoSyncHook,\n\t);\n\tinstall(\n\t\tCODEX_HARNESS_NAME,\n\t\tdeps.codexHookInstalledImpl ?? codexAutoSyncHookInstalled,\n\t\tdeps.installCodexHook ?? installCodexAutoSyncHook,\n\t);\n\n\tif (failures.length > 0) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `Auto-sync is on for this stack, but a trigger could not be written: ${failures.join(\"; \")}`,\n\t\t};\n\t}\n\n\tconst frequencyHours = permission.frequencyHours ?? DEFAULT_FREQUENCY_HOURS;\n\tconst local = getSettings(deps.settingsFile).autoSync;\n\tconst mirrored =\n\t\tlocal?.enabled === true && local.frequencyHours === frequencyHours;\n\tif (!mirrored) {\n\t\tsaveSettings(\n\t\t\t{ autoSyncAnswered: true, autoSync: { enabled: true, frequencyHours } },\n\t\t\tdeps.settingsFile,\n\t\t);\n\t}\n\n\t// Quiet when nothing changed. The steady state is already reported by the\n\t// `auto-sync: <last result>` line the interactive sync prints.\n\tif (installed.length === 0 && mirrored) return null;\n\treturn {\n\t\tok: true,\n\t\tmessage:\n\t\t\tinstalled.length > 0\n\t\t\t\t? `Auto-sync is on for this stack — installed the ${installed.join(\" and \")} trigger on this machine. About every ${frequencyHours}h when a session starts.`\n\t\t\t\t: `Auto-sync is on for this stack — about every ${frequencyHours}h when a ${harnessListLabel(detected)} session starts.`,\n\t};\n}\n\n/**\n * Everything an interactive sync does about auto-sync, after it publishes.\n *\n * One step with three inputs, because the stack's answer is what decides which\n * of them applies (#103): a stack that has decided is reconciled and never\n * asked again, and only a stack nobody has decided for reaches the ask. The ask\n * asked once and persisted is #62's rule, and the switch now outranks it —\n * re-asking an owner who already answered on the web is asking them twice.\n *\n * Returns true when it ASKED, so the caller knows to hold the connect upsell\n * back to a later sync (at most one ask per sync, #62).\n */\nexport async function settleAutoSync(\n\tpermission: AutoSyncPermission | null,\n\tdeps: EnableDeps = {},\n): Promise<boolean> {\n\tif (permission !== null) {\n\t\tconst result = await reconcileAutoSync(permission, deps);\n\t\tif (result === null) return false;\n\t\tif (result.ok) p.log.success(result.message);\n\t\telse p.log.warn(result.message);\n\t\treturn false;\n\t}\n\treturn offerAutoSyncOptIn(deps);\n}\n\n/**\n * The post-sync ask. Returns true when it asked (so the caller skips the\n * connect upsell this sync), false when it had nothing to ask.\n *\n * The hint names the harnesses this machine actually runs (#101): a Codex-only\n * user is told \"when a Codex session starts\", not a Claude Code sentence that\n * describes nothing they do.\n */\nexport async function offerAutoSyncOptIn(\n\tdeps: EnableDeps = {},\n): Promise<boolean> {\n\tif (getSettings(deps.settingsFile).autoSyncAnswered === true) return false;\n\n\tconst detected = await (deps.detectedImpl ?? detectedAdapters)();\n\tif (detected.length === 0) return false;\n\n\tconst answer = await p.select({\n\t\tmessage: \"Keep this stack fresh automatically?\",\n\t\toptions: [\n\t\t\t{\n\t\t\t\tvalue: \"later\",\n\t\t\t\tlabel: \"Not now\",\n\t\t\t\thint: \"this question will not come back\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalue: \"enable\",\n\t\t\t\tlabel: \"Enable\",\n\t\t\t\thint: `a silent daily sync when a ${harnessListLabel(detected)} session starts`,\n\t\t\t},\n\t\t],\n\t\tinitialValue: \"later\",\n\t});\n\n\tif (p.isCancel(answer)) return true;\n\n\tif (answer === \"enable\") {\n\t\t// The set is reused, not re-detected: the user answered the hint they saw.\n\t\tconst result = await enableAutoSync(DEFAULT_FREQUENCY_HOURS, {\n\t\t\t...deps,\n\t\t\tdetectedImpl: async () => detected,\n\t\t});\n\t\tif (result.ok) {\n\t\t\tp.log.success(result.message);\n\t\t} else {\n\t\t\tp.log.error(result.message);\n\t\t}\n\t\treturn true;\n\t}\n\n\tsaveSettings({ autoSyncAnswered: true }, deps.settingsFile);\n\tp.log.message(\n\t\t`If you change your mind: ${limeBold(\"npx @use-aistack/cli sync --auto on\")} ${dim(\n\t\t\t\"(and --auto off to revoke)\",\n\t\t)}`,\n\t);\n\treturn true;\n}\n","// The background trigger (#62, map #60): a `SessionStart` hook in\n// ~/.claude/settings.json, `async: true`.\n//\n// SessionStart, not SessionEnd — teardown is not guaranteed (crash, SIGKILL,\n// closed terminal), and at start-of-session the previous sessions are fully on\n// disk. The command runs `@latest` through npx, so unattended machines update\n// by construction. The `||` fallback covers the offline case: when the network\n// resolve of `@latest` fails, the second npx runs the cached copy.\n// `sync --auto` always exits 0, so the fallback never fires on a sync failure.\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nexport const CLAUDE_SETTINGS_FILE = join(homedir(), \".claude\", \"settings.json\");\n\nexport const AUTO_SYNC_HOOK_COMMAND =\n\t\"npx -y @use-aistack/cli@latest sync --auto || npx -y --prefer-offline @use-aistack/cli sync --auto\";\n\ninterface HookEntry {\n\ttype: string;\n\tcommand?: string;\n\tasync?: boolean;\n}\n\ninterface HookMatcher {\n\tmatcher?: string;\n\thooks?: HookEntry[];\n}\n\ninterface ClaudeSettings {\n\thooks?: Record<string, HookMatcher[]>;\n\t[key: string]: unknown;\n}\n\n/** Recognize our hook across versions: package name plus the auto flag. */\nfunction isOurs(entry: HookEntry): boolean {\n\treturn (\n\t\ttypeof entry.command === \"string\" &&\n\t\tentry.command.includes(\"@use-aistack/cli\") &&\n\t\tentry.command.includes(\"sync --auto\")\n\t);\n}\n\nexport interface HookResult {\n\tok: boolean;\n\tmessage: string;\n}\n\nfunction readClaudeSettings(\n\tfile: string,\n): { settings: ClaudeSettings } | { error: string } {\n\tif (!existsSync(file)) return { settings: {} };\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\tif (raw && typeof raw === \"object\" && !Array.isArray(raw)) {\n\t\t\treturn { settings: raw as ClaudeSettings };\n\t\t}\n\t\treturn { error: `${file} does not hold a JSON object` };\n\t} catch {\n\t\t// Never rewrite a file we cannot parse — it is the user's Claude Code\n\t\t// configuration, and a rewrite would destroy whatever is in it.\n\t\treturn { error: `${file} is not valid JSON — fix it, then retry` };\n\t}\n}\n\n/**\n * Add the SessionStart auto-sync hook. Idempotent: an existing aistack\n * auto-sync entry (any version of the command) is replaced, not duplicated.\n * All other hooks are preserved byte-for-byte in structure.\n */\nexport function installAutoSyncHook(\n\tfile: string = CLAUDE_SETTINGS_FILE,\n): HookResult {\n\tconst read = readClaudeSettings(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst hooks = settings.hooks ?? {};\n\tconst sessionStart = Array.isArray(hooks.SessionStart)\n\t\t? hooks.SessionStart\n\t\t: [];\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tkept.push({\n\t\thooks: [{ type: \"command\", command: AUTO_SYNC_HOOK_COMMAND, async: true }],\n\t});\n\n\tsettings.hooks = { ...hooks, SessionStart: kept };\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: `SessionStart hook written to ${file}` };\n}\n\n/**\n * Remove only our hook. Other SessionStart hooks and other events stay. A\n * missing file or an absent hook is success — the goal state already holds.\n */\nexport function removeAutoSyncHook(\n\tfile: string = CLAUDE_SETTINGS_FILE,\n): HookResult {\n\tif (!existsSync(file)) return { ok: true, message: \"no hook to remove\" };\n\tconst read = readClaudeSettings(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst sessionStart = settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) {\n\t\treturn { ok: true, message: \"no hook to remove\" };\n\t}\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tconst hooks = { ...settings.hooks };\n\tif (kept.length > 0) {\n\t\thooks.SessionStart = kept;\n\t} else {\n\t\tdelete hooks.SessionStart;\n\t}\n\tif (Object.keys(hooks).length > 0) {\n\t\tsettings.hooks = hooks;\n\t} else {\n\t\tdelete settings.hooks;\n\t}\n\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: `hook removed from ${file}` };\n}\n\n/** Is the auto-sync hook present? Used by status reporting. */\nexport function autoSyncHookInstalled(\n\tfile: string = CLAUDE_SETTINGS_FILE,\n): boolean {\n\tconst read = readClaudeSettings(file);\n\tif (\"error\" in read) return false;\n\tconst sessionStart = read.settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) return false;\n\treturn sessionStart.some((m) => (m.hooks ?? []).some((h) => isOurs(h)));\n}\n","// `sync --auto` — the silent background run (#62, map #60).\n//\n// The tenets from map #29 hold: passive analysis, never passive publish. TWO\n// gates let this path publish, and either one closes it:\n//\n// 1. The machine's own flag (`autoSync.enabled` in\n// ~/.config/aistack/settings.json). Local, free, and checked first.\n// 2. The STACK's permission, read from `/api/sync-config` (#102, #103). The\n// owner holds this one, from any machine and from the web switch, so a\n// revoke reaches a machine whose hooks are still live.\n//\n// No prompts, no upsells, no email, no dialogs. This path also never installs\n// or removes a hook — the interactive sync owns that (`reconcileAutoSync`).\n// The escalation ladder is: one log line per run → the next interactive sync\n// reports the last result → after 3 consecutive failures, one visible\n// systemMessage line.\n//\n// This function never sets a nonzero exit code. The hook command falls back to\n// the npx cache on `||`, and a nonzero exit from a mere sync failure would\n// fire that fallback and run the whole sync twice.\n\nimport {\n\tappendFileSync,\n\tmkdirSync,\n\treadFileSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { syncPublish } from \"../api.js\";\nimport {\n\ttype AutoSyncState,\n\tDEFAULT_FREQUENCY_HOURS,\n\tgetSettings,\n\tgetToken,\n\tsaveSettings,\n} from \"../config.js\";\nimport { loadSyncConfig } from \"../harness/shared/allowlist.js\";\nimport { stageSync } from \"../sync/stage.js\";\n\nexport const SYNC_LOG_FILE = join(homedir(), \".config\", \"aistack\", \"sync.log\");\n\n/** The log stays small: newest 200 lines, older lines fall off. */\nexport const SYNC_LOG_MAX_LINES = 200;\n\n/** The one-line fix, named in the escalation message and nowhere vaguer. */\nconst FIX_COMMAND = \"npx @use-aistack/cli sync\";\n\nexport type AutoSyncDeps = {\n\tbaseUrl: string;\n\tnow?: () => number;\n\tsettingsFile?: string;\n\tlogFile?: string;\n\tstageImpl?: typeof stageSync;\n\tpublishImpl?: typeof syncPublish;\n\tgetTokenImpl?: () => string | null;\n\tloadConfigImpl?: typeof loadSyncConfig;\n\t/** Where the systemMessage JSON goes. Defaults to stdout. */\n\temit?: (line: string) => void;\n};\n\n/** What the next interactive sync says when the stack has taken the permission away. */\nexport const REVOKED_RESULT = \"off — auto-sync is switched off for this stack\";\n\nexport function appendLogLine(file: string, line: string): void {\n\tmkdirSync(dirname(file), { recursive: true });\n\tappendFileSync(file, `${line}\\n`);\n\tconst lines = readFileSync(file, \"utf-8\").split(\"\\n\").filter(Boolean);\n\tif (lines.length > SYNC_LOG_MAX_LINES) {\n\t\twriteFileSync(file, `${lines.slice(-SYNC_LOG_MAX_LINES).join(\"\\n\")}\\n`);\n\t}\n}\n\nexport async function runAutoSync(deps: AutoSyncDeps): Promise<void> {\n\tconst now = (deps.now ?? Date.now)();\n\tconst settingsFile = deps.settingsFile;\n\tconst logFile = deps.logFile ?? SYNC_LOG_FILE;\n\tconst emit =\n\t\tdeps.emit ?? ((line: string) => process.stdout.write(`${line}\\n`));\n\tconst stamp = new Date(now).toISOString();\n\n\tconst settings = getSettings(settingsFile);\n\tconst config = settings.autoSync;\n\n\t// The hard gate. A hook left behind after a revoke publishes nothing.\n\tif (config?.enabled !== true) {\n\t\tappendLogLine(logFile, `${stamp} skipped — auto-sync is not enabled`);\n\t\treturn;\n\t}\n\n\t// The freshness gate keys on the last ATTEMPT, not the last success. A\n\t// broken setup then retries once per frequency window, not once per\n\t// session start, and still reaches the 3-failure escalation.\n\tconst frequencyHours = config.frequencyHours || DEFAULT_FREQUENCY_HOURS;\n\tconst state: AutoSyncState = settings.autoSyncState ?? {};\n\tconst lastRunAt = state.lastRunAt ?? 0;\n\tif (now - lastRunAt < frequencyHours * 3_600_000) return;\n\n\tconst stage = deps.stageImpl ?? stageSync;\n\tconst publish = deps.publishImpl ?? syncPublish;\n\tconst loadConfig = deps.loadConfigImpl ?? loadSyncConfig;\n\tconst token = (deps.getTokenImpl ?? getToken)();\n\n\tlet failure: string | null = null;\n\tlet url: string | undefined;\n\tlet revoked = false;\n\ttry {\n\t\t// The permission the STACK holds (#102/#103), read BEFORE the scan. The\n\t\t// run needs the network to publish anyway, so asking first costs nothing\n\t\t// and spares a revoked machine a full walk of its own history.\n\t\tconst loaded = await loadConfig({\n\t\t\tbaseUrl: deps.baseUrl,\n\t\t\t...(token ? { token } : {}),\n\t\t});\n\t\tif (loaded.config.autoSync?.enabled === false) {\n\t\t\t// EXPLICIT OFF ONLY. An absent flag still publishes, because that\n\t\t\t// publish is what seeds the stack from this machine (#102).\n\t\t\trevoked = true;\n\t\t} else {\n\t\t\tconst staged = await stage({\n\t\t\t\tbaseUrl: deps.baseUrl,\n\t\t\t\tnow: () => now,\n\t\t\t\ttrigger: \"auto\",\n\t\t\t\t// The same body the gate above read, so the permission and the\n\t\t\t\t// destination cannot come from two different fetches.\n\t\t\t\tloadConfigImpl: async () => loaded,\n\t\t\t\tgetTokenImpl: () => token,\n\t\t\t});\n\t\t\tif (staged.blockedReason !== null) {\n\t\t\t\tfailure = staged.blockedReason;\n\t\t\t} else {\n\t\t\t\tconst res = await publish(staged.token as string, staged.bodyJson);\n\t\t\t\turl = res.url;\n\t\t\t}\n\t\t}\n\t} catch (e) {\n\t\tfailure = e instanceof Error ? e.message : String(e);\n\t}\n\n\t// A revoke is not a failure: the streak, the warning and the last success\n\t// all stay as they were. Only the attempt is stamped, so the machine asks\n\t// once per frequency window instead of once per session start.\n\tif (revoked) {\n\t\tsaveSettings(\n\t\t\t{\n\t\t\t\tautoSyncState: { ...state, lastRunAt: now, lastResult: REVOKED_RESULT },\n\t\t\t},\n\t\t\tsettingsFile,\n\t\t);\n\t\tappendLogLine(logFile, `${stamp} skipped — ${REVOKED_RESULT}`);\n\t\treturn;\n\t}\n\n\tif (failure === null) {\n\t\tsaveSettings(\n\t\t\t{\n\t\t\t\tautoSyncState: {\n\t\t\t\t\tlastRunAt: now,\n\t\t\t\t\tlastSuccessAt: now,\n\t\t\t\t\tlastResult: `ok — published at ${stamp}`,\n\t\t\t\t\tconsecutiveFailures: 0,\n\t\t\t\t\tfailureWarned: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\tsettingsFile,\n\t\t);\n\t\tappendLogLine(logFile, `${stamp} ok — published${url ? ` ${url}` : \"\"}`);\n\t\treturn;\n\t}\n\n\tconst consecutiveFailures = (state.consecutiveFailures ?? 0) + 1;\n\tconst shouldWarn = consecutiveFailures >= 3 && state.failureWarned !== true;\n\tsaveSettings(\n\t\t{\n\t\t\tautoSyncState: {\n\t\t\t\t...state,\n\t\t\t\tlastRunAt: now,\n\t\t\t\tlastResult: `failed at ${stamp} — ${failure}`,\n\t\t\t\tconsecutiveFailures,\n\t\t\t\tfailureWarned: state.failureWarned === true || shouldWarn,\n\t\t\t},\n\t\t},\n\t\tsettingsFile,\n\t);\n\tappendLogLine(\n\t\tlogFile,\n\t\t`${stamp} fail (${consecutiveFailures} in a row) — ${failure}`,\n\t);\n\n\t// One visible line, once per failure streak. SessionStart hook JSON:\n\t// Claude Code shows `systemMessage` to the user when the async hook lands.\n\tif (shouldWarn) {\n\t\temit(\n\t\t\tJSON.stringify({\n\t\t\t\tsystemMessage: `aistack auto-sync failed ${consecutiveFailures} times in a row (${failure}). Run \\`${FIX_COMMAND}\\` in a terminal to fix it, or \\`${FIX_COMMAND} --auto off\\` to stop these runs.`,\n\t\t\t}),\n\t\t);\n\t}\n}\n","// Stage one send: scan every ACTIVE harness → build → derive the gate's text\n// from the exact bytes. Active, not installed: a harness with nothing in the\n// window is not scanned and does not publish, so a dead Claude Code install no\n// longer lands a stale snapshot next to a live Codex one (#101).\n//\n// Wayfinder ticket #41 (map #29), widened to the adapter seam by #67 (map\n// #60). The staged `bodyJson` string IS what a publish transmits — the summary\n// and the dialog are derived from it and from nothing else, so the user can\n// never approve a sentence about different bytes (#35's binding constraint).\n// The publish tool takes only the stage id; it can name WHICH staged send to\n// release, never what is in it.\n//\n// One stage covers ALL detected harnesses (#66 decision 4): the payloads ride\n// in one request so the server can land them atomically, and the kept-private\n// union is one list because consent is per name, not per harness.\n\nimport { createHash } from \"node:crypto\";\nimport { getSettings, getToken, type Settings } from \"../config.js\";\nimport { detectedAdapters } from \"../harness/index.js\";\nimport {\n\ttype KeptPrivateAtom,\n\ttype LoadedSyncConfig,\n\tloadSyncConfig,\n\ttype NameCategory,\n\ttype SyncConfig,\n} from \"../harness/shared/allowlist.js\";\nimport {\n\ttype BuiltPayload,\n\tbuildPayload,\n\tbuildSyncBody,\n\tmergeKeptPrivate,\n\ttype SyncBody,\n\ttype SyncTrigger,\n} from \"../harness/shared/payload.js\";\nimport {\n\tDEFAULT_WINDOW_DAYS,\n\ttype ScanStats,\n\twindowStartMs,\n} from \"../harness/shared/window.js\";\nimport type { HarnessAdapter } from \"../harness/types.js\";\nimport { buildGateDialog, buildGateSummary } from \"./summary.js\";\n\nexport type StagedSend = {\n\t/** Content-derived: the sha256 prefix of `bodyJson`. Same bytes, same id. */\n\tid: string;\n\t/** The exact request body a publish sends, already serialized. */\n\tbodyJson: string;\n\tbody: SyncBody;\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>;\n\tsummary: string;\n\tdialog: string;\n\tconfig: SyncConfig;\n\ttoken: string | null;\n\tstagedAt: number;\n\t/**\n\t * `null` when this stage may not publish, with `blockedReason` saying why.\n\t * A gate that cannot name its destination must not send (#33 decision 7),\n\t * so no token and no resolved stack both block here, before any dialog.\n\t */\n\tblockedReason: string | null;\n};\n\nexport type StageDeps = {\n\tbaseUrl: string;\n\tnow?: () => number;\n\tgetTokenImpl?: () => string | null;\n\tloadConfigImpl?: (opts: {\n\t\tbaseUrl: string;\n\t\ttoken?: string;\n\t}) => Promise<LoadedSyncConfig>;\n\t/** Override the adapter set. Tests only. */\n\tadaptersImpl?: (sinceMs: number) => Promise<HarnessAdapter[]>;\n\tgetSettingsImpl?: () => Settings;\n\twindowDays?: number;\n\t/**\n\t * How this sync fired (#103). Defaults to `manual`, because every caller but\n\t * the background run has a human at the keyboard.\n\t */\n\ttrigger?: SyncTrigger;\n};\n\nexport function stageId(bodyJson: string): string {\n\treturn createHash(\"sha256\").update(bodyJson).digest(\"hex\").slice(0, 12);\n}\n\nexport async function stageSync(deps: StageDeps): Promise<StagedSend> {\n\tconst now = (deps.now ?? Date.now)();\n\tconst token = (deps.getTokenImpl ?? getToken)();\n\tconst loadConfig = deps.loadConfigImpl ?? loadSyncConfig;\n\tconst adapters = deps.adaptersImpl ?? detectedAdapters;\n\tconst windowDays = deps.windowDays ?? DEFAULT_WINDOW_DAYS;\n\n\tconst { config, source } = await loadConfig({\n\t\tbaseUrl: deps.baseUrl,\n\t\t...(token ? { token } : {}),\n\t});\n\n\tconst built: BuiltPayload[] = [];\n\tconst scanStats: Record<string, ScanStats> = {};\n\t// One window start for detection AND for the scan (#101), so a harness that\n\t// counts as detected is exactly a harness with something in the window.\n\tconst sinceMs = windowStartMs(now, windowDays);\n\tfor (const adapter of await adapters(sinceMs)) {\n\t\tconst { aggregate, stats } = await adapter.scan({ sinceMs });\n\t\tscanStats[adapter.name] = stats;\n\t\tbuilt.push(\n\t\t\tbuildPayload({\n\t\t\t\taggregate,\n\t\t\t\tstats,\n\t\t\t\tsyncConfig: config,\n\t\t\t\tnow,\n\t\t\t\twindowDays,\n\t\t\t\tharnessName: adapter.name,\n\t\t\t\tbuiltinTools: adapter.builtinTools,\n\t\t\t}),\n\t\t);\n\t}\n\n\t// The opt-in the machine currently holds, read at stage time so the gate's\n\t// bytes are the bytes sent (#78). Absent from the settings file means this\n\t// machine has never answered, which the backend reads as \"never told us\".\n\tconst settings = (deps.getSettingsImpl ?? getSettings)();\n\tconst body = buildSyncBody(built, config, settings.autoSync, deps.trigger);\n\tconst bodyJson = JSON.stringify(body);\n\tconst keptPrivate = mergeKeptPrivate(built.map((b) => b.keptPrivate));\n\n\tconst ctx = {\n\t\tbody,\n\t\tkeptPrivate,\n\t\tconfig,\n\t\tsource,\n\t\tbaseUrl: deps.baseUrl,\n\t\tscanStats,\n\t};\n\n\tlet blockedReason: string | null = null;\n\tif (built.length === 0) {\n\t\tblockedReason = `No active harness on this machine — no Claude Code and no Codex transcript from the last ${windowDays} days to read.`;\n\t} else if (token === null) {\n\t\tblockedReason =\n\t\t\t\"This machine is not linked. Run `npx @use-aistack/cli login` first.\";\n\t} else if (config.stack === null) {\n\t\tblockedReason =\n\t\t\tsource === \"bundled\"\n\t\t\t\t? \"Could not fetch your settings from aistack, so the destination stack is unknown. Publish needs it. Check the network and preview again.\"\n\t\t\t\t: `The token resolves no destination stack. Create one at ${deps.baseUrl}/stacks/new, then sync again.`;\n\t}\n\n\treturn {\n\t\tid: stageId(bodyJson),\n\t\tbodyJson,\n\t\tbody,\n\t\tkeptPrivate,\n\t\tsummary: buildGateSummary(ctx),\n\t\tdialog: buildGateDialog(ctx),\n\t\tconfig,\n\t\ttoken,\n\t\tstagedAt: now,\n\t\tblockedReason,\n\t};\n}\n","// The approve gate's two beats, as text.\n//\n// Wayfinder ticket #41 (map #29), shape fixed by the spike #35 and the copy\n// locked in #48. Beat one is the FULL summary, printed as ordinary scrollable\n// transcript output. Beat two is the SHORT elicitation message — it must stay\n// short, or `Accept` falls below the fold and the gate times out (#35, 1H).\n//\n// Everything here derives from the exact bytes that will be sent (`body`),\n// plus the local-only kept-private list that deliberately never enters them.\n// Nothing in this file is accepted as a caller-supplied argument beside the\n// payload — the spike promoted that from a caution to a demonstrated property.\n\nimport { harnessLabel } from \"../harness/index.js\";\nimport type {\n\tKeptPrivateAtom,\n\tNameCategory,\n\tSyncConfig,\n\tSyncConfigSource,\n} from \"../harness/shared/allowlist.js\";\nimport { NAME_CATEGORIES } from \"../harness/shared/allowlist.js\";\nimport type { MeasuredPayload, SyncBody } from \"../harness/shared/payload.js\";\nimport type { ScanStats } from \"../harness/shared/window.js\";\n\nexport type GateContext = {\n\t/** The exact request body a publish would send. */\n\tbody: SyncBody;\n\t/** The local-only review list — never inside any payload (#44). */\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>;\n\tconfig: SyncConfig;\n\tsource: SyncConfigSource;\n\t/** Web origin for the URLs the gate prints, e.g. https://aistack.to */\n\tbaseUrl: string;\n\t/**\n\t * Per-harness scan stats, keyed by harness name — the LOCAL-ONLY detail\n\t * behind the payload's bare coverage counts (#75): unreadable file names,\n\t * error classes, foreign-file originators. Like `keptPrivate`, it rides\n\t * beside the body and never inside it.\n\t */\n\tscanStats?: Record<string, ScanStats>;\n};\n\n// ---------------------------------------------------------------------------\n// Formatting\n// ---------------------------------------------------------------------------\n\n/** `4.27B`, `40.7M`, `216k`, `950` — three significant digits, like #40. */\nexport function fmtTokens(n: number): string {\n\tconst sig = (v: number): string => {\n\t\tconst s = v.toPrecision(3);\n\t\treturn s.includes(\".\") ? s.replace(/\\.?0+$/, \"\") : s;\n\t};\n\tif (n >= 1e9) return `${sig(n / 1e9)}B`;\n\tif (n >= 1e6) return `${sig(n / 1e6)}M`;\n\tif (n >= 1e3) return `${sig(n / 1e3)}k`;\n\treturn String(n);\n}\n\n/** `≈$5,840` — whole dollars; the ≈ and \"at API prices\" wording are #37's. */\nexport function fmtUSD(n: number): string {\n\treturn `≈$${Math.round(n).toLocaleString(\"en-US\")}`;\n}\n\nconst fmtPct = (share: number): string => `${(share * 100).toFixed(1)}%`;\n\n/**\n * The dollar figure the gate names, or `null` when none may render.\n *\n * Mirrors the public display's rule (#46): a dollar figure never renders\n * without its pricing table. Summing only the models that carry the field\n * matches what actually goes up — an unpriceable model publishes tokens, not\n * dollars.\n */\nexport function totalUSD(payload: MeasuredPayload): number | null {\n\tlet sum = 0;\n\tlet any = false;\n\tfor (const m of payload.models) {\n\t\tif (m.apiEquivalentUSD === undefined) continue;\n\t\t// The citation may sit on the model (#136) or, in the old single-vendor\n\t\t// shape, on the payload. A figure neither cites stays unrendered.\n\t\tif (m.pricingTable === undefined && payload.pricingTable === null) continue;\n\t\tsum += m.apiEquivalentUSD;\n\t\tany = true;\n\t}\n\treturn any ? sum : null;\n}\n\n/** DISTINCT kept-private names, from the send bytes (`inventory.withheld`). */\nexport function withheldCount(payload: MeasuredPayload): number {\n\tconst w = payload.inventory.withheld;\n\treturn (\n\t\tw.builtinTools + w.mcpServers + w.skills + w.subagents + w.slashCommands\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Beat two — the elicitation message. Copy locked in #48; keep it SHORT.\n// ---------------------------------------------------------------------------\n\nexport function buildGateDialog(ctx: GateContext): string {\n\tconst { payloads, keptPrivate } = ctx.body;\n\tconst tokens = payloads.reduce((a, p) => a + p.activity.totalTokens, 0);\n\tconst usds = payloads\n\t\t.map((p) => totalUSD(p))\n\t\t.filter((u): u is number => u !== null);\n\tconst usd = usds.length > 0 ? usds.reduce((a, b) => a + b, 0) : null;\n\tconst days = payloads[0]?.window.days ?? 0;\n\tconst facts = [\n\t\t`${fmtTokens(tokens)} tokens`,\n\t\t`${days} days`,\n\t\t...(usd === null ? [] : [fmtUSD(usd)]),\n\t].join(\" · \");\n\n\tconst n = payloads.reduce((a, p) => a + withheldCount(p), 0);\n\tconst lines = [`Publish to aistack? ${facts}`];\n\tif (n > 0) {\n\t\tlines.push(\n\t\t\tkeptPrivate === undefined\n\t\t\t\t? `${n} name${n === 1 ? \"\" : \"s\"} stay${n === 1 ? \"s\" : \"\"} on this machine`\n\t\t\t\t: `${n} name${n === 1 ? \"\" : \"s\"} go${n === 1 ? \"es\" : \"\"} up for you to review`,\n\t\t);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Beat one — the full summary, transcript output.\n// ---------------------------------------------------------------------------\n\nconst CATEGORY_LABEL: Record<NameCategory, string> = {\n\tbuiltinTools: \"tools\",\n\tmcpServers: \"mcp\",\n\tskills: \"skills\",\n\tsubagents: \"agents\",\n\tslashCommands: \"commands\",\n};\n\n/** Kept-private rows for the gate: one row per group, then singles (#48). */\nexport function keptPrivateRows(\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>,\n): Array<{ label: string; names: number }> {\n\tconst groups = new Map<string, number>();\n\tconst singles: string[] = [];\n\tfor (const category of NAME_CATEGORIES) {\n\t\tfor (const atom of keptPrivate[category]) {\n\t\t\tif (atom.group === null) singles.push(atom.name);\n\t\t\telse groups.set(atom.group, (groups.get(atom.group) ?? 0) + 1);\n\t\t}\n\t}\n\tconst rows = [...groups].map(([label, names]) => ({ label, names }));\n\tfor (const name of singles) rows.push({ label: name, names: 1 });\n\trows.sort((a, b) => b.names - a.names || a.label.localeCompare(b.label));\n\treturn rows;\n}\n\nconst KEPT_PRIVATE_ROWS_SHOWN = 6;\n\n// The harness display names live with the harness names themselves (#101), so\n// one harness has one label everywhere. Re-exported: this module is where the\n// gate's renderers reach for it.\nexport { harnessLabel };\n\n/** How many unreadable files get named before the list truncates. */\nconst UNREADABLE_FILES_SHOWN = 5;\n\n/**\n * The local-only lines behind the bare coverage counts (#75). Everything here\n * stays on this machine: relative paths, error classes, and originator names\n * never enter the payload.\n */\nexport function scanNoteLines(stats: ScanStats, label: string): string[] {\n\tconst out: string[] = [];\n\tconst shown = stats.unreadableFiles.slice(0, UNREADABLE_FILES_SHOWN);\n\tfor (const f of shown) {\n\t\tout.push(` ${f.path} (${f.reason})`);\n\t}\n\tif (stats.unreadableFiles.length > shown.length) {\n\t\tout.push(\n\t\t\t` ...${stats.unreadableFiles.length - shown.length} more`,\n\t\t);\n\t}\n\tif (stats.filesZstdUnsupported > 0) {\n\t\tout.push(\n\t\t\t` ${stats.filesZstdUnsupported} compressed rollout${stats.filesZstdUnsupported === 1 ? \"\" : \"s\"} need Node 22.15 or newer`,\n\t\t);\n\t}\n\tif (stats.filesForeign > 0) {\n\t\tconst origins = [...stats.foreignOriginators]\n\t\t\t.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n\t\t\t.map(([name, n]) => (n > 1 ? `${name} ×${n}` : name))\n\t\t\t.join(\", \");\n\t\tout.push(\n\t\t\t`skipped ${stats.filesForeign} file${stats.filesForeign === 1 ? \"\" : \"s\"} not written by ${label} — left out (originators: ${origins})`,\n\t\t);\n\t}\n\treturn out;\n}\n\n/** One harness's payload block: window, activity, cost, models, inventory. */\nfunction payloadBlock(\n\tpayload: MeasuredPayload,\n\tshowHeader: boolean,\n\tstats?: ScanStats,\n): string[] {\n\tconst out: string[] = [];\n\tif (showHeader) {\n\t\tout.push(\n\t\t\t`— ${harnessLabel(payload.harness.name)}${payload.harness.version ? ` ${payload.harness.version}` : \"\"}`,\n\t\t);\n\t}\n\tout.push(\n\t\t`window ${payload.window.days} days · ${payload.window.from} → ${payload.window.to}`,\n\t);\n\tout.push(\n\t\t`activity ${payload.activity.sessions} sessions · ${payload.activity.activeDays} active days · ${fmtTokens(payload.activity.totalTokens)} tokens`,\n\t);\n\tconst usd = totalUSD(payload);\n\tout.push(\n\t\tusd === null\n\t\t\t? \"cost not published\"\n\t\t\t: `cost ${fmtUSD(usd)} at API prices`,\n\t);\n\n\t// Coverage is silent when clean; a degraded scan is named as a floor (#40).\n\tconst cov = payload.coverage;\n\tif (cov.filesUnreadable > 0 || cov.linesFailed > 0) {\n\t\tout.push(\n\t\t\t`coverage ${cov.filesUnreadable} files unreadable · ${cov.linesFailed} lines failed — this reading is a floor`,\n\t\t);\n\t}\n\t// Local-only detail behind those counts (#75): file names, error classes,\n\t// and the foreign-file line. Printed, never sent.\n\tif (stats) {\n\t\tout.push(...scanNoteLines(stats, harnessLabel(payload.harness.name)));\n\t}\n\n\tout.push(\"\");\n\tout.push(\"models\");\n\tfor (const m of payload.models) {\n\t\tconst dollars =\n\t\t\tusd !== null && m.apiEquivalentUSD !== undefined\n\t\t\t\t? ` ${fmtUSD(m.apiEquivalentUSD)}`\n\t\t\t\t: \"\";\n\t\tout.push(` ${m.id.padEnd(28)} ${fmtPct(m.tokenShare)}${dollars}`);\n\t}\n\n\tout.push(\"\");\n\tout.push(\"what publishes\");\n\tfor (const category of NAME_CATEGORIES) {\n\t\tconst atoms = payload.inventory[category];\n\t\tif (atoms.length === 0) continue;\n\t\tconst names = atoms.map((a) => a.name).join(\", \");\n\t\tout.push(` ${CATEGORY_LABEL[category].padEnd(9)} ${names}`);\n\t}\n\treturn out;\n}\n\nexport function buildGateSummary(ctx: GateContext): string {\n\tconst { body, keptPrivate, config, source, baseUrl } = ctx;\n\tconst { payloads } = body;\n\tconst host = baseUrl.replace(/^https?:\\/\\//, \"\");\n\tconst out: string[] = [];\n\n\tout.push(\"from your machine — sync preview\");\n\tout.push(\"\");\n\n\tif (config.stack === null) {\n\t\tout.push(\"to (no linked stack — publish is unavailable)\");\n\t} else {\n\t\tout.push(\n\t\t\t`to ${config.stack.name} · ${host}/stacks/${config.stack.slug}`,\n\t\t);\n\t}\n\n\t// One block per detected harness. With a single harness the header line is\n\t// dropped, so the single-harness preview reads exactly as it always did.\n\tfor (const payload of payloads) {\n\t\tconst stats = ctx.scanStats?.[payload.harness.name];\n\t\tout.push(...payloadBlock(payload, payloads.length > 1, stats));\n\t\tout.push(\"\");\n\t}\n\tif (out[out.length - 1] === \"\") out.pop();\n\n\tconst n = payloads.reduce((a, p) => a + withheldCount(p), 0);\n\tif (n > 0) {\n\t\tout.push(\"\");\n\t\tout.push(`kept private: ${n} name${n === 1 ? \"\" : \"s\"}`);\n\t\tconst rows = keptPrivateRows(keptPrivate);\n\t\tconst shown = rows.slice(0, KEPT_PRIVATE_ROWS_SHOWN);\n\t\tconst width = Math.max(...shown.map((r) => r.label.length));\n\t\tfor (const row of shown) {\n\t\t\tout.push(` ${row.label.padEnd(width)} ${row.names}`);\n\t\t}\n\t\tif (rows.length > shown.length) {\n\t\t\tout.push(` ...${rows.length - shown.length} more`);\n\t\t}\n\t\t// #48: beat one names the switch before the first upload, and points at\n\t\t// the changes page. Both lines are the locked copy, verbatim or near it.\n\t\tif (body.keptPrivate !== undefined && config.stack !== null) {\n\t\t\tout.push(` publish them at ${host}/stacks/${config.stack.slug}/changes`);\n\t\t\tout.push(\n\t\t\t\t\" (they go up for you to review — turn off: Review kept-private names, on your stack)\",\n\t\t\t);\n\t\t} else {\n\t\t\tout.push(\" they stay on this machine\");\n\t\t}\n\t}\n\n\t// Named at the gate because it is in the bytes (#78). It is not measurement\n\t// and not a name, but the rule is that the preview describes what goes, so a\n\t// field nobody can see in the preview does not get to ride along.\n\tif (body.autoSync !== undefined) {\n\t\tout.push(\"\");\n\t\tout.push(\n\t\t\t`auto-sync ${body.autoSync.enabled ? `on, about every ${body.autoSync.frequencyHours}h` : \"off\"}`,\n\t\t);\n\t}\n\n\tif (source === \"bundled\") {\n\t\tout.push(\"\");\n\t\tout.push(\n\t\t\t\"! could not fetch your settings from aistack — using the bundled list.\",\n\t\t);\n\t\tout.push(\n\t\t\t\" This publishes less: no cost, no ticked names, nothing staged for review.\",\n\t\t);\n\t}\n\n\treturn out.join(\"\\n\");\n}\n","// The local stdio MCP server — the send channel picked by the spike #35.\n//\n// Wayfinder ticket #41 (map #29). Two tools, two beats:\n//\n// sync_preview — scans locally, stages the exact send bytes, returns the\n// full summary as ordinary transcript output (beat one).\n// sync_publish — takes the stage id, raises a SHORT `elicitation/create`\n// with an ENUM field (beat two), and sends only on\n// `decision: \"publish\"`.\n//\n// Why elicitation and not `requiresUserInteraction`: the spike showed the\n// permission dialog can be silenced forever with one click and writes a grant\n// broader than the sentence shown, while an elicitation is raised INSIDE the\n// call — there is no string a model can spell to route around it, and no\n// \"don't ask again\" exists for it. The enum widget is the working one; the\n// boolean widget is dead in 2.1.220 and must never ship.\n//\n// Fail-closed, by construction: ESC, a timeout, a headless auto-cancel, an\n// error reply, or a client that never declared the elicitation capability all\n// resolve to \"nothing was sent\". The model's arguments count for nothing —\n// the only path to a send runs through the user's own keystrokes.\n//\n// Hand-rolled JSON-RPC over stdio, zero dependencies, structured so tests can\n// drive `handle()` directly and capture every outbound frame.\n\nimport { type SyncPublishResult, syncPublish } from \"../api.js\";\nimport { type StageDeps, type StagedSend, stageSync } from \"./stage.js\";\n\nconst SERVER_NAME = \"aistack\";\nconst SERVER_VERSION = \"0.3.0\";\n\n/** How long a staged preview stays publishable. Stale bytes must re-preview. */\nexport const STAGE_TTL_MS = 10 * 60 * 1000;\n\n/**\n * How long the gate waits for the human. Deliberately WELL past the harness's\n * own 120 s tool timeout (#35, 1H measured 92 s for a one-line answer): the\n * server must never be the first to give up. On expiry it resolves as cancel.\n */\nexport const ELICIT_TIMEOUT_MS = 10 * 60 * 1000;\n\nconst PREVIEW_TOOL = {\n\tname: \"sync_preview\",\n\tdescription:\n\t\t\"Scan local agent transcripts (Claude Code, Codex) and stage a measured-usage snapshot for aistack. \" +\n\t\t\"Returns the full preview of exactly what would publish. \" +\n\t\t\"Show the returned text to the user VERBATIM — it is the review surface. Nothing is sent.\",\n\tinputSchema: { type: \"object\", properties: {} },\n\tannotations: {\n\t\ttitle: \"aistack — preview sync (sends nothing)\",\n\t\treadOnlyHint: true,\n\t\topenWorldHint: true,\n\t},\n};\n\nconst PUBLISH_TOOL = {\n\tname: \"sync_publish\",\n\tdescription:\n\t\t\"Publish the staged aistack snapshot named by preview_id. \" +\n\t\t\"Asks the user for confirmation during the call; only their explicit choice sends anything. \" +\n\t\t\"Call sync_preview first and show its output.\",\n\tinputSchema: {\n\t\ttype: \"object\",\n\t\tproperties: {\n\t\t\tpreview_id: {\n\t\t\t\ttype: \"string\",\n\t\t\t\tdescription: \"The `preview id` line from sync_preview's output.\",\n\t\t\t},\n\t\t},\n\t\trequired: [\"preview_id\"],\n\t},\n\tannotations: {\n\t\ttitle: \"aistack — publish measured usage (asks the user first)\",\n\t\tdestructiveHint: false,\n\t\topenWorldHint: true,\n\t},\n};\n\ntype JsonRpcMessage = {\n\tjsonrpc?: string;\n\tid?: string | number;\n\tmethod?: string;\n\tparams?: Record<string, unknown> | undefined;\n\tresult?: unknown;\n\terror?: unknown;\n};\n\nexport type SyncServerDeps = {\n\tbaseUrl: string;\n\tstageImpl?: (deps: StageDeps) => Promise<StagedSend>;\n\tpublishImpl?: (token: string, bodyJson: string) => Promise<SyncPublishResult>;\n\tnow?: () => number;\n\telicitTimeoutMs?: number;\n\t/** Diagnostics only. NEVER stdout — that would corrupt the protocol. */\n\tlog?: (line: string) => void;\n};\n\nexport type SyncServer = {\n\thandle: (msg: JsonRpcMessage) => void;\n\t/** Test seam: the staged send, if any. */\n\tstaged: () => StagedSend | null;\n};\n\nconst textResult = (text: string, isError = false) => ({\n\tcontent: [{ type: \"text\", text }],\n\t...(isError ? { isError: true } : {}),\n});\n\nexport function createSyncServer(\n\tdeps: SyncServerDeps,\n\tsend: (msg: JsonRpcMessage) => void,\n): SyncServer {\n\tconst now = deps.now ?? Date.now;\n\tconst stage = deps.stageImpl ?? stageSync;\n\tconst publish = deps.publishImpl ?? syncPublish;\n\tconst log = deps.log ?? (() => {});\n\tconst elicitTimeoutMs = deps.elicitTimeoutMs ?? ELICIT_TIMEOUT_MS;\n\n\tlet clientSupportsElicitation = false;\n\tlet staged: StagedSend | null = null;\n\tlet nextRequestId = 1;\n\tconst pending = new Map<string, (reply: JsonRpcMessage | null) => void>();\n\n\tconst ok = (id: string | number | undefined, result: unknown) =>\n\t\tsend({ jsonrpc: \"2.0\", id, result });\n\tconst err = (\n\t\tid: string | number | undefined,\n\t\tcode: number,\n\t\tmessage: string,\n\t) => send({ jsonrpc: \"2.0\", id, error: { code, message } });\n\n\t/** Ask the client something; `null` reply means the gate timed out. */\n\tconst request = (\n\t\tmethod: string,\n\t\tparams: Record<string, unknown>,\n\t\tonReply: (reply: JsonRpcMessage | null) => void,\n\t) => {\n\t\tconst id = `aistack-${nextRequestId++}`;\n\t\tpending.set(id, onReply);\n\t\tsend({ jsonrpc: \"2.0\", id, method, params });\n\t\tconst timer = setTimeout(() => {\n\t\t\tif (pending.delete(id)) onReply(null);\n\t\t}, elicitTimeoutMs);\n\t\t(timer as { unref?: () => void }).unref?.();\n\t};\n\n\tconst runPreview = async (id: string | number | undefined) => {\n\t\ttry {\n\t\t\tstaged = await stage({ baseUrl: deps.baseUrl, now });\n\t\t} catch (e) {\n\t\t\tstaged = null;\n\t\t\tconst message = e instanceof Error ? e.message : String(e);\n\t\t\treturn ok(id, textResult(`Preview failed: ${message}`, true));\n\t\t}\n\t\tconst lines = [staged.summary, \"\"];\n\t\tif (staged.blockedReason === null) {\n\t\t\tlines.push(`preview id: ${staged.id}`);\n\t\t\tlines.push(\n\t\t\t\t\"To publish, call sync_publish with this preview id. The user confirms in a dialog during that call.\",\n\t\t\t);\n\t\t} else {\n\t\t\tlines.push(`publish unavailable: ${staged.blockedReason}`);\n\t\t}\n\t\treturn ok(id, textResult(lines.join(\"\\n\")));\n\t};\n\n\tconst runPublish = (\n\t\tid: string | number | undefined,\n\t\targs: Record<string, unknown> | undefined,\n\t) => {\n\t\t// Every refusal below is fail-closed: no dialog was shown, nothing sent.\n\t\tif (!clientSupportsElicitation) {\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: this Claude Code version did not declare the elicitation capability, \" +\n\t\t\t\t\t\t\"so the approve dialog cannot be shown. The gate never degrades silently — \" +\n\t\t\t\t\t\t\"update Claude Code and try again.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tconst previewId = args?.preview_id;\n\t\tif (staged === null) {\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: nothing is staged. Run sync_preview first and show its output to the user.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (typeof previewId !== \"string\" || previewId !== staged.id) {\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: preview_id does not match the staged preview. Run sync_preview again.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (staged.blockedReason !== null) {\n\t\t\treturn ok(id, textResult(`Not published: ${staged.blockedReason}`, true));\n\t\t}\n\t\tif (now() - staged.stagedAt > STAGE_TTL_MS) {\n\t\t\tstaged = null;\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: the staged preview is older than 10 minutes. Run sync_preview again so the user reviews current bytes.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tconst approvedStage = staged;\n\t\tlog(`elicitation raised for stage ${approvedStage.id}`);\n\t\trequest(\n\t\t\t\"elicitation/create\",\n\t\t\t{\n\t\t\t\tmessage: approvedStage.dialog,\n\t\t\t\trequestedSchema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tdecision: {\n\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t// The enum widget is the one that works (#35, 1G). Never a boolean.\n\t\t\t\t\t\t\tenum: [\"publish\", \"cancel\"],\n\t\t\t\t\t\t\tdescription: \"Publish the snapshot described above?\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"decision\"],\n\t\t\t\t},\n\t\t\t},\n\t\t\t(reply) => {\n\t\t\t\tconst result = reply?.result as\n\t\t\t\t\t| { action?: string; content?: { decision?: string } }\n\t\t\t\t\t| undefined;\n\t\t\t\tconst approved =\n\t\t\t\t\tresult?.action === \"accept\" &&\n\t\t\t\t\tresult?.content?.decision === \"publish\";\n\t\t\t\tif (!approved) {\n\t\t\t\t\tconst outcome =\n\t\t\t\t\t\treply === null ? \"timed out\" : (result?.action ?? \"error\");\n\t\t\t\t\tlog(`elicitation resolved without consent: ${outcome}`);\n\t\t\t\t\treturn ok(\n\t\t\t\t\t\tid,\n\t\t\t\t\t\ttextResult(\n\t\t\t\t\t\t\t`Not published: the confirmation was not accepted (${outcome}). Nothing left this machine.`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlog(`consent received, sending stage ${approvedStage.id}`);\n\t\t\t\tpublish(approvedStage.token as string, approvedStage.bodyJson).then(\n\t\t\t\t\t(res) => {\n\t\t\t\t\t\tif (staged?.id === approvedStage.id) staged = null;\n\t\t\t\t\t\tconst lines = [\n\t\t\t\t\t\t\t`Published. Snapshot received at ${new Date(res.receivedAt).toISOString()}.`,\n\t\t\t\t\t\t\tres.url,\n\t\t\t\t\t\t];\n\t\t\t\t\t\tconst kp = approvedStage.body.keptPrivate;\n\t\t\t\t\t\tif (res.keptPrivate.refused && kp !== undefined) {\n\t\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t\t\"Note: the kept-private names were refused by the server — the review switch is off there now. They stayed on this machine.\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else if (res.keptPrivate.stored > 0) {\n\t\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t\t`${res.keptPrivate.stored} kept-private names went up for your review at ${res.url}/changes`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tok(id, textResult(lines.join(\"\\n\")));\n\t\t\t\t\t},\n\t\t\t\t\t(e) => {\n\t\t\t\t\t\tconst message = e instanceof Error ? e.message : String(e);\n\t\t\t\t\t\tok(\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\ttextResult(`Publish failed after consent: ${message}`, true),\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t},\n\t\t);\n\t};\n\n\tconst handle = (msg: JsonRpcMessage) => {\n\t\tconst { id, method, params } = msg;\n\n\t\t// A reply to something we asked, not a new request.\n\t\tif (method === undefined && id !== undefined && pending.has(String(id))) {\n\t\t\tconst onReply = pending.get(String(id));\n\t\t\tpending.delete(String(id));\n\t\t\tonReply?.(msg);\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (method) {\n\t\t\tcase \"initialize\": {\n\t\t\t\tconst capabilities =\n\t\t\t\t\t(params?.capabilities as Record<string, unknown> | undefined) ?? {};\n\t\t\t\tclientSupportsElicitation = \"elicitation\" in capabilities;\n\t\t\t\tlog(\n\t\t\t\t\t`initialize: elicitation ${clientSupportsElicitation ? \"declared\" : \"ABSENT\"}`,\n\t\t\t\t);\n\t\t\t\treturn ok(id, {\n\t\t\t\t\tprotocolVersion:\n\t\t\t\t\t\t(params?.protocolVersion as string | undefined) ?? \"2025-06-18\",\n\t\t\t\t\tcapabilities: { tools: { listChanged: false } },\n\t\t\t\t\tserverInfo: { name: SERVER_NAME, version: SERVER_VERSION },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"ping\":\n\t\t\t\treturn ok(id, {});\n\n\t\t\tcase \"tools/list\":\n\t\t\t\treturn ok(id, { tools: [PREVIEW_TOOL, PUBLISH_TOOL] });\n\n\t\t\tcase \"tools/call\": {\n\t\t\t\tconst name = params?.name;\n\t\t\t\tconst args = params?.arguments as Record<string, unknown> | undefined;\n\t\t\t\tif (name === \"sync_preview\") return void runPreview(id);\n\t\t\t\tif (name === \"sync_publish\") return runPublish(id, args);\n\t\t\t\treturn err(id, -32602, `Unknown tool: ${String(name)}`);\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tif (method?.startsWith(\"notifications/\")) return;\n\t\t\t\tif (method !== undefined)\n\t\t\t\t\treturn err(id, -32601, `Method not found: ${method}`);\n\t\t}\n\t};\n\n\treturn { handle, staged: () => staged };\n}\n\n/** Wire the server to real stdio. Never returns; the harness owns the process. */\nexport function runStdioSyncServer(deps: SyncServerDeps): void {\n\tconst server = createSyncServer(deps, (msg) => {\n\t\tprocess.stdout.write(`${JSON.stringify(msg)}\\n`);\n\t});\n\tlet buffer = \"\";\n\tprocess.stdin.setEncoding(\"utf8\");\n\tprocess.stdin.on(\"data\", (chunk: string) => {\n\t\tbuffer += chunk;\n\t\tlet nl = buffer.indexOf(\"\\n\");\n\t\twhile (nl !== -1) {\n\t\t\tconst line = buffer.slice(0, nl).trim();\n\t\t\tbuffer = buffer.slice(nl + 1);\n\t\t\tif (line) {\n\t\t\t\ttry {\n\t\t\t\t\tserver.handle(JSON.parse(line));\n\t\t\t\t} catch (e) {\n\t\t\t\t\tdeps.log?.(`parse error: ${String(e)}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\tnl = buffer.indexOf(\"\\n\");\n\t\t}\n\t});\n}\n"],"mappings":"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","names":["path","p","dirname","dirname","dirname","path","existsSync","readFileSync","homedir","join","path","existsSync","readFileSync","homedir","join","path","readJson","existsSync","readFileSync","homedir","join","path","p","readJson","existsSync","readFileSync","join","homedir","existsSync","readdirSync","readFileSync","homedir","join","stat","intro","outro","intro","outro","existsSync","homedir","dirname","join","p","p","p","p","createAggregate","modelKeyFor","readdir","stat","homedir","path","path","homedir","basename","readdir","stat","p","createAggregate","createAggregate","readFileSync","readdir","realpath","stat","homedir","path","parseToml","path","homedir","basename","readdir","scan","exists","realpath","stat","p","ingestFile","readFileSync","parseToml","createAggregate","scan","createAggregate","noteConfiguredMcpServers","readFileSync","readdir","stat","homedir","path","path","homedir","basename","readdir","errorClass","readError","scan","open","readConfiguredMcpServers","readFileSync","noteConfiguredMcpServers","exists","p","stat","createAggregate","scan","createAggregate","createFileState","noteActivity","readCounts","key","createReadStream","readdir","realpath","stat","homedir","path","readline","path","homedir","basename","readdir","scan","exists","realpath","stat","ingestFile","p","readline","createReadStream","createFileState","createAggregate","scan","join","homedir","dirname","existsSync","intro","outro","existsSync","mkdirSync","readFileSync","writeFileSync","dirname","join","p","intro","join","existsSync","readFileSync","confirm","dirname","mkdirSync","writeFileSync","outro","p","intro","outro","p","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","dirname","join","codexHome","p","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","dirname","join","isOurs","mkdirSync","readFileSync","writeFileSync","homedir","dirname","join","createHash","p","lines","p","createHash","join","homedir","mkdirSync","dirname","lines","readFileSync","writeFileSync","failure","intro","outro","lines","log","request","lines"]}