chainlesschain 0.162.156 → 0.162.157

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 (187) hide show
  1. package/README.md +1 -1
  2. package/package.json +2 -2
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-DjBXlypQ.js → AIOps-CfMP2Oud.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-Cv9er9bp.js → ActionButton-Zu0N8BQL.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-CLKDjSNs.js → Analytics-DZTwj5T_.js} +3 -3
  7. package/src/assets/web-panel/assets/AppLayout-CVFJh47v.js +9 -0
  8. package/src/assets/web-panel/assets/Artifacts-BbN5V7au.js +1 -0
  9. package/src/assets/web-panel/assets/Artifacts-CRkepl4t.css +1 -0
  10. package/src/assets/web-panel/assets/{Audit-CXafD178.js → Audit-BMfiLj7T.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-CP5c-GSd.js → BackgroundAgents-BslPCdGF.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-BbYLQfyt.js → Backup-CQ_fMC1r.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-Bqe0MwZL.js → BaseInput-CsFsARiC.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-jNA_yUPH.js → Chat-CwfBobco.js} +6 -6
  15. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BOFLF01s.js → ChatBubbleRenderer-K6Nf3xeN.js} +1 -1
  16. package/src/assets/web-panel/assets/{Checkbox-Dlzz-zD1.js → Checkbox-DkSyjjPp.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-BAsDQjGf.js → Codegen-B7Fx3zZ6.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-Ju0HrAuS.js → Col-Dzfqn8Ep.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-DynJd43Z.js → Community-DhHPdzMM.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-CabbZVww.js → Compact-Ct18_8y7.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-DW4BmAoh.js → Compliance-CY64IqBi.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-Bk1S_7X-.js → Cowork-BYSmWlUl.js} +3 -3
  23. package/src/assets/web-panel/assets/{Cron-By4sh_NM.js → Cron-_4xtP3Ug.js} +2 -2
  24. package/src/assets/web-panel/assets/{Crosschain-DdZdbS1C.js → Crosschain-B4qY-Ycr.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-CkL-sQyS.js → DID-CkCzWkx1.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dashboard-g_mOpumg.js → Dashboard-C1scT7Ke.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-DtHyE7Ao.js → Dropdown-DBnQ2EWd.js} +1 -1
  28. package/src/assets/web-panel/assets/{EmailListRenderer-Kpvov5Wr.js → EmailListRenderer-C9JhOTq0.js} +1 -1
  29. package/src/assets/web-panel/assets/{FamilyGuardDashboard-C4kZoePR.js → FamilyGuardDashboard-CWXv5RUv.js} +1 -1
  30. package/src/assets/web-panel/assets/{Federation-BAwWoK2N.js → Federation-fYdcbz9W.js} +1 -1
  31. package/src/assets/web-panel/assets/{FormItemContext-DeI0i5f8.js → FormItemContext-CJqOT3Yj.js} +1 -1
  32. package/src/assets/web-panel/assets/{GenericCardRenderer-CkcKRtos.js → GenericCardRenderer-DGCsHc5K.js} +1 -1
  33. package/src/assets/web-panel/assets/{Git-kX6jjI9y.js → Git-EH2K-H0Q.js} +2 -2
  34. package/src/assets/web-panel/assets/{Governance-BXJCBm6x.js → Governance-DJn1X2pR.js} +1 -1
  35. package/src/assets/web-panel/assets/Inference-Dch1E8qd.js +1 -0
  36. package/src/assets/web-panel/assets/{KnowledgeGraph-3Dnuf1Wh.js → KnowledgeGraph-QIur8j8-.js} +1 -1
  37. package/src/assets/web-panel/assets/{Logs-lVAZCtUB.js → Logs-DUgHldPg.js} +2 -2
  38. package/src/assets/web-panel/assets/Marketplace-DafrSfvd.js +1 -0
  39. package/src/assets/web-panel/assets/{McpTools-CqjzefRU.js → McpTools-D2xRbfJx.js} +3 -3
  40. package/src/assets/web-panel/assets/{Memory-BFiPAxk6.js → Memory-C6RbVHAo.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-D0O43VwF.js → MobileBridge-BPtVBKqn.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-B3now48t.js → MobileProjects-Cuw832cr.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-DybhtaCS.js → Mtc-D6O9fWFa.js} +4 -4
  44. package/src/assets/web-panel/assets/{MtcAudit-D7MmY4F0.js → MtcAudit-CWZcXIH1.js} +5 -5
  45. package/src/assets/web-panel/assets/{Multisig-wZvGJnB8.js → Multisig-DhIBxTFd.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-BNPpgfvn.js → NLProgramming-wkBFS33e.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-B6FS3KBj.js → Notes-B09FxncA.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-CK0yla9U.js → NotificationSettings-oQwtH6-9.js} +1 -1
  49. package/src/assets/web-panel/assets/OrderTableRenderer-iFKeNf6Y.js +1 -0
  50. package/src/assets/web-panel/assets/{Organization-geqgwN1T.js → Organization-CsF-NIjz.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-BWJYZFbC.js → Overflow-bLxv9Dqi.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-DnU1505F.js → P2P-DVwQtm4q.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-BGY74KnC.js → PdhVaultBrowser-Bgic3QKm.js} +4 -4
  54. package/src/assets/web-panel/assets/{Permissions-CgArrWtQ.js → Permissions-as80-9ep.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-C2vYE61a.js → PersonalDataHub-BGmUw36a.js} +2 -2
  56. package/src/assets/web-panel/assets/{Pipeline-CBYLI4yW.js → Pipeline-BM96zBxG.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-B-PdW9aN.js → Privacy-BBOzBSWe.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-CuWQegbV.js → ProjectInit-GHX41V28.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-4d32m_cS.js → ProjectSettings-DraqzxKD.js} +2 -2
  60. package/src/assets/web-panel/assets/{Projects-VLA7ttwd.js → Projects-BzzdUml4.js} +1 -1
  61. package/src/assets/web-panel/assets/{Providers-B_Izb_oa.js → Providers-CSeYNAtW.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-C6B_ndXV.js → QrScannerModal-DlzBCdXh.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-C6tavL-y.js → QuickAsk-p8iB7od_.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-CF-LaZ6x.js → Recommend-DjOVXacd.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-DBqTTQ9_.js → RemoteSession-CIGk7gk-.js} +2 -2
  66. package/src/assets/web-panel/assets/Reputation-CjT3M0Di.js +1 -0
  67. package/src/assets/web-panel/assets/{Row-DO0fd48A.js → Row-tw8m6T0c.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-BV5LDzFR.js → RssFeed-B47KBx_f.js} +2 -2
  69. package/src/assets/web-panel/assets/Search-D2sx1PBe.js +1 -0
  70. package/src/assets/web-panel/assets/{Security-DwSrWRCw.js → Security-JmtL8g7D.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-BEf1QyM4.js → Services-BxrNqjin.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-LZXTpovy.js → Skeleton-udk8L18t.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-SKgc0Zug.js → Skills-DooLT9of.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-C_QVOtkN.js → Sla-ITVZE1xI.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-v5TW2V-X.js → SpeechSettings-DCkfqY1n.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-CQPjHdqU.js → SyncSettings-CkNlgFjM.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-DCd-uYcI.js → Tasks-BMAoyCM_.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-62VDIjtj.js → Templates-BnrFTlrz.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-BnFQ0pNy.js → Tenant-BjNsy3-i.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-D3v0MGSj.js → Terminal-C0gxLL7V.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-BI1Ka-cu.js → TimelineRenderer-CUtj_KHA.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-BwX1jXYP.js → Tokens-DOg61zlq.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-CSA8TqNx.js → Trigger-D4ldCewj.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-Bn3IjCx7.js → Trust-3lk_Zd8j.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-pUbOAqNi.js → UkeySign-kWhFR6Li.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-Db2RercF.js → VideoEditing-D3GOFVWg.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-CpR1BVAl.js → Wallet-DzrZfBuh.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-Yxtkn7Zp.js → WebAuthn-CyVJVp_g.js} +4 -4
  89. package/src/assets/web-panel/assets/{WorkflowEditor-CYLe9a2E.js → WorkflowEditor-i_b0wi9b.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-gsOGdak0.js → chat-DwPCK8I7.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-D9fN299a.js → colors-D0vaVqZU.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-vkd7s-ay.js → compact-item-Don7nGW2.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-C7YkbMyy.js → createContext-D84R5qy2.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-BBwWehQ7.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-ig7reRSU.js → hasIn-BeUqVVKU.js} +1 -1
  96. package/src/assets/web-panel/assets/{icons-DEmcnxBf.js → icons-DG5UD-6K.js} +4 -4
  97. package/src/assets/web-panel/assets/{index-Bt-9ZhIu.js → index-4MkfPLGv.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-B7QRpKif.js → index-7QUwcGTg.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-D2yhtk6a.js → index-B57al0Z5.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BpGlLbRv.js → index-BER98o4P.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DFM1oNe1.js → index-BNpJf-sf.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BYe9HRSt.js → index-BZ1N07Sp.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CspGymmg.js → index-BlfTf0ka.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-W52sHRdr.js → index-BohrJpBf.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-C7AY39sg.js → index-Br3SOdER.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Co2NYyew.js → index-C0_sKVi7.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DQ8U7kzC.js → index-CAdCTTBC.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-bVbgYBhv.js → index-CExWtm98.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CAkz1f1r.js → index-CL71rIwo.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CGzGe5tv.js → index-CfYRv-zA.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DFfxF7Vt.js → index-CnnxzIx0.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-C8HZMygt.js → index-CrnVEEQO.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-FzGr6CVk.js → index-CzT56x-_.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DuwbKHoS.js → index-D4b5XqIy.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BHHN_KTS.js → index-D84BnuFg.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-9TxdMYju.js → index-D8r7WKk-.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Dy_TKZ9h.js → index-DA0-EJKx.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BhftELHI.js → index-DBzBLX-K.js} +3 -3
  119. package/src/assets/web-panel/assets/{index-BkP17Z95.js → index-DFFzwrNs.js} +1 -1
  120. package/src/assets/web-panel/assets/index-DJ8etkBT.js +1 -0
  121. package/src/assets/web-panel/assets/{index-DPguq3X7.js → index-DMT8n9vB.js} +4 -4
  122. package/src/assets/web-panel/assets/{index-BfpVcXKa.js → index-DQor_7NF.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DvCJXmrx.js → index-DT40zd7K.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BvDEukXe.js → index-De85D9Pc.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-PkH39qsp.js → index-IGGttsCv.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BMw48d19.js → index-L73NrmlW.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CDWTycrg.js → index-LMo9bBLJ.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-nX8YE6cj.js → index-MKZrJrP_.js} +1 -1
  129. package/src/assets/web-panel/assets/index-OeaK-zXZ.js +1 -0
  130. package/src/assets/web-panel/assets/{index-SDrcQXO1.js → index-WRw1pM5E.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DZAxCnjS.js → index-YKLCotoo.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-gj8kxcAi.js → index-hjgrIX5I.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DhdXbsXX.js → index-m9bn0F1p.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-D6s_kbjt.js → index-wesQQ2cH.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-gpurplyq.js → index-x7VYlV41.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-BrgfVRg9.js → initDefaultProps-DGeU4vPS.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion-D_3xmwac.js → motion-Dgqj9ab3.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-DDDJdlxw.js → move-BFyEU6Lr.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-BK2uEWwn.js → mtc-parser-CueCXXPC.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-BPHKbDcf.js → omit-D2pU5OyE.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-LNnTscuz.js → pickAttrs-CKTf6zD6.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-CvmTZll5.js → placementArrow-DieJaGCN.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-eBj7qi2J.js → responsiveObserve-Bxr-rmU6.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-D68hHkrM.js → slide-CDEDk3Se.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-daTlaeo0.js → statusUtils-icf2eCTx.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-DtyKMtHB.js → styleChecker-qKvgmBhE.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-ekFwFs9h.js → useFlexGapSupport-1_UhiyJZ.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-jRaSEoF4.js → useFs-CFGLXDWh.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-DD5nGzjA.js → usePersonalDataHub-UafOXvQ6.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-KcgLUiom.js → vnode-BfPqSx79.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-Cyk2PQ-s.js → zoom-CXsVcRB6.js} +1 -1
  152. package/src/assets/web-panel/index.html +2 -2
  153. package/src/command-manifest.json +8 -1
  154. package/src/commands/agent.js +45 -4
  155. package/src/commands/artifacts.js +161 -0
  156. package/src/commands/browse.js +204 -2
  157. package/src/data/changelog.json +8 -2
  158. package/src/gateways/ws/artifact-protocol.js +219 -0
  159. package/src/gateways/ws/message-dispatcher.js +6 -0
  160. package/src/gateways/ws/ws-server.js +32 -0
  161. package/src/index.js +6 -0
  162. package/src/lib/accessibility.js +55 -0
  163. package/src/lib/artifact-store.js +236 -0
  164. package/src/lib/chrome-connector.js +314 -0
  165. package/src/lib/hook-runner.cjs +47 -7
  166. package/src/lib/ide-context.js +84 -0
  167. package/src/lib/permission-rules.cjs +26 -3
  168. package/src/lib/plugin-runtime/scopes.js +12 -0
  169. package/src/lib/safe-mode.js +40 -0
  170. package/src/lib/shell-selector.cjs +203 -0
  171. package/src/lib/skill-loader.js +19 -0
  172. package/src/lib/slash-dispatch.js +34 -0
  173. package/src/repl/agent-repl.js +99 -58
  174. package/src/runtime/agent-core.js +93 -8
  175. package/src/runtime/coding-agent-contract-shared.cjs +52 -0
  176. package/src/runtime/coding-agent-policy.cjs +15 -0
  177. package/src/runtime/headless-stream.js +51 -1
  178. package/src/runtime/policies/agent-policy.js +1 -0
  179. package/src/assets/web-panel/assets/AppLayout-CfyZNzmJ.js +0 -9
  180. package/src/assets/web-panel/assets/Inference-ujD2JSKo.js +0 -1
  181. package/src/assets/web-panel/assets/Marketplace-qnzNPm-A.js +0 -1
  182. package/src/assets/web-panel/assets/OrderTableRenderer-CXfPHIpy.js +0 -1
  183. package/src/assets/web-panel/assets/Reputation-B01FALUi.js +0 -1
  184. package/src/assets/web-panel/assets/Search-CTti4_MP.js +0 -1
  185. package/src/assets/web-panel/assets/devWarning-DEcR2h6k.js +0 -1
  186. package/src/assets/web-panel/assets/index-BEibanjg.js +0 -1
  187. package/src/assets/web-panel/assets/index-z_25tU3p.js +0 -1
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * shell-selector — Windows / PowerShell first-class shell selection
5
+ * (gap-analysis P1 #8).
6
+ *
7
+ * Decides WHICH shell a `run_shell` command (or a settings hook with a
8
+ * `shell` field) executes under:
9
+ *
10
+ * - per-call: run_shell `{ shell: "powershell" | "pwsh" | "cmd" | "default" }`
11
+ * - configured: settings `shell.windowsDefault` ("cmd" | "powershell" | "pwsh",
12
+ * layered .claude/settings.json, closest wins) or the
13
+ * CC_WINDOWS_SHELL env var (wins over settings)
14
+ * - default: unchanged platform default (cmd.exe via ComSpec on Windows,
15
+ * /bin/sh elsewhere) — the unconfigured path is byte-identical.
16
+ *
17
+ * PowerShell runs go through explicit argv (`powershell.exe -NoProfile
18
+ * [-ExecutionPolicy <p>] -Command <command>`), NOT `shell: "powershell.exe"`,
19
+ * so the enterprise ExecutionPolicy override (settings
20
+ * `shell.powershell.executionPolicy`) can be applied per-invocation without
21
+ * touching machine state. The policy value is validated against the fixed
22
+ * PowerShell enum — a settings typo can NOT smuggle extra argv.
23
+ *
24
+ * Scope notes:
25
+ * - `windowsDefault` only applies on win32 (hence the name). A per-call
26
+ * `pwsh` request is honored on any platform (PowerShell 7 is
27
+ * cross-platform); `powershell`/`cmd` requests outside Windows fall back
28
+ * to the platform default with a note.
29
+ * - `-ExecutionPolicy` is only attached on win32 (POSIX pwsh has no policy).
30
+ * - Hooks opt in per-hook via `{ "shell": "powershell" }` — the configured
31
+ * windowsDefault deliberately does NOT rewrite existing hooks.
32
+ */
33
+
34
+ const settingsLoader = require("./settings-loader.cjs");
35
+
36
+ const SHELL_KINDS = Object.freeze(["default", "cmd", "powershell", "pwsh"]);
37
+
38
+ /** The fixed PowerShell ExecutionPolicy enum (canonical casing). */
39
+ const EXECUTION_POLICIES = Object.freeze([
40
+ "Restricted",
41
+ "AllSigned",
42
+ "RemoteSigned",
43
+ "Unrestricted",
44
+ "Bypass",
45
+ "Undefined",
46
+ ]);
47
+
48
+ /** Normalize a shell kind; unknown/empty → null (fail to default). */
49
+ function normalizeShellKind(value) {
50
+ if (typeof value !== "string") return null;
51
+ const v = value.trim().toLowerCase();
52
+ if (v === "powershell.exe" || v === "windowspowershell") return "powershell";
53
+ if (v === "pwsh.exe") return "pwsh";
54
+ if (v === "cmd.exe") return "cmd";
55
+ return SHELL_KINDS.includes(v) ? v : null;
56
+ }
57
+
58
+ /** Normalize an ExecutionPolicy to canonical casing; unknown → null. */
59
+ function normalizeExecutionPolicy(value) {
60
+ if (typeof value !== "string") return null;
61
+ const v = value.trim().toLowerCase();
62
+ return EXECUTION_POLICIES.find((p) => p.toLowerCase() === v) || null;
63
+ }
64
+
65
+ /**
66
+ * Layered `shell` settings: `{ windowsDefault, powershell: { executionPolicy } }`.
67
+ * Closest layer wins per key; invalid values are ignored (fail-to-default).
68
+ * CC_WINDOWS_SHELL env overrides the settings-provided windowsDefault.
69
+ *
70
+ * @param {{ cwd?: string, env?: object, settingsFile?: string, onWarn?: Function }} [opts]
71
+ * @returns {{ windowsDefault: string|null, executionPolicy: string|null, files: string[] }}
72
+ */
73
+ // Per-process memo: run_shell resolves the config on EVERY call, and the
74
+ // layered settings walk is filesystem I/O. A short TTL keeps mid-session
75
+ // settings edits honored without re-reading per tool call.
76
+ const _configCache = new Map(); // key → { at, value }
77
+ const CONFIG_CACHE_TTL_MS = 3000;
78
+
79
+ function loadShellConfig(opts = {}) {
80
+ const cwd = opts.cwd || process.cwd();
81
+ const env = opts.env || process.env;
82
+ const cacheable = !opts.env && !opts.onWarn;
83
+ const cacheKey = cacheable ? `${cwd}|${opts.settingsFile || ""}` : null;
84
+ if (cacheKey) {
85
+ const hit = _configCache.get(cacheKey);
86
+ if (hit && Date.now() - hit.at < CONFIG_CACHE_TTL_MS) return hit.value;
87
+ }
88
+ let windowsDefault = null;
89
+ let executionPolicy = null;
90
+ const files = [];
91
+ try {
92
+ for (const file of settingsLoader.settingsPaths(cwd, opts.settingsFile)) {
93
+ const data = settingsLoader.readSettingsFile(file, {
94
+ onWarn: opts.onWarn,
95
+ });
96
+ const shell = data && typeof data === "object" ? data.shell : null;
97
+ if (!shell || typeof shell !== "object" || Array.isArray(shell)) continue;
98
+ const kind = normalizeShellKind(shell.windowsDefault);
99
+ if (kind) windowsDefault = kind;
100
+ const pol = normalizeExecutionPolicy(shell.powershell?.executionPolicy);
101
+ if (pol) executionPolicy = pol;
102
+ files.push(file);
103
+ }
104
+ } catch {
105
+ /* settings are best-effort — never fail shell resolution over them */
106
+ }
107
+ const envKind = normalizeShellKind(env.CC_WINDOWS_SHELL);
108
+ if (envKind) windowsDefault = envKind;
109
+ const value = { windowsDefault, executionPolicy, files };
110
+ if (cacheKey) _configCache.set(cacheKey, { at: Date.now(), value });
111
+ return value;
112
+ }
113
+
114
+ /** Test hook: drop the config memo. */
115
+ function _clearShellConfigCache() {
116
+ _configCache.clear();
117
+ }
118
+
119
+ /**
120
+ * Build the explicit argv for a PowerShell invocation of `command`.
121
+ * @param {string} command
122
+ * @param {"powershell"|"pwsh"} kind
123
+ * @param {{ executionPolicy?: string|null, platform?: string }} [opts]
124
+ * @returns {{ file: string, argv: string[] }}
125
+ */
126
+ function buildPowershellArgv(command, kind, opts = {}) {
127
+ const platform = opts.platform || process.platform;
128
+ const file = kind === "pwsh" ? "pwsh" : "powershell.exe";
129
+ const policy =
130
+ platform === "win32"
131
+ ? normalizeExecutionPolicy(opts.executionPolicy)
132
+ : null;
133
+ return {
134
+ file,
135
+ argv: [
136
+ "-NoProfile",
137
+ ...(policy ? ["-ExecutionPolicy", policy] : []),
138
+ "-Command",
139
+ String(command ?? ""),
140
+ ],
141
+ };
142
+ }
143
+
144
+ /**
145
+ * Resolve how to run one shell command.
146
+ *
147
+ * Precedence: per-call `requested` > configured windowsDefault (win32 only)
148
+ * > platform default. Returns either the default-shell marker (caller keeps
149
+ * its existing exec/spawn `shell: true` path — byte-identical) or an explicit
150
+ * `{ file, argv }` PowerShell invocation.
151
+ *
152
+ * @param {{ command: string, requested?: string, cwd?: string, env?: object,
153
+ * platform?: string, settingsFile?: string,
154
+ * config?: { windowsDefault?: string|null, executionPolicy?: string|null } }} opts
155
+ * @returns {{ kind: string, useDefaultShell: boolean, file?: string,
156
+ * argv?: string[], note?: string }}
157
+ */
158
+ function resolveShellInvocation(opts = {}) {
159
+ const platform = opts.platform || process.platform;
160
+ const config = opts.config || loadShellConfig(opts);
161
+ const requested = normalizeShellKind(opts.requested);
162
+ let kind =
163
+ requested ||
164
+ (platform === "win32" ? config.windowsDefault : null) ||
165
+ "default";
166
+ let note;
167
+
168
+ if (kind === "cmd") {
169
+ if (platform === "win32") {
170
+ // cmd IS the Windows default shell — take the untouched default path so
171
+ // an explicit `shell:"cmd"` opt-out of a PowerShell windowsDefault stays
172
+ // byte-identical to the historical behavior.
173
+ return { kind: "cmd", useDefaultShell: true };
174
+ }
175
+ note = "cmd is Windows-only; using the platform default shell";
176
+ return { kind: "default", useDefaultShell: true, note };
177
+ }
178
+ if (kind === "powershell" && platform !== "win32") {
179
+ // Windows PowerShell 5.1 doesn't exist off-Windows; pwsh might.
180
+ note =
181
+ "powershell (5.1) is Windows-only; using the platform default shell (request pwsh for PowerShell 7)";
182
+ return { kind: "default", useDefaultShell: true, note };
183
+ }
184
+ if (kind === "powershell" || kind === "pwsh") {
185
+ const { file, argv } = buildPowershellArgv(opts.command, kind, {
186
+ executionPolicy: config.executionPolicy,
187
+ platform,
188
+ });
189
+ return { kind, useDefaultShell: false, file, argv };
190
+ }
191
+ return { kind: "default", useDefaultShell: true };
192
+ }
193
+
194
+ module.exports = {
195
+ SHELL_KINDS,
196
+ EXECUTION_POLICIES,
197
+ normalizeShellKind,
198
+ normalizeExecutionPolicy,
199
+ loadShellConfig,
200
+ buildPowershellArgv,
201
+ resolveShellInvocation,
202
+ _clearShellConfigCache,
203
+ };
@@ -68,6 +68,19 @@ export function bundledSkillsDisabled(opts = {}) {
68
68
  }
69
69
  }
70
70
 
71
+ /**
72
+ * Whether ALL skill layers are disabled — `cc agent --bare` sets CC_SKILLS=0
73
+ * (safe-mode "feature=0 disables" convention). Single gate: `loadAll` is the
74
+ * one place every layer funnels through, so this empties list_skills /
75
+ * run_skill / persona auto-activation in one check.
76
+ * @param {{ env?: object }} [opts]
77
+ * @returns {boolean}
78
+ */
79
+ export function allSkillsDisabled(opts = {}) {
80
+ const raw = (opts.env || process.env).CC_SKILLS;
81
+ return raw != null && /^(0|false|no|off)$/i.test(String(raw).trim());
82
+ }
83
+
71
84
  /** Layer names in priority order (lowest → highest) */
72
85
  export const LAYER_NAMES = [
73
86
  "bundled",
@@ -475,6 +488,12 @@ export class CLISkillLoader {
475
488
  * @returns {object[]} Resolved skill list
476
489
  */
477
490
  loadAll(options = {}) {
491
+ // `cc agent --bare` (CC_SKILLS=0): no skill layer loads at all — cache the
492
+ // empty result so getResolvedSkills stays cheap on every call.
493
+ if (allSkillsDisabled(options)) {
494
+ this._cache = [];
495
+ return this._cache;
496
+ }
478
497
  const layers = this.getLayerPaths();
479
498
  const skillMap = new Map();
480
499
  // Claude-Code `disableBundledSkills`: hide the built-in layer so only
@@ -0,0 +1,34 @@
1
+ /**
2
+ * `cc agent --disable-slash-commands` — REPL slash-dispatch bypass predicate.
3
+ *
4
+ * When active, a "/"-leading line is NOT dispatched to the built-in slash
5
+ * handlers (nor custom .claude/commands macros nor MCP prompt expansion): it
6
+ * reaches the model verbatim, the same fall-through an unmatched slash line
7
+ * already takes. `/exit` and `/quit` stay live so the session remains
8
+ * closable from the keyboard.
9
+ *
10
+ * The REPL swaps the dispatched string for a sentinel no handler can match
11
+ * (leading NUL — nothing in the dispatch chain starts with a control char)
12
+ * and restores the user's real text for the model turn.
13
+ */
14
+
15
+ /** The dispatch sentinel — starts with NUL so no handler branch matches. */
16
+ export function slashBypassSentinel() {
17
+ return String.fromCharCode(0) + "slash-bypassed";
18
+ }
19
+
20
+ /**
21
+ * Should this line bypass slash dispatch?
22
+ * @param {string} line the trimmed input line
23
+ * @param {boolean} disabled whether --disable-slash-commands is active
24
+ * @returns {boolean}
25
+ */
26
+ export function slashDispatchBypassed(line, disabled) {
27
+ return (
28
+ disabled === true &&
29
+ typeof line === "string" &&
30
+ line.startsWith("/") &&
31
+ line !== "/exit" &&
32
+ line !== "/quit"
33
+ );
34
+ }
@@ -1262,6 +1262,24 @@ export async function startAgentRepl(options = {}) {
1262
1262
  options.vimMode === true || process.env.CC_VIM === "1" ? true : false;
1263
1263
  let _vim = null;
1264
1264
 
1265
+ // --disable-slash-commands (Claude-Code parity): built-in slash dispatch,
1266
+ // custom command macros and MCP prompt expansion are all bypassed — a
1267
+ // "/"-leading line reaches the model verbatim, the same fall-through an
1268
+ // unmatched slash line already takes. /exit and /quit stay live so the
1269
+ // session remains closable from the keyboard.
1270
+ const _slashCommandsDisabled =
1271
+ options.disableSlashCommands === true ||
1272
+ /^(1|true|yes|on)$/i.test(
1273
+ String(process.env.CC_DISABLE_SLASH_COMMANDS || "").trim(),
1274
+ );
1275
+
1276
+ // --ax-screen-reader / CC_SCREEN_READER: force the mono theme (no ANSI
1277
+ // color codes) so output linearizes cleanly. The repainting status line is
1278
+ // already killed by the CC_STATUSLINE=0 switch agent.js applies.
1279
+ const _screenReaderMode = /^(1|true|yes|on)$/i.test(
1280
+ String(process.env.CC_SCREEN_READER || "").trim(),
1281
+ );
1282
+
1265
1283
  // Color theme (Claude-Code `/theme` parity). Capture chalk's auto-detected
1266
1284
  // level BEFORE any theme touches it so `mono`→level 0 stays reversible, then
1267
1285
  // apply the persisted theme (config `cli.theme`). `mono` strips all color;
@@ -1282,6 +1300,10 @@ export async function startAgentRepl(options = {}) {
1282
1300
  } catch (_e) {
1283
1301
  /* config optional — keep default */
1284
1302
  }
1303
+ // Screen-reader mode overrides the persisted theme: colors are invisible to
1304
+ // a reader and ANSI codes are noise, so run mono for this session (the
1305
+ // stored cli.theme is untouched — /theme still shows and can change it).
1306
+ if (_screenReaderMode) _theme = "mono";
1285
1307
  applyThemeChalk(_theme, chalk, _chalkBaselineLevel);
1286
1308
  const themedPrompt = (text) => {
1287
1309
  const a = promptAccent(_theme);
@@ -1316,65 +1338,69 @@ export async function startAgentRepl(options = {}) {
1316
1338
  // cwd left unset on purpose: the completer resolves process.cwd() lazily
1317
1339
  // so it follows `/cd` mid-session.
1318
1340
  // Keep in sync with the rl.on("line") handlers + /help below.
1319
- slashCommands: [
1320
- "/add-dir",
1321
- "/agents",
1322
- "/auto",
1323
- "/btw",
1324
- "/cd",
1325
- "/clear",
1326
- "/compact",
1327
- "/config",
1328
- "/context",
1329
- "/copy",
1330
- "/cost",
1331
- "/cowork",
1332
- "/doctor",
1333
- "/effort",
1334
- "/exit",
1335
- "/export",
1336
- "/help",
1337
- "/hooks",
1338
- "/ide",
1339
- "/init",
1340
- "/mcp",
1341
- "/memory",
1342
- "/microcompact",
1343
- "/model",
1344
- "/output-style",
1345
- "/permissions",
1346
- "/plan",
1347
- "/pr-comments",
1348
- "/profile",
1349
- "/provider",
1350
- "/quit",
1351
- "/reindex",
1352
- "/release-notes",
1353
- "/reload-plugins",
1354
- "/reload-skills",
1355
- "/review",
1356
- "/rewind",
1357
- "/search",
1358
- "/session",
1359
- "/sessions",
1360
- "/stats",
1361
- "/status",
1362
- "/statusline",
1363
- "/sub-agents",
1364
- "/task",
1365
- "/tasks",
1366
- "/terminal-setup",
1367
- "/theme",
1368
- "/think",
1369
- "/todos",
1370
- "/ultrathink",
1371
- "/vim",
1372
- ],
1341
+ // --disable-slash-commands: nothing to complete — "/" input is plain text.
1342
+ slashCommands: _slashCommandsDisabled
1343
+ ? []
1344
+ : [
1345
+ "/add-dir",
1346
+ "/agents",
1347
+ "/auto",
1348
+ "/btw",
1349
+ "/cd",
1350
+ "/clear",
1351
+ "/compact",
1352
+ "/config",
1353
+ "/context",
1354
+ "/copy",
1355
+ "/cost",
1356
+ "/cowork",
1357
+ "/doctor",
1358
+ "/effort",
1359
+ "/exit",
1360
+ "/export",
1361
+ "/help",
1362
+ "/hooks",
1363
+ "/ide",
1364
+ "/init",
1365
+ "/mcp",
1366
+ "/memory",
1367
+ "/microcompact",
1368
+ "/model",
1369
+ "/output-style",
1370
+ "/permissions",
1371
+ "/plan",
1372
+ "/pr-comments",
1373
+ "/profile",
1374
+ "/provider",
1375
+ "/quit",
1376
+ "/reindex",
1377
+ "/release-notes",
1378
+ "/reload-plugins",
1379
+ "/reload-skills",
1380
+ "/review",
1381
+ "/rewind",
1382
+ "/search",
1383
+ "/session",
1384
+ "/sessions",
1385
+ "/stats",
1386
+ "/status",
1387
+ "/statusline",
1388
+ "/sub-agents",
1389
+ "/task",
1390
+ "/tasks",
1391
+ "/terminal-setup",
1392
+ "/theme",
1393
+ "/think",
1394
+ "/todos",
1395
+ "/ultrathink",
1396
+ "/vim",
1397
+ ],
1373
1398
  // User/project custom commands (.claude/commands/*.md) join TAB completion
1374
1399
  // alongside the built-ins above. Sync + best-effort; the completer
1375
1400
  // TTL-caches the result so this filesystem walk runs at most once per few
1376
1401
  // seconds, and process.cwd() makes it follow `/cd` mid-session.
1377
1402
  getDynamicSlashCommands: () => {
1403
+ if (_slashCommandsDisabled) return [];
1378
1404
  try {
1379
1405
  return discoverCommands(process.cwd()).map((cmd) => `/${cmd.name}`);
1380
1406
  } catch {
@@ -1719,7 +1745,20 @@ export async function startAgentRepl(options = {}) {
1719
1745
  _mlBuffer.length = 0;
1720
1746
  }
1721
1747
 
1722
- const trimmed = input.trim();
1748
+ const rawLine = input.trim();
1749
+ // --disable-slash-commands: hide "/"-leading input from every built-in
1750
+ // slash handler below by dispatching a sentinel no handler can match; the
1751
+ // turn tail restores the user's real text (promptText), so the line goes
1752
+ // to the model verbatim — the same fall-through an unmatched slash line
1753
+ // already takes. /exit and /quit stay live so the session can be closed.
1754
+ // Predicate + sentinel are pure (lib/slash-dispatch.js, unit-tested).
1755
+ const { slashDispatchBypassed, slashBypassSentinel } =
1756
+ await import("../lib/slash-dispatch.js");
1757
+ const _slashBypassed = slashDispatchBypassed(
1758
+ rawLine,
1759
+ _slashCommandsDisabled,
1760
+ );
1761
+ const trimmed = _slashBypassed ? slashBypassSentinel() : rawLine;
1723
1762
  if (!trimmed) {
1724
1763
  prompt();
1725
1764
  return;
@@ -4086,13 +4125,15 @@ export async function startAgentRepl(options = {}) {
4086
4125
  // parity. resolveSlashMacro maps a leading /name to a command macro and
4087
4126
  // expands its template; a non-match returns the line unchanged so a literal
4088
4127
  // prompt like "/etc/hosts" still reaches the LLM. Wire is unit-tested.
4089
- let promptText = trimmed;
4128
+ // Under --disable-slash-commands the sentinel never reached a handler;
4129
+ // restore the user's real text so the model sees it verbatim.
4130
+ let promptText = _slashBypassed ? rawLine : trimmed;
4090
4131
 
4091
4132
  // MCP server-provided prompts (Claude-Code parity): `/mcp__<server>__<name>
4092
4133
  // [json-args]` fetches a rendered prompt template from the connected MCP
4093
4134
  // server and uses its text as this turn's input. Falls through unchanged
4094
4135
  // when the line isn't an MCP prompt command.
4095
- if (promptText.startsWith("/mcp__")) {
4136
+ if (!_slashBypassed && promptText.startsWith("/mcp__")) {
4096
4137
  try {
4097
4138
  const expanded = await expandMcpPrompt(
4098
4139
  promptText,
@@ -4114,7 +4155,7 @@ export async function startAgentRepl(options = {}) {
4114
4155
  // `/pr-comments [<n>|<url>] [--repo owner/name]` (Claude-Code parity):
4115
4156
  // fetch a GitHub PR's reviews + conversation + inline comments via `gh` and
4116
4157
  // feed them as this turn's input so the agent can address the feedback.
4117
- if (promptText.startsWith("/pr-comments")) {
4158
+ if (!_slashBypassed && promptText.startsWith("/pr-comments")) {
4118
4159
  try {
4119
4160
  const { expandPrComments } = await import("./pr-comments.js");
4120
4161
  const res = await expandPrComments(promptText);
@@ -1041,6 +1041,7 @@ async function tryIdeDiffApprovalForEdit(
1041
1041
  hasIdeOpenDiff,
1042
1042
  requestIdeDiffApproval,
1043
1043
  formatReviewComments,
1044
+ summarizeUserAmendments,
1044
1045
  } = await import("../lib/ide-context.js");
1045
1046
  const mcpLike = {
1046
1047
  mcpClient: context.mcpClient,
@@ -1056,15 +1057,22 @@ async function tryIdeDiffApprovalForEdit(
1056
1057
  title: `cc agent: ${name} ${path.basename(proposal.filePath)}`,
1057
1058
  });
1058
1059
  if (verdict?.outcome === "accepted") {
1060
+ // When the reviewer amended the proposal in the diff before accepting,
1061
+ // hand the agent the actual -/+ delta — not just a flag — so its model
1062
+ // of the file matches what was really written (gap #4: the agent
1063
+ // perceives the user's edits).
1064
+ const amendments =
1065
+ verdict.finalText != null && verdict.finalText !== proposal.newContent
1066
+ ? summarizeUserAmendments(proposal.newContent, verdict.finalText)
1067
+ : null;
1059
1068
  return {
1060
1069
  outcome: "accepted",
1061
1070
  result: {
1062
1071
  success: true,
1063
1072
  path: proposal.filePath,
1064
1073
  appliedVia: "ide-diff",
1065
- ...(verdict.finalText != null &&
1066
- verdict.finalText !== proposal.newContent
1067
- ? { userEdited: true }
1074
+ ...(amendments
1075
+ ? { userEdited: true, userAmendments: amendments }
1068
1076
  : {}),
1069
1077
  policy: { decision: "allow", rule, via: "ide-diff" },
1070
1078
  },
@@ -2431,6 +2439,24 @@ async function executeToolInner(
2431
2439
  }
2432
2440
  }
2433
2441
 
2442
+ // P1 #8 Windows/PowerShell first-class: per-call args.shell or the
2443
+ // configured settings `shell.windowsDefault` may route this command
2444
+ // through PowerShell via explicit argv (`powershell.exe -NoProfile
2445
+ // [-ExecutionPolicy <p>] -Command <cmd>`). The unconfigured path keeps
2446
+ // the historical default shell byte-identical (useDefaultShell=true).
2447
+ const { resolveShellInvocation } =
2448
+ await import("../lib/shell-selector.cjs");
2449
+ const shellInv = resolveShellInvocation({
2450
+ command: args.command,
2451
+ requested: args.shell,
2452
+ cwd: args.cwd || cwd,
2453
+ });
2454
+ const shellMeta = shellInv.useDefaultShell
2455
+ ? shellInv.note
2456
+ ? { shell_note: shellInv.note }
2457
+ : {}
2458
+ : { shell: shellInv.kind };
2459
+
2434
2460
  // Background: spawn, register, return a task_id immediately. The agent
2435
2461
  // polls output + completion via check_shell. No timeout — that's the whole
2436
2462
  // point of backgrounding (builds, test suites, dev servers).
@@ -2463,9 +2489,9 @@ async function executeToolInner(
2463
2489
  child: null,
2464
2490
  };
2465
2491
  try {
2466
- const child = spawn(args.command, {
2492
+ const bgSpawnOpts = {
2467
2493
  cwd: task.cwd,
2468
- shell: true,
2494
+ shell: shellInv.useDefaultShell,
2469
2495
  windowsHide: true,
2470
2496
  // Same agent-identity env as the foreground path: CLAUDECODE marks
2471
2497
  // "running under the agent"; the session id correlates work to the
@@ -2484,7 +2510,12 @@ async function executeToolInner(
2484
2510
  // the whole tree (shell + its grandchild command). No-op on Windows
2485
2511
  // where the tree is killed via taskkill /T instead.
2486
2512
  detached: process.platform !== "win32",
2487
- });
2513
+ };
2514
+ // PowerShell route: explicit argv (shell:false above); default route
2515
+ // is the historical spawn(command, {shell:true}) byte-for-byte.
2516
+ const child = shellInv.useDefaultShell
2517
+ ? spawn(args.command, bgSpawnOpts)
2518
+ : spawn(shellInv.file, shellInv.argv, bgSpawnOpts);
2488
2519
  task.child = child;
2489
2520
  if (child.stdout) {
2490
2521
  child.stdout.setEncoding("utf8");
@@ -2532,6 +2563,7 @@ async function executeToolInner(
2532
2563
  task_id: id,
2533
2564
  status: task.status,
2534
2565
  command: task.command,
2566
+ ...shellMeta,
2535
2567
  hint: "Poll output and completion with the check_shell tool using this task_id. Kill long-lived servers with check_shell { task_id, kill: true } when done.",
2536
2568
  shellCommandPolicy: shellPolicy,
2537
2569
  approval: approvalOutcome,
@@ -2630,7 +2662,7 @@ async function executeToolInner(
2630
2662
  }
2631
2663
 
2632
2664
  try {
2633
- const output = execSync(args.command, {
2665
+ const fgExecOpts = {
2634
2666
  cwd: args.cwd || cwd,
2635
2667
  encoding: "utf8",
2636
2668
  timeout: _resolveShellTimeout(args.timeout),
@@ -2649,10 +2681,35 @@ async function executeToolInner(
2649
2681
  }
2650
2682
  : {}),
2651
2683
  },
2652
- });
2684
+ };
2685
+ let output;
2686
+ if (shellInv.useDefaultShell) {
2687
+ output = execSync(args.command, fgExecOpts);
2688
+ } else {
2689
+ // PowerShell route: explicit argv, no intermediate default shell.
2690
+ // Reproduce execSync's contract so the shared catch shapes errors
2691
+ // identically: throw on spawn error; throw an Error carrying
2692
+ // status/stdout/stderr on non-zero exit.
2693
+ const res = spawnSync(shellInv.file, shellInv.argv, {
2694
+ ...fgExecOpts,
2695
+ windowsHide: true,
2696
+ });
2697
+ if (res.error) throw res.error;
2698
+ if (res.status !== 0) {
2699
+ const e = new Error(
2700
+ `Command failed (${shellInv.kind}, exit ${res.status}): ${args.command}`,
2701
+ );
2702
+ e.status = res.status;
2703
+ e.stdout = res.stdout;
2704
+ e.stderr = res.stderr;
2705
+ throw e;
2706
+ }
2707
+ output = res.stdout ?? "";
2708
+ }
2653
2709
  return attachDescriptor(
2654
2710
  {
2655
2711
  stdout: output.substring(0, 30000),
2712
+ ...shellMeta,
2656
2713
  shellCommandPolicy: shellPolicy,
2657
2714
  approval: approvalOutcome,
2658
2715
  },
@@ -2672,6 +2729,7 @@ async function executeToolInner(
2672
2729
  : {}),
2673
2730
  stderr: (err.stderr || "").substring(0, 2000),
2674
2731
  exitCode: err.status,
2732
+ ...shellMeta,
2675
2733
  shellCommandPolicy: shellPolicy,
2676
2734
  approval: approvalOutcome,
2677
2735
  },
@@ -3104,6 +3162,31 @@ async function executeToolInner(
3104
3162
  }
3105
3163
  }
3106
3164
 
3165
+ case "publish_artifact": {
3166
+ // P1 #10: copy a finished deliverable into the user's artifact store.
3167
+ // Only METADATA returns to the conversation (the transcript never
3168
+ // carries the file body); `cc artifacts` lists/inspects/cleans.
3169
+ try {
3170
+ const { ArtifactStore } = await import("../lib/artifact-store.js");
3171
+ const store = new ArtifactStore();
3172
+ const entry = store.publish({
3173
+ filePath: path.resolve(cwd, String(args.path || "")),
3174
+ title: args.title,
3175
+ kind: args.kind,
3176
+ ttlDays: args.ttl_days,
3177
+ sessionId: sessionId ? String(sessionId) : null,
3178
+ });
3179
+ return attachDescriptor({
3180
+ published: entry,
3181
+ hint: "The user can browse this with `cc artifacts list` / `cc artifacts show <id>`.",
3182
+ });
3183
+ } catch (err) {
3184
+ return attachDescriptor({
3185
+ error: `publish_artifact failed: ${err.message}`,
3186
+ });
3187
+ }
3188
+ }
3189
+
3107
3190
  case "search_files": {
3108
3191
  // An explicit directory scopes the search to one root; otherwise span
3109
3192
  // cwd plus any --add-dir roots so cross-package searches find matches.
@@ -6438,6 +6521,8 @@ export function formatToolArgs(name, args) {
6438
6521
  return `"${(args.query || "").substring(0, 60)}"`;
6439
6522
  case "notify":
6440
6523
  return `${args.level || "info"}: ${(args.title || "").substring(0, 50)}`;
6524
+ case "publish_artifact":
6525
+ return `${args.kind || "other"}: ${(args.title || args.path || "").substring(0, 60)}`;
6441
6526
  case "schedule":
6442
6527
  return args.action === "cron"
6443
6528
  ? `cron ${args.cron || ""}`.trim()