reasonix 0.35.0 → 0.36.1

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.
Files changed (82) hide show
  1. package/README.md +17 -0
  2. package/README.zh-CN.md +17 -0
  3. package/dashboard/dist/app.js +42 -3
  4. package/dashboard/dist/app.js.map +1 -1
  5. package/dist/cli/{chat-AB5D7I3V.js → chat-7AF5SPAJ.js} +16 -15
  6. package/dist/cli/{chunk-RJ5GUVS2.js → chunk-2MCYGFLK.js} +10 -10
  7. package/dist/cli/chunk-2MCYGFLK.js.map +1 -0
  8. package/dist/cli/{chunk-GPHBJWCV.js → chunk-3OBWN2NH.js} +650 -493
  9. package/dist/cli/chunk-3OBWN2NH.js.map +1 -0
  10. package/dist/cli/{chunk-SW3CCXEV.js → chunk-4Q3GRJIU.js} +2 -2
  11. package/dist/cli/{chunk-5JXXEPDM.js → chunk-BHLHOS5Y.js} +8 -2
  12. package/dist/cli/chunk-BHLHOS5Y.js.map +1 -0
  13. package/dist/cli/{chunk-IDP65VCC.js → chunk-BJ376EN3.js} +9 -8
  14. package/dist/cli/chunk-BJ376EN3.js.map +1 -0
  15. package/dist/cli/{chunk-2AWTGJ2C.js → chunk-CRPQUBP6.js} +26 -9
  16. package/dist/cli/{chunk-2AWTGJ2C.js.map → chunk-CRPQUBP6.js.map} +1 -1
  17. package/dist/cli/{chunk-JJTOZPM3.js → chunk-IPCPEZWQ.js} +2 -2
  18. package/dist/cli/{chunk-SN7YH6FC.js → chunk-MLXUGPJE.js} +168 -35
  19. package/dist/cli/chunk-MLXUGPJE.js.map +1 -0
  20. package/dist/cli/{chunk-SX6L4HZZ.js → chunk-QPNZWUZF.js} +53 -6
  21. package/dist/cli/chunk-QPNZWUZF.js.map +1 -0
  22. package/dist/cli/{chunk-N2IC4XDL.js → chunk-QRUQ2BFT.js} +13 -8
  23. package/dist/cli/{chunk-N2IC4XDL.js.map → chunk-QRUQ2BFT.js.map} +1 -1
  24. package/dist/cli/{chunk-KZHMKOJH.js → chunk-T52GAWPP.js} +25 -3
  25. package/dist/cli/chunk-T52GAWPP.js.map +1 -0
  26. package/dist/cli/{chunk-I6YIAK6C.js → chunk-UNMYFZPZ.js} +2 -2
  27. package/dist/cli/{update-4TJWRUIN.js → chunk-WJ3YX4PZ.js} +51 -12
  28. package/dist/cli/chunk-WJ3YX4PZ.js.map +1 -0
  29. package/dist/cli/{chunk-RXGEGA7K.js → chunk-XQIFIB3U.js} +18 -7
  30. package/dist/cli/{chunk-RXGEGA7K.js.map → chunk-XQIFIB3U.js.map} +1 -1
  31. package/dist/cli/{chunk-2EBODRRO.js → chunk-ZJR4QLXB.js} +5 -1
  32. package/dist/cli/{chunk-2EBODRRO.js.map → chunk-ZJR4QLXB.js.map} +1 -1
  33. package/dist/cli/{code-XBEFHXVM.js → code-SWI4EBME.js} +19 -17
  34. package/dist/cli/code-SWI4EBME.js.map +1 -0
  35. package/dist/cli/{commands-MEZPSEHV.js → commands-FE2UDFBC.js} +3 -3
  36. package/dist/cli/{commit-CE4EFTUQ.js → commit-3IAGB22T.js} +5 -4
  37. package/dist/cli/commit-3IAGB22T.js.map +1 -0
  38. package/dist/cli/{doctor-A565GMWD.js → doctor-DKD34EFD.js} +7 -7
  39. package/dist/cli/index.js +33 -31
  40. package/dist/cli/index.js.map +1 -1
  41. package/dist/cli/{mcp-LDFK5QJI.js → mcp-2RDEQST6.js} +2 -2
  42. package/dist/cli/{mcp-browse-FYHEITCM.js → mcp-browse-VM5GLRBQ.js} +2 -2
  43. package/dist/cli/{mcp-inspect-T2HBR22P.js → mcp-inspect-CWSVCZUQ.js} +3 -3
  44. package/dist/cli/{replay-P2WC5N5X.js → replay-D7RT2DR7.js} +2 -2
  45. package/dist/cli/{run-QBWJETS3.js → run-FK5UBIIM.js} +11 -10
  46. package/dist/cli/run-FK5UBIIM.js.map +1 -0
  47. package/dist/cli/{server-SMLVXIW4.js → server-W4XJK4GX.js} +16 -16
  48. package/dist/cli/{server-SMLVXIW4.js.map → server-W4XJK4GX.js.map} +1 -1
  49. package/dist/cli/{sessions-55RIZVWG.js → sessions-YZXWMIWW.js} +8 -8
  50. package/dist/cli/{setup-QXMONZ4P.js → setup-IIAJXHP4.js} +196 -130
  51. package/dist/cli/setup-IIAJXHP4.js.map +1 -0
  52. package/dist/cli/update-GUCWB4UN.js +13 -0
  53. package/dist/cli/update-GUCWB4UN.js.map +1 -0
  54. package/dist/cli/{version-Q2HA3AAC.js → version-DWD6RLIU.js} +10 -10
  55. package/dist/index.d.ts +14 -2
  56. package/dist/index.js +270 -47
  57. package/dist/index.js.map +1 -1
  58. package/package.json +1 -1
  59. package/dist/cli/chunk-5JXXEPDM.js.map +0 -1
  60. package/dist/cli/chunk-GPHBJWCV.js.map +0 -1
  61. package/dist/cli/chunk-IDP65VCC.js.map +0 -1
  62. package/dist/cli/chunk-KZHMKOJH.js.map +0 -1
  63. package/dist/cli/chunk-RJ5GUVS2.js.map +0 -1
  64. package/dist/cli/chunk-SN7YH6FC.js.map +0 -1
  65. package/dist/cli/chunk-SX6L4HZZ.js.map +0 -1
  66. package/dist/cli/code-XBEFHXVM.js.map +0 -1
  67. package/dist/cli/commit-CE4EFTUQ.js.map +0 -1
  68. package/dist/cli/run-QBWJETS3.js.map +0 -1
  69. package/dist/cli/setup-QXMONZ4P.js.map +0 -1
  70. package/dist/cli/update-4TJWRUIN.js.map +0 -1
  71. /package/dist/cli/{chat-AB5D7I3V.js.map → chat-7AF5SPAJ.js.map} +0 -0
  72. /package/dist/cli/{chunk-SW3CCXEV.js.map → chunk-4Q3GRJIU.js.map} +0 -0
  73. /package/dist/cli/{chunk-JJTOZPM3.js.map → chunk-IPCPEZWQ.js.map} +0 -0
  74. /package/dist/cli/{chunk-I6YIAK6C.js.map → chunk-UNMYFZPZ.js.map} +0 -0
  75. /package/dist/cli/{commands-MEZPSEHV.js.map → commands-FE2UDFBC.js.map} +0 -0
  76. /package/dist/cli/{doctor-A565GMWD.js.map → doctor-DKD34EFD.js.map} +0 -0
  77. /package/dist/cli/{mcp-LDFK5QJI.js.map → mcp-2RDEQST6.js.map} +0 -0
  78. /package/dist/cli/{mcp-browse-FYHEITCM.js.map → mcp-browse-VM5GLRBQ.js.map} +0 -0
  79. /package/dist/cli/{mcp-inspect-T2HBR22P.js.map → mcp-inspect-CWSVCZUQ.js.map} +0 -0
  80. /package/dist/cli/{replay-P2WC5N5X.js.map → replay-D7RT2DR7.js.map} +0 -0
  81. /package/dist/cli/{sessions-55RIZVWG.js.map → sessions-YZXWMIWW.js.map} +0 -0
  82. /package/dist/cli/{version-Q2HA3AAC.js.map → version-DWD6RLIU.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/tools/jobs.ts","../../src/tools/shell.ts","../../src/core/pause-gate.ts","../../src/tools/shell/exec.ts","../../src/tools/shell-chain.ts","../../src/tools/shell/parse.ts"],"sourcesContent":["/** Background process registry for never-exiting commands; ready-signal detection short-circuits the startup wait. */\n\nimport { type ChildProcess, type SpawnOptions, spawn } from \"node:child_process\";\nimport * as pathMod from \"node:path\";\nimport { detectShellOperator, prepareSpawn, tokenizeCommand } from \"./shell.js\";\n\n/** Kills the whole tree — `child.kill` only hits the direct child, leaving npm-spawned dev servers orphaned. */\nfunction killProcessTree(pid: number, signal: \"SIGTERM\" | \"SIGKILL\"): void {\n if (process.platform === \"win32\") {\n // taskkill: /T = tree, /F = force (TerminateProcess, no cleanup).\n // Graceful path still uses /F on Windows because there's no signal\n // in the POSIX sense — the closest equivalent is Ctrl+Break, which\n // is unreliable from another console. /F with /T is what most\n // process managers ship on Windows.\n const args = [\"/pid\", String(pid), \"/T\"];\n if (signal === \"SIGKILL\") args.push(\"/F\");\n try {\n const killer = spawn(\"taskkill\", args, {\n stdio: \"ignore\",\n windowsHide: true,\n });\n // Swallow ENOENT / EACCES — we did our best. Not awaiting is\n // intentional: taskkill can take a few hundred ms and the caller\n // already has its own deadline.\n killer.on(\"error\", () => {\n /* ignore */\n });\n } catch {\n /* ignore */\n }\n return;\n }\n // POSIX: negative pid signals the whole process group. Requires the\n // spawn to have been detached (which `start()` does below).\n try {\n process.kill(-pid, signal);\n return;\n } catch {\n /* group-kill failed — fall back to direct */\n }\n try {\n process.kill(pid, signal);\n } catch {\n /* ignore — already dead */\n }\n}\n\n/** Per-job output ring. Capped so a chatty dev server doesn't OOM. */\nconst DEFAULT_OUTPUT_CAP_BYTES = 64 * 1024; // 64 KB\n\n/** First match cuts startup wait short; conservative patterns — a false negative costs a real stall. */\nconst READY_SIGNALS: ReadonlyArray<RegExp> = [\n // HTTP server banners\n /\\blistening on\\b/i,\n /\\blocal:\\s+https?:\\/\\//i,\n /\\bhttps?:\\/\\/(?:localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0)(?::\\d+)?\\b/i,\n /\\b(?:ready|server started|started server|app listening)\\b/i,\n // Bundlers / compilers\n /\\bcompiled successfully\\b/i,\n /\\bbuild complete(?:d)?\\b/i,\n /\\bwatching for (?:file )?changes\\b/i,\n /\\bready in \\d+/i,\n // Generic\n /\\bstartup (?:complete|finished)\\b/i,\n];\n\nexport interface JobStartOptions {\n /** Absolute path to cwd for the spawned child. */\n cwd: string;\n /** Capped at 30; ready-signal match short-circuits. Default 3. */\n waitSec?: number;\n /** Signal plumbed through from the calling tool's AbortSignal. */\n signal?: AbortSignal;\n /** Total per-job output buffer cap (bytes). Default 64 KB. */\n maxBufferBytes?: number;\n}\n\nexport interface JobStartResult {\n jobId: number;\n pid: number | null;\n /** True iff the child was still running at the point we returned. */\n stillRunning: boolean;\n /** True iff a READY_SIGNALS pattern matched during the wait window. */\n readyMatched: boolean;\n /** Preview of combined stdout+stderr accumulated during the wait. */\n preview: string;\n /** If the child exited during the wait, its exit code; else null. */\n exitCode: number | null;\n}\n\nexport interface JobRecord {\n id: number;\n command: string;\n pid: number | null;\n startedAt: number;\n /** Exit code once the process terminates; null while running. */\n exitCode: number | null;\n /** Combined stdout+stderr, ring-trimmed. */\n output: string;\n /** Counts all bytes the child wrote, not just what's still buffered in `output`. */\n totalBytesWritten: number;\n /** True iff the child is still alive. */\n running: boolean;\n /** Error from spawn() itself (ENOENT, etc.) once surfaced. */\n spawnError?: string;\n}\n\nexport class JobRegistry {\n private readonly jobs = new Map<number, InternalJob>();\n private nextId = 1;\n\n /** Resolves on (a) ready signal, (b) early exit, or (c) waitSec deadline — child keeps running regardless. */\n async start(command: string, opts: JobStartOptions): Promise<JobStartResult> {\n const trimmed = command.trim();\n if (!trimmed) throw new Error(\"run_background: empty command\");\n const op = detectShellOperator(trimmed);\n if (op !== null) {\n throw new Error(\n `run_background: shell operator \"${op}\" is not supported — spawn one process per background job. Compose via your orchestration, not the shell.`,\n );\n }\n const argv = tokenizeCommand(trimmed);\n if (argv.length === 0) throw new Error(\"run_background: empty command\");\n const waitMs = Math.max(0, Math.min(30, opts.waitSec ?? 3)) * 1000;\n const maxBytes = opts.maxBufferBytes ?? DEFAULT_OUTPUT_CAP_BYTES;\n\n const { bin, args, spawnOverrides } = prepareSpawn(argv);\n const spawnOpts: SpawnOptions = {\n cwd: pathMod.resolve(opts.cwd),\n shell: false,\n windowsHide: true,\n env: process.env,\n // POSIX: detach so the child becomes its own process-group leader.\n // Required for `process.kill(-pid, …)` later — without it a group\n // kill fails and we end up only signaling the wrapper, leaving\n // grandchildren (node → vite → esbuild …) orphaned.\n // Windows: detached would spawn a new console window; leave the\n // default and use taskkill /T for tree termination.\n detached: process.platform !== \"win32\",\n ...spawnOverrides,\n };\n\n let child: ChildProcess;\n try {\n child = spawn(bin, args, spawnOpts);\n } catch (err) {\n // Can't even spawn — record a dead job so the model sees the\n // failure in list_jobs, and return a synthetic result.\n const id = this.nextId++;\n const job: InternalJob = {\n id,\n command: trimmed,\n pid: null,\n startedAt: Date.now(),\n exitCode: null,\n output: `[spawn failed] ${(err as Error).message}`,\n totalBytesWritten: 0,\n running: false,\n spawnError: (err as Error).message,\n child: null,\n readyPromise: Promise.resolve(),\n signalReady: () => {},\n closedPromise: Promise.resolve(),\n signalClosed: () => {},\n outputWaiters: new Set(),\n };\n this.jobs.set(id, job);\n return {\n jobId: id,\n pid: null,\n stillRunning: false,\n readyMatched: false,\n preview: job.output,\n exitCode: null,\n };\n }\n\n const id = this.nextId++;\n let readyResolve: () => void = () => {};\n const readyPromise = new Promise<void>((res) => {\n readyResolve = res;\n });\n let closedResolve: () => void = () => {};\n const closedPromise = new Promise<void>((res) => {\n closedResolve = res;\n });\n const job: InternalJob = {\n id,\n command: trimmed,\n pid: child.pid ?? null,\n startedAt: Date.now(),\n exitCode: null,\n output: \"\",\n totalBytesWritten: 0,\n running: true,\n child,\n readyPromise,\n signalReady: readyResolve,\n closedPromise,\n signalClosed: closedResolve,\n outputWaiters: new Set(),\n };\n this.jobs.set(id, job);\n\n let readyMatched = false;\n // Sliding window for cross-chunk ready-signal matching. A banner\n // line might land split across two reads — we want the regex to\n // see it as one piece — but testing against the full `job.output`\n // (which can be tens of KB by the time the server is up) is\n // O(N²) when 9 regexes each run on a growing buffer per chunk.\n // 1KB is comfortably bigger than any banner line we look for and\n // bounds the per-chunk regex cost regardless of total output.\n let recentForReady = \"\";\n const READY_WINDOW = 1024;\n const onData = (chunk: Buffer | string) => {\n const s = chunk.toString();\n job.totalBytesWritten += s.length;\n job.output += s;\n if (job.output.length > maxBytes) {\n // Drop the oldest bytes, but keep a marker so the model can see\n // output was truncated. Trim on a rough line boundary to avoid\n // chopping a line mid-sentence.\n const overflow = job.output.length - maxBytes;\n const cut = job.output.indexOf(\"\\n\", overflow);\n const start = cut >= 0 ? cut + 1 : overflow;\n job.output = `[… older output dropped …]\\n${job.output.slice(start)}`;\n }\n if (!readyMatched) {\n recentForReady = (recentForReady + s).slice(-READY_WINDOW);\n for (const re of READY_SIGNALS) {\n if (re.test(recentForReady)) {\n readyMatched = true;\n job.signalReady();\n break;\n }\n }\n }\n if (job.outputWaiters.size > 0) {\n const waiters = [...job.outputWaiters];\n job.outputWaiters.clear();\n for (const wake of waiters) wake();\n }\n };\n child.stdout?.on(\"data\", onData);\n child.stderr?.on(\"data\", onData);\n child.on(\"error\", (err) => {\n job.running = false;\n job.spawnError = err.message;\n job.signalReady();\n job.signalClosed();\n });\n child.on(\"close\", (code) => {\n job.running = false;\n job.exitCode = code;\n job.signalReady();\n job.signalClosed();\n });\n\n const onAbort = () => this.stop(id, { graceMs: 100 });\n if (opts.signal?.aborted) {\n onAbort();\n } else {\n opts.signal?.addEventListener(\"abort\", onAbort, { once: true });\n }\n\n // Race: (a) ready signal, (b) child exit, (c) wait deadline.\n let timer: ReturnType<typeof setTimeout> | null = null;\n await Promise.race([\n readyPromise,\n new Promise<void>((res) => {\n timer = setTimeout(res, waitMs);\n }),\n ]);\n if (timer) clearTimeout(timer);\n\n return {\n jobId: id,\n pid: job.pid,\n stillRunning: job.running,\n readyMatched,\n preview: job.output,\n exitCode: job.exitCode,\n };\n }\n\n read(id: number, opts: { since?: number; tailLines?: number } = {}): JobReadResult | null {\n const job = this.jobs.get(id);\n if (!job) return null;\n const full = job.output;\n let slice = full;\n if (typeof opts.since === \"number\" && opts.since >= 0 && opts.since < full.length) {\n slice = full.slice(opts.since);\n }\n if (typeof opts.tailLines === \"number\" && opts.tailLines > 0) {\n const lines = slice.split(\"\\n\");\n const keep = lines.slice(Math.max(0, lines.length - opts.tailLines));\n slice = keep.join(\"\\n\");\n }\n return {\n output: slice,\n byteLength: full.length,\n running: job.running,\n exitCode: job.exitCode,\n command: job.command,\n pid: job.pid,\n spawnError: job.spawnError,\n };\n }\n\n async waitForJob(id: number, opts: { timeoutMs?: number } = {}): Promise<JobWaitResult | null> {\n const job = this.jobs.get(id);\n if (!job) return null;\n if (!job.running) {\n return {\n exited: true,\n exitCode: job.exitCode,\n latestOutput: job.output,\n };\n }\n\n const timeoutMs = Math.max(0, Math.min(30_000, opts.timeoutMs ?? 5_000));\n const startOutput = job.output;\n let wakeOutput: (() => void) | null = null;\n const outputPromise = new Promise<void>((resolve) => {\n wakeOutput = resolve;\n job.outputWaiters.add(resolve);\n });\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n await Promise.race([\n job.closedPromise,\n outputPromise,\n new Promise<void>((resolve) => {\n timer = setTimeout(resolve, timeoutMs);\n }),\n ]);\n if (timer) clearTimeout(timer);\n if (wakeOutput) job.outputWaiters.delete(wakeOutput);\n\n return {\n exited: !job.running,\n exitCode: job.exitCode,\n latestOutput: latestOutputSince(startOutput, job.output),\n };\n }\n\n /** SIGTERM, wait graceMs, then SIGKILL. Idempotent on already-exited jobs. */\n async stop(id: number, opts: { graceMs?: number } = {}): Promise<JobRecord | null> {\n const job = this.jobs.get(id);\n if (!job) return null;\n if (!job.running || !job.child) return snapshot(job);\n const graceMs = Math.max(0, opts.graceMs ?? 2000);\n // Tree kill — reaches grandchildren (vite, esbuild, etc.) instead\n // of just the npm/cmd.exe wrapper that our direct child represents.\n // Falls back to child.kill() only when we somehow don't have a pid.\n if (job.pid !== null) {\n killProcessTree(job.pid, \"SIGTERM\");\n } else {\n try {\n job.child.kill(\"SIGTERM\");\n } catch {\n /* already dead — fall through */\n }\n }\n // closedPromise (not readyPromise) — readyPromise can have fired at\n // startup on a ready-signal regex match, which would short-circuit\n // this race even though the process is still alive.\n await Promise.race([job.closedPromise, new Promise<void>((res) => setTimeout(res, graceMs))]);\n if (job.running) {\n if (job.pid !== null) {\n killProcessTree(job.pid, \"SIGKILL\");\n } else {\n try {\n job.child.kill(\"SIGKILL\");\n } catch {\n /* ignore */\n }\n }\n // Wait for the actual close handler — a fixed timer can return\n // before Node's `close` event fires under load (Windows taskkill\n // /T /F on a three-level tree can take ~1s to propagate).\n await Promise.race([job.closedPromise, new Promise<void>((res) => setTimeout(res, 5000))]);\n }\n return snapshot(job);\n }\n\n list(): JobRecord[] {\n return [...this.jobs.values()].map(snapshot);\n }\n\n async shutdown(deadlineMs = 5000): Promise<void> {\n const start = Date.now();\n const runningJobs = [...this.jobs.values()].filter((j) => j.running && j.child);\n if (runningJobs.length === 0) return;\n\n for (const job of runningJobs) {\n if (job.pid !== null) killProcessTree(job.pid, \"SIGTERM\");\n else\n try {\n job.child?.kill(\"SIGTERM\");\n } catch {\n /* ignore */\n }\n }\n const allClose = Promise.all(runningJobs.map((j) => j.readyPromise));\n const elapsed = () => Date.now() - start;\n // Grace window: give well-behaved apps time to clean up, capped at\n // half the deadline so we always leave room for a SIGKILL pass +\n // reap confirmation.\n const graceMs = Math.min(1500, Math.max(0, deadlineMs / 2));\n await Promise.race([allClose, new Promise<void>((res) => setTimeout(res, graceMs))]);\n // Force-kill everything still alive.\n for (const job of runningJobs) {\n if (!job.running) continue;\n if (job.pid !== null) killProcessTree(job.pid, \"SIGKILL\");\n else\n try {\n job.child?.kill(\"SIGKILL\");\n } catch {\n /* ignore */\n }\n }\n // Wait for close events post-SIGKILL. taskkill /T on Windows is\n // async — without this final wait, shutdown() can return while\n // grandchildren are still mid-teardown, which is what \"runningCount\n // non-zero after shutdown\" looks like.\n const remaining = Math.max(800, deadlineMs - elapsed());\n await Promise.race([allClose, new Promise<void>((res) => setTimeout(res, remaining))]);\n }\n\n /** Count of still-running jobs — drives the TUI status-bar indicator. */\n runningCount(): number {\n let n = 0;\n for (const job of this.jobs.values()) if (job.running) n++;\n return n;\n }\n}\n\ninterface InternalJob extends JobRecord {\n /** Underlying Node child process. Null only on spawn failure. */\n child: ChildProcess | null;\n /** Resolved when ready-signal fires OR the child exits. */\n readyPromise: Promise<void>;\n /** Fires readyPromise — called by ready-signal OR close/error handlers. */\n signalReady: () => void;\n /** Resolves only on close/error — never on ready-signal. Used by stop() to wait for actual exit. */\n closedPromise: Promise<void>;\n signalClosed: () => void;\n /** One-shot waiters for \"some new output arrived\". Cleared after every wake. */\n outputWaiters: Set<() => void>;\n}\n\nexport interface JobReadResult {\n output: string;\n /** Total bytes ever in the buffer (pre-slice). Caller passes back as `since`. */\n byteLength: number;\n running: boolean;\n exitCode: number | null;\n command: string;\n pid: number | null;\n spawnError?: string;\n}\n\nexport interface JobWaitResult {\n exited: boolean;\n exitCode: number | null;\n latestOutput: string;\n}\n\nfunction snapshot(job: InternalJob): JobRecord {\n return {\n id: job.id,\n command: job.command,\n pid: job.pid,\n startedAt: job.startedAt,\n exitCode: job.exitCode,\n output: job.output,\n totalBytesWritten: job.totalBytesWritten,\n running: job.running,\n spawnError: job.spawnError,\n };\n}\n\nfunction latestOutputSince(before: string, after: string): string {\n if (!before) return after;\n if (after.startsWith(before)) return after.slice(before.length);\n return after;\n}\n","/** cwd pinned to root; non-allowlisted commands throw to a UI confirm gate; spawn is `shell: false`, tokenized argv only. */\n\nimport * as pathMod from \"node:path\";\nimport { addProjectShellAllowed } from \"../config.js\";\nimport { pauseGate } from \"../core/pause-gate.js\";\nimport type { ToolRegistry } from \"../tools.js\";\nimport { JobRegistry } from \"./jobs.js\";\nimport {\n DEFAULT_MAX_OUTPUT_CHARS,\n DEFAULT_TIMEOUT_SEC,\n type RunCommandResult,\n runCommand,\n} from \"./shell/exec.js\";\nimport { isCommandAllowed } from \"./shell/parse.js\";\n\nexport {\n BUILTIN_ALLOWLIST,\n detectShellOperator,\n isAllowed,\n isCommandAllowed,\n isDqEscape,\n tokenizeCommand,\n} from \"./shell/parse.js\";\nexport type { ResolveExecutableOptions, RunCommandResult } from \"./shell/exec.js\";\nexport {\n injectPowerShellUtf8,\n killProcessTree,\n prepareSpawn,\n quoteForCmdExe,\n resolveExecutable,\n runCommand,\n smartDecodeOutput,\n withUtf8Codepage,\n} from \"./shell/exec.js\";\n\nexport interface ShellToolsOptions {\n /** Directory to run commands in. Must be an absolute path. */\n rootDir: string;\n /** Seconds before an individual command is killed. Default: 60. */\n timeoutSec?: number;\n maxOutputChars?: number;\n /** Getter form is load-bearing — newly-persisted \"always allow\" prefixes MUST take effect mid-session. */\n extraAllowed?: readonly string[] | (() => readonly string[]);\n /** Getter form lets `editMode === \"yolo\"` flip mid-session without re-registering tools. */\n allowAll?: boolean | (() => boolean);\n jobs?: JobRegistry;\n}\n\n/** Error thrown by `run_command` when the command isn't allowlisted. */\nexport class NeedsConfirmationError extends Error {\n readonly command: string;\n constructor(command: string) {\n super(\n `run_command: \"${command}\" needs the user's approval before it runs. STOP calling tools now — the TUI has already prompted the user to press y (run) or n (deny). Wait for their next message; it will either be the command's output (if they approved) or an instruction to continue without it (if they denied). Don't retry the command or call other shell commands in the meantime.`,\n );\n this.name = \"NeedsConfirmationError\";\n this.command = command;\n }\n}\n\nexport function registerShellTools(registry: ToolRegistry, opts: ShellToolsOptions): ToolRegistry {\n const rootDir = pathMod.resolve(opts.rootDir);\n const timeoutSec = opts.timeoutSec ?? DEFAULT_TIMEOUT_SEC;\n const maxOutputChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;\n const jobs = opts.jobs ?? new JobRegistry();\n // Resolved on every dispatch so newly-persisted \"always allow\"\n // prefixes take effect inside the session that added them, not just\n // on the next launch. Static arrays are wrapped into a constant\n // getter so the call site below is uniform.\n const getExtraAllowed: () => readonly string[] =\n typeof opts.extraAllowed === \"function\"\n ? opts.extraAllowed\n : (() => {\n const snapshot = opts.extraAllowed ?? [];\n return () => snapshot;\n })();\n // Resolve dynamically so the TUI can flip yolo mode mid-session and\n // have the registry pick it up on the next dispatch. Static booleans\n // are wrapped into a thunk for uniformity.\n const isAllowAll: () => boolean =\n typeof opts.allowAll === \"function\" ? opts.allowAll : () => opts.allowAll === true;\n\n registry.register({\n name: \"run_command\",\n description:\n \"Run a shell command in the project root and return its combined stdout+stderr.\\n\\nConstraints (read these before the first call):\\n• Chain operators `|`, `||`, `&&`, `;` ARE supported — parsed natively, no shell invoked, so semantics are identical on Windows / macOS / Linux. Each chain segment is allowlist-checked individually: `git status | grep main` runs if both halves are allowed.\\n• File redirects ARE supported: `>` truncate, `>>` append, `<` stdin from file, `2>` / `2>>` stderr to file, `2>&1` merge stderr→stdout, `&>` both to file. Targets resolve relative to the project root. At most one redirect per fd per segment.\\n• Background `&`, heredoc `<<`, command substitution `$(…)`, subshells `(…)`, and process substitution `<(…)` are NOT supported. Wrap a literal `&` arg in quotes; for input use a `<` file or the binary's own --input flag.\\n• Env-var expansion `$VAR` is NOT performed — `$VAR` is passed as a literal string. Use the binary's own --env flag or substitute the value yourself.\\n• `cd` DOES NOT PERSIST between calls — each call spawns a fresh process rooted at the project. `cd` also does not persist within parsed chains like `cd dir && command`. Use a command-native cwd flag instead: `npm --prefix <dir> run <script>`, `npm --prefix <dir> exec -- <bin>`, `git -C <dir> ...`, `cargo -C <dir> ...`, `pytest <dir>/tests`.\\n• Glob patterns (`*.ts`) are passed through as literal arguments — no shell expansion. Use `grep -r`, `rg`, `find -name`, etc.\\n• Avoid commands with unbounded output (`netstat -ano`, `find /`, etc.) — they waste tokens. Filter at source: `netstat -ano -p TCP`, `find src -name '*.ts'`, `grep -c`, `wc -l`.\\n\\nCommon read-only inspection and test/lint/typecheck commands run immediately; anything that could mutate state, install dependencies, or touch the network is refused until the user confirms it in the TUI. Prefer this over asking the user to run a command manually — after edits, run the project's tests to verify.\",\n // Plan-mode gate: allow allowlisted commands through (git status,\n // cargo check, ls, grep …) so the model can actually investigate\n // during planning. Anything that would otherwise trigger a\n // confirmation prompt is treated as \"not read-only\" and bounced.\n readOnlyCheck: (args: { command?: unknown }) => {\n if (isAllowAll()) return true;\n const cmd = typeof args?.command === \"string\" ? args.command.trim() : \"\";\n if (!cmd) return false;\n return isCommandAllowed(cmd, getExtraAllowed());\n },\n parameters: {\n type: \"object\",\n properties: {\n command: {\n type: \"string\",\n description:\n 'Full command line. POSIX-ish quoting. Chain operators `|`, `||`, `&&`, `;` and file redirects `>` / `>>` / `<` / `2>` / `2>>` / `2>&1` / `&>` work natively (no shell). Background `&`, heredoc `<<`, env-var expansion `$VAR`, and command substitution `$(…)` are rejected (or passed through as literal in the case of `$VAR`). To pass an operator character as a literal argument (e.g. a regex), wrap it in quotes: `grep \"a|b\" file.txt`.',\n },\n timeoutSec: {\n type: \"integer\",\n description: `Override the default ${timeoutSec}s timeout for a single command.`,\n },\n },\n required: [\"command\"],\n },\n fn: async (args: { command: string; timeoutSec?: number }, ctx) => {\n const cmd = args.command.trim();\n if (!cmd) throw new Error(\"run_command: empty command\");\n if (!isAllowAll() && !isCommandAllowed(cmd, getExtraAllowed())) {\n const gate = ctx?.confirmationGate ?? pauseGate;\n const choice = await gate.ask({ kind: \"run_command\", payload: { command: cmd } });\n if (choice.type === \"deny\") {\n throw new Error(\n `user denied: ${cmd}${choice.denyContext ? ` — ${choice.denyContext}` : \"\"}`,\n );\n }\n if (choice.type === \"always_allow\") {\n addProjectShellAllowed(rootDir, choice.prefix);\n }\n // \"run_once\" — fall through and execute\n }\n const effectiveTimeout = Math.max(1, Math.min(600, args.timeoutSec ?? timeoutSec));\n const result = await runCommand(cmd, {\n cwd: rootDir,\n timeoutSec: effectiveTimeout,\n maxOutputChars,\n signal: ctx?.signal,\n });\n return formatCommandResult(cmd, result);\n },\n });\n\n registry.register({\n name: \"run_background\",\n description:\n \"Spawn a long-running process (dev server, watcher, any command that doesn't naturally exit) and detach. Waits up to `waitSec` seconds for startup (or until the output matches a readiness signal like 'Local:', 'listening on', 'compiled successfully'), then returns the job id + startup preview. The process keeps running; call `job_output` to tail its logs, `stop_job` to kill it, `list_jobs` to see all running jobs.\\n\\nSame shell constraints as run_command: NO `&&` / `||` / `|` / `;` / `>` / `<` / `2>&1`, `cd` doesn't persist. Dev servers that need a subdirectory: use the tool's own --prefix / --cwd flag. For Vite specifically, `--prefix` on npm only tells npm where package.json is; vite's server root still defaults to process cwd, so pass `vite <project-dir>` or configure via `vite.config.ts` root.\\n\\nUSE THIS — not `run_command` — for: npm/yarn/pnpm run dev, uvicorn / flask run, go run, cargo watch, tsc --watch, webpack serve, anything with 'dev' / 'serve' / 'watch' in the name.\",\n parameters: {\n type: \"object\",\n properties: {\n command: {\n type: \"string\",\n description:\n \"Full command line. Same quoting rules as run_command (no pipes / redirects / chaining).\",\n },\n waitSec: {\n type: \"integer\",\n description:\n \"Max seconds to wait for startup before returning. 0..30, default 3. A ready-signal match short-circuits this.\",\n },\n },\n required: [\"command\"],\n },\n fn: async (args: { command: string; waitSec?: number }, ctx) => {\n const cmd = args.command.trim();\n if (!cmd) throw new Error(\"run_background: empty command\");\n if (!isAllowAll() && !isCommandAllowed(cmd, getExtraAllowed())) {\n const gate = ctx?.confirmationGate ?? pauseGate;\n const choice = await gate.ask({ kind: \"run_background\", payload: { command: cmd } });\n if (choice.type === \"deny\") {\n throw new Error(\n `user denied: ${cmd}${choice.denyContext ? ` — ${choice.denyContext}` : \"\"}`,\n );\n }\n if (choice.type === \"always_allow\") {\n addProjectShellAllowed(rootDir, choice.prefix);\n }\n // \"run_once\" — fall through and execute\n }\n const result = await jobs.start(cmd, {\n cwd: rootDir,\n waitSec: args.waitSec,\n signal: ctx?.signal,\n });\n return formatJobStart(result);\n },\n });\n\n registry.register({\n name: \"job_output\",\n description:\n \"Read the latest output of a background job started with `run_background`. By default returns the tail of the buffer (last 80 lines). Pass `since` (the `byteLength` from a previous call) to stream only new content incrementally. Tells you whether the job is still running, so you can stop polling when it's done.\",\n readOnly: true,\n parallelSafe: true,\n stormExempt: true,\n parameters: {\n type: \"object\",\n properties: {\n jobId: { type: \"integer\", description: \"Job id returned by run_background.\" },\n since: {\n type: \"integer\",\n description:\n \"Return only output written past this byte offset (for incremental polling).\",\n },\n tailLines: {\n type: \"integer\",\n description: \"Cap the returned slice to the last N lines. Default 80, 0 = unlimited.\",\n },\n },\n required: [\"jobId\"],\n },\n fn: async (args: { jobId: number; since?: number; tailLines?: number }) => {\n const out = jobs.read(args.jobId, {\n since: args.since,\n tailLines: args.tailLines ?? 80,\n });\n if (!out) return `job ${args.jobId}: not found (use list_jobs)`;\n return formatJobRead(args.jobId, out);\n },\n });\n\n registry.register({\n name: \"wait_for_job\",\n description:\n \"Block until a background job exits or produces new output, bounded by `timeoutMs`. Use this instead of polling `job_output` with identical args when you're intentionally waiting for state to change. Returns JSON with `exited`, `exitCode`, and `latestOutput`.\",\n readOnly: true,\n parameters: {\n type: \"object\",\n properties: {\n jobId: { type: \"integer\", description: \"Job id returned by run_background.\" },\n timeoutMs: {\n type: \"integer\",\n description:\n \"Max time to block before returning if nothing changes. Clamped to 0..30000. Default 5000.\",\n },\n },\n required: [\"jobId\"],\n },\n fn: async (args: { jobId: number; timeoutMs?: number }) => {\n const out = await jobs.waitForJob(args.jobId, { timeoutMs: args.timeoutMs });\n if (!out) return `job ${args.jobId}: not found (use list_jobs)`;\n return {\n jobId: args.jobId,\n exited: out.exited,\n exitCode: out.exitCode,\n latestOutput: out.latestOutput,\n };\n },\n });\n\n registry.register({\n name: \"stop_job\",\n description:\n \"Stop a background job started with `run_background`. SIGTERM first; SIGKILL after a short grace period if it doesn't exit cleanly. Returns the final output + exit code. Safe to call on an already-exited job.\",\n parameters: {\n type: \"object\",\n properties: {\n jobId: { type: \"integer\" },\n },\n required: [\"jobId\"],\n },\n fn: async (args: { jobId: number }) => {\n const rec = await jobs.stop(args.jobId);\n if (!rec) return `job ${args.jobId}: not found`;\n return formatJobStop(rec);\n },\n });\n\n registry.register({\n name: \"list_jobs\",\n description:\n \"List every background job started this session — running and exited — with id, command, pid, status. Use when you've lost track of which job_id corresponds to which process, or to see what's still alive.\",\n readOnly: true,\n parallelSafe: true,\n stormExempt: true,\n parameters: { type: \"object\", properties: {} },\n fn: async () => {\n const all = jobs.list();\n if (all.length === 0) return \"(no background jobs started this session)\";\n return all.map(formatJobRow).join(\"\\n\");\n },\n });\n\n return registry;\n}\n\nfunction formatJobStart(r: import(\"./jobs.js\").JobStartResult): string {\n const header = r.stillRunning\n ? `[job ${r.jobId} started · pid ${r.pid ?? \"?\"} · ${r.readyMatched ? \"READY signal matched\" : \"running (no ready signal yet)\"}]`\n : r.exitCode !== null\n ? `[job ${r.jobId} exited during startup · exit ${r.exitCode}]`\n : `[job ${r.jobId} failed to start]`;\n return r.preview ? `${header}\\n${r.preview}` : header;\n}\n\nfunction formatJobRead(jobId: number, r: import(\"./jobs.js\").JobReadResult): string {\n const status = r.running\n ? `running · pid ${r.pid ?? \"?\"}`\n : r.exitCode !== null\n ? `exited ${r.exitCode}`\n : r.spawnError\n ? `failed (${r.spawnError})`\n : \"stopped\";\n const header = `[job ${jobId} · ${status} · byteLength=${r.byteLength}]\\n$ ${r.command}`;\n return r.output ? `${header}\\n${r.output}` : header;\n}\n\nfunction formatJobStop(r: import(\"./jobs.js\").JobRecord): string {\n const running = r.running\n ? \"still running (SIGKILL may be pending)\"\n : `exit ${r.exitCode ?? \"?\"}`;\n const tail = tailLines(r.output, 40);\n const header = `[job ${r.id} stopped · ${running}]\\n$ ${r.command}`;\n return tail ? `${header}\\n${tail}` : header;\n}\n\nfunction formatJobRow(r: import(\"./jobs.js\").JobRecord): string {\n const age = ((Date.now() - r.startedAt) / 1000).toFixed(1);\n const state = r.running\n ? `running · pid ${r.pid ?? \"?\"}`\n : r.exitCode !== null\n ? `exit ${r.exitCode}`\n : r.spawnError\n ? \"failed\"\n : \"stopped\";\n return ` ${String(r.id).padStart(3)} ${state.padEnd(24)} ${age}s ago $ ${r.command}`;\n}\n\nfunction tailLines(s: string, n: number): string {\n if (!s) return \"\";\n const lines = s.split(\"\\n\");\n if (lines.length <= n) return s;\n const dropped = lines.length - n;\n return [`[… ${dropped} earlier lines …]`, ...lines.slice(-n)].join(\"\\n\");\n}\n\nexport function formatCommandResult(cmd: string, r: RunCommandResult): string {\n const header = r.timedOut\n ? `$ ${cmd}\\n[killed after timeout]`\n : `$ ${cmd}\\n[exit ${r.exitCode ?? \"?\"}]`;\n return r.output ? `${header}\\n${r.output}` : header;\n}\n","/** Generic pause gate — bridges tool functions and the App's modals via Promises. */\n// Tools call gate.ask(kind, payload) and await the result; the App subscribes\n// with gate.on() to show the right modal, then calls gate.resolve() on user pick.\n\nexport type ConfirmationChoice =\n | { type: \"deny\"; denyContext?: string }\n | { type: \"run_once\" }\n | { type: \"always_allow\"; prefix: string };\n\nexport type PlanVerdict = { type: \"approve\" } | { type: \"refine\" } | { type: \"cancel\" };\n\nexport type CheckpointVerdict =\n | { type: \"continue\" }\n | { type: \"revise\"; feedback?: string }\n | { type: \"stop\" };\n\nexport type RevisionVerdict = { type: \"accepted\" } | { type: \"rejected\" } | { type: \"cancelled\" };\n\nexport type ChoiceVerdict =\n | { type: \"pick\"; optionId: string }\n | { type: \"text\"; text: string }\n | { type: \"cancel\" };\n\nexport type ToolConfirmationAuditEvent =\n | {\n type: \"tool.confirm.allow\";\n kind: \"run_command\" | \"run_background\";\n payload: { command: string };\n }\n | {\n type: \"tool.confirm.deny\";\n kind: \"run_command\" | \"run_background\";\n payload: { command: string };\n denyContext?: string;\n }\n | {\n type: \"tool.confirm.always_allow\";\n kind: \"run_command\" | \"run_background\";\n payload: { command: string };\n prefix: string;\n };\n\ninterface PauseResponseMap {\n run_command: ConfirmationChoice;\n run_background: ConfirmationChoice;\n plan_proposed: PlanVerdict;\n plan_checkpoint: CheckpointVerdict;\n plan_revision: RevisionVerdict;\n choice: ChoiceVerdict;\n}\n\ntype PauseKind = keyof PauseResponseMap;\n\ninterface PausePayloadMap {\n run_command: { command: string };\n run_background: { command: string };\n plan_proposed: { plan: string; steps?: unknown[]; summary?: string };\n plan_checkpoint: { stepId: string; title?: string; result: string; notes?: string };\n plan_revision: { reason: string; remainingSteps: unknown[]; summary?: string };\n choice: { question: string; options: unknown[]; allowCustom: boolean };\n}\n\nexport type PauseRequest = {\n id: number;\n kind: PauseKind;\n payload: unknown;\n};\n\ntype GateListener = (request: PauseRequest) => void;\ntype AuditListener = (event: ToolConfirmationAuditEvent) => void;\n\n/** Named options for PauseGate.ask() — makes it obvious which field is kind vs payload. */\nexport interface PauseAskOpts<K extends PauseKind = PauseKind> {\n kind: K;\n payload: PausePayloadMap[K];\n}\n\nexport class PauseGate {\n private _nextId = 0;\n private _pending = new Map<number, { resolve: (data: unknown) => void; request: PauseRequest }>();\n private _listeners: Set<GateListener> = new Set();\n private _auditListener: AuditListener | null = null;\n\n /** Block until the user responds. Takes a named options object so the\n * kind and payload fields don't get confused at the call site. */\n ask<K extends PauseKind>(opts: PauseAskOpts<K>): Promise<PauseResponseMap[K]> {\n const { kind, payload } = opts;\n if (this._listeners.size === 0) {\n throw new Error(\n `${kind}: no confirmation listener registered — cannot prompt the user. This tool can only be used inside an interactive Reasonix session.`,\n );\n }\n return new Promise((resolve) => {\n const id = this._nextId++;\n const request: PauseRequest = { id, kind, payload };\n this._pending.set(id, { resolve: resolve as (d: unknown) => void, request });\n for (const fn of this._listeners) {\n try {\n fn(request);\n } catch {\n /* listener error shouldn't break the gate */\n }\n }\n });\n }\n\n /** Resolve a pending request. Called by the App's modal callback. */\n resolve(id: number, data: unknown): void {\n const p = this._pending.get(id);\n if (!p) return;\n this._pending.delete(id);\n this.emitAuditEvent(p.request, data);\n p.resolve(data);\n }\n\n setAuditListener(fn: AuditListener | null): void {\n this._auditListener = fn;\n }\n\n /** Subscribe to new pause requests. Returns an unsubscribe function. */\n on(fn: GateListener): () => void {\n this._listeners.add(fn);\n return () => {\n this._listeners.delete(fn);\n };\n }\n\n /** Current pending request, if any (polling fallback). */\n get current(): PauseRequest | null {\n for (const [, p] of this._pending) return p.request;\n return null;\n }\n\n private emitAuditEvent(request: PauseRequest, data: unknown): void {\n if (!this._auditListener) return;\n if (request.kind !== \"run_command\" && request.kind !== \"run_background\") return;\n if (!data || typeof data !== \"object\") return;\n const choice = data as Partial<ConfirmationChoice>;\n try {\n switch (choice.type) {\n case \"run_once\":\n this._auditListener({\n type: \"tool.confirm.allow\",\n kind: request.kind,\n payload: request.payload as { command: string },\n });\n break;\n case \"deny\":\n this._auditListener({\n type: \"tool.confirm.deny\",\n kind: request.kind,\n payload: request.payload as { command: string },\n denyContext: choice.denyContext,\n });\n break;\n case \"always_allow\":\n if (typeof choice.prefix !== \"string\") return;\n this._auditListener({\n type: \"tool.confirm.always_allow\",\n kind: request.kind,\n payload: request.payload as { command: string },\n prefix: choice.prefix,\n });\n break;\n default:\n break;\n }\n } catch {\n /* audit path must never break the gate */\n }\n }\n}\n\n/** Singleton shared between tools and the App. */\nexport const pauseGate = new PauseGate();\n","import { type ChildProcess, type SpawnOptions, spawn, spawnSync } from \"node:child_process\";\nimport { existsSync, statSync } from \"node:fs\";\nimport * as pathMod from \"node:path\";\nimport { parseCommandChain, runChain } from \"../shell-chain.js\";\nimport { tokenizeCommand } from \"./parse.js\";\n\nexport const DEFAULT_TIMEOUT_SEC = 60;\nexport const DEFAULT_MAX_OUTPUT_CHARS = 32_000;\n\n/** Kill child + descendants. Windows: taskkill /T /F. Unix: SIGKILL the process group when detached, else fall back to SIGKILL on the leader. */\nexport function killProcessTree(child: ChildProcess): void {\n if (!child.pid || child.killed) return;\n if (process.platform === \"win32\") {\n try {\n spawnSync(\"taskkill\", [\"/pid\", String(child.pid), \"/T\", \"/F\"], {\n stdio: \"ignore\",\n windowsHide: true,\n });\n return;\n } catch {\n /* fall through to SIGKILL */\n }\n }\n try {\n process.kill(-child.pid, \"SIGKILL\");\n return;\n } catch {\n /* not a process group leader — fall through */\n }\n try {\n child.kill(\"SIGKILL\");\n } catch {\n /* already gone */\n }\n}\n\nexport interface RunCommandResult {\n exitCode: number | null;\n /** Combined stdout+stderr, truncated to `maxOutputChars` with a marker. */\n output: string;\n /** True when the process was killed for exceeding `timeoutSec`. */\n timedOut: boolean;\n}\n\nexport async function runCommand(\n cmd: string,\n opts: {\n cwd: string;\n timeoutSec?: number;\n maxOutputChars?: number;\n signal?: AbortSignal;\n },\n): Promise<RunCommandResult> {\n const timeoutSec = opts.timeoutSec ?? DEFAULT_TIMEOUT_SEC;\n const maxChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;\n const argv = tokenizeCommand(cmd);\n if (argv.length === 0) throw new Error(\"run_command: empty command\");\n const chain = parseCommandChain(cmd);\n if (chain !== null) {\n return await runChain(chain, {\n cwd: opts.cwd,\n timeoutSec,\n maxOutputChars: maxChars,\n signal: opts.signal,\n });\n }\n const timeoutMs = timeoutSec * 1000;\n\n const spawnOpts: SpawnOptions = {\n cwd: opts.cwd,\n shell: false, // no shell-expansion — see header comment\n windowsHide: true,\n // PYTHONIOENCODING + PYTHONUTF8 force any spawned Python child\n // (run_command running `python script.py`, etc.) to emit UTF-8\n // on stdout/stderr. Without this, Chinese-Windows defaults\n // Python's stdout encoder to GBK and `print(\"…\")` raises\n // UnicodeEncodeError on emoji / non-GBK chars — the model then\n // sees a Python traceback instead of the script's real output\n // and goes around in circles trying to fix the wrong problem.\n // Harmless on non-Python processes (env vars they don't read).\n env: { ...process.env, PYTHONIOENCODING: \"utf-8\", PYTHONUTF8: \"1\" },\n };\n\n // Windows: two layered fixes on top of shell:false —\n // 1. Resolve bare command names via PATH × PATHEXT (CreateProcess\n // ignores PATHEXT, so `npm` alone misses `npm.cmd`).\n // 2. Node 21.7.3+ (CVE-2024-27980) refuses to spawn `.cmd`/`.bat`\n // directly even with shell:false and safe args — throws\n // EINVAL at invocation time. Wrap those via `cmd.exe /d /s /c`\n // with verbatim args + manual quoting, so shell metacharacters\n // in arguments stay literal.\n // Unix path is unchanged.\n const { bin, args, spawnOverrides } = prepareSpawn(argv);\n const effectiveSpawnOpts = { ...spawnOpts, ...spawnOverrides };\n\n return await new Promise<RunCommandResult>((resolve, reject) => {\n let child: import(\"node:child_process\").ChildProcess;\n try {\n child = spawn(bin, args, effectiveSpawnOpts);\n } catch (err) {\n reject(err);\n return;\n }\n // Collect raw Buffer chunks rather than decoding incrementally —\n // a multi-byte sequence can land split across chunks, and a naïve\n // chunk.toString() corrupts it before the second half arrives.\n // We decode once at close time, where smartDecodeOutput can also\n // sniff non-UTF-8 codepages cleanly. The byte cap mirrors the\n // prior char cap (2× maxChars worth) so a chatty process can't\n // OOM us.\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n const byteCap = maxChars * 2 * 4; // worst-case 4 bytes/char for utf-8/gbk\n let timedOut = false;\n let aborted = false;\n const killChildTree = () => killProcessTree(child);\n const killTimer = setTimeout(() => {\n timedOut = true;\n killChildTree();\n }, timeoutMs);\n const onAbort = () => {\n aborted = true;\n killChildTree();\n };\n // Check synchronously first — if the signal aborted before listener attach\n // (parent loop was already cancelled), addEventListener with `once:true`\n // never fires, child runs unbounded.\n if (opts.signal?.aborted) {\n onAbort();\n } else {\n opts.signal?.addEventListener(\"abort\", onAbort, { once: true });\n }\n\n const onData = (chunk: Buffer | string) => {\n const b = typeof chunk === \"string\" ? Buffer.from(chunk) : chunk;\n if (totalBytes >= byteCap) return;\n const remaining = byteCap - totalBytes;\n if (b.length > remaining) {\n chunks.push(b.subarray(0, remaining));\n totalBytes = byteCap;\n } else {\n chunks.push(b);\n totalBytes += b.length;\n }\n };\n child.stdout?.on(\"data\", onData);\n child.stderr?.on(\"data\", onData);\n child.on(\"error\", (err) => {\n clearTimeout(killTimer);\n opts.signal?.removeEventListener(\"abort\", onAbort);\n reject(err);\n });\n child.on(\"close\", (code) => {\n clearTimeout(killTimer);\n opts.signal?.removeEventListener(\"abort\", onAbort);\n const merged = Buffer.concat(chunks);\n const buf = smartDecodeOutput(merged);\n const output =\n buf.length > maxChars\n ? `${buf.slice(0, maxChars)}\\n\\n[… truncated ${buf.length - maxChars} chars …]`\n : buf;\n resolve({ exitCode: code, output, timedOut });\n });\n });\n}\n\n/** GBK fallback on Windows — cmd.exe's localized error DLL and native EXE stderr ignore chcp 65001. */\nexport function smartDecodeOutput(buf: Buffer): string {\n if (buf.length === 0) return \"\";\n try {\n return new TextDecoder(\"utf-8\", { fatal: true }).decode(buf);\n } catch {\n // Fall through to platform-specific fallback.\n }\n if (process.platform === \"win32\") {\n try {\n // TextDecoder supports gbk / gb18030 in Node 18+ via the WHATWG\n // Encoding spec. gb18030 is the modern superset; falling back\n // to it covers GBK byte sequences plus the rare 4-byte CJK\n // characters that appear in newer system messages.\n return new TextDecoder(\"gb18030\").decode(buf);\n } catch {\n // Decoder unavailable in this build — fall through.\n }\n }\n // Last resort: lossy UTF-8 with replacement chars. The model still\n // gets \"something happened\" with the structural exit-code marker\n // intact, which is more useful than throwing away the entire output.\n return buf.toString(\"utf8\");\n}\n\nexport interface ResolveExecutableOptions {\n platform?: NodeJS.Platform;\n env?: { PATH?: string; PATHEXT?: string };\n isFile?: (path: string) => boolean;\n pathDelimiter?: string;\n}\n\n/** CreateProcess ignores PATHEXT — bare `npm` fails ENOENT under `shell:false` without this resolver. */\nexport function resolveExecutable(cmd: string, opts: ResolveExecutableOptions = {}): string {\n const platform = opts.platform ?? process.platform;\n if (platform !== \"win32\") return cmd;\n if (!cmd) return cmd;\n // Already a path fragment — spawn handles these natively.\n if (cmd.includes(\"/\") || cmd.includes(\"\\\\\") || pathMod.isAbsolute(cmd)) return cmd;\n // If the model wrote `npm.cmd` explicitly, respect that verbatim.\n if (pathMod.extname(cmd)) return cmd;\n\n const env = opts.env ?? process.env;\n const pathExt = (env.PATHEXT ?? \".COM;.EXE;.BAT;.CMD\")\n .split(\";\")\n .map((e) => e.trim())\n .filter(Boolean);\n const delimiter = opts.pathDelimiter ?? (platform === \"win32\" ? \";\" : pathMod.delimiter);\n const pathDirs = (env.PATH ?? \"\").split(delimiter).filter(Boolean);\n const isFile = opts.isFile ?? defaultIsFile;\n\n for (const dir of pathDirs) {\n for (const ext of pathExt) {\n // Force win32 join so CI tests that pass `platform: \"win32\"`\n // from a Linux runner get backslash-joined paths; the real-\n // Windows runtime path lands here too and gets the correct\n // separator regardless of where pathMod defaults.\n const full = pathMod.win32.join(dir, cmd + ext);\n if (isFile(full)) return full;\n }\n }\n return cmd;\n}\n\nfunction defaultIsFile(full: string): boolean {\n try {\n return existsSync(full) && statSync(full).isFile();\n } catch {\n return false;\n }\n}\n\n/** Windows workarounds: PATHEXT lookup + CVE-2024-27980 prohibition on direct `.cmd`/`.bat` spawn. */\nexport function prepareSpawn(\n argv: readonly string[],\n opts: ResolveExecutableOptions = {},\n): { bin: string; args: string[]; spawnOverrides: SpawnOptions } {\n const head = argv[0] ?? \"\";\n const tail = argv.slice(1);\n const platform = opts.platform ?? process.platform;\n const resolved = resolveExecutable(head, opts);\n\n if (platform !== \"win32\") {\n return { bin: resolved, args: [...tail], spawnOverrides: {} };\n }\n\n // `.cmd` / `.bat` wrappers require cmd.exe on post-CVE Node.\n if (/\\.(cmd|bat)$/i.test(resolved)) {\n const cmdline = [resolved, ...tail].map(quoteForCmdExe).join(\" \");\n return {\n bin: \"cmd.exe\",\n args: [\"/d\", \"/s\", \"/c\", withUtf8Codepage(cmdline)],\n // windowsVerbatimArguments prevents Node from re-quoting the /c\n // payload — we've already composed an exact cmd.exe command\n // line. Without this Node wraps our already-quoted string in\n // another round of quotes and cmd.exe can't parse it.\n spawnOverrides: { windowsVerbatimArguments: true },\n };\n }\n\n // Bare command names that PATH × PATHEXT couldn't resolve to an\n // on-disk file — these are almost always cmd.exe built-ins (`dir`,\n // `echo`, `type`, `ver`, `vol`, `where`, `help`, …) which don't\n // exist as standalone executables. Direct spawn crashes with ENOENT;\n // routing through cmd.exe lets the built-in resolve, and if it's\n // genuinely unknown the user gets the standard \"'foo' is not\n // recognized\" message instead of a raw spawn failure.\n if (isBareWindowsName(resolved) && resolved === head) {\n const cmdline = [head, ...tail].map(quoteForCmdExe).join(\" \");\n return {\n bin: \"cmd.exe\",\n args: [\"/d\", \"/s\", \"/c\", withUtf8Codepage(cmdline)],\n spawnOverrides: { windowsVerbatimArguments: true },\n };\n }\n\n // PowerShell variants: chcp 65001 doesn't help here because PowerShell\n // sets its own [Console]::OutputEncoding at startup — usually system\n // codepage (CP936/CP932/CP949 on CJK Windows) or UTF-16. The result\n // is mojibake when our `chunk.toString()` UTF-8-decodes its stdout.\n // Inject a UTF-8 setup prelude into the `-Command` (or `-c`) arg so\n // any output produced thereafter is UTF-8.\n if (isPowerShellExe(resolved)) {\n const patched = injectPowerShellUtf8(tail);\n if (patched) {\n return { bin: resolved, args: patched, spawnOverrides: {} };\n }\n }\n\n return { bin: resolved, args: [...tail], spawnOverrides: {} };\n}\n\n/** Resolved bin path looks like Windows PowerShell or PowerShell Core. */\nfunction isPowerShellExe(resolved: string): boolean {\n return /(?:^|[\\\\/])(?:powershell|pwsh)(?:\\.exe)?$/i.test(resolved);\n}\n\n/** Targets `-Command` only — PowerShell quoting is finicky enough that wrapping script-file mode could break it. */\nexport function injectPowerShellUtf8(args: readonly string[]): string[] | null {\n const prelude =\n \"[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;$OutputEncoding=[System.Text.Encoding]::UTF8;\";\n for (let i = 0; i < args.length; i++) {\n const a = args[i] ?? \"\";\n if (/^-(?:Command|c)$/i.test(a) && i + 1 < args.length) {\n const out = [...args];\n out[i + 1] = `${prelude}${args[i + 1] ?? \"\"}`;\n return out;\n }\n }\n return null;\n}\n\n/** Single `&` (not `&&`) so the command still runs on Win7 where chcp can return non-zero. */\nexport function withUtf8Codepage(cmdline: string): string {\n return `chcp 65001 >nul & ${cmdline}`;\n}\n\nfunction isBareWindowsName(s: string): boolean {\n if (!s) return false;\n if (s.includes(\"/\") || s.includes(\"\\\\\")) return false;\n if (pathMod.isAbsolute(s)) return false;\n if (pathMod.extname(s)) return false;\n return true;\n}\n\n/** Doubles embedded quotes per cmd.exe's `\"\"` escape rule; bare alnum passes through unquoted. */\nexport function quoteForCmdExe(arg: string): string {\n if (arg === \"\") return '\"\"';\n if (!/[\\s\"&|<>^%(),;!]/.test(arg)) return arg;\n return `\"${arg.replace(/\"/g, '\"\"')}\"`;\n}\n","/** Parse + spawn `cmd1 | cmd2 && cmd3 > out` ourselves — never invoke a shell, sidestep PS5.1's `&&` parse error and codepage drift. */\n\nimport { type ChildProcess, type SpawnOptions, spawn } from \"node:child_process\";\nimport { closeSync, openSync } from \"node:fs\";\nimport * as pathMod from \"node:path\";\nimport { isDqEscape, killProcessTree, prepareSpawn, smartDecodeOutput } from \"./shell.js\";\n\nexport type ChainOp = \"|\" | \"||\" | \"&&\" | \";\";\n\nexport type RedirectKind = \">\" | \">>\" | \"<\" | \"2>\" | \"2>>\" | \"2>&1\" | \"&>\";\n\nexport interface Redirect {\n kind: RedirectKind;\n /** File path resolved against the chain's cwd; empty for `2>&1`. */\n target: string;\n}\n\nexport interface ChainSegment {\n argv: string[];\n redirects: Redirect[];\n}\n\nexport interface CommandChain {\n segments: ChainSegment[];\n /** length === segments.length - 1 */\n ops: ChainOp[];\n}\n\nexport class UnsupportedSyntaxError extends Error {\n constructor(detail: string) {\n super(`run_command: ${detail}`);\n this.name = \"UnsupportedSyntaxError\";\n }\n}\n\n/** Whitespace-bounded splitter — chain ops only count when they begin a token, so `--flag=1&2` stays literal. */\nfunction splitOnChainOps(cmd: string): { segs: string[]; ops: ChainOp[] } {\n const segs: string[] = [];\n const ops: ChainOp[] = [];\n let segStart = 0;\n let i = 0;\n let quote: '\"' | \"'\" | null = null;\n let atTokenStart = true;\n while (i < cmd.length) {\n const ch = cmd[i]!;\n if (quote) {\n if (ch === quote) quote = null;\n else if (quote === '\"' && isDqEscape(ch, cmd[i + 1])) i++;\n i++;\n atTokenStart = false;\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch;\n i++;\n atTokenStart = false;\n continue;\n }\n if (ch === \" \" || ch === \"\\t\") {\n i++;\n atTokenStart = true;\n continue;\n }\n if (atTokenStart) {\n let op: ChainOp | null = null;\n let opLen = 0;\n const next = cmd[i + 1];\n if (ch === \"|\" && next === \"|\") {\n op = \"||\";\n opLen = 2;\n } else if (ch === \"&\" && next === \"&\") {\n op = \"&&\";\n opLen = 2;\n } else if (ch === \"|\") {\n op = \"|\";\n opLen = 1;\n } else if (ch === \";\") {\n op = \";\";\n opLen = 1;\n }\n if (op !== null) {\n segs.push(cmd.slice(segStart, i));\n ops.push(op);\n i += opLen;\n segStart = i;\n atTokenStart = true;\n continue;\n }\n }\n i++;\n atTokenStart = false;\n }\n segs.push(cmd.slice(segStart));\n return { segs, ops };\n}\n\n/** Single-pass parser: extract argv + trailing/inline redirects from one segment string. */\nfunction parseSegment(segStr: string): ChainSegment {\n const argv: string[] = [];\n const redirects: Redirect[] = [];\n let cur = \"\";\n let curHasContent = false;\n let pending: RedirectKind | null = null;\n let quote: '\"' | \"'\" | null = null;\n const flush = () => {\n if (!curHasContent && cur.length === 0) return;\n if (pending) {\n redirects.push({ kind: pending, target: cur });\n pending = null;\n } else {\n argv.push(cur);\n }\n cur = \"\";\n curHasContent = false;\n };\n let i = 0;\n while (i < segStr.length) {\n const ch = segStr[i]!;\n if (quote) {\n if (ch === quote) {\n quote = null;\n } else if (quote === '\"' && isDqEscape(ch, segStr[i + 1])) {\n cur += segStr[++i] ?? \"\";\n curHasContent = true;\n } else {\n cur += ch;\n curHasContent = true;\n }\n i++;\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch;\n curHasContent = true;\n i++;\n continue;\n }\n if (ch === \" \" || ch === \"\\t\") {\n flush();\n i++;\n continue;\n }\n if (cur.length === 0 && !curHasContent) {\n const remaining = segStr.slice(i);\n let matched: { op: RedirectKind; len: number } | null = null;\n if (remaining.startsWith(\"2>&1\")) matched = { op: \"2>&1\", len: 4 };\n else if (remaining.startsWith(\"&>\")) matched = { op: \"&>\", len: 2 };\n else if (remaining.startsWith(\"2>>\")) matched = { op: \"2>>\", len: 3 };\n else if (remaining.startsWith(\"2>\")) matched = { op: \"2>\", len: 2 };\n else if (remaining.startsWith(\">>\")) matched = { op: \">>\", len: 2 };\n else if (remaining.startsWith(\">\")) matched = { op: \">\", len: 1 };\n else if (remaining.startsWith(\"<<\")) {\n throw new UnsupportedSyntaxError(\n 'shell operator \"<<\" is not supported — heredoc / here-string is not implemented; pass input via a \"<\" file or the binary\\'s --input flag',\n );\n } else if (remaining.startsWith(\"<\")) matched = { op: \"<\", len: 1 };\n if (matched) {\n if (pending !== null) {\n throw new UnsupportedSyntaxError(\n `redirect \"${pending}\" is missing a target file before \"${matched.op}\"`,\n );\n }\n if (matched.op === \"2>&1\") {\n redirects.push({ kind: \"2>&1\", target: \"\" });\n } else {\n pending = matched.op;\n }\n i += matched.len;\n continue;\n }\n if (ch === \"&\") {\n throw new UnsupportedSyntaxError(\n 'shell operator \"&\" is not supported — background runs need run_background, not run_command. Wrap a literal `&` arg in quotes.',\n );\n }\n }\n cur += ch;\n curHasContent = true;\n i++;\n }\n if (quote) throw new Error(`unclosed ${quote} in command`);\n flush();\n if (pending) throw new UnsupportedSyntaxError(`redirect \"${pending}\" is missing a target file`);\n if (argv.length === 0 && redirects.length > 0) {\n throw new UnsupportedSyntaxError(\n \"redirect without a command — segment must have at least one program argument\",\n );\n }\n validateRedirectFds(redirects);\n return { argv, redirects };\n}\n\n/** stdin (`<`) ≤1, stdout (`>`/`>>`/`&>`) ≤1, stderr (`2>`/`2>>`/`&>`/`2>&1`) ≤1; reject conflicts. */\nfunction validateRedirectFds(redirects: readonly Redirect[]): void {\n let stdin = 0;\n let stdout = 0;\n let stderr = 0;\n for (const r of redirects) {\n if (r.kind === \"<\") stdin++;\n else if (r.kind === \">\" || r.kind === \">>\") stdout++;\n else if (r.kind === \"2>\" || r.kind === \"2>>\" || r.kind === \"2>&1\") stderr++;\n else if (r.kind === \"&>\") {\n stdout++;\n stderr++;\n }\n }\n if (stdin > 1) throw new UnsupportedSyntaxError(\"multiple `<` stdin redirects in one segment\");\n if (stdout > 1)\n throw new UnsupportedSyntaxError(\n \"multiple stdout redirects in one segment (`>` / `>>` / `&>` conflict)\",\n );\n if (stderr > 1)\n throw new UnsupportedSyntaxError(\n \"multiple stderr redirects in one segment (`2>` / `2>>` / `&>` / `2>&1` conflict)\",\n );\n}\n\n/** Returns null on plain commands without redirects (caller takes the simple path). */\nexport function parseCommandChain(cmd: string): CommandChain | null {\n const { segs, ops } = splitOnChainOps(cmd);\n const segments: ChainSegment[] = [];\n for (let i = 0; i < segs.length; i++) {\n const trimmed = segs[i]!.trim();\n if (trimmed.length === 0) {\n const op = i === 0 ? ops[0]! : ops[i - 1]!;\n throw new UnsupportedSyntaxError(\n i === 0\n ? `empty segment before \"${op}\"`\n : i === segs.length - 1\n ? `chain ends with \"${op}\"`\n : `empty segment between \"${ops[i - 1]}\" and \"${ops[i]}\"`,\n );\n }\n segments.push(parseSegment(trimmed));\n }\n // Reject `cd` inside parsed chains — the executor cannot carry cwd\n // changes between segments, and silently running the wrong directory\n // is worse than rejecting early with clear guidance.\n for (const seg of segments) {\n const cmdName = seg.argv[0] ?? \"\";\n if (cmdName.toLowerCase() === \"cd\") {\n throw new UnsupportedSyntaxError(\n \"cd in parsed command chains does not change cwd for later segments. Use a command-native cwd flag instead, such as `npm --prefix <dir> run <script>`, `git -C <dir> ...`, or `cargo -C <dir> ...`.\",\n );\n }\n }\n\n if (ops.length === 0 && segments[0]!.redirects.length === 0) return null;\n return { segments, ops };\n}\n\n/** Each segment must individually clear the allowlist for the chain to auto-run. */\nexport function chainAllowed(\n chain: CommandChain,\n isAllowed: (segmentCmd: string) => boolean,\n): boolean {\n for (const seg of chain.segments) {\n if (!isAllowed(seg.argv.join(\" \"))) return false;\n }\n return true;\n}\n\nexport interface ChainResult {\n exitCode: number | null;\n output: string;\n timedOut: boolean;\n}\n\ninterface ChainGroup {\n segments: ChainSegment[];\n /** Op connecting the PREVIOUS group to THIS one (`||`, `&&`, `;`); null on the first group. */\n opBefore: Exclude<ChainOp, \"|\"> | null;\n}\n\n/** Pipe groups are runs of segments joined by `|`; sequential ops (`||`, `&&`, `;`) split them. */\nfunction groupChain(chain: CommandChain): ChainGroup[] {\n const groups: ChainGroup[] = [{ segments: [chain.segments[0]!], opBefore: null }];\n for (let i = 0; i < chain.ops.length; i++) {\n const op = chain.ops[i]!;\n const next = chain.segments[i + 1]!;\n if (op === \"|\") {\n groups[groups.length - 1]!.segments.push(next);\n } else {\n groups.push({ segments: [next], opBefore: op });\n }\n }\n return groups;\n}\n\nexport interface RunChainOptions {\n cwd: string;\n timeoutSec: number;\n maxOutputChars: number;\n signal?: AbortSignal;\n}\n\nexport async function runChain(chain: CommandChain, opts: RunChainOptions): Promise<ChainResult> {\n const groups = groupChain(chain);\n const buf = new OutputBuffer(opts.maxOutputChars * 2 * 4);\n const deadline = Date.now() + opts.timeoutSec * 1000;\n let lastExit: number | null = 0;\n let timedOut = false;\n for (const group of groups) {\n if (group.opBefore === \"&&\" && lastExit !== 0) continue;\n if (group.opBefore === \"||\" && lastExit === 0) continue;\n const remainingMs = deadline - Date.now();\n if (remainingMs <= 0) {\n timedOut = true;\n break;\n }\n const result = await runPipeGroup(group.segments, {\n cwd: opts.cwd,\n timeoutMs: remainingMs,\n buf,\n signal: opts.signal,\n });\n lastExit = result.exitCode;\n if (result.timedOut) {\n timedOut = true;\n break;\n }\n if (opts.signal?.aborted) break;\n }\n const output = buf.toString();\n const truncated =\n output.length > opts.maxOutputChars\n ? `${output.slice(0, opts.maxOutputChars)}\\n\\n[… truncated ${output.length - opts.maxOutputChars} chars …]`\n : output;\n return { exitCode: lastExit, output: truncated, timedOut };\n}\n\ninterface PipeGroupResult {\n exitCode: number | null;\n timedOut: boolean;\n}\n\ninterface PipeGroupOptions {\n cwd: string;\n timeoutMs: number;\n buf: OutputBuffer;\n signal?: AbortSignal;\n}\n\ninterface SegmentStdio {\n /** Input fd for `<` redirect, or null when reading from prev pipe / nothing. */\n stdinFd: number | null;\n /** Output fd for `>`/`>>`/`&>` redirect, or null when writing to pipe / our buffer. */\n stdoutFd: number | null;\n /** Output fd for `2>`/`2>>`/`&>` redirect, or null when default. */\n stderrFd: number | null;\n mergeStderrToStdout: boolean;\n toClose: number[];\n}\n\nfunction openRedirects(redirects: readonly Redirect[], cwd: string): SegmentStdio {\n let stdinFd: number | null = null;\n let stdoutFd: number | null = null;\n let stderrFd: number | null = null;\n let mergeStderrToStdout = false;\n let bothFd: number | null = null;\n const toClose: number[] = [];\n const open = (target: string, flags: \"r\" | \"w\" | \"a\"): number => {\n const resolved = pathMod.resolve(cwd, target);\n const fd = openSync(resolved, flags);\n toClose.push(fd);\n return fd;\n };\n for (const r of redirects) {\n if (r.kind === \"<\") stdinFd = open(r.target, \"r\");\n else if (r.kind === \">\") stdoutFd = open(r.target, \"w\");\n else if (r.kind === \">>\") stdoutFd = open(r.target, \"a\");\n else if (r.kind === \"2>\") stderrFd = open(r.target, \"w\");\n else if (r.kind === \"2>>\") stderrFd = open(r.target, \"a\");\n else if (r.kind === \"&>\") {\n bothFd = open(r.target, \"w\");\n stdoutFd = bothFd;\n stderrFd = bothFd;\n } else if (r.kind === \"2>&1\") {\n mergeStderrToStdout = true;\n }\n }\n return { stdinFd, stdoutFd, stderrFd, mergeStderrToStdout, toClose };\n}\n\nasync function runPipeGroup(\n segments: ChainSegment[],\n opts: PipeGroupOptions,\n): Promise<PipeGroupResult> {\n const env = { ...process.env, PYTHONIOENCODING: \"utf-8\", PYTHONUTF8: \"1\" };\n const children: ChildProcess[] = [];\n const allFds: number[] = [];\n let timedOut = false;\n const killAll = () => {\n for (const c of children) killProcessTree(c);\n };\n const killTimer = setTimeout(() => {\n timedOut = true;\n killAll();\n }, opts.timeoutMs);\n const onAbort = () => killAll();\n if (opts.signal?.aborted) {\n onAbort();\n } else {\n opts.signal?.addEventListener(\"abort\", onAbort, { once: true });\n }\n try {\n for (let i = 0; i < segments.length; i++) {\n const isFirst = i === 0;\n const isLast = i === segments.length - 1;\n const seg = segments[i]!;\n const io = openRedirects(seg.redirects, opts.cwd);\n allFds.push(...io.toClose);\n const { bin, args, spawnOverrides } = prepareSpawn(seg.argv);\n const stdoutSpec = io.stdoutFd !== null ? io.stdoutFd : \"pipe\";\n const stderrSpec =\n io.stderrFd !== null ? io.stderrFd : io.mergeStderrToStdout ? stdoutSpec : \"pipe\";\n const stdinSpec = io.stdinFd !== null ? io.stdinFd : isFirst ? \"ignore\" : \"pipe\";\n const spawnOpts: SpawnOptions = {\n cwd: opts.cwd,\n shell: false,\n windowsHide: true,\n env,\n stdio: [stdinSpec, stdoutSpec, stderrSpec],\n ...spawnOverrides,\n };\n let child: ChildProcess;\n try {\n child = spawn(bin, args, spawnOpts);\n } catch (err) {\n for (const fd of allFds) tryClose(fd);\n killAll();\n clearTimeout(killTimer);\n opts.signal?.removeEventListener(\"abort\", onAbort);\n throw err;\n }\n children.push(child);\n if (!isFirst && io.stdinFd === null) {\n const prev = children[i - 1]!;\n prev.stdout?.on(\"error\", () => {});\n child.stdin?.on(\"error\", () => {});\n const prevMergesStderr =\n segments[i - 1]!.redirects.some((r) => r.kind === \"2>&1\") && !!prev.stderr;\n if (prevMergesStderr && prev.stderr) {\n prev.stderr.on(\"error\", () => {});\n let openSources = 2;\n const closeIfDone = () => {\n if (--openSources === 0) child.stdin?.end();\n };\n prev.stdout?.pipe(child.stdin!, { end: false });\n prev.stderr.pipe(child.stdin!, { end: false });\n prev.stdout?.once(\"end\", closeIfDone);\n prev.stderr.once(\"end\", closeIfDone);\n } else {\n prev.stdout?.pipe(child.stdin!);\n }\n }\n if (child.stderr && io.stderrFd === null && !(io.mergeStderrToStdout && !isLast)) {\n child.stderr.on(\"data\", (chunk: Buffer | string) => opts.buf.push(toBuf(chunk)));\n }\n if (isLast && child.stdout && io.stdoutFd === null) {\n child.stdout.on(\"data\", (chunk: Buffer | string) => opts.buf.push(toBuf(chunk)));\n if (io.mergeStderrToStdout && child.stderr && io.stderrFd === null) {\n child.stderr.removeAllListeners(\"data\");\n child.stderr.on(\"data\", (chunk: Buffer | string) => opts.buf.push(toBuf(chunk)));\n }\n }\n }\n const exits = await Promise.all(\n children.map(\n (c) =>\n new Promise<number | null>((resolve) => {\n c.once(\"error\", () => resolve(null));\n c.once(\"close\", (code) => resolve(code));\n }),\n ),\n );\n return { exitCode: exits[exits.length - 1] ?? null, timedOut };\n } finally {\n for (const fd of allFds) tryClose(fd);\n clearTimeout(killTimer);\n opts.signal?.removeEventListener(\"abort\", onAbort);\n }\n}\n\nfunction tryClose(fd: number): void {\n try {\n closeSync(fd);\n } catch {\n /* already closed by spawn handover or kernel */\n }\n}\n\nfunction toBuf(chunk: Buffer | string): Buffer {\n return typeof chunk === \"string\" ? Buffer.from(chunk) : chunk;\n}\n\nclass OutputBuffer {\n private chunks: Buffer[] = [];\n private bytes = 0;\n constructor(private readonly cap: number) {}\n push(b: Buffer): void {\n if (this.bytes >= this.cap) return;\n const remaining = this.cap - this.bytes;\n if (b.length > remaining) {\n this.chunks.push(b.subarray(0, remaining));\n this.bytes = this.cap;\n } else {\n this.chunks.push(b);\n this.bytes += b.length;\n }\n }\n toString(): string {\n return smartDecodeOutput(Buffer.concat(this.chunks));\n }\n}\n","import { type CommandChain, chainAllowed, parseCommandChain } from \"../shell-chain.js\";\n\n/** Read-only reports + test runners whose failure mode is \"exit 1 with output\". */\nexport const BUILTIN_ALLOWLIST: ReadonlyArray<string> = [\n // Repo inspection\n \"git status\",\n \"git diff\",\n \"git log\",\n \"git show\",\n \"git blame\",\n \"git branch\",\n \"git remote\",\n \"git rev-parse\",\n \"git config --get\",\n // Filesystem inspection\n \"ls\",\n \"pwd\",\n \"cat\",\n \"head\",\n \"tail\",\n \"wc\",\n \"file\",\n \"tree\",\n \"find\",\n \"grep\",\n \"rg\",\n // Language version probes\n \"node --version\",\n \"node -v\",\n \"npm --version\",\n \"npx --version\",\n \"python --version\",\n \"python3 --version\",\n \"cargo --version\",\n \"go version\",\n \"rustc --version\",\n \"deno --version\",\n \"bun --version\",\n // Test runners (non-destructive by convention)\n \"npm test\",\n \"npm run test\",\n \"npx vitest run\",\n \"npx vitest\",\n \"npx jest\",\n \"pytest\",\n \"python -m pytest\",\n \"cargo test\",\n \"cargo check\",\n \"cargo clippy\",\n \"go test\",\n \"go vet\",\n \"deno test\",\n \"bun test\",\n // Linters / typecheckers (read-only by convention)\n \"npm run lint\",\n \"npm run typecheck\",\n \"npx tsc --noEmit\",\n \"npx biome check\",\n \"npx eslint\",\n \"npx prettier --check\",\n \"ruff\",\n \"mypy\",\n];\n\n/** Inside `\"…\"` only `\\\"` and `\\\\` are escapes — `\\X` otherwise stays literal so Windows paths like `\"C:\\Users\\foo\\.bar\"` survive tokenization. */\nexport function isDqEscape(prev: string, next: string | undefined): boolean {\n return prev === \"\\\\\" && (next === '\"' || next === \"\\\\\");\n}\n\n/** No env / glob / backtick / `$(…)` expansion — prevents bypass of allowlist via concatenation. */\nexport function tokenizeCommand(cmd: string): string[] {\n const out: string[] = [];\n let cur = \"\";\n let quote: '\"' | \"'\" | null = null;\n for (let i = 0; i < cmd.length; i++) {\n const ch = cmd[i]!;\n if (quote) {\n if (ch === quote) {\n quote = null;\n } else if (quote === '\"' && isDqEscape(ch, cmd[i + 1])) {\n cur += cmd[++i];\n } else {\n cur += ch;\n }\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch;\n continue;\n }\n if (ch === \" \" || ch === \"\\t\") {\n if (cur.length > 0) {\n out.push(cur);\n cur = \"\";\n }\n continue;\n }\n cur += ch;\n }\n if (quote) throw new Error(`unclosed ${quote} in command`);\n if (cur.length > 0) out.push(cur);\n return out;\n}\n\n/** Up-front detection — without it, `dir | findstr foo` quotes `|` literal and pipe silently fails. */\nexport function detectShellOperator(cmd: string): string | null {\n const opPrefix = /^(?:2>&1|&>|\\|{1,2}|&{1,2}|2>{1,2}|>{1,2}|<{1,2})/;\n let cur = \"\";\n let curQuoted = false;\n let quote: '\"' | \"'\" | null = null;\n const check = (): string | null => {\n if (cur.length === 0 && !curQuoted) return null;\n if (!curQuoted) {\n const m = opPrefix.exec(cur);\n if (m) return m[0] ?? null;\n }\n return null;\n };\n for (let i = 0; i < cmd.length; i++) {\n const ch = cmd[i]!;\n if (quote) {\n if (ch === quote) {\n quote = null;\n } else if (quote === '\"' && isDqEscape(ch, cmd[i + 1])) {\n cur += cmd[++i];\n curQuoted = true;\n } else {\n cur += ch;\n curQuoted = true;\n }\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch;\n curQuoted = true;\n continue;\n }\n if (ch === \" \" || ch === \"\\t\") {\n const op = check();\n if (op) return op;\n cur = \"\";\n curQuoted = false;\n continue;\n }\n cur += ch;\n }\n if (quote) return null; // let tokenizeCommand throw the unclosed-quote error\n return check();\n}\n\n/** Per-prefix demotion: an otherwise-allowlisted match falls back to the confirm gate when one of these tokens appears in the tail. Issue #257: `git branch -D` skipped review. Each token also matches its `--flag=value` form. */\nconst RISKY_ARGS: Readonly<Record<string, ReadonlyArray<string>>> = {\n // Branch / remote mutation\n \"git branch\": [\"-d\", \"-D\", \"--delete\", \"-m\", \"-M\", \"--move\", \"-c\", \"-C\", \"--copy\", \"--force\"],\n \"git remote\": [\"add\", \"remove\", \"rm\", \"rename\", \"set-url\", \"set-head\", \"prune\"],\n // `--output` writes to an arbitrary path; `--ext-diff` invokes user-config'd external programs.\n \"git diff\": [\"--output\", \"--ext-diff\"],\n \"git log\": [\"--output\"],\n \"git show\": [\"--output\"],\n // `-exec*` / `-ok*` are RCE; `-delete` and `-fprint*` / `-fls` write to arbitrary paths.\n find: [\n \"-delete\",\n \"-exec\",\n \"-execdir\",\n \"-ok\",\n \"-okdir\",\n \"-fprint\",\n \"-fprint0\",\n \"-fprintf\",\n \"-fls\",\n ],\n // `-o FILE` writes the tree to an arbitrary path.\n tree: [\"-o\"],\n // Auto-fix mutates source files.\n \"npx eslint\": [\"--fix\", \"--fix-dry-run\"],\n \"npx biome check\": [\"--write\", \"--apply\", \"--apply-unsafe\"],\n ruff: [\"--fix\", \"--unsafe-fixes\", \"format\"],\n};\n\nfunction tailHasRisky(tail: readonly string[], risky: readonly string[]): boolean {\n for (const a of tail) {\n for (const r of risky) {\n if (a === r) return true;\n if (a.startsWith(`${r}=`)) return true;\n }\n }\n return false;\n}\n\n/** Allowlist match on leading argv tokens; demoted by `RISKY_ARGS` when a destructive flag appears in the tail. */\nexport function isAllowed(cmd: string, extra: readonly string[] = []): boolean {\n let argv: string[];\n try {\n argv = tokenizeCommand(cmd);\n } catch {\n return false;\n }\n if (argv.length === 0) return false;\n\n const allowlist = [...BUILTIN_ALLOWLIST, ...extra];\n for (const prefix of allowlist) {\n const prefixTokens = prefix.split(\" \");\n if (argv.length < prefixTokens.length) continue;\n let match = true;\n for (let i = 0; i < prefixTokens.length; i++) {\n if (argv[i] !== prefixTokens[i]) {\n match = false;\n break;\n }\n }\n if (!match) continue;\n\n const risky = RISKY_ARGS[prefix];\n if (risky && tailHasRisky(argv.slice(prefixTokens.length), risky)) return false;\n return true;\n }\n return false;\n}\n\n/** For chain commands, every segment must individually clear the allowlist. */\nexport function isCommandAllowed(cmd: string, extra: readonly string[] = []): boolean {\n let chain: CommandChain | null;\n try {\n chain = parseCommandChain(cmd);\n } catch {\n return false;\n }\n if (chain === null) return isAllowed(cmd, extra);\n return chainAllowed(chain, (seg) => isAllowed(seg, extra));\n}\n"],"mappings":"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","names":["spawn","pathMod","pathMod","resolve","spawn","pathMod","isAllowed","resolve","resolve","spawn","delimiter","snapshot","killProcessTree","spawn","id","job","resolve"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/commands/code.tsx","../../src/tools/scaffold.ts"],"sourcesContent":["/**\n * `reasonix code [dir]` — opinionated wrapper around `reasonix chat` for\n * code-editing workflows.\n *\n * What it does differently from plain chat:\n * - Registers native filesystem tools rooted at the given directory\n * (CWD by default). No subprocess, no `npx install` step, R1-\n * friendly schemas. Replaced the old `@modelcontextprotocol/server-filesystem`\n * subprocess in 0.4.9 because its `edit_file` argv shape was the\n * biggest driver of R1 DSML hallucinations.\n * - Uses a coding-focused system prompt (src/code/prompt.ts) that\n * teaches the model to propose edits as SEARCH/REPLACE blocks.\n * - Defaults to the `smart` preset (reasoner + harvest) because\n * coding tasks pay back R1 thinking.\n * - Scopes its session to the directory so projects don't share\n * conversation history.\n * - Hooks `codeMode` into the TUI so assistant replies get parsed\n * for SEARCH/REPLACE blocks and applied on disk after each turn.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { basename, resolve } from \"node:path\";\nimport { loadEditMode, loadProjectShellAllowed, readConfig } from \"../../config.js\";\nimport { bootstrapSemanticSearchInCodeMode } from \"../../index/semantic/tool.js\";\nimport { sanitizeName } from \"../../memory/session.js\";\nimport { ToolRegistry } from \"../../tools.js\";\nimport { registerChoiceTool } from \"../../tools/choice.js\";\nimport { registerFilesystemTools } from \"../../tools/filesystem.js\";\nimport { JobRegistry } from \"../../tools/jobs.js\";\nimport { registerMemoryTools } from \"../../tools/memory.js\";\nimport { registerPlanTool } from \"../../tools/plan.js\";\nimport { registerScaffoldTools } from \"../../tools/scaffold.js\";\nimport { registerShellTools } from \"../../tools/shell.js\";\nimport { registerTodoTool } from \"../../tools/todo.js\";\nimport { markPhase } from \"../startup-profile.js\";\nimport { chatCommand } from \"./chat.js\";\n\nexport interface CodeOptions {\n /** Directory to root the filesystem tools at. Defaults to process.cwd(). */\n dir?: string;\n /** Override the default `smart` model. */\n model?: string;\n /** Disable session persistence. */\n noSession?: boolean;\n /** Transcript file for replay/diff. */\n transcript?: string;\n /** Skip the session picker — always resume prior messages. */\n forceResume?: boolean;\n /** Skip the session picker — always wipe prior messages and start fresh. */\n forceNew?: boolean;\n /**\n * Soft USD spend cap. Off by default. Same semantics as `chat`:\n * warns at 80%, refuses next turn at 100%. Mid-session adjustable\n * via `/budget <usd>` slash command.\n */\n budgetUsd?: number;\n /** Suppress the auto-launched embedded web dashboard. */\n noDashboard?: boolean;\n /** Inline string appended to the code system prompt after the generated base prompt. */\n systemAppend?: string;\n /** Path to a UTF-8 text file whose contents are appended to the code system prompt. */\n systemAppendFile?: string;\n /** Default true. Pass false (CLI: `--no-alt-screen`) to keep chat output in shell scrollback. */\n altScreen?: boolean;\n}\n\nexport async function codeCommand(opts: CodeOptions = {}): Promise<void> {\n markPhase(\"code_command_enter\");\n const { codeSystemPrompt } = await import(\"../../code/prompt.js\");\n const rootDir = resolve(opts.dir ?? process.cwd());\n // Per-directory session so switching projects doesn't mix histories.\n // `code-<sanitized-basename>` fits the session name rules without\n // truncating most project names.\n const session = opts.noSession ? undefined : `code-${sanitizeName(basename(rootDir))}`;\n\n // Native filesystem tools. No subprocess, ~50-200 ms faster per call\n // than the MCP server was, and `edit_file` takes a flat SEARCH/REPLACE\n // shape instead of the `string=\"false\"` JSON-in-string array that\n // triggered R1's DSML hallucinations all through 0.4.x.\n const tools = new ToolRegistry();\n // Background-process registry shared between the shell tools and the\n // TUI's /jobs + /kill slashes + exit cleanup. One per `reasonix code`\n // run — orphan prevention on SIGINT / process exit kills everything\n // it owns, so dev servers don't outlive the Reasonix process.\n const jobs = new JobRegistry();\n // Bundled re-registration so `/cwd <path>` can swap every rootDir-\n // dependent tool atomically. ToolRegistry.register is keyed by name\n // and overwrites in-place, so re-calling these against the existing\n // registry replaces the closures cleanly without disturbing tool\n // specs (names/descriptions/params don't reference rootDir, so the\n // prefix cache survives).\n const registerRootedTools = (root: string): void => {\n registerFilesystemTools(tools, { rootDir: root });\n registerShellTools(tools, {\n rootDir: root,\n // Per-project \"always allow\" list persisted from prior ShellConfirm\n // choices; merged on top of the built-in allowlist in shell.ts.\n // GETTER form — re-read every dispatch so a prefix the user adds\n // via ShellConfirm mid-session takes effect on the next shell call\n // instead of waiting for `/new` or a relaunch.\n extraAllowed: () => loadProjectShellAllowed(root),\n // `yolo` edit-mode disables shell confirmations entirely. Re-read\n // from config on each dispatch so /mode yolo (or Shift+Tab cycling\n // through to it) flips the gate live without forcing a relaunch.\n allowAll: () => loadEditMode() === \"yolo\",\n jobs,\n });\n // `remember` / `forget` / `recall_memory` — cross-session user memory.\n // Project scope hashes off rootDir so switching projects gets a fresh\n // per-project memory store; the global scope is shared across runs.\n registerMemoryTools(tools, { projectRoot: root });\n };\n // Async tail to `registerRootedTools`. Kept separate because the FS /\n // shell / memory re-registration above is sync and must happen before\n // the next tool dispatch, while semantic-index probing reads disk and\n // can race ahead in the background. On `/cwd`, App.tsx fires this\n // after the sync swap and surfaces the result via postInfo.\n const reBootstrapSemantic = async (root: string): Promise<{ enabled: boolean }> => {\n const result = await bootstrapSemanticSearchInCodeMode(tools, root);\n if (!result.enabled) tools.unregister(\"semantic_search\");\n return result;\n };\n registerRootedTools(rootDir);\n // `submit_plan` is always in the spec list so the prefix cache stays\n // stable across plan-mode toggles (Pillar 1). The tool itself is a\n // no-op outside plan mode and throws `PlanProposedError` when the\n // user has `/plan`-enabled the session.\n registerPlanTool(tools);\n // `ask_choice` — branching primitive. Independent of plan mode: the\n // model uses it to put a 2–4 way choice in front of the user\n // (strategy, style, library pick) without trying to squeeze the\n // menu into a submit_plan body. Keeping it always-registered\n // preserves the prefix cache across plan-mode toggles.\n registerChoiceTool(tools);\n // `todo_write` — lightweight in-session task tracker, no approval gate.\n // Independent of plan mode (readOnly=true so it stays callable in /plan).\n registerTodoTool(tools);\n // `create_skill` / `add_mcp_server` — let the model scaffold from chat.\n // Both writes go through the same paths the wizard / `/skill new` use,\n // so the on-disk shape stays one source of truth. New servers take\n // effect on next launch (no live client churn).\n registerScaffoldTools(tools, { projectRoot: rootDir });\n // `run_skill` is intentionally NOT registered here — App.tsx wires it\n // up with the subagent runner attached, so `runAs: subagent` skills\n // can spawn isolated child loops. Doing it here would mean the App's\n // re-registration would shadow the no-runner version, which works\n // (last write wins) but obscures the wiring.\n\n // Bootstrap semantic_search. Silent: registers the tool when an\n // on-disk index already exists, skips entirely otherwise. Setup\n // happens via the explicit `reasonix index` command — never\n // by surprise on launch.\n markPhase(\"semantic_bootstrap_start\");\n const semantic = await reBootstrapSemantic(rootDir);\n markPhase(\n semantic.enabled ? \"semantic_bootstrap_done_enabled\" : \"semantic_bootstrap_done_skipped\",\n );\n\n process.stderr.write(\n `▸ reasonix code: rooted at ${rootDir}, session \"${session ?? \"(ephemeral)\"}\" · ${tools.size} native tool(s)${\n semantic.enabled ? \" · semantic_search on\" : \"\"\n }\\n`,\n );\n\n // Belt-and-suspenders cleanup: even though spawn(detached:false)\n // should tie child processes to the parent's lifetime, Windows cmd.exe\n // wrappers occasionally leak. We DON'T install SIGINT/SIGTERM\n // handlers here — that overrode Node's default \"exit on Ctrl+C\" with\n // a silent no-op, which made Ctrl+C feel broken in the TUI. App.tsx\n // owns the SIGINT path now (it shows the quit-armed banner and calls\n // exit() on confirmation); this 'exit' hook just guarantees the job\n // registry is drained on the way out, regardless of which exit path\n // fired.\n process.once(\"exit\", () => {\n void jobs.shutdown();\n });\n\n let systemAppendFileContents: string | undefined;\n if (opts.systemAppend !== undefined && opts.systemAppend.trim().length === 0) {\n process.stderr.write(\"--system-append is empty — no prompt text will be appended\\n\");\n }\n if (opts.systemAppendFile) {\n const filePath = resolve(opts.systemAppendFile);\n try {\n systemAppendFileContents = readFileSync(filePath, \"utf8\");\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n process.stderr.write(\n `Error: cannot read --system-append-file \"${filePath}\": ${e.code ? `[${e.code}] ` : \"\"}${e.message}\\n`,\n );\n process.exit(1);\n }\n }\n\n await chatCommand({\n model: opts.model ?? \"deepseek-v4-flash\",\n budgetUsd: opts.budgetUsd,\n system: codeSystemPrompt(rootDir, {\n hasSemanticSearch: semantic.enabled,\n systemAppend: opts.systemAppend,\n systemAppendFile: systemAppendFileContents,\n }),\n transcript: opts.transcript,\n session,\n seedTools: tools,\n codeMode: {\n rootDir,\n jobs,\n reregisterTools: registerRootedTools,\n reBootstrapSemantic,\n },\n mcp: readConfig().mcp,\n forceResume: opts.forceResume,\n forceNew: opts.forceNew,\n noDashboard: opts.noDashboard,\n altScreen: opts.altScreen,\n });\n}\n","/** Agent-facing tools for scaffolding skills + MCP servers from chat. Persists via the same paths the wizard / `/skill new` use. */\n\nimport { defaultConfigPath, readConfig, writeConfig } from \"../config.js\";\nimport { MCP_CATALOG } from \"../mcp/catalog.js\";\nimport { preflightStdioSpec } from \"../mcp/preflight.js\";\nimport { type McpSpec, parseMcpSpec } from \"../mcp/spec.js\";\nimport { SkillStore } from \"../skills.js\";\nimport type { ToolRegistry } from \"../tools.js\";\n\nexport interface ScaffoldToolsOptions {\n homeDir?: string;\n projectRoot?: string;\n /** Override config path — tests point this at a tmp file. */\n configPath?: string;\n}\n\nconst VALID_SKILL_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/;\nconst VALID_SERVER_NAME = /^[a-zA-Z_][a-zA-Z0-9_-]{0,63}$/;\nconst VALID_TOOL_NAME = /^[a-zA-Z_][a-zA-Z0-9_-]*$/;\n\nexport function registerScaffoldTools(\n registry: ToolRegistry,\n opts: ScaffoldToolsOptions = {},\n): ToolRegistry {\n const configPath = opts.configPath ?? defaultConfigPath();\n\n registry.register({\n name: \"create_skill\",\n description:\n 'Scaffold a new skill (`SKILL.md` in `.reasonix/skills/<name>.md`) the user can invoke later via `/skill <name>`. Use this when the user asks the agent to add a playbook, automate a recurring workflow, or capture a multi-step recipe as a named skill. The frontmatter is filled from the structured args here (description / allowed_tools / run_as / model) so the model never has to write raw YAML. Use `run_as: \"subagent\"` for read-and-synthesize playbooks where only the final answer should come back; default `\"inline\"` appends the body to the parent log so the user sees the steps. Refuses to overwrite an existing skill — pick a different name or ask the user to delete the old one.',\n parameters: {\n type: \"object\",\n properties: {\n name: {\n type: \"string\",\n description:\n \"Skill identifier — letters/digits/`_`/`-`/`.`, 1–64 chars. Becomes the `name` frontmatter and the `<name>.md` filename.\",\n },\n description: {\n type: \"string\",\n description:\n 'One-line summary shown in the pinned skills index. Lead with the verb (\"Run X and …\") so the parent agent can scan it.',\n },\n body: {\n type: \"string\",\n description:\n \"Markdown body of the skill — the playbook the model follows when invoked. Plain prose + bullets; reference tools by name.\",\n },\n scope: {\n type: \"string\",\n enum: [\"project\", \"global\"],\n description:\n \"`project` = `.reasonix/skills/` under the workspace (default, requires `reasonix code`); `global` = `~/.reasonix/skills/` shared across all repos.\",\n },\n allowed_tools: {\n type: \"array\",\n items: { type: \"string\" },\n description:\n \"Optional whitelist of tool names the subagent registry is scoped to (only meaningful for `run_as: subagent`). Common values: `read_file`, `search_content`, `directory_tree`, `run_command`. Omit to give the subagent the full inherited toolset.\",\n },\n run_as: {\n type: \"string\",\n enum: [\"inline\", \"subagent\"],\n description:\n \"`inline` (default) appends the body to the parent log as a tool result. `subagent` spawns an isolated child loop and only the final answer comes back — use for read-and-synthesize playbooks (explore, research, review).\",\n },\n model: {\n type: \"string\",\n enum: [\"deepseek-v4-flash\", \"deepseek-v4-pro\"],\n description:\n \"Subagent model override (only meaningful for `run_as: subagent`). Default is the same as `spawn_subagent` — `deepseek-v4-flash`. Set to `deepseek-v4-pro` only when the playbook empirically needs the stronger model.\",\n },\n },\n required: [\"name\", \"description\", \"body\"],\n },\n fn: async (args: {\n name?: unknown;\n description?: unknown;\n body?: unknown;\n scope?: unknown;\n allowed_tools?: unknown;\n run_as?: unknown;\n model?: unknown;\n }) => {\n const name = typeof args.name === \"string\" ? args.name.trim() : \"\";\n if (!VALID_SKILL_NAME.test(name)) {\n return JSON.stringify({\n error: `invalid skill name: ${JSON.stringify(name)} — use letters, digits, _, -, .`,\n });\n }\n const description =\n typeof args.description === \"string\" ? args.description.trim().replace(/\\n+/g, \" \") : \"\";\n if (!description) {\n return JSON.stringify({\n error: \"create_skill requires a non-empty 'description'\",\n });\n }\n const body = typeof args.body === \"string\" ? args.body : \"\";\n if (!body.trim()) {\n return JSON.stringify({ error: \"create_skill requires a non-empty 'body'\" });\n }\n const scope: \"project\" | \"global\" =\n args.scope === \"global\" ? \"global\" : opts.projectRoot ? \"project\" : \"global\";\n const runAs: \"inline\" | \"subagent\" = args.run_as === \"subagent\" ? \"subagent\" : \"inline\";\n const allowedTools = parseAllowedTools(args.allowed_tools);\n if (allowedTools && \"error\" in allowedTools) {\n return JSON.stringify({ error: allowedTools.error });\n }\n const model =\n typeof args.model === \"string\" && args.model.startsWith(\"deepseek-\")\n ? args.model\n : undefined;\n\n const content = serializeSkill({\n name,\n description,\n runAs,\n allowedTools: allowedTools ?? undefined,\n model,\n body,\n });\n\n const store = new SkillStore({\n homeDir: opts.homeDir,\n projectRoot: opts.projectRoot,\n });\n const result = store.createWithContent(name, scope, content);\n if (\"error\" in result) {\n return JSON.stringify({ error: result.error });\n }\n return JSON.stringify({\n success: true,\n path: result.path,\n scope,\n name,\n run_as: runAs,\n });\n },\n });\n\n registry.register({\n name: \"add_mcp_server\",\n description:\n 'Register a new MCP server in the user\\'s Reasonix config (`mcp` array). Takes effect on the next session — does NOT spawn the server now. Use stdio for local commands (npx packages, local binaries), `sse` or `streamable-http` for remote endpoints. Pass `from_catalog: \"<name>\"` (e.g. `\"filesystem\"`, `\"memory\"`, `\"github\"`) to auto-fill `command` + `args` from the bundled catalog — the user still has to supply user-args (filesystem: a sandbox dir; github: GITHUB_PERSONAL_ACCESS_TOKEN in env). Refuses to add a server whose name collides with an existing entry.',\n parameters: {\n type: \"object\",\n properties: {\n name: {\n type: \"string\",\n description:\n \"Server name — used as the namespace prefix on every tool the server exposes. Letters/digits/`_`/`-`, must start with a letter or `_`.\",\n },\n transport: {\n type: \"string\",\n enum: [\"stdio\", \"sse\", \"streamable-http\"],\n description:\n \"`stdio` = spawn a local command and pipe MCP over stdin/stdout. `sse` = HTTP+SSE remote. `streamable-http` = Streamable HTTP remote. Required unless `from_catalog` is set.\",\n },\n command: {\n type: \"string\",\n description:\n 'Argv[0] for stdio servers — typically `npx` or a binary path. Required when `transport: \"stdio\"` (and no `from_catalog`).',\n },\n args: {\n type: \"array\",\n items: { type: \"string\" },\n description:\n 'Remaining argv for stdio servers — e.g. `[\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/path/to/dir\"]`. The dir at the tail is enforced to exist by the preflight check.',\n },\n url: {\n type: \"string\",\n description:\n \"Endpoint URL for `sse` / `streamable-http` transports. Must be `http://` or `https://`.\",\n },\n from_catalog: {\n type: \"string\",\n description:\n \"Optional shortcut — name out of the bundled catalog (`filesystem`, `memory`, `github`, `puppeteer`, `everything`). When set, fills `command` + `args` from the catalog entry; you still supply `name` (defaults to the catalog name) and any user-args via `args`.\",\n },\n },\n required: [\"name\"],\n },\n fn: async (args: {\n name?: unknown;\n transport?: unknown;\n command?: unknown;\n args?: unknown;\n url?: unknown;\n from_catalog?: unknown;\n }) => {\n const name = typeof args.name === \"string\" ? args.name.trim() : \"\";\n if (!VALID_SERVER_NAME.test(name)) {\n return JSON.stringify({\n error: `invalid server name: ${JSON.stringify(name)} — must match [a-zA-Z_][a-zA-Z0-9_-]*`,\n });\n }\n\n const specStr = buildSpecString({\n name,\n transport: typeof args.transport === \"string\" ? args.transport : undefined,\n command: typeof args.command === \"string\" ? args.command : undefined,\n argv: Array.isArray(args.args)\n ? (args.args.filter((a) => typeof a === \"string\") as string[])\n : undefined,\n url: typeof args.url === \"string\" ? args.url : undefined,\n fromCatalog: typeof args.from_catalog === \"string\" ? args.from_catalog : undefined,\n });\n if (\"error\" in specStr) {\n return JSON.stringify({ error: specStr.error });\n }\n\n let parsed: McpSpec;\n try {\n parsed = parseMcpSpec(specStr.spec);\n } catch (err) {\n return JSON.stringify({ error: (err as Error).message });\n }\n if (parsed.transport === \"stdio\") {\n try {\n preflightStdioSpec(parsed);\n } catch (err) {\n return JSON.stringify({ error: (err as Error).message });\n }\n }\n\n const cfg = readConfig(configPath);\n const existing = cfg.mcp ?? [];\n const collision = existing.find((s) => parseSpecName(s) === name);\n if (collision) {\n return JSON.stringify({\n error: `MCP server ${JSON.stringify(name)} already registered: ${collision}`,\n });\n }\n cfg.mcp = [...existing, specStr.spec];\n writeConfig(cfg, configPath);\n return JSON.stringify({\n success: true,\n name,\n transport: parsed.transport,\n spec: specStr.spec,\n config_path: configPath,\n active_on_next_launch: true,\n });\n },\n });\n\n return registry;\n}\n\ninterface SerializeSkillArgs {\n name: string;\n description: string;\n runAs: \"inline\" | \"subagent\";\n allowedTools?: readonly string[];\n model?: string;\n body: string;\n}\n\nexport function serializeSkill(args: SerializeSkillArgs): string {\n const lines: string[] = [\"---\", `name: ${args.name}`, `description: ${args.description}`];\n if (args.runAs === \"subagent\") {\n lines.push(\"runAs: subagent\");\n }\n if (args.allowedTools && args.allowedTools.length > 0) {\n lines.push(`allowed-tools: ${args.allowedTools.join(\", \")}`);\n }\n if (args.model) {\n lines.push(`model: ${args.model}`);\n }\n lines.push(\"---\", \"\");\n return `${lines.join(\"\\n\")}\\n${args.body.trim()}\\n`;\n}\n\nfunction parseAllowedTools(raw: unknown): readonly string[] | { error: string } | undefined {\n if (raw === undefined || raw === null) return undefined;\n if (!Array.isArray(raw)) {\n return { error: \"'allowed_tools' must be an array of tool-name strings\" };\n }\n const out: string[] = [];\n for (const v of raw) {\n if (typeof v !== \"string\") {\n return { error: \"'allowed_tools' entries must be strings\" };\n }\n const trimmed = v.trim();\n if (!trimmed) continue;\n if (!VALID_TOOL_NAME.test(trimmed)) {\n return { error: `invalid tool name in allowed_tools: ${JSON.stringify(trimmed)}` };\n }\n out.push(trimmed);\n }\n return out.length > 0 ? out : undefined;\n}\n\ninterface BuildSpecInput {\n name: string;\n transport?: string;\n command?: string;\n argv?: string[];\n url?: string;\n fromCatalog?: string;\n}\n\nfunction buildSpecString(input: BuildSpecInput): { spec: string } | { error: string } {\n if (input.fromCatalog) {\n const entry = MCP_CATALOG.find((e) => e.name === input.fromCatalog);\n if (!entry) {\n const known = MCP_CATALOG.map((e) => e.name).join(\", \");\n return {\n error: `unknown catalog entry: ${JSON.stringify(input.fromCatalog)} — known: ${known}`,\n };\n }\n const userArgs = input.argv ?? [];\n if (entry.userArgs && userArgs.length === 0) {\n return {\n error: `catalog entry \"${entry.name}\" needs ${entry.userArgs} — pass it via the 'args' parameter`,\n };\n }\n const tail = userArgs.map(quoteIfNeeded).join(\" \");\n const body = `npx -y ${entry.package}${tail ? ` ${tail}` : \"\"}`;\n return { spec: `${input.name}=${body}` };\n }\n\n const transport = input.transport;\n if (!transport) {\n return { error: \"add_mcp_server requires 'transport' (or 'from_catalog')\" };\n }\n if (transport === \"stdio\") {\n if (!input.command || !input.command.trim()) {\n return { error: \"stdio transport requires 'command'\" };\n }\n const tail = (input.argv ?? []).map(quoteIfNeeded).join(\" \");\n const body = `${quoteIfNeeded(input.command.trim())}${tail ? ` ${tail}` : \"\"}`;\n return { spec: `${input.name}=${body}` };\n }\n if (transport === \"sse\" || transport === \"streamable-http\") {\n if (!input.url || !/^https?:\\/\\//i.test(input.url)) {\n return { error: `${transport} transport requires an http(s):// 'url'` };\n }\n const prefix = transport === \"streamable-http\" ? \"streamable+\" : \"\";\n return { spec: `${input.name}=${prefix}${input.url.trim()}` };\n }\n return { error: `unknown transport: ${JSON.stringify(transport)}` };\n}\n\nfunction parseSpecName(spec: string): string | null {\n const m = spec.trim().match(/^([a-zA-Z_][a-zA-Z0-9_-]*)=/);\n return m ? (m[1] ?? null) : null;\n}\n\nfunction quoteIfNeeded(s: string): string {\n return /\\s|\"/.test(s) ? `\"${s.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"` : s;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/commands/commit.ts"],"sourcesContent":["/** Drafts via diff + recent log (style mimicry); commit uses `-F -` so multi-line bodies survive shell quoting. */\n\nimport { spawn, spawnSync } from \"node:child_process\";\nimport { mkdtempSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { stdin, stdout } from \"node:process\";\nimport { createInterface } from \"node:readline/promises\";\nimport { DeepSeekClient } from \"../../client.js\";\nimport { loadApiKey } from \"../../config.js\";\nimport { loadDotenv } from \"../../env.js\";\n\nexport interface CommitOptions {\n /** Override the default model (deepseek-v4-flash). */\n model?: string;\n /** Skip the confirmation step — useful in scripts where the diff has been pre-reviewed. */\n yes?: boolean;\n}\n\nconst DEFAULT_MODEL = \"deepseek-v4-flash\";\nconst DIFF_BYTE_CAP = 80 * 1024;\nconst LOG_COUNT = 10;\n\nconst SYSTEM_PROMPT = `You draft git commit messages.\n\nOutput ONLY the commit message — no preamble, no \\`\\`\\` fences, no \"Here's a commit message:\" lead-in. The first line of your output IS the commit subject.\n\nMatch the project's existing style:\n- Look at the recent commits provided. Mirror their voice, conventional-commit prefix usage (or absence), tense, length, body structure.\n- If recent commits use a \"type(scope): summary\" prefix, use it. If they don't, don't invent one.\n- Subject line: one line, ≤72 chars, imperative mood, no trailing period.\n- Body (optional): explain WHY when the diff isn't self-evident. Wrap at ~72 chars. Skip the body for trivial changes — repeating the subject in the body is noise.\n\nThe diff is the source of truth for what changed; describe THAT, not your guesses about the broader project. If the diff includes a deletion you can't explain from the surrounding context, name it but don't speculate about why.\n\nNo emojis unless the recent commits use them.\nNo co-author trailers, no \"Generated with X\" footers.`;\n\nfunction runGit(\n args: string[],\n opts: { input?: string } = {},\n): { stdout: string; stderr: string; status: number | null } {\n const result = spawnSync(\"git\", args, {\n encoding: \"utf8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n input: opts.input,\n maxBuffer: 32 * 1024 * 1024,\n });\n return {\n stdout: result.stdout ?? \"\",\n stderr: result.stderr ?? \"\",\n status: result.status,\n };\n}\n\nfunction dieIfNotGitRepo(): void {\n const r = runGit([\"rev-parse\", \"--is-inside-work-tree\"]);\n if (r.status !== 0) {\n process.stderr.write(\"reasonix commit: not inside a git repository.\\n\");\n process.exit(1);\n }\n}\n\ninterface DiffResult {\n diff: string;\n source: \"staged\" | \"working-tree\";\n truncated: boolean;\n}\n\nfunction readDiff(): DiffResult | null {\n const staged = runGit([\"diff\", \"--staged\", \"--no-color\"]);\n if (staged.status !== 0) {\n process.stderr.write(`reasonix commit: git diff --staged failed: ${staged.stderr.trim()}\\n`);\n process.exit(1);\n }\n if (staged.stdout.trim().length > 0) {\n return capDiff(staged.stdout, \"staged\");\n }\n const wt = runGit([\"diff\", \"--no-color\"]);\n if (wt.stdout.trim().length === 0) {\n return null;\n }\n return capDiff(wt.stdout, \"working-tree\");\n}\n\nfunction capDiff(raw: string, source: \"staged\" | \"working-tree\"): DiffResult {\n if (raw.length <= DIFF_BYTE_CAP) {\n return { diff: raw, source, truncated: false };\n }\n const head = raw.slice(0, Math.floor(DIFF_BYTE_CAP * 0.7));\n const tail = raw.slice(-Math.floor(DIFF_BYTE_CAP * 0.3));\n return {\n diff: `${head}\\n\\n[… ${raw.length - DIFF_BYTE_CAP} bytes of diff truncated …]\\n\\n${tail}`,\n source,\n truncated: true,\n };\n}\n\nfunction readRecentCommits(): string {\n const r = runGit([\"log\", `-${LOG_COUNT}`, \"--no-merges\", \"--format=%s%n%b%n---END---\"]);\n if (r.status !== 0) {\n // Repo may not have any commits yet (initial commit case). Don't\n // fail — let the model work from the diff alone.\n return \"\";\n }\n return r.stdout.trim();\n}\n\nasync function draftMessage(\n client: DeepSeekClient,\n model: string,\n diff: DiffResult,\n recentCommits: string,\n): Promise<string> {\n const userParts: string[] = [];\n if (recentCommits) {\n userParts.push(`Recent commits (style reference):\\n\\n${recentCommits}`);\n }\n if (diff.source === \"working-tree\") {\n userParts.push(\n \"(NOTE: diff is from the working tree, not the staging area — nothing is staged yet. The user will stage selectively after seeing the draft.)\",\n );\n }\n userParts.push(`Diff to summarize:\\n\\n${diff.diff}`);\n\n const resp = await client.chat({\n model,\n messages: [\n { role: \"system\", content: SYSTEM_PROMPT },\n { role: \"user\", content: userParts.join(\"\\n\\n\") },\n ],\n temperature: 0.2,\n });\n return stripCodeFences(resp.content.trim());\n}\n\nfunction stripCodeFences(s: string): string {\n // Some models still wrap output in ``` despite the system prompt\n // telling them not to. Strip a single leading + trailing fence pair\n // if present. Only operates on a wrapping pair — internal fences\n // (a code block inside the body) stay.\n const trimmed = s.trim();\n const fenceOpen = /^```[a-zA-Z]*\\n/;\n const fenceClose = /\\n?```$/;\n if (fenceOpen.test(trimmed) && fenceClose.test(trimmed)) {\n return trimmed.replace(fenceOpen, \"\").replace(fenceClose, \"\").trim();\n }\n return trimmed;\n}\n\nfunction printDraft(message: string): void {\n const sep = \"─\".repeat(60);\n process.stdout.write(`\\n${sep}\\n${message}\\n${sep}\\n\\n`);\n}\n\nasync function promptChoice(): Promise<\"accept\" | \"regen\" | \"edit\" | \"cancel\"> {\n const rl = createInterface({ input: stdin, output: stdout });\n try {\n const answer = await rl.question(\"[a]ccept / [r]egenerate / [e]dit / [c]ancel: \");\n const k = answer.trim().toLowerCase();\n if (k === \"\" || k === \"a\" || k === \"y\" || k === \"yes\") return \"accept\";\n if (k === \"r\" || k === \"regen\" || k === \"regenerate\") return \"regen\";\n if (k === \"e\" || k === \"edit\") return \"edit\";\n return \"cancel\";\n } finally {\n rl.close();\n }\n}\n\nfunction editInExternal(initial: string): string | null {\n const editor = process.env.GIT_EDITOR ?? process.env.VISUAL ?? process.env.EDITOR;\n if (!editor) {\n process.stderr.write(\n \"reasonix commit: no $EDITOR / $VISUAL / $GIT_EDITOR set — can't open editor. Pick [a]ccept and `git commit --amend` afterwards.\\n\",\n );\n return null;\n }\n const dir = mkdtempSync(join(tmpdir(), \"reasonix-commit-\"));\n const path = join(dir, \"COMMIT_EDITMSG\");\n writeFileSync(path, initial, \"utf8\");\n // spawnSync with shell:true is required so $EDITOR strings like\n // `code --wait` work — they're shell command lines, not argv tuples.\n // The trust boundary is the user's own env var; matches how git\n // itself launches editors.\n const result = spawnSync(`${editor} \"${path}\"`, {\n stdio: \"inherit\",\n shell: true,\n });\n if (result.status !== 0) {\n try {\n unlinkSync(path);\n } catch {\n /* ignore */\n }\n process.stderr.write(\n `reasonix commit: editor exited ${result.status} — keeping prior draft.\\n`,\n );\n return null;\n }\n let edited: string;\n try {\n edited = readFileSync(path, \"utf8\");\n } catch {\n return null;\n } finally {\n try {\n unlinkSync(path);\n } catch {\n /* ignore */\n }\n }\n // Strip git's standard `# …` comment lines, even though we didn't\n // emit any — a user habituated to `git commit` may add `#`-prefixed\n // notes by reflex.\n const cleaned = edited\n .split(/\\r?\\n/)\n .filter((line) => !/^\\s*#/.test(line))\n .join(\"\\n\")\n .trim();\n return cleaned || null;\n}\n\nfunction commitWithMessage(message: string): void {\n // -F - reads the message from stdin, sidestepping shell quoting and\n // letting multi-line bodies through cleanly. Inherit stdio so the\n // user sees git's own confirmation / pre-commit hook output.\n const child = spawn(\"git\", [\"commit\", \"-F\", \"-\"], {\n stdio: [\"pipe\", \"inherit\", \"inherit\"],\n });\n child.stdin.write(message);\n child.stdin.end();\n child.on(\"close\", (code) => {\n if (code !== 0) {\n process.stderr.write(`reasonix commit: git commit exited ${code}.\\n`);\n process.exit(code ?? 1);\n }\n });\n}\n\nexport async function commitCommand(opts: CommitOptions = {}): Promise<void> {\n loadDotenv();\n dieIfNotGitRepo();\n\n const apiKey = loadApiKey() ?? process.env.DEEPSEEK_API_KEY;\n if (!apiKey) {\n process.stderr.write(\n \"reasonix commit: DEEPSEEK_API_KEY not set. Run `reasonix setup` to save one, or export it.\\n\",\n );\n process.exit(1);\n }\n\n const diff = readDiff();\n if (!diff) {\n process.stderr.write(\n \"reasonix commit: no staged changes and working tree is clean — nothing to commit.\\n\",\n );\n process.exit(1);\n }\n if (diff.source === \"working-tree\") {\n process.stderr.write(\n \"reasonix commit: nothing staged — drafting from working-tree diff. Stage your changes and re-run, or use the draft as a starting point.\\n\",\n );\n }\n if (diff.truncated) {\n process.stderr.write(\n \"reasonix commit: diff exceeded 80KB; head + tail sent to the model. Large diffs often produce vague drafts — consider committing in smaller chunks.\\n\",\n );\n }\n\n const client = new DeepSeekClient({ apiKey });\n const model = opts.model ?? DEFAULT_MODEL;\n const recentCommits = readRecentCommits();\n\n let message = \"\";\n let firstPass = true;\n while (true) {\n if (firstPass) {\n process.stdout.write(\"Drafting commit message…\\n\");\n } else {\n process.stdout.write(\"Regenerating…\\n\");\n }\n firstPass = false;\n try {\n message = await draftMessage(client, model, diff, recentCommits);\n } catch (err) {\n process.stderr.write(`reasonix commit: model call failed — ${(err as Error).message}\\n`);\n process.exit(1);\n }\n if (!message) {\n process.stderr.write(\"reasonix commit: model returned an empty draft. Try again.\\n\");\n process.exit(1);\n }\n printDraft(message);\n\n if (opts.yes) break;\n if (diff.source === \"working-tree\") {\n // Refuse to commit a working-tree-derived draft — the staging\n // area is empty so `git commit` would fail anyway. Print the\n // draft so the user can copy it; exit 0 because we did our job.\n process.stdout.write(\n \"(no staged changes — draft printed above for you to copy. Stage with `git add` and re-run to commit.)\\n\",\n );\n return;\n }\n\n const choice = await promptChoice();\n if (choice === \"accept\") break;\n if (choice === \"cancel\") {\n process.stderr.write(\"commit cancelled.\\n\");\n return;\n }\n if (choice === \"edit\") {\n const edited = editInExternal(message);\n if (edited) {\n message = edited;\n printDraft(message);\n // Re-prompt: the user may want to edit again, accept, etc.\n const next = await promptChoice();\n if (next === \"accept\") break;\n if (next === \"cancel\") {\n process.stderr.write(\"commit cancelled.\\n\");\n return;\n }\n // next is \"regen\" or another \"edit\" — fall through to the\n // loop top to re-draft (regen) or land back at this branch.\n }\n // editor returned no edit — loop top will regen by default.\n }\n // Anything else (regen, or unsuccessful edit) → loop top redraws.\n }\n\n commitWithMessage(message);\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/commands/run.ts"],"sourcesContent":["import type { WriteStream } from \"node:fs\";\nimport { stdin, stdout } from \"node:process\";\nimport { createInterface } from \"node:readline/promises\";\nimport {\n defaultConfigPath,\n isPlausibleKey,\n loadApiKey,\n readConfig,\n saveApiKey,\n} from \"../../config.js\";\nimport { loadDotenv } from \"../../env.js\";\nimport { CacheFirstLoop, DeepSeekClient, ImmutablePrefix } from \"../../index.js\";\nimport { McpClient } from \"../../mcp/client.js\";\nimport { preflightStdioSpec } from \"../../mcp/preflight.js\";\nimport { bridgeMcpTools } from \"../../mcp/registry.js\";\nimport { parseMcpSpec } from \"../../mcp/spec.js\";\nimport { SseTransport } from \"../../mcp/sse.js\";\nimport { type McpTransport, StdioTransport } from \"../../mcp/stdio.js\";\nimport { StreamableHttpTransport } from \"../../mcp/streamable-http.js\";\nimport { appendUsage } from \"../../telemetry/usage.js\";\nimport { ToolRegistry } from \"../../tools.js\";\nimport { openTranscriptFile, recordFromLoopEvent, writeRecord } from \"../../transcript/log.js\";\nimport { formatMcpLifecycleEvent } from \"../ui/mcp-lifecycle.js\";\nimport { formatMcpSlowToast } from \"../ui/mcp-toast.js\";\n\nexport interface RunOptions {\n task: string;\n model: string;\n system: string;\n budgetUsd?: number;\n /** JSONL transcript path — lets `reasonix replay` / `diff` audit this run. */\n transcript?: string;\n /** Zero or more MCP server specs. Each: `\"name=cmd args...\"` or `\"cmd args...\"`. */\n mcp?: string[];\n /** Global prefix — only honored when a single anonymous server is given. */\n mcpPrefix?: string;\n}\n\nasync function ensureApiKey(): Promise<string> {\n const existing = loadApiKey();\n if (existing) return existing;\n\n if (!stdin.isTTY) {\n process.stderr.write(\n \"DEEPSEEK_API_KEY is not set and stdin is not a TTY (cannot prompt).\\n\" +\n \"Set the env var, or run `reasonix chat` once interactively to save a key.\\n\",\n );\n process.exit(1);\n }\n\n process.stdout.write(\n \"DeepSeek API key not configured.\\nGet one at https://platform.deepseek.com/api_keys\\n\",\n );\n const rl = createInterface({ input: stdin, output: stdout });\n try {\n while (true) {\n const answer = (await rl.question(\"API key › \")).trim();\n if (!answer) continue;\n if (!isPlausibleKey(answer)) {\n process.stdout.write(\"Invalid format. Keys start with 'sk-' and are 30+ chars.\\n\");\n continue;\n }\n saveApiKey(answer);\n process.stdout.write(`Saved to ${defaultConfigPath()}\\n\\n`);\n return answer;\n }\n } finally {\n rl.close();\n }\n}\n\nexport async function runCommand(opts: RunOptions): Promise<void> {\n loadDotenv();\n const apiKey = await ensureApiKey();\n process.env.DEEPSEEK_API_KEY = apiKey;\n\n // Optional MCP setup — mirrors chat's flow. Must happen before loop\n // construction so the tools make it into the prefix.\n const requestedSpecs = opts.mcp ?? [];\n const clients: McpClient[] = [];\n let tools: ToolRegistry | undefined;\n let successCount = 0;\n const disabledNames = new Set(readConfig().mcpDisabled ?? []);\n if (requestedSpecs.length > 0) {\n tools = new ToolRegistry();\n for (const raw of requestedSpecs) {\n let label = \"anon\";\n let mcp: McpClient | undefined;\n try {\n const spec = parseMcpSpec(raw);\n label = spec.name ?? \"anon\";\n if (spec.name && disabledNames.has(spec.name)) {\n process.stderr.write(`${formatMcpLifecycleEvent({ state: \"disabled\", name: label })}\\n`);\n continue;\n }\n process.stderr.write(`${formatMcpLifecycleEvent({ state: \"handshake\", name: label })}\\n`);\n const t0 = Date.now();\n const prefix = spec.name\n ? `${spec.name}_`\n : requestedSpecs.length === 1 && opts.mcpPrefix\n ? opts.mcpPrefix\n : \"\";\n if (spec.transport === \"stdio\") preflightStdioSpec(spec);\n const transport: McpTransport =\n spec.transport === \"sse\"\n ? new SseTransport({ url: spec.url })\n : spec.transport === \"streamable-http\"\n ? new StreamableHttpTransport({ url: spec.url })\n : new StdioTransport({ command: spec.command, args: spec.args });\n mcp = new McpClient({ transport });\n await mcp.initialize();\n const bridge = await bridgeMcpTools(mcp, {\n registry: tools,\n namePrefix: prefix,\n serverName: label,\n onSlow: (info) =>\n process.stderr.write(\n `${formatMcpSlowToast({ name: info.serverName, p95Ms: info.p95Ms, sampleSize: info.sampleSize })}\\n`,\n ),\n });\n process.stderr.write(\n `${formatMcpLifecycleEvent({\n state: \"connected\",\n name: label,\n tools: bridge.registeredNames.length,\n ms: Date.now() - t0,\n })}\\n`,\n );\n clients.push(mcp);\n successCount++;\n } catch (err) {\n // Non-fatal — skip and continue, same as `reasonix chat`. A\n // one-shot `run` invocation with a broken MCP server otherwise\n // fails the whole run over a side-concern tool the task might\n // not even touch.\n await mcp?.close().catch(() => undefined);\n process.stderr.write(\n `${formatMcpLifecycleEvent({ state: \"failed\", name: label, reason: (err as Error).message })}\\n → run \\`reasonix setup\\` to remove broken entries from your saved config.\\n`,\n );\n }\n }\n if (successCount === 0) tools = undefined;\n }\n\n const client = new DeepSeekClient();\n const prefix = new ImmutablePrefix({\n system: opts.system,\n toolSpecs: tools?.specs(),\n });\n const loop = new CacheFirstLoop({\n client,\n prefix,\n tools,\n model: opts.model,\n budgetUsd: opts.budgetUsd,\n });\n const prefixHash = prefix.fingerprint;\n\n let transcriptStream: WriteStream | null = null;\n if (opts.transcript) {\n transcriptStream = openTranscriptFile(opts.transcript, {\n version: 1,\n source: \"reasonix run\",\n model: opts.model,\n startedAt: new Date().toISOString(),\n });\n // Also persist the user turn itself (the loop's event stream starts with\n // assistant output, not the prompt we're about to send).\n writeRecord(transcriptStream, {\n ts: new Date().toISOString(),\n turn: 1,\n role: \"user\",\n content: opts.task,\n });\n }\n\n try {\n for await (const ev of loop.step(opts.task)) {\n if (ev.role === \"assistant_delta\" && ev.content) process.stdout.write(ev.content);\n if (ev.role === \"tool\") process.stdout.write(`\\n[tool ${ev.toolName}] ${ev.content}\\n`);\n if (ev.role === \"error\") process.stderr.write(`\\n[error] ${ev.error}\\n`);\n if (ev.role === \"done\") process.stdout.write(\"\\n\");\n if (ev.role === \"assistant_final\" && ev.stats?.usage) {\n // `reasonix run` is often used in CI / scripting — we want\n // those turns to show up in `reasonix stats` too so the\n // dashboard reflects all DeepSeek spend, not just TUI sessions.\n appendUsage({ session: null, model: ev.stats.model, usage: ev.stats.usage });\n }\n // Persist every non-streaming event — deltas would flood the file and\n // aren't useful for replay (replay renders final content, not keystrokes).\n if (transcriptStream && ev.role !== \"assistant_delta\") {\n writeRecord(transcriptStream, recordFromLoopEvent(ev, { model: opts.model, prefixHash }));\n }\n }\n } finally {\n transcriptStream?.end();\n }\n\n const s = loop.stats.summary();\n process.stdout.write(\n `\\n— turns:${s.turns} cache:${(s.cacheHitRatio * 100).toFixed(1)}% ` +\n `cost:$${s.totalCostUsd.toFixed(6)} save-vs-claude:${s.savingsVsClaudePct.toFixed(1)}%\\n`,\n );\n if (opts.transcript) {\n process.stdout.write(`\\ntranscript: ${opts.transcript}\\n`);\n process.stdout.write(` → npx reasonix replay ${opts.transcript}\\n`);\n }\n\n for (const c of clients) await c.close();\n}\n"],"mappings":"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","names":["prefix"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/commands/setup.tsx","../../src/cli/ui/Wizard.tsx"],"sourcesContent":["/**\n * `reasonix setup` — re-mount the first-run wizard on demand so users\n * can reconfigure (add/remove MCP servers, switch preset) without\n * editing JSON by hand.\n *\n * Invoked both explicitly (`reasonix setup`) and implicitly (the no-args\n * entry point when `setupCompleted` is false).\n */\n\nimport { render } from \"ink\";\nimport React from \"react\";\nimport { loadApiKey, readConfig } from \"../../config.js\";\nimport { loadDotenv } from \"../../env.js\";\nimport { Wizard } from \"../ui/Wizard.js\";\n\nexport interface SetupOptions {\n /**\n * When true, bypass the API-key step even if no key is saved — useful\n * from test harnesses. Normal CLI use always pushes through the key\n * step when missing.\n */\n skipKeyStep?: boolean;\n}\n\nexport async function setupCommand(_opts: SetupOptions = {}): Promise<void> {\n loadDotenv();\n const existingKey = loadApiKey();\n const existing = readConfig();\n\n const { waitUntilExit, unmount } = render(\n <Wizard\n existingApiKey={existingKey}\n initial={{ preset: existing.preset, mcp: existing.mcp }}\n onComplete={() => {\n // Ink handles its own enter-to-exit inside the \"saved\" step; we\n // just wait for the app to exit naturally.\n }}\n onCancel={() => {\n unmount();\n }}\n />,\n { exitOnCtrlC: true, patchConsole: false },\n );\n await waitUntilExit();\n}\n","/**\n * First-run / re-configure wizard.\n *\n * Walks a new user through: language → API key → preset pick → MCP server\n * pick → per-server args → save. Saved output lives in\n * `~/.reasonix/config.json` so the next `reasonix chat` starts with\n * everything already wired.\n */\n\nimport { mkdirSync, statSync } from \"node:fs\";\nimport { Box, Text, useApp, useInput } from \"ink\";\nimport TextInput from \"ink-text-input\";\n// biome-ignore lint/style/useImportType: JSX (jsx: \"react\") needs React as a value at runtime\nimport React, { useEffect, useState } from \"react\";\nimport {\n type PresetName,\n type ReasonixConfig,\n defaultConfigPath,\n isPlausibleKey,\n readConfig,\n redactKey,\n writeConfig,\n} from \"../../config.js\";\nimport {\n detectSystemLanguage,\n getLanguage,\n getSupportedLanguages,\n notifyLanguageChange,\n onLanguageChange,\n setLanguage,\n t,\n} from \"../../i18n/index.js\";\nimport type { LanguageCode } from \"../../i18n/types.js\";\nimport { type CatalogEntry, MCP_CATALOG } from \"../../mcp/catalog.js\";\nimport { MultiSelect, type SelectItem, SingleSelect } from \"./Select.js\";\nimport { PRESET_DESCRIPTIONS } from \"./presets.js\";\n\nexport interface WizardProps {\n /** Called once the config has been saved. */\n onComplete: (cfg: ReasonixConfig) => void;\n /** Called if the user presses Esc to abort. */\n onCancel?: () => void;\n /** Skip the API-key step if a key already exists (env or config). */\n existingApiKey?: string;\n /** Pre-fill selections when re-running (reconfigure flow). */\n initial?: {\n preset?: PresetName;\n mcp?: string[];\n };\n}\n\ntype Step = \"language\" | \"apiKey\" | \"preset\" | \"mcp\" | \"mcpArgs\" | \"review\" | \"saved\";\n\ninterface WizardData {\n language: LanguageCode;\n apiKey: string;\n preset: PresetName;\n selectedCatalog: string[];\n catalogArgs: Record<string, string>;\n}\n\nconst CATALOG_BY_NAME = new Map(MCP_CATALOG.map((e) => [e.name, e]));\n\nconst LANGUAGE_LABELS: Record<LanguageCode, string> = {\n EN: \"English\",\n \"zh-CN\": \"简体中文\",\n};\n\nexport function Wizard({ onComplete, onCancel, existingApiKey, initial }: WizardProps) {\n const { exit } = useApp();\n const [, setLanguageVersion] = useState(0);\n useEffect(() => onLanguageChange(() => setLanguageVersion((v) => v + 1)), []);\n\n const [step, setStep] = useState<Step>(\"language\");\n const [data, setData] = useState<WizardData>({\n language: getLanguage(),\n apiKey: existingApiKey ?? \"\",\n preset: initial?.preset ?? \"auto\",\n selectedCatalog: deriveInitialCatalog(initial?.mcp ?? []),\n catalogArgs: {},\n });\n const [error, setError] = useState<string | null>(null);\n\n useInput((_input, key) => {\n if (key.escape && step !== \"saved\" && onCancel) onCancel();\n });\n\n if (step === \"language\") {\n return (\n <LanguageStep\n initialValue={data.language}\n onSubmit={(lang) => {\n setLanguage(lang);\n notifyLanguageChange();\n setData((d) => ({ ...d, language: lang }));\n setStep(existingApiKey ? \"preset\" : \"apiKey\");\n }}\n />\n );\n }\n\n if (step === \"apiKey\") {\n return (\n <ApiKeyStep\n onSubmit={(key) => {\n setData((d) => ({ ...d, apiKey: key }));\n setError(null);\n setStep(\"preset\");\n }}\n error={error}\n onError={setError}\n />\n );\n }\n\n if (step === \"preset\") {\n return (\n <StepFrame title={t(\"wizard.presetTitle\")} step={1} total={3}>\n <SingleSelect<PresetName>\n items={presetItems()}\n initialValue={data.preset}\n onSubmit={(preset) => {\n setData((d) => ({ ...d, preset }));\n setStep(\"mcp\");\n }}\n />\n <Box marginTop={1}>\n <Text dimColor>{t(\"wizard.selectFooter\")}</Text>\n </Box>\n </StepFrame>\n );\n }\n\n if (step === \"mcp\") {\n return (\n <StepFrame title={t(\"wizard.mcpTitle\")} step={2} total={3}>\n <MultiSelect\n items={mcpItems()}\n initialSelected={data.selectedCatalog}\n onSubmit={(selected) => {\n setData((d) => ({ ...d, selectedCatalog: selected }));\n const needsArgs = selected.some((name) => CATALOG_BY_NAME.get(name)?.userArgs);\n setStep(needsArgs ? \"mcpArgs\" : \"review\");\n }}\n footer={t(\"wizard.mcpFooterMulti\")}\n />\n </StepFrame>\n );\n }\n\n if (step === \"mcpArgs\") {\n const pending = data.selectedCatalog.filter((name) => {\n const entry = CATALOG_BY_NAME.get(name);\n return entry?.userArgs && !data.catalogArgs[name];\n });\n if (pending.length === 0) {\n setStep(\"review\");\n return null;\n }\n const currentName = pending[0]!;\n const entry = CATALOG_BY_NAME.get(currentName)!;\n return (\n <McpArgsStep\n entry={entry}\n error={error}\n onSubmit={(value) => {\n setData((d) => ({\n ...d,\n catalogArgs: { ...d.catalogArgs, [currentName]: value },\n }));\n setError(null);\n }}\n onError={setError}\n />\n );\n }\n\n if (step === \"review\") {\n const specs = data.selectedCatalog.map((name) => buildSpec(name, data.catalogArgs));\n return (\n <StepFrame title={t(\"wizard.reviewTitle\")} step={3} total={3}>\n <Box flexDirection=\"column\">\n <SummaryLine\n label={t(\"wizard.reviewLabelLanguage\")}\n value={LANGUAGE_LABELS[data.language]}\n />\n <SummaryLine label={t(\"wizard.reviewLabelApiKey\")} value={redactKey(data.apiKey)} />\n <SummaryLine label={t(\"wizard.reviewLabelPreset\")} value={data.preset} />\n <SummaryLine\n label={t(\"wizard.reviewLabelMcp\")}\n value={\n specs.length === 0\n ? t(\"wizard.reviewMcpNone\")\n : t(\"wizard.reviewMcpServers\", { count: specs.length })\n }\n />\n {specs.map((spec, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: review-only render, order fixed\n <Box key={i} paddingLeft={14}>\n <Text dimColor>· {spec}</Text>\n </Box>\n ))}\n <Box marginTop={1}>\n <Text>{t(\"wizard.reviewSavesTo\", { path: defaultConfigPath() })}</Text>\n </Box>\n {error ? (\n <Box marginTop={1}>\n <Text color=\"red\">{error}</Text>\n </Box>\n ) : null}\n <Box marginTop={1}>\n <Text dimColor>{t(\"wizard.reviewFooter\")}</Text>\n </Box>\n </Box>\n <ReviewConfirm\n onConfirm={() => {\n try {\n const specsNow = data.selectedCatalog.map((name) =>\n buildSpec(name, data.catalogArgs),\n );\n const prev = readConfig();\n const next: ReasonixConfig = {\n ...prev,\n apiKey: data.apiKey,\n preset: data.preset,\n mcp: specsNow,\n setupCompleted: true,\n };\n writeConfig(next);\n setStep(\"saved\");\n onComplete(next);\n } catch (e) {\n setError(t(\"wizard.reviewSaveError\", { message: (e as Error).message }));\n }\n }}\n />\n </StepFrame>\n );\n }\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" borderColor=\"green\" paddingX={1}>\n <Text bold color=\"green\">\n {t(\"wizard.savedTitle\")}\n </Text>\n <Box marginTop={1}>\n <Text>{t(\"ui.welcome\")}</Text>\n </Box>\n <Box marginTop={1}>\n <Text dimColor>{t(\"wizard.savedFooter\")}</Text>\n </Box>\n <ExitOnEnter onExit={exit} />\n </Box>\n );\n}\n\n// ---------- step components ----------\n\nfunction LanguageStep({\n initialValue,\n onSubmit,\n}: {\n initialValue: LanguageCode;\n onSubmit: (lang: LanguageCode) => void;\n}) {\n const items: SelectItem<LanguageCode>[] = getSupportedLanguages().map((code) => ({\n value: code,\n label: LANGUAGE_LABELS[code],\n hint: code === detectSystemLanguage() ? \"(detected)\" : undefined,\n }));\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" borderColor=\"cyan\" paddingX={1}>\n <Text bold color=\"cyan\">\n {t(\"wizard.languageTitle\")}\n </Text>\n <Box marginTop={1}>\n <Text dimColor>{t(\"wizard.languageSubtitle\")}</Text>\n </Box>\n <Box marginTop={1}>\n <SingleSelect<LanguageCode>\n items={items}\n initialValue={initialValue}\n onSubmit={onSubmit}\n footer={t(\"wizard.selectFooter\")}\n />\n </Box>\n </Box>\n );\n}\n\nfunction ApiKeyStep({\n onSubmit,\n error,\n onError,\n}: {\n onSubmit: (key: string) => void;\n error: string | null;\n onError: (e: string | null) => void;\n}) {\n const [value, setValue] = useState(\"\");\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" borderColor=\"cyan\" paddingX={1}>\n <Text bold color=\"cyan\">\n {t(\"wizard.welcomeTitle\")}\n </Text>\n <Box marginTop={1}>\n <Text>{t(\"wizard.apiKeyPrompt\")}</Text>\n </Box>\n <Text dimColor>{t(\"wizard.apiKeyGetOne\")}</Text>\n <Text dimColor>{t(\"wizard.apiKeySavedLocally\", { path: defaultConfigPath() })}</Text>\n <Box marginTop={1}>\n <Text bold color=\"cyan\">\n {t(\"wizard.apiKeyInputLabel\")}\n </Text>\n <TextInput\n value={value}\n onChange={setValue}\n onSubmit={(raw) => {\n const trimmed = raw.trim();\n if (!isPlausibleKey(trimmed)) {\n onError(t(\"wizard.apiKeyInvalid\"));\n setValue(\"\");\n return;\n }\n onSubmit(trimmed);\n }}\n mask=\"•\"\n placeholder=\"sk-...\"\n />\n </Box>\n {error ? (\n <Box marginTop={1}>\n <Text color=\"red\">{error}</Text>\n </Box>\n ) : value ? (\n <Box marginTop={1}>\n <Text dimColor>{t(\"wizard.apiKeyPreview\", { redacted: redactKey(value) })}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nfunction McpArgsStep({\n entry,\n error,\n onSubmit,\n onError,\n}: {\n entry: CatalogEntry;\n error: string | null;\n onSubmit: (value: string) => void;\n onError: (e: string | null) => void;\n}) {\n const [value, setValue] = useState(\"\");\n const [pendingCreate, setPendingCreate] = useState<string | null>(null);\n\n useInput((input, key) => {\n if (!pendingCreate) return;\n const ch = input.toLowerCase();\n if (ch === \"y\" || key.return) {\n try {\n mkdirSync(pendingCreate, { recursive: true });\n const created = pendingCreate;\n setPendingCreate(null);\n setValue(\"\");\n onError(null);\n onSubmit(created);\n } catch (e) {\n onError(\n t(\"wizard.mcpArgsDirCreateFailed\", {\n path: pendingCreate,\n message: (e as Error).message,\n }),\n );\n setPendingCreate(null);\n }\n } else if (ch === \"n\" || key.escape) {\n setPendingCreate(null);\n onError(null);\n }\n });\n\n if (pendingCreate) {\n return (\n <StepFrame title={t(\"wizard.mcpArgsTitle\", { name: entry.name })} step={2} total={3}>\n <Box flexDirection=\"column\">\n <Text>{t(\"wizard.mcpArgsDirMissing\", { path: pendingCreate })}</Text>\n <Box marginTop={1}>\n <Text dimColor>{t(\"wizard.mcpArgsDirCreateHint\")}</Text>\n </Box>\n {error ? (\n <Box marginTop={1}>\n <Text color=\"red\">{error}</Text>\n </Box>\n ) : null}\n </Box>\n </StepFrame>\n );\n }\n\n return (\n <StepFrame title={t(\"wizard.mcpArgsTitle\", { name: entry.name })} step={2} total={3}>\n <Box flexDirection=\"column\">\n <Text>{entry.summary}</Text>\n {entry.note ? (\n <Box marginTop={1}>\n <Text dimColor>{entry.note}</Text>\n </Box>\n ) : null}\n <Box marginTop={1}>\n <Text>{t(\"wizard.mcpArgsRequiredParam\")}</Text>\n <Text bold>{entry.userArgs}</Text>\n </Box>\n <Box marginTop={1}>\n <Text bold color=\"cyan\">\n {entry.userArgs}\n {\" › \"}\n </Text>\n <TextInput\n value={value}\n onChange={setValue}\n onSubmit={(raw) => {\n const trimmed = raw.trim();\n if (!trimmed) {\n onError(t(\"wizard.mcpArgsEmpty\", { name: entry.name }));\n return;\n }\n if (entry.name === \"filesystem\") {\n const check = checkFilesystemPath(trimmed);\n if (check.kind === \"missing\") {\n setPendingCreate(trimmed);\n return;\n }\n if (check.kind === \"not-a-dir\") {\n onError(t(\"wizard.mcpArgsNotADir\", { path: trimmed }));\n return;\n }\n }\n onSubmit(trimmed);\n setValue(\"\");\n }}\n placeholder={placeholderFor(entry)}\n />\n </Box>\n {error ? (\n <Box marginTop={1}>\n <Text color=\"red\">{error}</Text>\n </Box>\n ) : null}\n </Box>\n </StepFrame>\n );\n}\n\nfunction checkFilesystemPath(p: string): { kind: \"ok\" | \"missing\" | \"not-a-dir\" } {\n try {\n return { kind: statSync(p).isDirectory() ? \"ok\" : \"not-a-dir\" };\n } catch {\n return { kind: \"missing\" };\n }\n}\n\nfunction ReviewConfirm({ onConfirm }: { onConfirm: () => void }) {\n useInput((_i, key) => {\n if (key.return) onConfirm();\n });\n return null;\n}\n\nfunction ExitOnEnter({ onExit }: { onExit: () => void }) {\n useInput((_i, key) => {\n if (key.return) onExit();\n });\n return null;\n}\n\nfunction StepFrame({\n title,\n step,\n total,\n children,\n}: {\n title: string;\n step: number;\n total: number;\n children: React.ReactNode;\n}) {\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" borderColor=\"cyan\" paddingX={1}>\n <Box>\n <Text dimColor>{t(\"wizard.stepCounter\", { step, total })}</Text>\n <Text bold color=\"cyan\">\n {title}\n </Text>\n </Box>\n <Box marginTop={1} flexDirection=\"column\">\n {children}\n </Box>\n </Box>\n );\n}\n\nfunction SummaryLine({ label, value }: { label: string; value: string }) {\n return (\n <Box>\n <Text>{label.padEnd(12)}</Text>\n <Text bold>{value}</Text>\n </Box>\n );\n}\n\nfunction presetItems(): SelectItem<PresetName>[] {\n return ([\"auto\", \"flash\", \"pro\"] as const).map((name) => ({\n value: name as PresetName,\n label: `${name} — ${PRESET_DESCRIPTIONS[name].headline}`,\n hint: PRESET_DESCRIPTIONS[name].cost,\n }));\n}\n\nfunction mcpItems(): SelectItem<string>[] {\n return MCP_CATALOG.map((entry) => {\n const hintParts: string[] = [entry.summary];\n if (entry.userArgs) hintParts.push(t(\"wizard.mcpUserArgsHint\", { arg: entry.userArgs }));\n if (entry.note) hintParts.push(entry.note);\n return {\n value: entry.name,\n label: entry.name,\n hint: hintParts.join(\" · \"),\n };\n });\n}\n\nfunction placeholderFor(entry: CatalogEntry): string {\n if (entry.name === \"filesystem\") return \"e.g. /tmp/reasonix-sandbox\";\n if (entry.name === \"sqlite\") return \"e.g. ./notes.sqlite\";\n return entry.userArgs ?? \"\";\n}\n\nfunction deriveInitialCatalog(existingSpecs: string[]): string[] {\n const packageToName = new Map(MCP_CATALOG.map((e) => [e.package, e.name]));\n const out: string[] = [];\n for (const spec of existingSpecs) {\n for (const [pkg, name] of packageToName) {\n if (spec.includes(pkg)) {\n out.push(name);\n break;\n }\n }\n }\n return out;\n}\n\n/**\n * Build the `--mcp` spec string for a catalog entry. Same format\n * `mcpCommandFor` produces for `reasonix mcp list`, minus the leading\n * `--mcp \"...\"` wrapper — we store the inner spec directly.\n */\nexport function buildSpec(name: string, argsByName: Record<string, string>): string {\n const entry = CATALOG_BY_NAME.get(name);\n if (!entry) return name;\n const userArg = entry.userArgs ? argsByName[name] : undefined;\n const tail = userArg ? ` ${quoteIfNeeded(userArg)}` : \"\";\n return `${entry.name}=npx -y ${entry.package}${tail}`;\n}\n\nfunction quoteIfNeeded(s: string): string {\n // Escape backslashes BEFORE quotes — otherwise a trailing `\\` in the\n // input would consume the closing quote when a downstream parser\n // un-escapes the output (CodeQL js/incomplete-sanitization).\n return /\\s|\"/.test(s) ? `\"${s.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"` : s;\n}\n"],"mappings":"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","names":["React","entry","React"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/commands/update.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { VERSION, compareVersions, getLatestVersion, isNpxInstall } from \"../../version.js\";\n\nexport type UpdateAction = \"up-to-date\" | \"newer-local\" | \"npx-hint\" | \"run-npm-install\";\n\nexport interface UpdatePlan {\n action: UpdateAction;\n /** Human-readable summary; the CLI prints this verbatim. */\n message: string;\n command?: string[];\n}\n\nexport interface PlanUpdateInput {\n current: string;\n latest: string;\n /** Overrides `isNpxInstall()` (tests). */\n npx?: boolean;\n}\n\n/** Pure decision — split out so tests don't need to spawn child processes or hit the network. */\nexport function planUpdate(input: PlanUpdateInput): UpdatePlan {\n const diff = compareVersions(input.current, input.latest);\n if (diff > 0) {\n return {\n action: \"newer-local\",\n message: `current (${input.current}) is newer than the published ${input.latest} — nothing to do.`,\n };\n }\n if (diff === 0) {\n return { action: \"up-to-date\", message: `reasonix ${input.current} is up to date.` };\n }\n if (input.npx) {\n return {\n action: \"npx-hint\",\n message: [\n `reasonix ${input.latest} is available.`,\n \"you're running via npx — the next `npx reasonix ...` launch will auto-fetch\",\n \"the latest (npx caches packages for a short window). to force a refresh\",\n \"sooner, clear the cache: `npm cache clean --force`.\",\n ].join(\"\\n\"),\n };\n }\n return {\n action: \"run-npm-install\",\n message: `upgrading reasonix ${input.current} → ${input.latest}`,\n command: [\"npm\", \"install\", \"-g\", \"reasonix@latest\"],\n };\n}\n\nexport interface UpdateCommandOptions {\n /** Skip spawning npm; print the decision only. */\n dryRun?: boolean;\n /** Test seam: override the registry lookup. Returns null = offline. */\n fetchLatest?: () => Promise<string | null>;\n /** Test seam: override the npx detector. */\n isNpx?: () => boolean;\n /** Test seam: override the spawner. Must return exit code. */\n spawnInstall?: (argv: string[]) => Promise<number>;\n /** Test seam: stdout writer. */\n write?: (msg: string) => void;\n /** Test seam: process exit — tests don't want to tear down vitest. */\n exit?: (code: number) => void;\n}\n\nfunction defaultSpawn(argv: string[]): Promise<number> {\n return new Promise((resolve, reject) => {\n // `shell: true` on Windows is what lets `npm` resolve to `npm.cmd`\n // without routing through our `prepareSpawn` helper. The args here\n // are literal strings under our control — no user input flows in,\n // so injection is not a concern. Avoiding `prepareSpawn` keeps\n // this command free of a dep on the shell tools module.\n const child = spawn(argv[0]!, argv.slice(1), {\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n });\n child.once(\"error\", reject);\n child.once(\"exit\", (code) => resolve(code ?? 1));\n });\n}\n\nexport async function updateCommand(opts: UpdateCommandOptions = {}): Promise<void> {\n const write = opts.write ?? ((m: string) => process.stdout.write(m));\n const exit = opts.exit ?? ((c: number) => process.exit(c));\n const fetchLatest = opts.fetchLatest ?? (() => getLatestVersion({ force: true }));\n const isNpx = opts.isNpx ?? isNpxInstall;\n const doSpawn = opts.spawnInstall ?? defaultSpawn;\n\n write(`current: reasonix ${VERSION}\\n`);\n const latest = await fetchLatest();\n if (!latest) {\n write(\"could not reach registry.npmjs.org — check your network.\\n\");\n exit(1);\n return;\n }\n write(`latest: reasonix ${latest}\\n`);\n\n const plan = planUpdate({ current: VERSION, latest, npx: isNpx() });\n write(`\\n${plan.message}\\n`);\n\n if (plan.action !== \"run-npm-install\" || !plan.command) return;\n if (opts.dryRun) {\n write(`(dry run) would run: ${plan.command.join(\" \")}\\n`);\n return;\n }\n write(`\\nrunning: ${plan.command.join(\" \")}\\n`);\n const code = await doSpawn(plan.command);\n if (code !== 0) {\n write(`\\nnpm exited with code ${code}. upgrade did not complete.\\n`);\n exit(code);\n }\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,aAAa;AAoBf,SAAS,WAAW,OAAoC;AAC7D,QAAM,OAAO,gBAAgB,MAAM,SAAS,MAAM,MAAM;AACxD,MAAI,OAAO,GAAG;AACZ,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,SAAS,YAAY,MAAM,OAAO,iCAAiC,MAAM,MAAM;AAAA,IACjF;AAAA,EACF;AACA,MAAI,SAAS,GAAG;AACd,WAAO,EAAE,QAAQ,cAAc,SAAS,YAAY,MAAM,OAAO,kBAAkB;AAAA,EACrF;AACA,MAAI,MAAM,KAAK;AACb,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,YAAY,MAAM,MAAM;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,EACF;AACA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,SAAS,sBAAsB,MAAM,OAAO,WAAM,MAAM,MAAM;AAAA,IAC9D,SAAS,CAAC,OAAO,WAAW,MAAM,iBAAiB;AAAA,EACrD;AACF;AAiBA,SAAS,aAAa,MAAiC;AACrD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AAMtC,UAAM,QAAQ,MAAM,KAAK,CAAC,GAAI,KAAK,MAAM,CAAC,GAAG;AAAA,MAC3C,OAAO;AAAA,MACP,OAAO,QAAQ,aAAa;AAAA,IAC9B,CAAC;AACD,UAAM,KAAK,SAAS,MAAM;AAC1B,UAAM,KAAK,QAAQ,CAAC,SAAS,QAAQ,QAAQ,CAAC,CAAC;AAAA,EACjD,CAAC;AACH;AAEA,eAAsB,cAAc,OAA6B,CAAC,GAAkB;AAClF,QAAM,QAAQ,KAAK,UAAU,CAAC,MAAc,QAAQ,OAAO,MAAM,CAAC;AAClE,QAAM,OAAO,KAAK,SAAS,CAAC,MAAc,QAAQ,KAAK,CAAC;AACxD,QAAM,cAAc,KAAK,gBAAgB,MAAM,iBAAiB,EAAE,OAAO,KAAK,CAAC;AAC/E,QAAM,QAAQ,KAAK,SAAS;AAC5B,QAAM,UAAU,KAAK,gBAAgB;AAErC,QAAM,qBAAqB,OAAO;AAAA,CAAI;AACtC,QAAM,SAAS,MAAM,YAAY;AACjC,MAAI,CAAC,QAAQ;AACX,UAAM,iEAA4D;AAClE,SAAK,CAAC;AACN;AAAA,EACF;AACA,QAAM,qBAAqB,MAAM;AAAA,CAAI;AAErC,QAAM,OAAO,WAAW,EAAE,SAAS,SAAS,QAAQ,KAAK,MAAM,EAAE,CAAC;AAClE,QAAM;AAAA,EAAK,KAAK,OAAO;AAAA,CAAI;AAE3B,MAAI,KAAK,WAAW,qBAAqB,CAAC,KAAK,QAAS;AACxD,MAAI,KAAK,QAAQ;AACf,UAAM,wBAAwB,KAAK,QAAQ,KAAK,GAAG,CAAC;AAAA,CAAI;AACxD;AAAA,EACF;AACA,QAAM;AAAA,WAAc,KAAK,QAAQ,KAAK,GAAG,CAAC;AAAA,CAAI;AAC9C,QAAM,OAAO,MAAM,QAAQ,KAAK,OAAO;AACvC,MAAI,SAAS,GAAG;AACd,UAAM;AAAA,uBAA0B,IAAI;AAAA,CAA+B;AACnE,SAAK,IAAI;AAAA,EACX;AACF;","names":[]}