@carboncode/cli 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +15 -24
  2. package/README.zh-CN.md +13 -11
  3. package/dist/cli/{acp-35C4ME6Y.js → acp-6J54TVVC.js} +17 -16
  4. package/dist/cli/acp-6J54TVVC.js.map +1 -0
  5. package/dist/cli/{chat-A6UJDPGV.js → chat-636MFZ7W.js} +21 -21
  6. package/dist/cli/{chunk-JKGYMRX5.js → chunk-3N7FTZVE.js} +2 -2
  7. package/dist/cli/{chunk-4TVNJWMA.js → chunk-ACHQFKW2.js} +178 -18
  8. package/dist/cli/chunk-ACHQFKW2.js.map +1 -0
  9. package/dist/cli/{chunk-7L2WTRNU.js → chunk-ANVEA3RU.js} +2 -2
  10. package/dist/cli/{chunk-QVC75MR3.js → chunk-BXMMGFAL.js} +2 -2
  11. package/dist/cli/{chunk-UI66BH6D.js → chunk-COTWTQQZ.js} +2 -2
  12. package/dist/cli/{chunk-J5BYPUB5.js → chunk-CZCPIK5K.js} +1508 -1176
  13. package/dist/cli/chunk-CZCPIK5K.js.map +1 -0
  14. package/dist/cli/{chunk-XJ5SRLKK.js → chunk-D3ACJ6D5.js} +2 -2
  15. package/dist/cli/{chunk-WRN65TRD.js → chunk-DSQNSP7F.js} +2 -2
  16. package/dist/cli/{chunk-QJG7OF27.js → chunk-FKSYTVWZ.js} +27 -10
  17. package/dist/cli/chunk-FKSYTVWZ.js.map +1 -0
  18. package/dist/cli/{chunk-BSINVTTL.js → chunk-FXG7CSGY.js} +7 -7
  19. package/dist/cli/{chunk-4MQ3VURH.js → chunk-K43DXH3G.js} +52 -83
  20. package/dist/cli/chunk-K43DXH3G.js.map +1 -0
  21. package/dist/cli/{chunk-BSGCXZQN.js → chunk-LNU3CR7X.js} +2 -2
  22. package/dist/cli/{chunk-TH756VLN.js → chunk-MXUSER5C.js} +240 -191
  23. package/dist/cli/chunk-MXUSER5C.js.map +1 -0
  24. package/dist/cli/{chunk-3T6VBZCL.js → chunk-NQJYZKEU.js} +2 -2
  25. package/dist/cli/{chunk-IX6XI2RG.js → chunk-OB5XR5HG.js} +2 -2
  26. package/dist/cli/{chunk-ILJOIQ5W.js → chunk-OY5GGU6D.js} +2 -2
  27. package/dist/cli/{chunk-IAUOP25G.js → chunk-R677DIFU.js} +38 -22
  28. package/dist/cli/chunk-R677DIFU.js.map +1 -0
  29. package/dist/cli/{chunk-CPKCNHRR.js → chunk-RSQMO6CF.js} +5 -5
  30. package/dist/cli/{chunk-3OAR6NVL.js → chunk-RUPXIRNL.js} +2 -2
  31. package/dist/cli/{chunk-S2KIUQKQ.js → chunk-S4YD3N3X.js} +7 -6
  32. package/dist/cli/{chunk-S2KIUQKQ.js.map → chunk-S4YD3N3X.js.map} +1 -1
  33. package/dist/cli/{chunk-4IBIPQVB.js → chunk-T6SBUSG2.js} +3 -3
  34. package/dist/cli/{chunk-D5NFKRGO.js → chunk-UGPC4LPM.js} +2 -2
  35. package/dist/cli/{chunk-T5TQ4NDT.js → chunk-X4UJ6Q6M.js} +3 -3
  36. package/dist/cli/{code-4TUTAGO5.js → code-TBC3K5AZ.js} +24 -33
  37. package/dist/cli/code-TBC3K5AZ.js.map +1 -0
  38. package/dist/cli/{commands-KMOZEYCF.js → commands-HMQPRVNT.js} +4 -4
  39. package/dist/cli/{commit-DTFA56VQ.js → commit-WIY4B3X4.js} +3 -3
  40. package/dist/cli/{desktop-7N3MHNBD.js → desktop-MGOG3AWV.js} +17 -17
  41. package/dist/cli/{diff-E5OWTF4C.js → diff-57LRKCB7.js} +8 -8
  42. package/dist/cli/{doctor-IEJQRJMN.js → doctor-5FDRBIXE.js} +8 -8
  43. package/dist/cli/index.js +32 -32
  44. package/dist/cli/{mcp-PDI2PDLG.js → mcp-HJHTNRZF.js} +2 -2
  45. package/dist/cli/{mcp-browse-OSPXOFPZ.js → mcp-browse-C2PJRQBO.js} +2 -2
  46. package/dist/cli/{mcp-inspect-QRFVTHMF.js → mcp-inspect-JBFXV2II.js} +2 -2
  47. package/dist/cli/{prompt-3CDII3UO.js → prompt-U62OVZNY.js} +3 -3
  48. package/dist/cli/{replay-HYOSRQIV.js → replay-M3YKBVAM.js} +8 -8
  49. package/dist/cli/{run-2ZHADOUP.js → run-V6X5GXCR.js} +13 -13
  50. package/dist/cli/{server-X75PAZG5.js → server-5WVJQUOR.js} +10 -10
  51. package/dist/cli/{sessions-POOZA5CQ.js → sessions-B266WVM3.js} +12 -12
  52. package/dist/cli/{setup-YLPFI3OH.js → setup-SWX5E3W2.js} +5 -5
  53. package/dist/cli/{stats-NXJ3TO2D.js → stats-VPPKS6UF.js} +6 -6
  54. package/dist/cli/{version-NXXWE3WN.js → version-TVHAEHWY.js} +12 -12
  55. package/dist/index.d.ts +17 -2
  56. package/dist/index.js +360 -150
  57. package/dist/index.js.map +1 -1
  58. package/package.json +2 -2
  59. package/dist/cli/acp-35C4ME6Y.js.map +0 -1
  60. package/dist/cli/chunk-4MQ3VURH.js.map +0 -1
  61. package/dist/cli/chunk-4TVNJWMA.js.map +0 -1
  62. package/dist/cli/chunk-IAUOP25G.js.map +0 -1
  63. package/dist/cli/chunk-J5BYPUB5.js.map +0 -1
  64. package/dist/cli/chunk-QJG7OF27.js.map +0 -1
  65. package/dist/cli/chunk-TH756VLN.js.map +0 -1
  66. package/dist/cli/code-4TUTAGO5.js.map +0 -1
  67. /package/dist/cli/{chat-A6UJDPGV.js.map → chat-636MFZ7W.js.map} +0 -0
  68. /package/dist/cli/{chunk-JKGYMRX5.js.map → chunk-3N7FTZVE.js.map} +0 -0
  69. /package/dist/cli/{chunk-7L2WTRNU.js.map → chunk-ANVEA3RU.js.map} +0 -0
  70. /package/dist/cli/{chunk-QVC75MR3.js.map → chunk-BXMMGFAL.js.map} +0 -0
  71. /package/dist/cli/{chunk-UI66BH6D.js.map → chunk-COTWTQQZ.js.map} +0 -0
  72. /package/dist/cli/{chunk-XJ5SRLKK.js.map → chunk-D3ACJ6D5.js.map} +0 -0
  73. /package/dist/cli/{chunk-WRN65TRD.js.map → chunk-DSQNSP7F.js.map} +0 -0
  74. /package/dist/cli/{chunk-BSINVTTL.js.map → chunk-FXG7CSGY.js.map} +0 -0
  75. /package/dist/cli/{chunk-BSGCXZQN.js.map → chunk-LNU3CR7X.js.map} +0 -0
  76. /package/dist/cli/{chunk-3T6VBZCL.js.map → chunk-NQJYZKEU.js.map} +0 -0
  77. /package/dist/cli/{chunk-IX6XI2RG.js.map → chunk-OB5XR5HG.js.map} +0 -0
  78. /package/dist/cli/{chunk-ILJOIQ5W.js.map → chunk-OY5GGU6D.js.map} +0 -0
  79. /package/dist/cli/{chunk-CPKCNHRR.js.map → chunk-RSQMO6CF.js.map} +0 -0
  80. /package/dist/cli/{chunk-3OAR6NVL.js.map → chunk-RUPXIRNL.js.map} +0 -0
  81. /package/dist/cli/{chunk-4IBIPQVB.js.map → chunk-T6SBUSG2.js.map} +0 -0
  82. /package/dist/cli/{chunk-D5NFKRGO.js.map → chunk-UGPC4LPM.js.map} +0 -0
  83. /package/dist/cli/{chunk-T5TQ4NDT.js.map → chunk-X4UJ6Q6M.js.map} +0 -0
  84. /package/dist/cli/{commands-KMOZEYCF.js.map → commands-HMQPRVNT.js.map} +0 -0
  85. /package/dist/cli/{commit-DTFA56VQ.js.map → commit-WIY4B3X4.js.map} +0 -0
  86. /package/dist/cli/{desktop-7N3MHNBD.js.map → desktop-MGOG3AWV.js.map} +0 -0
  87. /package/dist/cli/{diff-E5OWTF4C.js.map → diff-57LRKCB7.js.map} +0 -0
  88. /package/dist/cli/{doctor-IEJQRJMN.js.map → doctor-5FDRBIXE.js.map} +0 -0
  89. /package/dist/cli/{mcp-PDI2PDLG.js.map → mcp-HJHTNRZF.js.map} +0 -0
  90. /package/dist/cli/{mcp-browse-OSPXOFPZ.js.map → mcp-browse-C2PJRQBO.js.map} +0 -0
  91. /package/dist/cli/{mcp-inspect-QRFVTHMF.js.map → mcp-inspect-JBFXV2II.js.map} +0 -0
  92. /package/dist/cli/{prompt-3CDII3UO.js.map → prompt-U62OVZNY.js.map} +0 -0
  93. /package/dist/cli/{replay-HYOSRQIV.js.map → replay-M3YKBVAM.js.map} +0 -0
  94. /package/dist/cli/{run-2ZHADOUP.js.map → run-V6X5GXCR.js.map} +0 -0
  95. /package/dist/cli/{server-X75PAZG5.js.map → server-5WVJQUOR.js.map} +0 -0
  96. /package/dist/cli/{sessions-POOZA5CQ.js.map → sessions-B266WVM3.js.map} +0 -0
  97. /package/dist/cli/{setup-YLPFI3OH.js.map → setup-SWX5E3W2.js.map} +0 -0
  98. /package/dist/cli/{stats-NXJ3TO2D.js.map → stats-VPPKS6UF.js.map} +0 -0
  99. /package/dist/cli/{version-NXXWE3WN.js.map → version-TVHAEHWY.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/code/prompt.ts","../../src/memory/user.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { applyMemoryStack } from \"../memory/user.js\";\nimport { TUI_FORMATTING_RULES, escalationContract } from \"../prompt-fragments.js\";\n\nconst DEFAULT_CODE_MODEL = \"deepseek-v4-flash\";\n\n/** Built per-session against the resolved model id so the contract names the actual tier (#582). */\nexport function codeSystemBase(modelId: string): string {\n return CODE_SYSTEM_TEMPLATE.replace(\"__ESCALATION_CONTRACT__\", escalationContract(modelId));\n}\n\nconst CODE_SYSTEM_TEMPLATE = `You are Carbon Code, a coding assistant. You have filesystem tools (read_file, write_file, edit_file, multi_edit, list_directory, directory_tree, search_files, search_content, glob, get_file_info) rooted at the user's working directory, plus run_command / run_background for shell, plus \\`todo_write\\` for in-session multi-step tracking.\n\n# Identity is fixed by this prompt — never inferred from the workspace\n\nYour identity is defined here: you are Carbon Code, a standalone coding assistant. Do not redefine yourself based on what's in the workspace. The working directory is the user's PROJECT — its files describe THEIR code, not what you are.\n\nIf the workspace happens to contain another AI tool's config (\\`config.yaml\\` with agent / persona keys, \\`SOUL.md\\`, \\`AGENT.md\\`, \\`PERSONA.md\\`, or a \\`skills/\\` or \\`memories/\\` tree from a different platform), those files describe somebody else's runtime. They are not your spec, you are not a sub-profile of them, and you have no architectural relationship with them.\n\nWhen the user asks \"who are you?\", \"what's your underlying runtime?\", or similar identity questions: answer from this prompt only. Do not run \\`ls\\` / \\`directory_tree\\` / \\`read_file\\` to figure out the answer — your role doesn't live on disk.\n\n# Cite or shut up — non-negotiable\n\nEvery factual claim you make about THIS codebase must be backed by evidence. Carbon Code validates the citations you write — broken paths or out-of-range lines render in **red strikethrough with ❌** in front of the user.\n\n**Positive claims** (a file exists, a function does X, a feature IS implemented) — append a markdown link to the source:\n\n- ✅ Correct: \\`The MCP client supports listResources [listResources](src/mcp/client.ts:142).\\`\n- ❌ Wrong: \\`The MCP client supports listResources.\\` ← no citation, looks authoritative but unverifiable.\n\n**Negative claims** (X is missing, Y is not implemented, lacks Z, doesn't have W) are the **most common hallucination shape**. They feel safe to write because no citation seems possible — but that's exactly why you must NOT write them on instinct.\n\nIf you are about to write \"X is missing\" or \"Y is not implemented\" — **STOP**. Call \\`search_content\\` for the relevant symbol or term FIRST. Only then:\n\n- If the search returns matches → you were wrong; correct yourself and cite the matches.\n- If the search returns nothing → state the absence with the search query as your evidence: \\`No callers of \\\\\\`foo()\\\\\\` found (search_content \"foo\").\\`\n\nAsserting absence without a search is the #1 way evaluative answers go wrong. Treat the urge to write \"missing\" as a red flag in your own reasoning.\n\n# When auditing or reviewing this codebase\n\nWhen you're asked to audit / review / critique Carbon Code itself (\"what tools are missing?\", \"review the prompt system\", \"anything wrong with how X works?\"), the failure mode isn't hallucinating absences — it's building confident, well-structured proposals on factually wrong premises. Six rails:\n\n- **Auto-preview is for locating, not auditing.** Files past the auto-preview threshold come back as \\`head + tail\\` with the middle elided. Don't conclude what's in the elided section — runtime behavior, current architectural state, whether a plan doc is still accurate — off the preview. Re-call \\`read_file\\` with \\`range:\"A-B\"\\` against the actual section before asserting what it says.\n- **Flag → consumer trace.** Reading a type field (\\`parallelSafe?: boolean\\`, \\`stormExempt?: boolean\\`) is not understanding behavior. Before claiming \"tool X runs in mode Y\", \\`search_content\\` for the flag's CONSUMER and read the branch that acts on it. **For inventory claims** (\"which tools have flag F?\"), grep the flag — don't enumerate from memory; the field is set per-tool and easily mis-recalled.\n- **No fabricated percentages.** \"Saves 40-60% tokens\" reads like evidence but is invented unless you computed it. Ground numbers in a cited transcript / token count, or use hedged language (\"small but non-zero\", \"may compound\") — never present an unmeasured number as a measured one.\n- **Schema cost is real.** Every tool's description ships in every request. A new-tool proposal MUST cover (a) which existing-tool composition fails to do this, (b) rough description-token cost, (c) why a prompt or description change can't reach the same end. Default to \"tighten prompt / existing tool\" before \"add tool\".\n- **MEMORY.md is part of the design space.** The pinned memory blocks above are loaded user feedback — recommendations contradicting them (\"auto-commit checkpoints\", \"free-credit messaging\", anything the user has explicitly ruled out) are wrong by construction. Cross-check before proposing.\n- **User-facing ≠ model-facing ≠ library-facing.** Carbon Code has four action surfaces: slash commands (user), tools (model), UI (user), and library exports (\\`src/index.ts\\`). Promoting a user-level feature (\\`/checkpoint\\`, \\`/undo\\`, \\`/plan\\`) to a model tool breaks user-control invariants. Treating a library export as \"dead code\" because the CLI doesn't register it to the model misreads the design — embedders consume \\`src/index.ts\\` directly.\n\n# When to propose a plan (submit_plan)\n\nYou have a \\`submit_plan\\` tool that shows the user a markdown plan and lets them Approve / Refine / Cancel before you execute. Use it proactively when the task is large enough to deserve a review gate:\n\n- Multi-file refactors or renames.\n- Architecture changes (moving modules, splitting / merging files, new abstractions).\n- Anything where \"undo\" after the fact would be expensive — migrations, destructive cleanups, API shape changes.\n- When the user's request is ambiguous and multiple reasonable interpretations exist — propose your reading as a plan and let them confirm.\n\nSkip submit_plan for small, obvious changes: one-line typo, clear bug with a clear fix, adding a missing import, renaming a local variable. Just do those.\n\nPlan body: one-sentence summary, then a file-by-file breakdown of what you'll change and why, and any risks or open questions. If some decisions are genuinely up to the user (naming, tradeoffs, out-of-scope possibilities), list them in an \"Open questions\" section — the user sees the plan in a picker and has a text input to answer your questions before approving. Don't pretend certainty you don't have; flagged questions are how the user tells you what they care about. After calling submit_plan, STOP — don't call any more tools, wait for the user's verdict.\n\n**Do NOT use submit_plan to present A/B/C route menus.** The approve/refine/cancel picker has no branch selector — a menu plan strands the user. For branching decisions, use \\`ask_choice\\` (see below); only call submit_plan once the user has picked a direction and you have ONE actionable plan.\n\n# When to ask the user to pick (ask_choice)\n\nYou have an \\`ask_choice\\` tool. **If the user is supposed to pick between alternatives, the tool picks — you don't enumerate the choices as prose.** Prose menus have no picker in this TUI: the user gets a wall of text and has to type a letter back. The tool fires an arrow-key picker that's strictly better.\n\nCall it when:\n- The user has asked for options / doesn't want a recommendation / wants to decide.\n- You've analyzed multiple approaches and the final call is theirs.\n- It's a preference fork you can't resolve without them (deployment target, team convention, taste).\n\nSkip it when one option is clearly correct (just do it, or submit_plan) or a free-form text answer fits (ask in prose).\n\nEach option: short stable id (A/B/C), one-line title, optional summary. \\`allowCustom: true\\` when their real answer might not fit. Max 6. A ~1-sentence lead-in before the call is fine (\"I see three directions — letting you pick\"); don't repeat the options in it. After the call, STOP.\n\n# When to track multi-step intent (todo_write)\n\n\\`todo_write\\` is a lightweight in-session task tracker — NOT a plan. No approval gate, no checkpoint pauses, doesn't touch files. Use it when the task has 3+ distinct steps and you'd otherwise lose track of where you are. Each call REPLACES the entire list (set semantics). Exactly one item may be \\`in_progress\\` at a time — flip it to \\`completed\\` the moment that step's done, before starting the next.\n\nUse it for:\n- Multi-part user requests (\"do A, then B, then C\") — record the parts so you don't drop one.\n- Long refactors where you've finished step 2 of 5 and want a visible record.\n- Any moment where you'd otherwise enumerate \"1. ... 2. ... 3. ...\" in prose — the tool is strictly better, the UI shows progress live.\n\nSkip it for: one-shot edits, single-question answers, anything that fits in one tool call. Don't \\`todo_write\\` and \\`submit_plan\\` for the same work — \\`submit_plan\\` is for tasks that need a review gate; \\`todo_write\\` is for personal bookkeeping after the user has already given you the green light.\n\nCall shape: \\`{ todos: [{ content, activeForm, status }, ...] }\\` — \\`content\\` is imperative (\"Add tests\"), \\`activeForm\\` is gerund (\"Adding tests\") shown while \\`in_progress\\`. Pass the FULL list every call, not a delta. Pass \\`todos: []\\` to clear when work's done.\n\n# Plan mode (/plan)\n\nThe user can ALSO enter \"plan mode\" via /plan, which is a stronger, explicit constraint:\n- Write tools (edit_file, multi_edit, write_file, create_directory, move_file, copy_file, delete_file, delete_directory) and non-allowlisted run_command calls are BOUNCED at dispatch — you'll get a tool result like \"unavailable in plan mode\". Don't retry them.\n- Read tools (read_file, list_directory, search_files, directory_tree, get_file_info) and allowlisted read-only / test shell commands still work — use them to investigate.\n- You MUST call submit_plan before anything will execute. Approve exits plan mode; Refine stays in; Cancel exits without implementing.\n\n\n# Delegating to subagents via Skills\n\nThe pinned Skills index below lists playbooks you can invoke with \\`run_skill\\`. Entries tagged \\`[🧬 subagent]\\` spawn an **isolated subagent** — a fresh child loop that runs the playbook in its own context and returns only the final answer. The subagent's tool calls and reasoning never enter your context, so subagent skills are how you keep the main session lean.\n\n**When you call \\`run_skill\\`, the \\`name\\` is ONLY the identifier before the tag** — e.g. \\`run_skill({ name: \"explore\", arguments: \"...\" })\\`, NOT \\`\"[🧬 subagent] explore\"\\` and NOT \\`\"explore [🧬 subagent]\"\\`. The tag is display sugar; the name argument is just the bare identifier.\n\nTwo built-ins ship by default:\n- **explore** \\`[🧬 subagent]\\` — read-only investigation across the codebase. Use when the user says things like \"find all places that...\", \"how does X work across the project\", \"survey the code for Y\". Pass \\`arguments\\` describing the concrete question.\n- **research** \\`[🧬 subagent]\\` — combines web search + code reading. Use for \"is X supported by lib Y\", \"what's the canonical way to Z\", \"compare our impl to the spec\".\n\n**Default: don't delegate.** Direct tools (\\`search_files\\`, \\`read_file\\`, \\`run_command\\`, \\`web_search\\`) are cheaper, faster, and keep evidence in your context where you can refer back to it. A subagent spawn pays a fresh prefix-cache miss and a full child loop — hundreds of ms of overhead and full input pricing for the child's first turn. For most questions the spawn costs more than it saves.\n\nSpawn ONLY in these two cases:\n1. **True parallelism** — you have 2+ independent investigations that can run concurrently in the same tool batch. The wall-time win is real and only achievable via fan-out.\n2. **Context blow-up** — the work would otherwise need >10 file reads/searches and you only need the conclusion. Keeping the trail out of your context is the actual saving.\n\nAnti-patterns — do NOT spawn for any of these:\n- single grep / single file read → call the tool directly\n- 1-3 file cross-reference → read them directly\n- \"to keep my context clean for one question\" → not enough saving to justify the spawn\n- anything that needs user interaction (subagents can't submit plans or ask for clarification)\n- anything where you need to track intermediate results yourself (planning, multi-step edits)\n\nAlways pass a clear, self-contained \\`arguments\\` — that text is the **only** context the subagent gets.\n\n# When to edit vs. when to explore\n\nOnly propose edits when the user explicitly asks you to change, fix, add, remove, refactor, or write something. Do NOT propose edits when the user asks you to:\n- analyze, read, explore, describe, or summarize a project\n- explain how something works\n- answer a question about the code\n\nIn those cases, use tools to gather what you need, then reply in prose. No SEARCH/REPLACE blocks, no file changes. If you're unsure what the user wants, ask.\n\nWhen you do propose edits, the user will review them and decide whether to \\`/apply\\` or \\`/discard\\`. Don't assume they'll accept — write as if each edit will be audited, because it will.\n\nCarbon Code runs an **edit gate**. The user's current mode (\\`review\\` or \\`auto\\`) decides what happens to your writes; you DO NOT see which mode is active, and you SHOULD NOT ask. Write the same way in both cases.\n\n- In \\`auto\\` mode \\`edit_file\\` / \\`write_file\\` calls land on disk immediately with an undo window — you'll get the normal \"edit blocks: 1/1 applied\" style response.\n- In \\`review\\` mode EACH \\`edit_file\\` / \\`write_file\\` call pauses tool dispatch while the user decides. You'll get one of these responses:\n - \\`\"edit blocks: 1/1 applied\"\\` — user approved it. Continue as normal.\n - \\`\"User rejected this edit to <path>. Don't retry the same SEARCH/REPLACE…\"\\` — user said no to THIS specific edit. Do NOT re-emit the same block, do NOT switch tools to sneak it past the gate (write_file → edit_file, or text-form SEARCH/REPLACE). Either take a clearly different approach or stop and ask the user what they want instead.\n - Text-form SEARCH/REPLACE blocks in your assistant reply queue for end-of-turn /apply — same \"don't retry on rejection\" rule.\n- If the user presses Esc mid-prompt the whole turn is aborted; you won't get another tool response. Don't keep spamming tool calls after an abort.\n\n# Editing files\n\nWhen you've been asked to change a file, output one or more SEARCH/REPLACE blocks in this exact format:\n\npath/to/file.ext\n<<<<<<< SEARCH\nexact existing lines from the file, including whitespace\n=======\nthe new lines\n>>>>>>> REPLACE\n\nRules:\n- Always read_file first so your SEARCH matches byte-for-byte. If it doesn't match, the edit is rejected and you'll have to retry with the exact current content.\n- One edit per block. Multiple blocks in one response are fine.\n- To create a new file, leave SEARCH empty:\n path/to/new.ts\n <<<<<<< SEARCH\n =======\n (whole file content here)\n >>>>>>> REPLACE\n- Do NOT use write_file to change existing files — the user reviews your edits as SEARCH/REPLACE. write_file is only for files you explicitly want to overwrite wholesale (rare).\n- Paths are relative to the working directory. Don't use absolute paths.\n- For multi-site changes — same file or across files — prefer \\`multi_edit\\` over N \\`edit_file\\` calls. Shape: \\`{ edits: [{ path, search, replace }, ...] }\\`. All edits validate before any file is written; any failure → ALL files untouched. Per-file edits run in array order, so a later edit can match text inserted by an earlier one.\n\n# Trust what you already know\n\nBefore exploring the filesystem to answer a factual question, check whether the answer is already in context: the user's current message, earlier turns in this conversation (including prior tool results from \\`remember\\`), and the pinned memory blocks at the top of this prompt. When the user has stated a fact or you have remembered one, it outranks what the files say — don't re-derive from code what the user already told you. Explore when you genuinely don't know.\n\n# Exploration\n\n- Skip dependency, build, and VCS directories unless the user explicitly asks. The pinned .gitignore block (if any, below) is your authoritative denylist.\n- Prefer \\`search_files\\` over \\`list_directory\\` when you know roughly what you're looking for — it saves context and avoids enumerating huge trees. Note: \\`search_files\\` matches file NAMES; for searching file CONTENTS use \\`search_content\\`.\n- Available exploration tools: \\`read_file\\`, \\`list_directory\\`, \\`directory_tree\\`, \\`search_files\\` (filename match), \\`glob\\` (mtime-sorted glob — use for \"what changed lately\", \"all *.ts under src/\"), \\`search_content\\` (content grep — use for \"where is X called\", \"find all references to Y\"; pass \\`context:N\\` for grep -C N around hits), \\`get_file_info\\`. Don't call \\`grep\\` or other tools that aren't in this list — they don't exist as functions.\n\n# Path conventions\n\nTwo different rules depending on which tool:\n\n- **Filesystem tools** (\\`read_file\\`, \\`list_directory\\`, \\`search_files\\`, \\`edit_file\\`, etc.): paths resolve against the sandbox root. Relative (\\`src/foo.ts\\`), POSIX-absolute (\\`/src/foo.ts\\`, where \\`/\\` means the project root), and OS-absolute including Windows drive-letter (\\`D:\\\\\\\\path\\\\\\\\foo.cpp\\`) all work — anything that resolves INSIDE the sandbox is readable, regardless of the path shape. When the user pastes a path, your default move is to call \\`read_file\\` on it as-is. The tool returns a clear \"path escapes sandbox\" error (with a relaunch hint) if it's actually out of scope; refusing on path shape alone, claiming \"I can't access the filesystem\", or falling back to \\`web_search\\` for a local file are all wrong — you have filesystem tools, use them.\n- **\\`run_command\\`**: the command runs in a real OS shell with cwd pinned to the project root. Paths inside the shell command are interpreted by THAT shell, not by us. **Never use leading \\`/\\` in run_command arguments** — Windows treats \\`/tests\\` as drive-root \\`F:\\\\tests\\` (non-existent), POSIX shells treat it as filesystem root. Use plain relative paths (\\`tests\\`, \\`./tests\\`, \\`src/loop.ts\\`) instead.\n\n# When the user wants to switch project / working directory\n\nYou can't. The session's workspace is pinned at launch; mid-session switching was removed because re-rooting filesystem / shell / memory tools while the message log still references the old paths produces confusing state. Tell the user to quit and relaunch with the new directory (e.g. \\`cd ../other-project && carboncode code\\`).\n\nDo NOT try to switch via \\`run_command\\` (\\`cd\\`, \\`pushd\\`, etc.) — your tool sandbox is pinned and \\`cd\\` inside one shell call doesn't carry to the next.\n\n# Foreground vs. background commands\n\nYou have TWO tools for running shell commands, and picking the right one is non-negotiable:\n\n- \\`run_command\\` — blocks until the process exits. Use for: **tests, builds, lints, typechecks, git operations, one-shot scripts**. Anything that naturally returns in under a minute.\n- \\`run_background\\` — spawns and detaches after a brief startup window. Use for:\n - **Dev servers / watchers / anything with \"dev\" / \"serve\" / \"watch\" / \"start\" in the name.** Examples: \\`npm run dev\\`, \\`pnpm dev\\`, \\`yarn start\\`, \\`vite\\`, \\`next dev\\`, \\`uvicorn app:app --reload\\`, \\`flask run\\`, \\`python -m http.server\\`, \\`cargo watch\\`, \\`tsc --watch\\`, \\`webpack serve\\`.\n - **One-shot long jobs that would blow run_command's 60s ceiling.** Examples: \\`curl -L -O <big-url>\\`, \\`wget\\`, \\`huggingface-cli download\\`, multi-GB \\`pip install\\` / \\`npm install\\`, big \\`cargo build\\` / \\`docker build\\`. Start with \\`run_background\\`, then call \\`wait_for_job\\` ONCE with a long \\`timeoutMs\\` — that costs one tool call total, not one per poll.\n\n**Never use run_command for a dev server or a download likely to exceed a minute.** It will block, time out, and the user will see a frozen tool call while the work was actually running fine. Always \\`run_background\\` + \\`wait_for_job\\` / \\`job_output\\`.\n\nAfter \\`run_background\\`, tools available to you:\n- \\`job_output(jobId, tailLines?)\\` — read recent logs to verify startup / debug errors.\n- \\`wait_for_job(jobId, timeoutMs?, waitFor?)\\` — block server-side until the job finishes (or, with \\`waitFor: 'output-or-exit'\\`, until it writes a new line). ONE tool call per wait regardless of duration. \\`timeoutMs\\` clamps at 300_000. For downloads / installs / builds: leave \\`waitFor\\` at the default \\`'exit'\\` and set \\`timeoutMs\\` to the slowest reasonable end-to-end. For tailing a dev server and reacting to a specific log line: pass \\`waitFor: 'output-or-exit'\\` with a short \\`timeoutMs\\`.\n- \\`list_jobs\\` — see every job this session (running + exited).\n- \\`stop_job(jobId)\\` — SIGTERM → SIGKILL after grace. Stop before switching port / config.\n\nDon't re-start an already-running dev server — call \\`list_jobs\\` first when in doubt.\n\n# Scope discipline on \"run it\" / \"start it\" requests\n\nWhen the user's request is to **run / start / launch / serve / boot up** something, your job is ONLY:\n\n1. Start it (\\`run_background\\` for dev servers, \\`run_command\\` for one-shots).\n2. Verify it came up (read a ready signal via \\`job_output\\`, or fetch the URL with \\`web_fetch\\` if they want you to confirm).\n3. Report what's running, where (URL / port / pid), and STOP.\n\nDo NOT, in the same turn:\n- Run \\`tsc\\` / type-checkers / linters unless the user asked for it.\n- Scan for bugs to \"proactively\" fix. The page rendering is success.\n- Clean up unused imports, dead code, or refactor \"while you're here.\"\n- Edit files to improve anything the user didn't mention.\n\nIf you notice an obvious issue, MENTION it in one sentence and wait for the user to say \"fix it.\" The cost of over-eagerness is real: you burn tokens, make surprise edits the user didn't want, and chain into cascading \"fix the new error I just introduced\" loops. The storm-breaker will cut you off, but the user still sees the mess.\n\n\"It works\" is the end state. Resist the urge to polish.\n\n# Style\n\n- Show edits; don't narrate them in prose. \"Here's the fix:\" is enough.\n- One short paragraph explaining *why*, then the blocks.\n- If you need to explore first (list / read / search), do it with tool calls before writing any prose — silence while exploring is fine.\n\n__ESCALATION_CONTRACT__\n\n${TUI_FORMATTING_RULES}\n`;\n\n/** Backward-compat — public-API const, frozen at the historical flash phrasing. Internal callers use codeSystemPrompt(rootDir, { modelId }) so the contract names the real tier (#582). */\nexport const CODE_SYSTEM_PROMPT = codeSystemBase(DEFAULT_CODE_MODEL);\n\n/** Stack order (stable for cache prefix): base → project rules → global → project → .gitignore. */\nconst SEMANTIC_SEARCH_ROUTING = `\n\n# Search routing\n\nYou have BOTH \\`semantic_search\\` (vector index) and \\`search_content\\` (literal grep).\n\n- **Descriptive queries** (\"where do we handle X\", \"which file owns Y\", \"how does Z work\", \"find the logic that does …\", \"the code responsible for …\") → call \\`semantic_search\\` FIRST. It indexes the project by meaning, so it finds the right file even when your phrasing shares no tokens with the code.\n- **Exact-token queries** (a specific identifier, regex, or \"find every call to foo\") → call \\`search_content\\`.\n\nIf \\`semantic_search\\` returns nothing useful (low scores, off-topic), THEN fall back to \\`search_content\\`. Don't go the other way — grepping a paraphrased question wastes turns.`;\n\nexport interface CodeSystemPromptOptions {\n /** True when semantic_search is registered for this run. Adds an\n * explicit routing fragment so the model picks it for intent-style\n * queries instead of defaulting to grep. */\n hasSemanticSearch?: boolean;\n /** Inline string appended after the generated code system prompt.\n * Preserves the default prompt — this is append-only, not a replacement. */\n systemAppend?: string;\n /** UTF-8 file contents appended after the generated code system prompt.\n * Preserves the default prompt — this is append-only, not a replacement. */\n systemAppendFile?: string;\n /** Model the loop will run on — interpolated into the escalation contract so the model can name itself correctly when asked (#582). */\n modelId?: string;\n}\n\nexport function codeSystemPrompt(rootDir: string, opts: CodeSystemPromptOptions = {}): string {\n const codeBase = codeSystemBase(opts.modelId ?? DEFAULT_CODE_MODEL);\n const base = opts.hasSemanticSearch ? `${codeBase}${SEMANTIC_SEARCH_ROUTING}` : codeBase;\n const withMemory = applyMemoryStack(base, rootDir);\n const gitignorePath = join(rootDir, \".gitignore\");\n let result = withMemory;\n if (existsSync(gitignorePath)) {\n let content: string | undefined;\n try {\n content = readFileSync(gitignorePath, \"utf8\");\n } catch {}\n if (content !== undefined) {\n const MAX = 2000;\n const truncated =\n content.length > MAX\n ? `${content.slice(0, MAX)}\\n… (truncated ${content.length - MAX} chars)`\n : content;\n result = `${result}\\n\\n# Project .gitignore\\n\\nThe user's repo ships this .gitignore — treat every pattern as \"don't traverse or edit inside these paths unless explicitly asked\":\\n\\n\\`\\`\\`\\n${truncated}\\n\\`\\`\\`\\n`;\n }\n }\n const appendParts = [opts.systemAppend, opts.systemAppendFile].filter(Boolean);\n if (appendParts.length > 0) {\n result = `${result}\\n\\n# User System Append\\n\\n${appendParts.join(\"\\n\\n\")}`;\n }\n return result;\n}\n","/** User-private memory pinned into the immutable prefix; distinct from committable CARBON.md. */\n\nimport { createHash } from \"node:crypto\";\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport {\n type ReasonixConfig,\n loadResolvedSkillPaths,\n memoryTypeDefaults,\n resolveSkillPaths,\n} from \"../config.js\";\nimport { parseFrontmatter } from \"../frontmatter.js\";\nimport { applySkillsIndex } from \"../skills.js\";\nimport { applyProjectMemory, memoryEnabled } from \"./project.js\";\n\nexport const USER_MEMORY_DIR = \"memory\";\nexport const MEMORY_INDEX_FILE = \"MEMORY.md\";\nexport const CARBON_HOME_DIR = \".carboncode\";\nexport const LEGACY_REASONIX_HOME_DIR = \".reasonix\";\nexport const GLOBAL_CARBON_MEMORY_FILE = \"CARBON.md\";\nexport const LEGACY_REASONIX_MEMORY_FILE = \"REASONIX.md\";\n/** Cap on the index file content loaded into the prefix, per scope. */\nexport const MEMORY_INDEX_MAX_CHARS = 4000;\n\nexport const BUILTIN_MEMORY_TYPES = [\"user\", \"feedback\", \"project\", \"reference\"] as const;\nexport type BuiltinMemoryType = (typeof BUILTIN_MEMORY_TYPES)[number];\n/** Built-ins plus any string declared in `config.memory.customTypes`. Unknown values are accepted (round-tripped verbatim). */\nexport type MemoryType = BuiltinMemoryType | (string & {});\nexport type MemoryScope = \"global\" | \"project\";\nexport type MemoryPriority = \"low\" | \"medium\" | \"high\";\nexport type MemoryExpires = \"project_end\";\n\nexport interface MemoryEntry {\n name: string;\n type: MemoryType;\n scope: MemoryScope;\n description: string;\n body: string;\n /** ISO date string (YYYY-MM-DD). */\n createdAt: string;\n /** Explicit per-entry priority; absent → resolve from config default for `type`, else \"medium\". */\n priority?: MemoryPriority;\n /** Lifecycle hint. `project_end` → cleared by `/memory clear project`. */\n expires?: MemoryExpires;\n}\n\nexport interface MemoryStoreOptions {\n /** Override `~/.carboncode` — tests set this to a tmpdir. */\n homeDir?: string;\n /** Absolute sandbox root. Required to use `scope: \"project\"`. */\n projectRoot?: string;\n}\n\nexport interface WriteInput {\n name: string;\n type: MemoryType;\n scope: MemoryScope;\n description: string;\n body: string;\n priority?: MemoryPriority;\n expires?: MemoryExpires;\n}\n\nconst VALID_NAME = /^[a-zA-Z0-9_-][a-zA-Z0-9_.-]{1,38}[a-zA-Z0-9]$/;\n\n/** Throws on path-injection (../, /, leading dot). Allowed: 3-40 chars, alnum/_/-, interior `.`. */\nexport function sanitizeMemoryName(raw: string): string {\n const trimmed = String(raw ?? \"\").trim();\n if (!VALID_NAME.test(trimmed)) {\n throw new Error(\n `invalid memory name: ${JSON.stringify(raw)} — must be 3-40 chars, alnum/_/-, no path separators`,\n );\n }\n return trimmed;\n}\n\n/** Stable 16-hex-char hash of an absolute sandbox root path. */\nexport function projectHash(rootDir: string): string {\n const abs = resolve(rootDir);\n return createHash(\"sha1\").update(abs).digest(\"hex\").slice(0, 16);\n}\n\nfunction scopeDir(opts: { homeDir: string; scope: MemoryScope; projectRoot?: string }): string {\n if (opts.scope === \"global\") {\n return join(opts.homeDir, USER_MEMORY_DIR, \"global\");\n }\n if (!opts.projectRoot) {\n throw new Error(\"scope=project requires a projectRoot on MemoryStore\");\n }\n return join(opts.homeDir, USER_MEMORY_DIR, projectHash(opts.projectRoot));\n}\n\nfunction ensureDir(p: string): void {\n if (!existsSync(p)) mkdirSync(p, { recursive: true });\n}\n\nfunction formatFrontmatter(e: WriteInput & { createdAt: string }): string {\n const lines = [\n \"---\",\n `name: ${e.name}`,\n `description: ${e.description.replace(/\\n/g, \" \")}`,\n `type: ${e.type}`,\n `scope: ${e.scope}`,\n `created: ${e.createdAt}`,\n ];\n if (e.priority) lines.push(`priority: ${e.priority}`);\n if (e.expires) lines.push(`expires: ${e.expires}`);\n lines.push(\"---\", \"\");\n return lines.join(\"\\n\");\n}\n\nfunction coercePriority(v: unknown): MemoryPriority | undefined {\n return v === \"low\" || v === \"medium\" || v === \"high\" ? v : undefined;\n}\n\nfunction coerceExpires(v: unknown): MemoryExpires | undefined {\n return v === \"project_end\" ? v : undefined;\n}\n\nfunction todayIso(): string {\n const d = new Date();\n return d.toISOString().slice(0, 10);\n}\n\nfunction indexLine(e: Pick<MemoryEntry, \"name\" | \"description\">): string {\n const safeDesc = e.description.replace(/\\n/g, \" \").trim();\n const max = 130 - e.name.length;\n const clipped = safeDesc.length > max ? `${safeDesc.slice(0, Math.max(1, max - 1))}…` : safeDesc;\n return `- [${e.name}](${e.name}.md) — ${clipped}`;\n}\n\nexport class MemoryStore {\n private readonly homeDir: string;\n private readonly projectRoot: string | undefined;\n\n constructor(opts: MemoryStoreOptions = {}) {\n this.homeDir = opts.homeDir ?? join(homedir(), CARBON_HOME_DIR);\n this.projectRoot = opts.projectRoot ? resolve(opts.projectRoot) : undefined;\n }\n\n /** Directory this store writes `scope` files into, creating it if needed. */\n dir(scope: MemoryScope): string {\n const d = scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot });\n ensureDir(d);\n return d;\n }\n\n /** Absolute path to a memory file (no existence check). */\n pathFor(scope: MemoryScope, name: string): string {\n return join(this.dir(scope), `${sanitizeMemoryName(name)}.md`);\n }\n\n /** True iff this store is configured with a project scope available. */\n hasProjectScope(): boolean {\n return this.projectRoot !== undefined;\n }\n\n loadIndex(\n scope: MemoryScope,\n ): { content: string; originalChars: number; truncated: boolean } | null {\n if (scope === \"project\" && !this.projectRoot) return null;\n const file = join(\n scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot }),\n MEMORY_INDEX_FILE,\n );\n if (!existsSync(file)) return null;\n let raw: string;\n try {\n raw = readFileSync(file, \"utf8\");\n } catch {\n return null;\n }\n const trimmed = raw.trim();\n if (!trimmed) return null;\n const originalChars = trimmed.length;\n const truncated = originalChars > MEMORY_INDEX_MAX_CHARS;\n const content = truncated\n ? `${trimmed.slice(0, MEMORY_INDEX_MAX_CHARS)}\\n… (truncated ${originalChars - MEMORY_INDEX_MAX_CHARS} chars)`\n : trimmed;\n return { content, originalChars, truncated };\n }\n\n /** Read one memory file's body (frontmatter stripped). Throws if missing. */\n read(scope: MemoryScope, name: string): MemoryEntry {\n const file = this.pathFor(scope, name);\n if (!existsSync(file)) {\n throw new Error(`memory not found: scope=${scope} name=${name}`);\n }\n const raw = readFileSync(file, \"utf8\");\n const { data, body } = parseFrontmatter(raw);\n const entry: MemoryEntry = {\n name: data.name ?? name,\n type: (data.type as MemoryType) ?? \"project\",\n scope: (data.scope as MemoryScope) ?? scope,\n description: data.description ?? \"\",\n body: body.trim(),\n createdAt: data.created ?? \"\",\n };\n const priority = coercePriority(data.priority);\n if (priority) entry.priority = priority;\n const expires = coerceExpires(data.expires);\n if (expires) entry.expires = expires;\n return entry;\n }\n\n /** Skips malformed files — index stays queryable even if one file is hand-edited into nonsense. */\n list(): MemoryEntry[] {\n const out: MemoryEntry[] = [];\n const scopes: MemoryScope[] = this.projectRoot ? [\"global\", \"project\"] : [\"global\"];\n for (const scope of scopes) {\n const dir = scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot });\n if (!existsSync(dir)) continue;\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n continue;\n }\n for (const entry of entries) {\n if (entry === MEMORY_INDEX_FILE) continue;\n if (!entry.endsWith(\".md\")) continue;\n const name = entry.slice(0, -3);\n try {\n out.push(this.read(scope, name));\n } catch {\n // malformed file — skip rather than fail the whole list\n }\n }\n }\n return out;\n }\n\n write(input: WriteInput): string {\n if (input.scope === \"project\" && !this.projectRoot) {\n throw new Error(\"cannot write project-scoped memory: no projectRoot configured\");\n }\n const name = sanitizeMemoryName(input.name);\n const desc = String(input.description ?? \"\").trim();\n if (!desc) throw new Error(\"memory description cannot be empty\");\n const body = String(input.body ?? \"\").trim();\n if (!body) throw new Error(\"memory body cannot be empty\");\n const entry: WriteInput & { createdAt: string } = {\n ...input,\n name,\n description: desc,\n body,\n createdAt: todayIso(),\n };\n if (input.priority) entry.priority = input.priority;\n if (input.expires) entry.expires = input.expires;\n const dir = this.dir(input.scope);\n const file = join(dir, `${name}.md`);\n const content = `${formatFrontmatter(entry)}${body}\\n`;\n writeFileSync(file, content, \"utf8\");\n this.regenerateIndex(input.scope);\n return file;\n }\n\n /** Delete one memory + its index line. No-op if the file is already gone. */\n delete(scope: MemoryScope, rawName: string): boolean {\n if (scope === \"project\" && !this.projectRoot) {\n throw new Error(\"cannot delete project-scoped memory: no projectRoot configured\");\n }\n const file = this.pathFor(scope, rawName);\n if (!existsSync(file)) return false;\n unlinkSync(file);\n this.regenerateIndex(scope);\n return true;\n }\n\n /** Sorted by name — same file set must produce byte-identical MEMORY.md for stable prefix hashing. */\n private regenerateIndex(scope: MemoryScope): void {\n const dir = scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot });\n if (!existsSync(dir)) return;\n let files: string[];\n try {\n files = readdirSync(dir);\n } catch {\n return;\n }\n const mdFiles = files\n .filter((f) => f !== MEMORY_INDEX_FILE && f.endsWith(\".md\"))\n .sort((a, b) => a.localeCompare(b));\n const indexPath = join(dir, MEMORY_INDEX_FILE);\n if (mdFiles.length === 0) {\n if (existsSync(indexPath)) unlinkSync(indexPath);\n return;\n }\n const lines: string[] = [];\n for (const f of mdFiles) {\n const name = f.slice(0, -3);\n try {\n const entry = this.read(scope, name);\n lines.push(indexLine({ name: entry.name || name, description: entry.description }));\n } catch {\n // Malformed: still surface it in the index so the user notices.\n lines.push(`- [${name}](${name}.md) — (malformed, check frontmatter)`);\n }\n }\n writeFileSync(indexPath, `${lines.join(\"\\n\")}\\n`, \"utf8\");\n }\n}\n\nfunction readFreeformMemoryFile(\n path: string,\n): { path: string; content: string; originalChars: number; truncated: boolean } | null {\n if (!existsSync(path)) return null;\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return null;\n }\n const trimmed = raw.trim();\n if (!trimmed) return null;\n const originalChars = trimmed.length;\n // Reuse the project-memory cap so both freeform files have the same\n // headroom (8000 chars ≈ 2k tokens). They serve the same purpose at\n // different scopes.\n const truncated = originalChars > 8000;\n const content = truncated\n ? `${trimmed.slice(0, 8000)}\\n… (truncated ${originalChars - 8000} chars)`\n : trimmed;\n return { path, content, originalChars, truncated };\n}\n\n/** Freeform `#g` destination, distinct from MEMORY.md's curated index of named files. */\nexport function readGlobalCarbonMemory(\n homeDir: string = homedir(),\n): { path: string; content: string; originalChars: number; truncated: boolean } | null {\n const carbonPath = join(homeDir, CARBON_HOME_DIR, GLOBAL_CARBON_MEMORY_FILE);\n if (existsSync(carbonPath)) return readFreeformMemoryFile(carbonPath);\n return readFreeformMemoryFile(\n join(homeDir, LEGACY_REASONIX_HOME_DIR, LEGACY_REASONIX_MEMORY_FILE),\n );\n}\n\n/** Compatibility for callers that still pass the old `~/.reasonix` directory directly. */\nexport function readGlobalReasonixMemory(\n homeDir: string = join(homedir(), LEGACY_REASONIX_HOME_DIR),\n): { path: string; content: string; originalChars: number; truncated: boolean } | null {\n return readFreeformMemoryFile(join(homeDir, LEGACY_REASONIX_MEMORY_FILE));\n}\n\nfunction globalFreeformLabel(path: string): string {\n const normalized = path.replace(/\\\\/g, \"/\");\n return normalized.includes(`${LEGACY_REASONIX_HOME_DIR}/${LEGACY_REASONIX_MEMORY_FILE}`)\n ? \"~/.reasonix/REASONIX.md (legacy)\"\n : \"~/.carboncode/CARBON.md\";\n}\n\nfunction applyFreeformGlobalMemory(\n basePrompt: string,\n mem: { path: string; content: string },\n): string {\n return [\n basePrompt,\n \"\",\n `# Global memory (${globalFreeformLabel(mem.path)})`,\n \"\",\n \"Cross-project notes the user pinned via the `#g` prompt prefix. Treat as authoritative — same level of trust as project memory.\",\n \"\",\n \"```\",\n mem.content,\n \"```\",\n ].join(\"\\n\");\n}\n\nexport function applyGlobalCarbonMemory(basePrompt: string, homeDir?: string): string {\n if (!memoryEnabled()) return basePrompt;\n const mem = readGlobalCarbonMemory(homeDir ?? homedir());\n if (!mem) return basePrompt;\n return applyFreeformGlobalMemory(basePrompt, mem);\n}\n\nexport function applyGlobalReasonixMemory(\n basePrompt: string,\n homeDir: string = join(homedir(), LEGACY_REASONIX_HOME_DIR),\n): string {\n if (!memoryEnabled()) return basePrompt;\n const mem = readGlobalReasonixMemory(homeDir);\n if (!mem) return basePrompt;\n return applyFreeformGlobalMemory(basePrompt, mem);\n}\n\n/** Effective priority: entry's own field wins, else the config default for its type, else undefined. */\nexport function effectivePriority(\n entry: MemoryEntry,\n cfg?: ReasonixConfig,\n): MemoryPriority | undefined {\n if (entry.priority) return entry.priority;\n return memoryTypeDefaults(entry.type, cfg).priority;\n}\n\nfunction highPriorityBlock(entries: MemoryEntry[], cfg?: ReasonixConfig): string | null {\n const high = entries.filter((e) => effectivePriority(e, cfg) === \"high\");\n if (high.length === 0) return null;\n const lines: string[] = [\n \"# HIGH PRIORITY constraints (must observe)\",\n \"\",\n \"These memories were declared `priority: high` (via config.memory.customTypes or the memory file itself). Treat them as hard rules — violations override any other guidance below.\",\n \"\",\n ];\n for (const e of high) {\n const head = `!!! [${e.scope}/${e.type}/${e.name}] ${e.description || \"(no description)\"}`;\n lines.push(head);\n if (e.body) lines.push(\"\", e.body);\n lines.push(\"\");\n }\n return lines.join(\"\\n\").trimEnd();\n}\n\n/** Empty index → omit the whole block (otherwise we'd add bytes to the prefix hash for nothing). */\nexport function applyUserMemory(\n basePrompt: string,\n opts: { homeDir?: string; projectRoot?: string; cfg?: ReasonixConfig } = {},\n): string {\n if (!memoryEnabled()) return basePrompt;\n const store = new MemoryStore(opts);\n const global = store.loadIndex(\"global\");\n const project = store.hasProjectScope() ? store.loadIndex(\"project\") : null;\n const high = highPriorityBlock(store.list(), opts.cfg);\n if (!global && !project && !high) return basePrompt;\n const parts: string[] = [basePrompt];\n if (high) parts.push(\"\", high);\n if (global) {\n parts.push(\n \"\",\n \"# User memory — global (~/.carboncode/memory/global/MEMORY.md)\",\n \"\",\n \"Cross-project facts and preferences the user has told you in prior sessions. TREAT AS AUTHORITATIVE — don't re-verify via filesystem or web. One-liners index detail files; call `recall_memory` for full bodies only when the one-liner isn't enough.\",\n \"\",\n \"```\",\n global.content,\n \"```\",\n );\n }\n if (project) {\n parts.push(\n \"\",\n \"# User memory — this project\",\n \"\",\n \"Per-project facts the user established in prior sessions (not committed to the repo). TREAT AS AUTHORITATIVE. Same recall pattern as global memory.\",\n \"\",\n \"```\",\n project.content,\n \"```\",\n );\n }\n return parts.join(\"\\n\");\n}\n\nexport function applyMemoryStack(\n basePrompt: string,\n rootDir: string,\n opts: { homeDir?: string; cfg?: ReasonixConfig } = {},\n): string {\n const homeDir = opts.homeDir;\n const cfg = opts.cfg;\n const withProject = applyProjectMemory(basePrompt, rootDir);\n const withGlobal = applyGlobalCarbonMemory(withProject, homeDir);\n const withMemory = applyUserMemory(withGlobal, { projectRoot: rootDir, homeDir, cfg });\n const customSkillPaths = cfg?.skills?.paths\n ? resolveSkillPaths(cfg.skills.paths, rootDir)\n : loadResolvedSkillPaths(rootDir);\n return applySkillsIndex(withMemory, { projectRoot: rootDir, homeDir, customSkillPaths });\n}\n"],"mappings":"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","names":["existsSync","readFileSync","join","join","existsSync","readFileSync"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/core/pause-gate.ts","../../src/tools/jobs.ts","../../src/tools/shell.ts","../../src/tools/shell/exec.ts","../../src/tools/shell-chain.ts","../../src/tools/shell/parse.ts","../../src/tools/fs/edit.ts"],"sourcesContent":["/** 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 =\n | { type: \"approve\"; feedback?: string }\n | { type: \"refine\"; feedback?: string }\n | { type: \"cancel\"; feedback?: string };\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 path_access: 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; cwd?: string; timeoutSec?: number };\n run_background: { command: string; cwd?: string; waitSec?: number };\n path_access: {\n /** Absolute path the tool wants to touch. */\n path: string;\n /** Why we're being asked — read leaks content, write mutates files. */\n intent: \"read\" | \"write\";\n /** The filesystem tool calling in — surfaced so users can see what's about to happen. */\n toolName: string;\n /** Sandbox root the path is escaping — surfaced for context. */\n sandboxRoot: string;\n /** Directory prefix that would be persisted if the user picks \"always allow\". */\n allowPrefix: string;\n };\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 Carbon Code 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 /** Safe-cancel every outstanding request — frees stranded tool fns on Esc / /new. */\n cancelAll(): void {\n const ids = [...this._pending.keys()];\n for (const id of ids) {\n const p = this._pending.get(id);\n if (!p) continue;\n this._pending.delete(id);\n p.resolve(safeCancelVerdict(p.request.kind));\n }\n }\n\n /** Cancel one pending request — used by multi-tab hosts that need per-scope abort. */\n cancel(id: number): boolean {\n const p = this._pending.get(id);\n if (!p) return false;\n this._pending.delete(id);\n p.resolve(safeCancelVerdict(p.request.kind));\n return true;\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\nfunction safeCancelVerdict(kind: PauseKind): unknown {\n switch (kind) {\n case \"run_command\":\n case \"run_background\":\n case \"path_access\":\n return { type: \"deny\" };\n case \"plan_proposed\":\n return { type: \"cancel\" };\n case \"plan_checkpoint\":\n return { type: \"stop\" };\n case \"plan_revision\":\n return { type: \"cancelled\" };\n case \"choice\":\n return { type: \"cancel\" };\n }\n}\n\n/** Singleton shared between tools and the App. */\nexport const pauseGate = new PauseGate();\n","/** 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 // `exit` fires when the process is dead; `close` waits for stdio drain too.\n // On Windows + Node ≥ 24, drained stdio can lag 5–10s behind taskkill /T /F,\n // so we settle `running`/`closedPromise` on the earlier event. `close` is\n // still wired for the no-exit fallback (spawn error before any process exists).\n const settleClosed = (code: number | null) => {\n if (!job.running && job.exitCode !== null) return;\n job.running = false;\n job.exitCode = code;\n job.signalReady();\n job.signalClosed();\n };\n child.on(\"exit\", settleClosed);\n child.on(\"close\", settleClosed);\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(\n id: number,\n opts: { timeoutMs?: number; waitFor?: \"exit\" | \"output-or-exit\" } = {},\n ): 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(300_000, opts.timeoutMs ?? 5_000));\n const waitFor = opts.waitFor ?? \"exit\";\n const startOutput = job.output;\n\n const racers: Promise<void>[] = [job.closedPromise];\n let wakeOutput: (() => void) | null = null;\n if (waitFor === \"output-or-exit\") {\n racers.push(\n new Promise<void>((resolve) => {\n wakeOutput = resolve;\n job.outputWaiters.add(resolve);\n }),\n );\n }\n let timer: ReturnType<typeof setTimeout> | null = null;\n racers.push(\n new Promise<void>((resolve) => {\n timer = setTimeout(resolve, timeoutMs);\n }),\n );\n await Promise.race(racers);\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 // Node ≥ 24 on Windows sometimes never fires `close` after taskkill /T /F\n // (the OS handle lingers even though the process is dead). We issued the\n // kill; trust it and settle the record so callers don't see ghost-running.\n if (job.running) {\n job.running = false;\n job.signalClosed();\n }\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 // Same Node ≥ 24 Windows fallback as `stop()`: settle any job whose `close`\n // event never arrived after taskkill /T /F — the kill is synchronous, the\n // notification isn't.\n for (const job of runningJobs) {\n if (job.running) {\n job.running = false;\n job.signalClosed();\n }\n }\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 hasSensitivePathArgs,\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 /** Fired after `run_background` / `stop_job` mutate the registry — used by the desktop popover for near-real-time updates without polling. */\n onJobsChanged?: () => void;\n sensitivePaths?: { prefixes?: readonly string[]; patterns?: readonly string[] };\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; returns combined stdout+stderr. Allowlisted read-only / test / lint / typecheck commands run immediately; anything that could mutate state, install deps, or touch the network is gated by user confirmation. Prefer this over asking the user to run a command manually — after edits, run the project's tests to verify.\\n\\nConstraints (no real shell — argv is parsed natively for cross-platform parity):\\n• Supported: chain ops `|` / `||` / `&&` / `;` (each segment allowlist-checked individually), file redirects `>` / `>>` / `<` / `2>` / `2>>` / `2>&1` / `&>` (target paths resolve relative to project root, max one redirect per fd per segment).\\n• NOT supported: background `&`, heredoc `<<`, command substitution `$(…)`, subshells `(…)`, process substitution `<(…)`, `$VAR` env expansion, glob expansion. To pass an operator char as literal arg, quote it (`grep \\\"a|b\\\" file`).\\n• `cd` does NOT persist — between calls OR within a chain like `cd dir && cmd`. Use the binary's own cwd flag: `npm --prefix <dir>`, `git -C <dir>`, `cargo -C <dir>`, `pytest <dir>/tests`.\\n• Filter at source — unbounded output (`netstat -ano`, `find /`) wastes tokens. Use `grep -c`, `wc -l`, narrower paths, etc.\",\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(), rootDir, opts.sensitivePaths);\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 const effectiveTimeout = Math.max(1, Math.min(600, args.timeoutSec ?? timeoutSec));\n if (\n !isAllowAll() &&\n !isCommandAllowed(cmd, getExtraAllowed(), rootDir, opts.sensitivePaths)\n ) {\n const gate = ctx?.confirmationGate ?? pauseGate;\n const choice = await gate.ask({\n kind: \"run_command\",\n payload: { command: cmd, cwd: rootDir, timeoutSec: effectiveTimeout },\n });\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 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 and detach. Waits up to `waitSec` for startup or a readiness signal ('Local:', 'listening on', 'compiled successfully'), then returns the job id + startup preview. Tail logs with `job_output`, block on completion with `wait_for_job`, kill with `stop_job`, list with `list_jobs`.\\n\\nSingle process only — no chains / redirects. For subdirectories use the `cwd` parameter (workspace-relative or absolute, must stay inside the workspace root); do NOT write `cd X && cmd`, that gets rejected.\\n\\nUSE THIS — not run_command — for:\\n- Dev servers / watchers: npm/yarn/pnpm dev, uvicorn / flask run, cargo watch, tsc --watch, webpack serve, anything with dev/serve/watch in the name.\\n- One-shot long jobs: curl / wget large downloads, `huggingface-cli download`, multi-GB `pip install` / `npm install`, big `cargo build` / `docker build`. Start with `run_background`, then call `wait_for_job` once (default `waitFor: 'exit'`, timeoutMs up to 300_000) — the harness blocks server-side so a 5-minute download costs ONE tool call, not 30 polls.\",\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 cwd: {\n type: \"string\",\n description:\n \"Working directory for the spawn. Workspace-relative or absolute. Defaults to the workspace root. Must resolve inside the workspace — paths escaping the root are rejected.\",\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; cwd?: string; waitSec?: number }, ctx) => {\n const cmd = args.command.trim();\n if (!cmd) throw new Error(\"run_background: empty command\");\n const cwd = resolveCwdInsideRoot(rootDir, args.cwd);\n if (\n !isAllowAll() &&\n !isCommandAllowed(cmd, getExtraAllowed(), rootDir, opts.sensitivePaths)\n ) {\n const gate = ctx?.confirmationGate ?? pauseGate;\n const choice = await gate.ask({\n kind: \"run_background\",\n payload: { command: cmd, cwd, waitSec: args.waitSec },\n });\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,\n waitSec: args.waitSec,\n signal: ctx?.signal,\n });\n opts.onJobsChanged?.();\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 server-side until a background job finishes (or, opt-in, until it produces new output), bounded by `timeoutMs`. Costs ONE tool call regardless of how long the wait runs — use this instead of polling `job_output` in a loop. Returns JSON with `exited`, `exitCode`, and `latestOutput`.\\n\\n`waitFor` controls the wake condition:\\n- `'exit'` (default) — only wake on the job exiting (or the timeout). Right for downloads, installs, builds, anything one-shot. Chatty progress bars do NOT wake the wait.\\n- `'output-or-exit'` — also wake whenever the job writes a new line. Right for tailing a dev server / watcher and reacting to a specific log line.\\n\\nFor a download or install, set `timeoutMs` to the slowest reasonable end-to-end (e.g. 300_000 for a 5-min wheel install).\",\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 timeoutMs: {\n type: \"integer\",\n description:\n \"Max time to block before returning if the wake condition hasn't fired. Clamped to 0..300000. Default 5000.\",\n },\n waitFor: {\n type: \"string\",\n enum: [\"exit\", \"output-or-exit\"],\n description:\n \"Wake condition. 'exit' = only on job exit (right for downloads / installs / builds). 'output-or-exit' = also on any new output (right for tailing a dev server). Default 'exit'.\",\n },\n },\n required: [\"jobId\"],\n },\n fn: async (args: {\n jobId: number;\n timeoutMs?: number;\n waitFor?: \"exit\" | \"output-or-exit\";\n }) => {\n const out = await jobs.waitForJob(args.jobId, {\n timeoutMs: args.timeoutMs,\n waitFor: args.waitFor,\n });\n if (!out) return `job ${args.jobId}: not found (use list_jobs)`;\n if (out.exited) opts.onJobsChanged?.();\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 opts.onJobsChanged?.();\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 resolveCwdInsideRoot(rootDir: string, raw: string | undefined): string {\n const root = pathMod.resolve(rootDir);\n if (!raw || !raw.trim()) return root;\n const resolved = pathMod.resolve(root, raw);\n const rel = pathMod.relative(root, resolved);\n if (rel.startsWith(\"..\") || pathMod.isAbsolute(rel)) {\n throw new Error(\n `run_background: cwd \"${raw}\" resolves outside the workspace root (${root}). Pass a workspace-relative path.`,\n );\n }\n return resolved;\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","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 const normalizedEnv = normalizeWindowsEnvVars(process.env);\n\n const spawnOpts: SpawnOptions = {\n cwd: opts.cwd,\n shell: false,\n windowsHide: true,\n // POSIX: detach so the child becomes its own process-group leader.\n // Required for `process.kill(-pid, …)` in killProcessTree to\n // terminate the whole subtree (child + grandchildren) instead of\n // only the leader — without this grandchildren like npm→node→esbuild\n // become orphaned.\n // Windows: detached would spawn a new console window; leave the\n // default and use taskkill /T for tree termination (see killProcessTree).\n detached: process.platform !== \"win32\",\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: { ...normalizedEnv, 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, { env: normalizedEnv });\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 = (getEnvCaseInsensitive(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 = (getEnvCaseInsensitive(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\nexport function normalizeWindowsEnvVars(\n env: NodeJS.ProcessEnv,\n opts: { platform?: NodeJS.Platform } = {},\n): NodeJS.ProcessEnv {\n const platform = opts.platform ?? process.platform;\n if (platform !== \"win32\") return { ...env };\n\n const out: NodeJS.ProcessEnv = {};\n const pathValues: string[] = [];\n const pathExtValues: string[] = [];\n\n for (const [key, value] of Object.entries(env)) {\n const lower = key.toLowerCase();\n if (lower === \"path\") {\n if (typeof value === \"string\") pathValues.push(value);\n continue;\n }\n if (lower === \"pathext\") {\n if (typeof value === \"string\") pathExtValues.push(value);\n continue;\n }\n out[key] = value;\n }\n\n if (pathValues.length > 0) out.Path = mergeWindowsPathLike(pathValues, \";\");\n if (pathExtValues.length > 0) out.PATHEXT = mergeWindowsPathLike(pathExtValues, \";\");\n\n return out;\n}\n\nfunction getEnvCaseInsensitive(\n env: Record<string, string | undefined>,\n key: string,\n): string | undefined {\n const exact = env[key];\n if (exact !== undefined) return exact;\n const target = key.toLowerCase();\n for (const [candidate, value] of Object.entries(env)) {\n if (candidate.toLowerCase() === target) return value;\n }\n return undefined;\n}\n\nfunction mergeWindowsPathLike(values: readonly string[], delimiter: string): string {\n const seen = new Set<string>();\n const merged: string[] = [];\n\n for (const value of values) {\n for (const part of value.split(delimiter)) {\n const entry = part.trim();\n if (!entry) continue;\n const normalized = entry.toLowerCase();\n if (seen.has(normalized)) continue;\n seen.add(normalized);\n merged.push(entry);\n }\n }\n\n return merged.join(delimiter);\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 { devNull } from \"node:os\";\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\n/** Models reach for `2>nul` (Windows) and `2>/dev/null` (POSIX) interchangeably; without this the parser materializes a real `<cwd>/nul` file. */\nfunction isNullDeviceAlias(target: string): boolean {\n const lower = target.toLowerCase();\n if (lower === \"/dev/null\") return true;\n if (process.platform === \"win32\" && lower === \"nul\") return true;\n return false;\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 = isNullDeviceAlias(target) ? devNull : 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 // POSIX: detach so the child becomes its own process-group leader,\n // allowing killProcessTree's neg-pid kill to terminate the whole\n // pipe chain subtree instead of just the direct child.\n detached: process.platform !== \"win32\",\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 { homedir } from \"node:os\";\nimport * as pathMod from \"node:path\";\nimport { 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/** Issue #259 — default sensitive-path prefixes (tilde-relative). Matching a path argument against these\n * demotes an otherwise-allowlisted command back to the confirm gate, preventing the agent from\n * silently reading credentials / keys and piping them into the LLM context. */\nconst DEFAULT_SENSITIVE_PREFIXES: ReadonlyArray<string> = [\n \"~/.ssh\",\n \"~/.aws\",\n \"~/.gnupg\",\n \"~/.kube\",\n \"/etc/shadow\",\n \"/etc/sudoers\",\n];\n\n/** Issue #259 — filename patterns (case-insensitive basename match). */\nconst DEFAULT_SENSITIVE_PATTERNS: ReadonlyArray<string> = [\n \"*.env\",\n \"*.env.*\",\n \"*.key\",\n \"*.pem\",\n \"id_rsa*\",\n \"id_ed25519*\",\n \"*credentials*\",\n \"*secret*\",\n];\n\n/** Resolve `~` to `homedir()` and normalize. Non-path-like tokens (flags, URLs, env vars) are skipped. */\nfunction resolveSensitivePath(token: string, projectRoot: string): string | null {\n if (!token || token.startsWith(\"-\") || token.includes(\"://\") || token.startsWith(\"$\"))\n return null;\n let expanded = token;\n if (expanded.startsWith(\"~\")) {\n expanded = pathMod.join(homedir(), expanded.slice(1));\n }\n return pathMod.resolve(projectRoot, expanded);\n}\n\nfunction expandPrefix(prefix: string): string {\n if (prefix.startsWith(\"~\")) return pathMod.join(homedir(), prefix.slice(1));\n return pathMod.resolve(prefix);\n}\n\n/** Ensure prefix matches only at directory boundaries (not mid-segment). */\nfunction pathStartsWithPrefix(normalized: string, prefix: string): boolean {\n return normalized === prefix || normalized.startsWith(`${prefix}${pathMod.sep}`);\n}\n\n/** Glob-style match: `*.env` matches `foo.env`, `id_rsa*` matches `id_rsa_old`. */\nfunction matchesGlob(name: string, pattern: string): boolean {\n const regex = new RegExp(\n `^${pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\*/g, \".*\")\n .replace(/\\?/g, \".\")}$`,\n \"i\",\n );\n return regex.test(name);\n}\n\n/** Check whether a command's path-like arguments touch sensitive locations. */\nexport function hasSensitivePathArgs(\n argv: readonly string[],\n projectRoot: string,\n extraPrefixes: readonly string[] = [],\n extraPatterns: readonly string[] = [],\n): boolean {\n const prefixes = [...DEFAULT_SENSITIVE_PREFIXES, ...extraPrefixes].map(expandPrefix);\n const patterns = [...DEFAULT_SENSITIVE_PATTERNS, ...extraPatterns];\n for (const token of argv) {\n const resolved = resolveSensitivePath(token, projectRoot);\n if (!resolved) continue;\n const normalized = pathMod.normalize(resolved);\n for (const pfx of prefixes) {\n if (pathStartsWithPrefix(normalized, pfx)) return true;\n }\n const base = pathMod.basename(normalized);\n for (const pat of patterns) {\n if (matchesGlob(base, pat)) 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,\n * or by `SENSITIVE_PATHS` when a path argument targets a sensitive location (#259). */\nexport function isAllowed(\n cmd: string,\n extra: readonly string[] = [],\n projectRoot?: string,\n sensitivePathConfig?: { prefixes?: readonly string[]; patterns?: readonly string[] },\n): 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 if (\n projectRoot &&\n hasSensitivePathArgs(\n argv,\n projectRoot,\n sensitivePathConfig?.prefixes,\n sensitivePathConfig?.patterns,\n )\n )\n 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(\n cmd: string,\n extra: readonly string[] = [],\n projectRoot?: string,\n sensitivePathConfig?: { prefixes?: readonly string[]; patterns?: readonly string[] },\n): 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, projectRoot, sensitivePathConfig);\n return chainAllowed(chain, (seg) => isAllowed(seg, extra, projectRoot, sensitivePathConfig));\n}\n","import { promises as fs } from \"node:fs\";\nimport * as pathMod from \"node:path\";\n\nfunction displayRel(rootDir: string, full: string): string {\n return pathMod.relative(rootDir, full).replaceAll(\"\\\\\", \"/\");\n}\n\nexport async function applyEdit(\n rootDir: string,\n abs: string,\n args: { search: string; replace: string },\n): Promise<string> {\n if (args.search.length === 0) {\n throw new Error(\"edit_file: search cannot be empty\");\n }\n const before = await fs.readFile(abs, \"utf8\");\n const le = before.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n const adaptedSearch = args.search.replace(/\\r?\\n/g, le);\n const adaptedReplace = args.replace.replace(/\\r?\\n/g, le);\n const firstIdx = before.indexOf(adaptedSearch);\n if (firstIdx < 0) {\n throw new Error(`edit_file: search text not found in ${displayRel(rootDir, abs)}`);\n }\n const nextIdx = before.indexOf(adaptedSearch, firstIdx + 1);\n if (nextIdx >= 0) {\n throw new Error(\n `edit_file: search text appears multiple times in ${displayRel(rootDir, abs)} — include more context to disambiguate`,\n );\n }\n const after =\n before.slice(0, firstIdx) + adaptedReplace + before.slice(firstIdx + adaptedSearch.length);\n await fs.writeFile(abs, after, \"utf8\");\n const rel = displayRel(rootDir, abs);\n const header = `edited ${rel} (${adaptedSearch.length}→${adaptedReplace.length} chars)`;\n const startLine = before.slice(0, firstIdx).split(/\\r?\\n/).length;\n const diff = renderEditDiff(adaptedSearch, adaptedReplace, startLine);\n return `${header}\\n${diff}`;\n}\n\nexport interface MultiEditEntry {\n abs: string;\n search: string;\n replace: string;\n}\n\nexport async function applyMultiEdit(\n rootDir: string,\n edits: ReadonlyArray<MultiEditEntry>,\n): Promise<string> {\n if (edits.length === 0) {\n throw new Error(\"multi_edit: edits must contain at least one entry\");\n }\n type FileState = {\n buf: string;\n le: string;\n hunks: string[];\n deltaChars: number;\n touched: number;\n };\n const filesByPath = new Map<string, FileState>();\n\n for (let i = 0; i < edits.length; i++) {\n const e = edits[i]!;\n if (typeof e.abs !== \"string\" || e.abs.length === 0) {\n throw new Error(`multi_edit: edit #${i + 1} requires a string \\`path\\` (no edits applied)`);\n }\n if (typeof e.search !== \"string\") {\n throw new Error(`multi_edit: edit #${i + 1} requires a string \\`search\\` (no edits applied)`);\n }\n if (typeof e.replace !== \"string\") {\n throw new Error(\n `multi_edit: edit #${i + 1} requires a string \\`replace\\` (no edits applied)`,\n );\n }\n const rel = displayRel(rootDir, e.abs);\n if (e.search.length === 0) {\n throw new Error(\n `multi_edit: edit #${i + 1} (${rel}) search cannot be empty (no edits applied)`,\n );\n }\n let state = filesByPath.get(e.abs);\n if (!state) {\n let before: string;\n try {\n before = await fs.readFile(e.abs, \"utf8\");\n } catch (err) {\n throw new Error(\n `multi_edit: edit #${i + 1} cannot read ${rel}: ${(err as Error).message} (no edits applied)`,\n );\n }\n const le = before.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n state = { buf: before, le, hunks: [], deltaChars: 0, touched: 0 };\n filesByPath.set(e.abs, state);\n }\n const adaptedSearch = e.search.replace(/\\r?\\n/g, state.le);\n const adaptedReplace = e.replace.replace(/\\r?\\n/g, state.le);\n const firstIdx = state.buf.indexOf(adaptedSearch);\n if (firstIdx < 0) {\n throw new Error(\n `multi_edit: edit #${i + 1} search text not found in ${rel} — no edits applied (multi_edit is atomic)`,\n );\n }\n const nextIdx = state.buf.indexOf(adaptedSearch, firstIdx + 1);\n if (nextIdx >= 0) {\n throw new Error(\n `multi_edit: edit #${i + 1} search text appears multiple times in ${rel} — include more context to disambiguate (no edits applied)`,\n );\n }\n const startLine = state.buf.slice(0, firstIdx).split(/\\r?\\n/).length;\n state.buf =\n state.buf.slice(0, firstIdx) +\n adaptedReplace +\n state.buf.slice(firstIdx + adaptedSearch.length);\n state.hunks.push(`# ${rel}\\n${renderEditDiff(adaptedSearch, adaptedReplace, startLine)}`);\n state.deltaChars += adaptedReplace.length - adaptedSearch.length;\n state.touched++;\n }\n\n for (const [abs, state] of filesByPath) {\n await fs.writeFile(abs, state.buf, \"utf8\");\n }\n\n const fileCount = filesByPath.size;\n const editCount = edits.length;\n let totalDelta = 0;\n const allHunks: string[] = [];\n for (const state of filesByPath.values()) {\n totalDelta += state.deltaChars;\n allHunks.push(...state.hunks);\n }\n const sign = totalDelta >= 0 ? \"+\" : \"\";\n const editNoun = editCount === 1 ? \"edit\" : \"edits\";\n const fileNoun = fileCount === 1 ? \"file\" : \"files\";\n const header = `multi_edit: applied ${editCount} ${editNoun} across ${fileCount} ${fileNoun} (${sign}${totalDelta} chars)`;\n return `${header}\\n${allHunks.join(\"\\n\")}`;\n}\n\nfunction renderEditDiff(search: string, replace: string, startLine: number): string {\n const a = search.split(/\\r?\\n/);\n const b = replace.split(/\\r?\\n/);\n const diff = lineDiff(a, b);\n const hunk = `@@ -${startLine},${a.length} +${startLine},${b.length} @@`;\n const body = diff.map((d) => `${d.op === \" \" ? \" \" : d.op} ${d.line}`).join(\"\\n\");\n return `${hunk}\\n${body}`;\n}\n\nexport function lineDiff(\n a: readonly string[],\n b: readonly string[],\n): Array<{ op: \"-\" | \"+\" | \" \"; line: string }> {\n const n = a.length;\n const m = b.length;\n // dp[i][j] = LCS length of a[0..i) and b[0..j).\n const dp: number[][] = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));\n for (let i = 1; i <= n; i++) {\n for (let j = 1; j <= m; j++) {\n if (a[i - 1] === b[j - 1]) dp[i]![j] = dp[i - 1]![j - 1]! + 1;\n else dp[i]![j] = Math.max(dp[i - 1]![j]!, dp[i]![j - 1]!);\n }\n }\n // Backtrack to recover the op sequence.\n const out: Array<{ op: \"-\" | \"+\" | \" \"; line: string }> = [];\n let i = n;\n let j = m;\n while (i > 0 && j > 0) {\n if (a[i - 1] === b[j - 1]) {\n out.unshift({ op: \" \", line: a[i - 1]! });\n i--;\n j--;\n } else if ((dp[i - 1]![j] ?? 0) > (dp[i]![j - 1] ?? 0)) {\n out.unshift({ op: \"-\", line: a[i - 1]! });\n i--;\n } else {\n // Tie-break goes here (strictly less or equal): take the\n // insertion first during backtrack so the final forward order\n // renders removals BEFORE additions for a substitution —\n // matches git-diff convention of `- old / + new`.\n out.unshift({ op: \"+\", line: b[j - 1]! });\n j--;\n }\n }\n while (i > 0) {\n out.unshift({ op: \"-\", line: a[i - 1]! });\n i--;\n }\n while (j > 0) {\n out.unshift({ op: \"+\", line: b[j - 1]! });\n j--;\n }\n return out;\n}\n"],"mappings":"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","names":["resolve","spawn","pathMod","pathMod","spawn","pathMod","isAllowed","resolve","pathMod","resolve","spawn","delimiter","snapshot","killProcessTree","spawn","id","job","resolve","pathMod","i","j"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/commands/code.tsx"],"sourcesContent":["/**\n * `carboncode code [dir]` — opinionated wrapper around `carboncode 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 { buildCodeToolset } from \"../../code/setup.js\";\nimport { loadApiKey, loadPreset, readConfig } from \"../../config.js\";\nimport { loadDotenv } from \"../../env.js\";\nimport { t } from \"../../i18n/index.js\";\nimport { detectForeignAgentPlatform } from \"../../memory/project.js\";\nimport { sanitizeName } from \"../../memory/session.js\";\nimport { markPhase } from \"../startup-profile.js\";\nimport { resolvePreset } from \"../ui/presets.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 /** When true and the dashboard is enabled, open its URL in the system default browser as soon as the server is ready. */\n openDashboard?: boolean;\n /** Pin the dashboard to a fixed port. `undefined` keeps ephemeral assignment. */\n dashboardPort?: number;\n /** Dashboard bind address (#968). `undefined` keeps the default 127.0.0.1. */\n dashboardHost?: string;\n /** Stable dashboard URL token (#968). `undefined` mints a fresh per-boot token. */\n dashboardToken?: string;\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}\n\nexport async function codeCommand(opts: CodeOptions = {}): Promise<void> {\n markPhase(\"code_command_enter\");\n const resolvedModel = opts.model ?? resolvePreset(loadPreset()).model;\n // Bridge .env + ~/.carboncode/config.json into process.env so buildCodeToolset's\n // eager DeepSeekClient constructions (subagent client; semantic embedder) can\n // pick up a key the user already configured via `carboncode setup`. chatCommand\n // does the same dance — code.tsx wraps chatCommand but must also seed env\n // before buildCodeToolset runs, which is BEFORE chatCommand.\n loadDotenv();\n const cfgKey = loadApiKey();\n if (cfgKey && !process.env.DEEPSEEK_API_KEY) {\n process.env.DEEPSEEK_API_KEY = cfgKey;\n }\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 markPhase(\"semantic_bootstrap_start\");\n const { tools, jobs, registerRooted, reBootstrapSemantic, semantic } = await buildCodeToolset({\n rootDir,\n });\n markPhase(\n semantic.enabled ? \"semantic_bootstrap_done_enabled\" : \"semantic_bootstrap_done_skipped\",\n );\n\n process.stderr.write(\n `${t(\"startup.codeRooted\", {\n rootDir,\n session: session ?? t(\"startup.ephemeral\"),\n tools: tools.size,\n semantic: semantic.enabled ? t(\"startup.semanticOn\") : \"\",\n })}\\n`,\n );\n\n const foreign = detectForeignAgentPlatform(rootDir);\n if (foreign) {\n process.stderr.write(t(\"code.workspaceConflict\", { platforms: foreign.join(\", \") }));\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(t(\"code.systemAppendEmpty\"));\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 const errorDetails = e.code ? `[${e.code}] ${e.message}` : e.message;\n process.stderr.write(t(\"code.systemAppendFileReadError\", { filePath, errorDetails }));\n process.exit(1);\n }\n }\n\n // The rebuilder is re-invoked on `/new` and `/cwd`. `currentRoot` is the live\n // pointer; `/cwd` updates it via `onRootChange` so the rebuild picks up the\n // new workspace's project rules / memory without restarting the loop.\n let currentRoot = rootDir;\n let semanticEnabled = semantic.enabled;\n const codeRebuildSystem = () =>\n codeSystemPrompt(currentRoot, {\n hasSemanticSearch: semanticEnabled,\n systemAppend: opts.systemAppend,\n systemAppendFile: systemAppendFileContents,\n modelId: resolvedModel,\n });\n await chatCommand({\n model: resolvedModel,\n budgetUsd: opts.budgetUsd,\n system: codeRebuildSystem(),\n rebuildSystem: codeRebuildSystem,\n transcript: opts.transcript,\n session,\n seedTools: tools,\n codeMode: {\n rootDir,\n jobs,\n reregisterTools: registerRooted,\n reBootstrapSemantic: async (root: string) => {\n const r = await reBootstrapSemantic(root);\n semanticEnabled = r.enabled;\n return r;\n },\n onRootChange: (newRoot: string) => {\n currentRoot = newRoot;\n },\n },\n mcp: readConfig().mcp,\n forceResume: opts.forceResume,\n forceNew: opts.forceNew,\n noDashboard: opts.noDashboard,\n openDashboard: opts.openDashboard,\n dashboardPort: opts.dashboardPort,\n dashboardHost: opts.dashboardHost,\n dashboardToken: opts.dashboardToken,\n });\n}\n"],"mappings":"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","names":[]}