chainlesschain 0.162.155 → 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 (194) 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-Cth_zZeA.js → AIOps-CfMP2Oud.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-zfwA3Oxn.js → ActionButton-Zu0N8BQL.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-FeKc4aU2.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-C2gRq5Qi.js → Audit-BMfiLj7T.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-TeTIFiW-.js → BackgroundAgents-BslPCdGF.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-CLJk3k6i.js → Backup-CQ_fMC1r.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-5ms537NY.js → BaseInput-CsFsARiC.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-4Nr5U9PJ.js → Chat-CwfBobco.js} +6 -6
  15. package/src/assets/web-panel/assets/ChatBubbleRenderer-K6Nf3xeN.js +1 -0
  16. package/src/assets/web-panel/assets/{Checkbox-B2Bca5ev.js → Checkbox-DkSyjjPp.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-D6Uysdbv.js → Codegen-B7Fx3zZ6.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-B3XQ8xc7.js → Col-Dzfqn8Ep.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-CqwKtoPz.js → Community-DhHPdzMM.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-BZZINTus.js → Compact-Ct18_8y7.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-Dr3BXkWA.js → Compliance-CY64IqBi.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-rx_x9Wqc.js → Cowork-BYSmWlUl.js} +3 -3
  23. package/src/assets/web-panel/assets/{Cron-BpZr7rHc.js → Cron-_4xtP3Ug.js} +2 -2
  24. package/src/assets/web-panel/assets/{Crosschain-CeV5EYVg.js → Crosschain-B4qY-Ycr.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-fe2Qaowz.js → DID-CkCzWkx1.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dashboard-Fh_EdWVa.js → Dashboard-C1scT7Ke.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-N1PIwWYf.js → Dropdown-DBnQ2EWd.js} +1 -1
  28. package/src/assets/web-panel/assets/{EmailListRenderer-B6vK93Ed.js → EmailListRenderer-C9JhOTq0.js} +1 -1
  29. package/src/assets/web-panel/assets/{FamilyGuardDashboard-e3iUZhAg.js → FamilyGuardDashboard-CWXv5RUv.js} +1 -1
  30. package/src/assets/web-panel/assets/{Federation-uInc9YcN.js → Federation-fYdcbz9W.js} +1 -1
  31. package/src/assets/web-panel/assets/{FormItemContext-DmQZSX0z.js → FormItemContext-CJqOT3Yj.js} +1 -1
  32. package/src/assets/web-panel/assets/{GenericCardRenderer-CV_Z8OAo.js → GenericCardRenderer-DGCsHc5K.js} +1 -1
  33. package/src/assets/web-panel/assets/{Git-jXAhNN8d.js → Git-EH2K-H0Q.js} +2 -2
  34. package/src/assets/web-panel/assets/{Governance-Blw1esBB.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-5xrglMTK.js → KnowledgeGraph-QIur8j8-.js} +1 -1
  37. package/src/assets/web-panel/assets/{Logs-BGIG9798.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-7U8tEhwK.js → McpTools-D2xRbfJx.js} +4 -4
  40. package/src/assets/web-panel/assets/{Memory-BrvboPLE.js → Memory-C6RbVHAo.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-nzcd7d1H.js → MobileBridge-BPtVBKqn.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-DETVNKa7.js → MobileProjects-Cuw832cr.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-SZcCYec3.js → Mtc-D6O9fWFa.js} +4 -4
  44. package/src/assets/web-panel/assets/{MtcAudit-Cx_d3N17.js → MtcAudit-CWZcXIH1.js} +5 -5
  45. package/src/assets/web-panel/assets/{Multisig-DtcmSUo-.js → Multisig-DhIBxTFd.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-Dgx2QHl-.js → NLProgramming-wkBFS33e.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-oHTGEHoJ.js → Notes-B09FxncA.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-qKD-IlOl.js → NotificationSettings-oQwtH6-9.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-CiU4Sy1J.js → OrderTableRenderer-iFKeNf6Y.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-BBNXnltP.js → Organization-CsF-NIjz.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-Df2fDbvT.js → Overflow-bLxv9Dqi.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-DUHj6zjO.js → P2P-DVwQtm4q.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-B2nWgdoV.js → PdhVaultBrowser-Bgic3QKm.js} +3 -3
  54. package/src/assets/web-panel/assets/{Permissions-CiW0afny.js → Permissions-as80-9ep.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-cc1mHU_D.js → PersonalDataHub-BGmUw36a.js} +2 -2
  56. package/src/assets/web-panel/assets/{Pipeline-B93zTapU.js → Pipeline-BM96zBxG.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-Db1jXWfd.js → Privacy-BBOzBSWe.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-DFoZ1h9U.js → ProjectInit-GHX41V28.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-BkkUnmYs.js → ProjectSettings-DraqzxKD.js} +2 -2
  60. package/src/assets/web-panel/assets/{Projects-Xa779XLC.js → Projects-BzzdUml4.js} +1 -1
  61. package/src/assets/web-panel/assets/{Providers-DOF01IEN.js → Providers-CSeYNAtW.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-TohRsKd0.js → QrScannerModal-DlzBCdXh.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-DjLcr1ZU.js → QuickAsk-p8iB7od_.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend--Ism6bMx.js → Recommend-DjOVXacd.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-B8CL3YHM.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-BioxZBBH.js → Row-tw8m6T0c.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-B2LipwH3.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-TibHuJWP.js → Security-JmtL8g7D.js} +3 -3
  71. package/src/assets/web-panel/assets/{Services-DtkuWseW.js → Services-BxrNqjin.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-BIcPQae6.js → Skeleton-udk8L18t.js} +1 -1
  73. package/src/assets/web-panel/assets/Skills-DooLT9of.js +1 -0
  74. package/src/assets/web-panel/assets/{Sla-uUSU185m.js → Sla-ITVZE1xI.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-B7Bgc9mk.js → SpeechSettings-DCkfqY1n.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-K-XrJ2s9.js → SyncSettings-CkNlgFjM.js} +2 -2
  77. package/src/assets/web-panel/assets/Tasks-BMAoyCM_.js +1 -0
  78. package/src/assets/web-panel/assets/{Templates-CaFnyGDK.js → Templates-BnrFTlrz.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-C2X0PtEC.js → Tenant-BjNsy3-i.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-CKU5yVsT.js → Terminal-C0gxLL7V.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-8trZeYDC.js → TimelineRenderer-CUtj_KHA.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-BUEEe9US.js → Tokens-DOg61zlq.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-COU97I7Y.js → Trigger-D4ldCewj.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-DNWQChaP.js → Trust-3lk_Zd8j.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-DWX_VtCp.js → UkeySign-kWhFR6Li.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-D9eIDhsp.js → VideoEditing-D3GOFVWg.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-BOnCCgIL.js → Wallet-DzrZfBuh.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-DN4dD-W8.js → WebAuthn-CyVJVp_g.js} +5 -5
  89. package/src/assets/web-panel/assets/{WorkflowEditor-ClvDb_jX.js → WorkflowEditor-i_b0wi9b.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-DIxQ_Eeb.js → chat-DwPCK8I7.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-BOX3WKcP.js → colors-D0vaVqZU.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-Bny82VDi.js → compact-item-Don7nGW2.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-Cnoy3enS.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-BJ7fkx3F.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-CZ6CHiCQ.js → index-4MkfPLGv.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-7GLXvG8p.js → index-7QUwcGTg.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-C1nHEOFE.js → index-B57al0Z5.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-Kio1ekYg.js → index-BER98o4P.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-D9QEc7Tq.js → index-BNpJf-sf.js} +1 -1
  102. package/src/assets/web-panel/assets/{index--CcGiwAI.js → index-BZ1N07Sp.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BWPey0Uf.js → index-BlfTf0ka.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-B7Fah_BX.js → index-BohrJpBf.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BJ8-oLHI.js → index-Br3SOdER.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-D2qNdrNW.js → index-C0_sKVi7.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CQ-xLCVb.js → index-CAdCTTBC.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DSRIjAWO.js → index-CExWtm98.js} +1 -1
  109. package/src/assets/web-panel/assets/{index--Xb2lq6E.js → index-CL71rIwo.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BDkN6YOI.js → index-CfYRv-zA.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Bk5yST66.js → index-CnnxzIx0.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-2N9Y1cLx.js → index-CrnVEEQO.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CW6n-y1L.js → index-CzT56x-_.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-OaNFXV8N.js → index-D4b5XqIy.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CEPTiBWy.js → index-D84BnuFg.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CZCTan8Q.js → index-D8r7WKk-.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-rn6xd12s.js → index-DA0-EJKx.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BcmXbIwv.js → index-DBzBLX-K.js} +3 -3
  119. package/src/assets/web-panel/assets/{index-C--PPqwi.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-Ty4w18mR.js → index-DMT8n9vB.js} +4 -4
  122. package/src/assets/web-panel/assets/{index-qOBKrw-g.js → index-DQor_7NF.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CN6emwbq.js → index-DT40zd7K.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BM0mQhVv.js → index-De85D9Pc.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-ceH9DsAi.js → index-IGGttsCv.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DBhkJqVF.js → index-L73NrmlW.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BbZynivw.js → index-LMo9bBLJ.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Bvheiep2.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-DxX8hhmL.js → index-WRw1pM5E.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-B6WoW8DJ.js → index-YKLCotoo.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BP7HFsAK.js → index-hjgrIX5I.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-CnjtAKdw.js → index-m9bn0F1p.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-CEq5Vh0i.js → index-wesQQ2cH.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-B1-h2QrC.js → index-x7VYlV41.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-BmxMBuUZ.js → initDefaultProps-DGeU4vPS.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion-DnXNwo2k.js → motion-Dgqj9ab3.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-WnIJwmZN.js → move-BFyEU6Lr.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-DAto-c2w.js → mtc-parser-CueCXXPC.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-BtvW9W0H.js → omit-D2pU5OyE.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-DFqI9-_C.js → pickAttrs-CKTf6zD6.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-Dax6wOK3.js → placementArrow-DieJaGCN.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-BUFWWC83.js → responsiveObserve-Bxr-rmU6.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-DyARyuSz.js → slide-CDEDk3Se.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-BZSDugpi.js → statusUtils-icf2eCTx.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-B7o_K-4R.js → styleChecker-qKvgmBhE.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-Dg7Bg8VG.js → useFlexGapSupport-1_UhiyJZ.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-DQYXo5Rk.js → useFs-CFGLXDWh.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-E8NBAnkq.js → usePersonalDataHub-UafOXvQ6.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-DD71_w1b.js → vnode-BfPqSx79.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-zc9EMd3q.js → zoom-CXsVcRB6.js} +1 -1
  152. package/src/assets/web-panel/index.html +2 -2
  153. package/src/command-manifest.json +22 -1
  154. package/src/commands/agenda.js +228 -0
  155. package/src/commands/agent.js +45 -4
  156. package/src/commands/artifacts.js +161 -0
  157. package/src/commands/batch.js +345 -0
  158. package/src/commands/browse.js +204 -2
  159. package/src/data/changelog.json +13 -2
  160. package/src/gateways/ws/artifact-protocol.js +219 -0
  161. package/src/gateways/ws/message-dispatcher.js +6 -0
  162. package/src/gateways/ws/ws-server.js +32 -0
  163. package/src/index.js +18 -0
  164. package/src/lib/accessibility.js +55 -0
  165. package/src/lib/agent-batch.js +235 -0
  166. package/src/lib/agent-notify.js +66 -0
  167. package/src/lib/agent-schedule-store.js +330 -0
  168. package/src/lib/artifact-store.js +236 -0
  169. package/src/lib/chrome-connector.js +314 -0
  170. package/src/lib/hook-runner.cjs +47 -7
  171. package/src/lib/ide-context.js +84 -0
  172. package/src/lib/permission-rules.cjs +26 -3
  173. package/src/lib/plugin-runtime/scopes.js +12 -0
  174. package/src/lib/safe-mode.js +40 -0
  175. package/src/lib/shell-selector.cjs +203 -0
  176. package/src/lib/skill-loader.js +19 -0
  177. package/src/lib/slash-dispatch.js +34 -0
  178. package/src/repl/agent-repl.js +99 -58
  179. package/src/runtime/agent-core.js +197 -8
  180. package/src/runtime/coding-agent-contract-shared.cjs +156 -0
  181. package/src/runtime/coding-agent-policy.cjs +43 -0
  182. package/src/runtime/headless-stream.js +51 -1
  183. package/src/runtime/policies/agent-policy.js +1 -0
  184. package/src/assets/web-panel/assets/AppLayout-C6WIrDZk.js +0 -9
  185. package/src/assets/web-panel/assets/ChatBubbleRenderer-BFEnA8LN.js +0 -1
  186. package/src/assets/web-panel/assets/Inference-CoK8Ia0Z.js +0 -1
  187. package/src/assets/web-panel/assets/Marketplace-DFqhJMuY.js +0 -1
  188. package/src/assets/web-panel/assets/Reputation-CaRxB3Ep.js +0 -1
  189. package/src/assets/web-panel/assets/Search-BpjLBV8u.js +0 -1
  190. package/src/assets/web-panel/assets/Skills-DoTWIQOX.js +0 -1
  191. package/src/assets/web-panel/assets/Tasks-B3hEXNcI.js +0 -1
  192. package/src/assets/web-panel/assets/devWarning-Dr9dzlQ5.js +0 -1
  193. package/src/assets/web-panel/assets/index-B92M3t6B.js +0 -1
  194. package/src/assets/web-panel/assets/index-d2UcGpK-.js +0 -1
@@ -0,0 +1,314 @@
1
+ /**
2
+ * Chrome connector — attach to the USER'S real Chrome over the DevTools
3
+ * protocol (CDP) so page state (DOM / console / network / screenshot) can be
4
+ * captured WITH the user's login sessions, unlike `cc browse fetch/scrape`
5
+ * which cold-launches a blank headless browser.
6
+ *
7
+ * Three layers:
8
+ * discoverCdp() GET http://127.0.0.1:<port>/json/version — is a
9
+ * debuggable Chrome listening?
10
+ * launch helpers find the Chrome executable + build the argv to start
11
+ * one with --remote-debugging-port. By default a
12
+ * DEDICATED profile dir is used (~/.chainlesschain/
13
+ * chrome-profile) — sign in once there and the state
14
+ * persists. Reusing the DEFAULT profile is opt-in
15
+ * (defaultProfile: true) and requires every Chrome
16
+ * window to be closed first (Chrome refuses the debug
17
+ * port otherwise).
18
+ * captureState() playwright connectOverCDP → pick a tab → observe for
19
+ * watchMs (console messages, failed/4xx-5xx network),
20
+ * snapshot url/title/DOM (capped), optional screenshot.
21
+ * Disconnects without killing the browser.
22
+ *
23
+ * SECURITY: the CDP port is an unauthenticated local control channel for the
24
+ * whole browser — anything on the machine can drive it while it is open.
25
+ * Loopback-only is enforced here; the docs tell users to close the connected
26
+ * Chrome when done. This is the same trade Claude-Code's Chrome connector
27
+ * makes.
28
+ */
29
+ import { spawn } from "child_process";
30
+ import fs from "fs";
31
+ import os from "os";
32
+ import path from "path";
33
+ import http from "http";
34
+
35
+ export const DEFAULT_CDP_PORT = 9222;
36
+ export const DEFAULT_DOM_CAP = 150000;
37
+
38
+ const _deps = {
39
+ fs,
40
+ spawn,
41
+ homedir: () => os.homedir(),
42
+ platform: () => process.platform,
43
+ env: () => process.env,
44
+ httpGet,
45
+ importPlaywright: () => import("playwright"),
46
+ };
47
+
48
+ function httpGet(url, timeoutMs = 3000) {
49
+ return new Promise((resolve) => {
50
+ const req = http.get(url, { timeout: timeoutMs }, (res) => {
51
+ let data = "";
52
+ res.on("data", (d) => (data += d));
53
+ res.on("end", () => resolve({ status: res.statusCode, body: data }));
54
+ });
55
+ req.on("timeout", () => {
56
+ req.destroy();
57
+ resolve({ status: 0, body: "" });
58
+ });
59
+ req.on("error", () => resolve({ status: 0, body: "" }));
60
+ });
61
+ }
62
+
63
+ /** Is a debuggable Chrome listening on the port? → {ok, browser?, wsUrl?} */
64
+ export async function discoverCdp({
65
+ port = DEFAULT_CDP_PORT,
66
+ deps = _deps,
67
+ } = {}) {
68
+ const res = await deps.httpGet(`http://127.0.0.1:${port}/json/version`);
69
+ if (res.status !== 200) return { ok: false, port };
70
+ try {
71
+ const info = JSON.parse(res.body);
72
+ return {
73
+ ok: true,
74
+ port,
75
+ browser: info.Browser || "",
76
+ wsUrl: info.webSocketDebuggerUrl || "",
77
+ };
78
+ } catch {
79
+ return { ok: false, port };
80
+ }
81
+ }
82
+
83
+ /** Well-known Chrome/Edge/Chromium install paths per platform. */
84
+ export function chromeCandidates({ deps = _deps } = {}) {
85
+ const env = deps.env();
86
+ const custom = env.CHROME_PATH ? [env.CHROME_PATH] : [];
87
+ if (deps.platform() === "win32") {
88
+ const bases = [
89
+ env["PROGRAMFILES"],
90
+ env["PROGRAMFILES(X86)"],
91
+ env["LOCALAPPDATA"],
92
+ ].filter(Boolean);
93
+ // path.win32, not host path: platform() is injectable, so this branch
94
+ // must build Windows paths even when the host (e.g. Linux CI) is not.
95
+ return [
96
+ ...custom,
97
+ ...bases.map((b) =>
98
+ path.win32.join(b, "Google", "Chrome", "Application", "chrome.exe"),
99
+ ),
100
+ ...bases.map((b) =>
101
+ path.win32.join(b, "Microsoft", "Edge", "Application", "msedge.exe"),
102
+ ),
103
+ ];
104
+ }
105
+ if (deps.platform() === "darwin") {
106
+ return [
107
+ ...custom,
108
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
109
+ "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
110
+ ];
111
+ }
112
+ return [
113
+ ...custom,
114
+ "/usr/bin/google-chrome",
115
+ "/usr/bin/google-chrome-stable",
116
+ "/usr/bin/chromium",
117
+ "/usr/bin/chromium-browser",
118
+ "/usr/bin/microsoft-edge",
119
+ ];
120
+ }
121
+
122
+ /** First existing Chrome-family executable, or null. */
123
+ export function findChromeExecutable({ deps = _deps } = {}) {
124
+ for (const candidate of chromeCandidates({ deps })) {
125
+ try {
126
+ if (deps.fs.existsSync(candidate)) return candidate;
127
+ } catch {
128
+ /* inaccessible — keep looking */
129
+ }
130
+ }
131
+ return null;
132
+ }
133
+
134
+ /** The dedicated connector profile dir (login once there; state persists). */
135
+ export function connectorProfileDir({ deps = _deps } = {}) {
136
+ return path.join(deps.homedir(), ".chainlesschain", "chrome-profile");
137
+ }
138
+
139
+ /**
140
+ * Argv for launching a debuggable Chrome. Dedicated profile by default;
141
+ * `defaultProfile: true` drops --user-data-dir to reuse the user's real
142
+ * profile (requires ALL Chrome windows closed — callers must check
143
+ * discoverCdp/refusal and say so).
144
+ */
145
+ export function buildChromeLaunchArgs({
146
+ port = DEFAULT_CDP_PORT,
147
+ url,
148
+ defaultProfile = false,
149
+ profileDir,
150
+ deps = _deps,
151
+ } = {}) {
152
+ const args = [
153
+ `--remote-debugging-port=${port}`,
154
+ // No first-run wizards in the dedicated profile.
155
+ "--no-first-run",
156
+ "--no-default-browser-check",
157
+ ];
158
+ if (!defaultProfile) {
159
+ args.push(`--user-data-dir=${profileDir || connectorProfileDir({ deps })}`);
160
+ }
161
+ if (url) args.push(String(url));
162
+ return args;
163
+ }
164
+
165
+ /** Spawn the (detached) debuggable Chrome. Returns {ok, executable, args, error?}. */
166
+ export function launchChrome(opts = {}) {
167
+ const deps = opts.deps || _deps;
168
+ const executable = opts.executable || findChromeExecutable({ deps });
169
+ if (!executable) {
170
+ return {
171
+ ok: false,
172
+ error:
173
+ "no Chrome/Edge executable found — install Chrome or set CHROME_PATH",
174
+ };
175
+ }
176
+ const args = buildChromeLaunchArgs({ ...opts, deps });
177
+ try {
178
+ const child = deps.spawn(executable, args, {
179
+ detached: true,
180
+ stdio: "ignore",
181
+ windowsHide: false,
182
+ });
183
+ child.unref();
184
+ return { ok: true, executable, args, pid: child.pid };
185
+ } catch (err) {
186
+ return { ok: false, executable, args, error: err.message };
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Attach over CDP, observe the chosen tab for `watchMs`, and return its
192
+ * state: {url, title, tabs, console[], network[], html, htmlTruncated,
193
+ * screenshotPath?}. Console/network are collected FROM ATTACH TIME (CDP has
194
+ * no retroactive console history) — reload or interact during the watch
195
+ * window to capture activity. Disconnects without killing the browser.
196
+ */
197
+ export async function captureState({
198
+ port = DEFAULT_CDP_PORT,
199
+ tab = 0,
200
+ watchMs = 3000,
201
+ domCap = DEFAULT_DOM_CAP,
202
+ includeDom = true,
203
+ screenshotPath = null,
204
+ reload = false,
205
+ deps = _deps,
206
+ } = {}) {
207
+ let playwright;
208
+ try {
209
+ playwright = await deps.importPlaywright();
210
+ } catch {
211
+ return {
212
+ ok: false,
213
+ error: "playwright is not installed — npm install playwright",
214
+ };
215
+ }
216
+ let browser;
217
+ try {
218
+ browser = await playwright.chromium.connectOverCDP(
219
+ `http://127.0.0.1:${port}`,
220
+ );
221
+ } catch (err) {
222
+ return {
223
+ ok: false,
224
+ error:
225
+ `cannot attach to CDP on port ${port} (${err.message.split("\n")[0]}) — ` +
226
+ "launch a debuggable Chrome first (cc browse chrome launch)",
227
+ };
228
+ }
229
+ try {
230
+ // Right after connectOverCDP the page targets may not have attached yet
231
+ // (an attach immediately after `chrome launch` reliably hits this) —
232
+ // poll briefly before concluding the browser has no tabs.
233
+ let pages = [];
234
+ for (let i = 0; i < 20; i++) {
235
+ pages = browser.contexts().flatMap((c) => c.pages());
236
+ if (pages.length > 0) break;
237
+ await new Promise((r) => setTimeout(r, 150));
238
+ }
239
+ if (pages.length === 0) {
240
+ return { ok: false, error: "the connected Chrome has no open tabs" };
241
+ }
242
+ const index = Math.min(Math.max(0, Number(tab) || 0), pages.length - 1);
243
+ const page = pages[index];
244
+
245
+ const consoleEntries = [];
246
+ const networkEntries = [];
247
+ const onConsole = (m) => {
248
+ if (consoleEntries.length < 200) {
249
+ consoleEntries.push({ type: m.type(), text: m.text().slice(0, 500) });
250
+ }
251
+ };
252
+ const onRequestFailed = (r) => {
253
+ if (networkEntries.length < 200) {
254
+ networkEntries.push({
255
+ kind: "failed",
256
+ url: r.url().slice(0, 300),
257
+ error: r.failure()?.errorText || "",
258
+ });
259
+ }
260
+ };
261
+ const onResponse = (res) => {
262
+ if (res.status() >= 400 && networkEntries.length < 200) {
263
+ networkEntries.push({
264
+ kind: "http-error",
265
+ url: res.url().slice(0, 300),
266
+ status: res.status(),
267
+ });
268
+ }
269
+ };
270
+ page.on("console", onConsole);
271
+ page.on("requestfailed", onRequestFailed);
272
+ page.on("response", onResponse);
273
+ try {
274
+ if (reload) {
275
+ await page
276
+ .reload({ waitUntil: "domcontentloaded", timeout: watchMs + 15000 })
277
+ .catch(() => {});
278
+ }
279
+ await page.waitForTimeout(Math.max(0, Number(watchMs) || 0));
280
+
281
+ const state = {
282
+ ok: true,
283
+ port,
284
+ tab: index,
285
+ url: page.url(),
286
+ title: await page.title().catch(() => ""),
287
+ tabs: pages.map((p, i) => ({ index: i, url: p.url() })),
288
+ console: consoleEntries,
289
+ network: networkEntries,
290
+ };
291
+ if (includeDom) {
292
+ const html = await page.content().catch(() => "");
293
+ state.html = html.slice(0, domCap);
294
+ state.htmlTruncated = html.length > domCap;
295
+ }
296
+ if (screenshotPath) {
297
+ await page.screenshot({ path: screenshotPath }).catch((err) => {
298
+ state.screenshotError = err.message.split("\n")[0];
299
+ });
300
+ if (!state.screenshotError) state.screenshotPath = screenshotPath;
301
+ }
302
+ return state;
303
+ } finally {
304
+ page.off("console", onConsole);
305
+ page.off("requestfailed", onRequestFailed);
306
+ page.off("response", onResponse);
307
+ }
308
+ } finally {
309
+ // connectOverCDP close() disconnects the client; the browser lives on.
310
+ await browser.close().catch(() => {});
311
+ }
312
+ }
313
+
314
+ export { _deps };
@@ -21,6 +21,11 @@
21
21
  */
22
22
 
23
23
  const cpDefault = require("node:child_process");
24
+ const {
25
+ normalizeShellKind,
26
+ buildPowershellArgv,
27
+ loadShellConfig,
28
+ } = require("./shell-selector.cjs");
24
29
 
25
30
  const _deps = { spawnSync: cpDefault.spawnSync };
26
31
 
@@ -101,7 +106,12 @@ function tryParseDecision(stdout) {
101
106
  *
102
107
  * @param {string} command
103
108
  * @param {object} [input] the hook event payload (tool_name, tool_input, …)
104
- * @param {object} [opts] { timeout=60000, cwd, event }
109
+ * @param {object} [opts] { timeout=60000, cwd, event, shell }
110
+ * `shell: "powershell"|"pwsh"` runs the hook through
111
+ * PowerShell via explicit argv (P1 #8 — per-hook
112
+ * opt-in from the settings hook entry; the shell is
113
+ * taken literally, so a missing executable surfaces
114
+ * as the usual non-blocking spawn error)
105
115
  * @returns {{ decision:string, reason:string|null, exitCode:number|null,
106
116
  * stdout?:string, stderr?:string, additionalContext?:string,
107
117
  * nonBlockingError?:boolean, error?:string }}
@@ -121,20 +131,33 @@ function runCommandHook(command, input = {}, opts = {}) {
121
131
  if (!command) {
122
132
  return { decision: HOOK_DECISIONS.CONTINUE, reason: null, exitCode: 0 };
123
133
  }
134
+ const shellKind = normalizeShellKind(opts.shell);
135
+ const usePowershell = shellKind === "powershell" || shellKind === "pwsh";
124
136
  let res;
125
137
  try {
126
- res = _deps.spawnSync(command, {
138
+ const common = {
127
139
  input: JSON.stringify(input),
128
140
  cwd: cwd || process.cwd(),
129
141
  encoding: "utf-8",
130
142
  timeout,
131
- shell: true,
132
143
  maxBuffer: 8 * 1024 * 1024,
133
144
  env: {
134
145
  ...process.env,
135
146
  CLAUDE_HOOK_EVENT: event || input.hook_event_name || "",
136
147
  },
137
- });
148
+ };
149
+ if (usePowershell) {
150
+ // Per-hook `shell: powershell|pwsh` (settings hook entry): explicit argv
151
+ // instead of the default shell; the enterprise ExecutionPolicy override
152
+ // (settings shell.powershell.executionPolicy) applies here too.
153
+ const { executionPolicy } = loadShellConfig({
154
+ cwd: common.cwd,
155
+ });
156
+ const inv = buildPowershellArgv(command, shellKind, { executionPolicy });
157
+ res = _deps.spawnSync(inv.file, inv.argv, common);
158
+ } else {
159
+ res = _deps.spawnSync(command, { ...common, shell: true });
160
+ }
138
161
  } catch (err) {
139
162
  return {
140
163
  decision: HOOK_DECISIONS.CONTINUE,
@@ -210,13 +233,30 @@ function runHooks(commandHooks, input = {}, opts = {}) {
210
233
  const r = runCommandHook(h.command, input, {
211
234
  ...opts,
212
235
  timeout: h.timeout != null ? h.timeout * 1000 : opts.timeout,
236
+ // per-hook shell selection (P1 #8): `{ "type":"command", "command":…,
237
+ // "shell":"powershell" }` in the settings hook entry
238
+ shell: h.shell != null ? h.shell : opts.shell,
213
239
  });
214
240
  results.push({ command: h.command, ...r });
215
- if (r.decision === HOOK_DECISIONS.BLOCK || r.decision === HOOK_DECISIONS.ASK) {
216
- return { decision: r.decision, reason: r.reason, hook: h.command, results };
241
+ if (
242
+ r.decision === HOOK_DECISIONS.BLOCK ||
243
+ r.decision === HOOK_DECISIONS.ASK
244
+ ) {
245
+ return {
246
+ decision: r.decision,
247
+ reason: r.reason,
248
+ hook: h.command,
249
+ results,
250
+ };
217
251
  }
218
252
  }
219
253
  return { decision: HOOK_DECISIONS.CONTINUE, reason: null, results };
220
254
  }
221
255
 
222
- module.exports = { runCommandHook, runHooks, tryParseDecision, HOOK_DECISIONS, _deps };
256
+ module.exports = {
257
+ runCommandHook,
258
+ runHooks,
259
+ tryParseDecision,
260
+ HOOK_DECISIONS,
261
+ _deps,
262
+ };
@@ -17,6 +17,8 @@
17
17
  * disconnecting the IDE tools themselves.
18
18
  */
19
19
 
20
+ import { simpleDiff } from "./note-versioning.js";
21
+
20
22
  /** Hard cap on the selected text we inline into the prompt. */
21
23
  const SELECTION_TEXT_CAP = 2000;
22
24
  /** At most this many open-editor entries are listed. */
@@ -420,6 +422,88 @@ export function formatReviewComments(comments, { path: filePath } = {}) {
420
422
  return `${header}\n${lines.join("\n")}`;
421
423
  }
422
424
 
425
+ /**
426
+ * Render what the reviewer changed when they edited the proposal inside the
427
+ * IDE diff before accepting: a compact -/+ line summary of proposal → final,
428
+ * so the agent SEES the amendments instead of just a `userEdited` flag (its
429
+ * mental model of the file would otherwise be its own rejected proposal).
430
+ * Returns null when the texts are equal or not both strings. Pure.
431
+ */
432
+ export function summarizeUserAmendments(
433
+ proposedText,
434
+ finalText,
435
+ { maxLines = 30, maxChars = 2400 } = {},
436
+ ) {
437
+ if (typeof proposedText !== "string" || typeof finalText !== "string") {
438
+ return null;
439
+ }
440
+ if (proposedText === finalText) return null;
441
+ const a = proposedText.split("\n");
442
+ const b = finalText.split("\n");
443
+
444
+ // Common prefix/suffix trim — cheap, and bounds the LCS below.
445
+ let pre = 0;
446
+ while (pre < a.length && pre < b.length && a[pre] === b[pre]) pre++;
447
+ let endA = a.length;
448
+ let endB = b.length;
449
+ while (endA > pre && endB > pre && a[endA - 1] === b[endB - 1]) {
450
+ endA--;
451
+ endB--;
452
+ }
453
+ const midA = a.slice(pre, endA);
454
+ const midB = b.slice(pre, endB);
455
+
456
+ const out = [];
457
+ const MAX_DP_CELLS = 1_000_000; // simpleDiff is a full-matrix LCS — guard it
458
+ if (
459
+ midA.length === 0 ||
460
+ midB.length === 0 ||
461
+ (midA.length + 1) * (midB.length + 1) > MAX_DP_CELLS
462
+ ) {
463
+ // Pure insertion/deletion (an empty side would round-trip through
464
+ // join/split as one phantom "" line) or a pathological diff: one direct
465
+ // block (still tells the agent WHERE and WHAT).
466
+ out.push(` @ line ${pre + 1}:`);
467
+ for (const l of midA) out.push(` - ${l}`);
468
+ for (const l of midB) out.push(` + ${l}`);
469
+ } else {
470
+ let lineB = pre; // 1-based final-file line = lineB + 1
471
+ let inBlock = false;
472
+ for (const d of simpleDiff(midA.join("\n"), midB.join("\n"))) {
473
+ if (d.type === "same") {
474
+ inBlock = false;
475
+ lineB++;
476
+ continue;
477
+ }
478
+ if (!inBlock) {
479
+ out.push(` @ line ${lineB + 1}:`);
480
+ inBlock = true;
481
+ }
482
+ if (d.type === "remove") {
483
+ out.push(` - ${d.line}`);
484
+ } else {
485
+ out.push(` + ${d.line}`);
486
+ lineB++;
487
+ }
488
+ }
489
+ }
490
+
491
+ const header =
492
+ "The user edited the proposal in the IDE diff before accepting — the " +
493
+ "file was written with THEIR version. Their changes relative to your " +
494
+ "proposal (-, proposed → +, applied):";
495
+ let body = out;
496
+ if (out.length > maxLines) {
497
+ body = out.slice(0, maxLines);
498
+ body.push(` … (${out.length - maxLines} more lines changed)`);
499
+ }
500
+ let text = `${header}\n${body.join("\n")}`;
501
+ if (text.length > maxChars) {
502
+ text = `${text.slice(0, maxChars)}\n … (truncated)`;
503
+ }
504
+ return text;
505
+ }
506
+
423
507
  // ─── Explicit @selection / @diagnostics at-mentions (Claude-Code parity) ────
424
508
  //
425
509
  // The ambient `<ide-context>` block above shares the selection on every turn.
@@ -51,6 +51,12 @@ const DECISIONS = Object.freeze({
51
51
  */
52
52
  const TOOL_GROUPS = Object.freeze({
53
53
  bash: ["run_shell"],
54
+ // Claude-Code registers PowerShell as its own tool; cc runs every shell
55
+ // through run_shell (with an optional per-call `shell` arg), so
56
+ // `PowerShell(...)` rules map to the same family — a Claude-Code
57
+ // settings.json with PowerShell rules works unchanged. Shell-specific
58
+ // scoping is expressible as `PowerShell(shell:pwsh)` (param-rule form).
59
+ powershell: ["run_shell"],
54
60
  read: ["read_file", "list_dir"],
55
61
  grep: ["search_files"],
56
62
  glob: ["search_files"],
@@ -358,8 +364,23 @@ const SUGGEST_UMBRELLA = Object.freeze({
358
364
 
359
365
  /** Commands whose first token is a dispatcher — keep 2 tokens in the prefix. */
360
366
  const MULTI_VERB = new Set([
361
- "git", "npm", "npx", "yarn", "pnpm", "docker", "kubectl", "cargo",
362
- "go", "pip", "pip3", "python", "python3", "node", "dotnet", "gh", "brew",
367
+ "git",
368
+ "npm",
369
+ "npx",
370
+ "yarn",
371
+ "pnpm",
372
+ "docker",
373
+ "kubectl",
374
+ "cargo",
375
+ "go",
376
+ "pip",
377
+ "pip3",
378
+ "python",
379
+ "python3",
380
+ "node",
381
+ "dotnet",
382
+ "gh",
383
+ "brew",
363
384
  ]);
364
385
 
365
386
  /**
@@ -382,7 +403,9 @@ function suggestAllowRule(tool, args = {}) {
382
403
  const raw = args.path || args.file_path || args.dir || args.directory || "";
383
404
  if (!raw) return umbrella;
384
405
  const slash = String(raw).replace(/\\/g, "/");
385
- const dir = slash.includes("/") ? slash.slice(0, slash.lastIndexOf("/")) : ".";
406
+ const dir = slash.includes("/")
407
+ ? slash.slice(0, slash.lastIndexOf("/"))
408
+ : ".";
386
409
  const base = dir === "" ? "/" : dir;
387
410
  const norm = /^(\.|\/|~)/.test(base) ? base : `./${base}`;
388
411
  return `${umbrella}(${norm}/**)`;
@@ -117,6 +117,18 @@ export function activeVersion(scope, name, opts = {}) {
117
117
  * @returns {Array<{scope, name, version, root, manifest}>}
118
118
  */
119
119
  export function discoverPlugins(opts = {}) {
120
+ // `cc agent --bare` (CC_PLUGINS=0): the plugin runtime is fully off. Every
121
+ // component collector (hooks/monitors/MCP/LSP/bin/settings-env/skills/
122
+ // agents) funnels through this discovery, so one gate disables them all.
123
+ // Other processes (`cc plugin installed` …) don't inherit the agent's env,
124
+ // so plugin management tooling is unaffected.
125
+ const rawPlugins = (opts.env || process.env).CC_PLUGINS;
126
+ if (
127
+ rawPlugins != null &&
128
+ /^(0|false|no|off)$/i.test(String(rawPlugins).trim())
129
+ ) {
130
+ return [];
131
+ }
120
132
  const scopes = opts.scopes || SCOPES;
121
133
  const byName = new Map(); // name → record (later scope overrides earlier)
122
134
  for (const scope of scopes) {
@@ -43,3 +43,43 @@ export function safeModeRequested(opts = {}, env = process.env) {
43
43
  const raw = env.CC_SAFE_MODE || env.CLAUDE_CODE_SAFE_MODE;
44
44
  return raw != null && /^(1|true|yes|on)$/i.test(String(raw).trim());
45
45
  }
46
+
47
+ /**
48
+ * `cc agent --bare` — everything --safe-mode disables PLUS skills, plugins and
49
+ * MCP auto-connect: a minimal, fast agent surface for scripted invocations.
50
+ * The two extra env switches are consumed at the single chokepoints all
51
+ * loading funnels through (skill-loader `loadAll`, plugin-runtime
52
+ * `discoverPlugins`); MCP/IDE/PDH/JetBrains auto-connect are flipped off on
53
+ * the parsed options by the agent command itself (an explicit --mcp-config
54
+ * still loads — bare kills ambient auto-connects, not explicit input).
55
+ * Permission rules stay active, same as safe mode.
56
+ */
57
+ export const BARE_MODE_EXTRA_SWITCHES = Object.freeze({
58
+ CC_SKILLS: "0", // every skill layer (list_skills/run_skill surface empties)
59
+ CC_PLUGINS: "0", // plugin runtime (hooks/monitors/MCP/LSP/bin/env/skills/agents)
60
+ });
61
+
62
+ /**
63
+ * Apply bare mode to an env: all safe-mode switches + the bare extras.
64
+ * @returns {string[]} the switch names applied (for the startup notice)
65
+ */
66
+ export function applyBareMode(env = process.env) {
67
+ const applied = applySafeMode(env);
68
+ for (const [k, v] of Object.entries(BARE_MODE_EXTRA_SWITCHES)) {
69
+ env[k] = v;
70
+ }
71
+ return [...applied, ...Object.keys(BARE_MODE_EXTRA_SWITCHES)];
72
+ }
73
+
74
+ /**
75
+ * Whether bare mode is requested — the explicit `--bare` flag or the ambient
76
+ * `CC_BARE` env var (truthy values: 1 / true / yes / on).
77
+ * @param {{ bare?: boolean }} [opts]
78
+ * @param {object} [env=process.env]
79
+ * @returns {boolean}
80
+ */
81
+ export function bareModeRequested(opts = {}, env = process.env) {
82
+ if (opts && opts.bare === true) return true;
83
+ const raw = env.CC_BARE;
84
+ return raw != null && /^(1|true|yes|on)$/i.test(String(raw).trim());
85
+ }