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.
- package/README.md +1 -1
- package/package.json +2 -2
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-Cth_zZeA.js → AIOps-CfMP2Oud.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-zfwA3Oxn.js → ActionButton-Zu0N8BQL.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-FeKc4aU2.js → Analytics-DZTwj5T_.js} +3 -3
- package/src/assets/web-panel/assets/AppLayout-CVFJh47v.js +9 -0
- package/src/assets/web-panel/assets/Artifacts-BbN5V7au.js +1 -0
- package/src/assets/web-panel/assets/Artifacts-CRkepl4t.css +1 -0
- package/src/assets/web-panel/assets/{Audit-C2gRq5Qi.js → Audit-BMfiLj7T.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-TeTIFiW-.js → BackgroundAgents-BslPCdGF.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CLJk3k6i.js → Backup-CQ_fMC1r.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-5ms537NY.js → BaseInput-CsFsARiC.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-4Nr5U9PJ.js → Chat-CwfBobco.js} +6 -6
- package/src/assets/web-panel/assets/ChatBubbleRenderer-K6Nf3xeN.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-B2Bca5ev.js → Checkbox-DkSyjjPp.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-D6Uysdbv.js → Codegen-B7Fx3zZ6.js} +1 -1
- package/src/assets/web-panel/assets/{Col-B3XQ8xc7.js → Col-Dzfqn8Ep.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CqwKtoPz.js → Community-DhHPdzMM.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BZZINTus.js → Compact-Ct18_8y7.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Dr3BXkWA.js → Compliance-CY64IqBi.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-rx_x9Wqc.js → Cowork-BYSmWlUl.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-BpZr7rHc.js → Cron-_4xtP3Ug.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CeV5EYVg.js → Crosschain-B4qY-Ycr.js} +1 -1
- package/src/assets/web-panel/assets/{DID-fe2Qaowz.js → DID-CkCzWkx1.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-Fh_EdWVa.js → Dashboard-C1scT7Ke.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-N1PIwWYf.js → Dropdown-DBnQ2EWd.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-B6vK93Ed.js → EmailListRenderer-C9JhOTq0.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-e3iUZhAg.js → FamilyGuardDashboard-CWXv5RUv.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-uInc9YcN.js → Federation-fYdcbz9W.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DmQZSX0z.js → FormItemContext-CJqOT3Yj.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CV_Z8OAo.js → GenericCardRenderer-DGCsHc5K.js} +1 -1
- package/src/assets/web-panel/assets/{Git-jXAhNN8d.js → Git-EH2K-H0Q.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-Blw1esBB.js → Governance-DJn1X2pR.js} +1 -1
- package/src/assets/web-panel/assets/Inference-Dch1E8qd.js +1 -0
- package/src/assets/web-panel/assets/{KnowledgeGraph-5xrglMTK.js → KnowledgeGraph-QIur8j8-.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BGIG9798.js → Logs-DUgHldPg.js} +2 -2
- package/src/assets/web-panel/assets/Marketplace-DafrSfvd.js +1 -0
- package/src/assets/web-panel/assets/{McpTools-7U8tEhwK.js → McpTools-D2xRbfJx.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-BrvboPLE.js → Memory-C6RbVHAo.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-nzcd7d1H.js → MobileBridge-BPtVBKqn.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DETVNKa7.js → MobileProjects-Cuw832cr.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-SZcCYec3.js → Mtc-D6O9fWFa.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-Cx_d3N17.js → MtcAudit-CWZcXIH1.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-DtcmSUo-.js → Multisig-DhIBxTFd.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Dgx2QHl-.js → NLProgramming-wkBFS33e.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-oHTGEHoJ.js → Notes-B09FxncA.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-qKD-IlOl.js → NotificationSettings-oQwtH6-9.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-CiU4Sy1J.js → OrderTableRenderer-iFKeNf6Y.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BBNXnltP.js → Organization-CsF-NIjz.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Df2fDbvT.js → Overflow-bLxv9Dqi.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DUHj6zjO.js → P2P-DVwQtm4q.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-B2nWgdoV.js → PdhVaultBrowser-Bgic3QKm.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-CiW0afny.js → Permissions-as80-9ep.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-cc1mHU_D.js → PersonalDataHub-BGmUw36a.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-B93zTapU.js → Pipeline-BM96zBxG.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-Db1jXWfd.js → Privacy-BBOzBSWe.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DFoZ1h9U.js → ProjectInit-GHX41V28.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BkkUnmYs.js → ProjectSettings-DraqzxKD.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Xa779XLC.js → Projects-BzzdUml4.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-DOF01IEN.js → Providers-CSeYNAtW.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-TohRsKd0.js → QrScannerModal-DlzBCdXh.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DjLcr1ZU.js → QuickAsk-p8iB7od_.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend--Ism6bMx.js → Recommend-DjOVXacd.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-B8CL3YHM.js → RemoteSession-CIGk7gk-.js} +2 -2
- package/src/assets/web-panel/assets/Reputation-CjT3M0Di.js +1 -0
- package/src/assets/web-panel/assets/{Row-BioxZBBH.js → Row-tw8m6T0c.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-B2LipwH3.js → RssFeed-B47KBx_f.js} +2 -2
- package/src/assets/web-panel/assets/Search-D2sx1PBe.js +1 -0
- package/src/assets/web-panel/assets/{Security-TibHuJWP.js → Security-JmtL8g7D.js} +3 -3
- package/src/assets/web-panel/assets/{Services-DtkuWseW.js → Services-BxrNqjin.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BIcPQae6.js → Skeleton-udk8L18t.js} +1 -1
- package/src/assets/web-panel/assets/Skills-DooLT9of.js +1 -0
- package/src/assets/web-panel/assets/{Sla-uUSU185m.js → Sla-ITVZE1xI.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-B7Bgc9mk.js → SpeechSettings-DCkfqY1n.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-K-XrJ2s9.js → SyncSettings-CkNlgFjM.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-BMAoyCM_.js +1 -0
- package/src/assets/web-panel/assets/{Templates-CaFnyGDK.js → Templates-BnrFTlrz.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-C2X0PtEC.js → Tenant-BjNsy3-i.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CKU5yVsT.js → Terminal-C0gxLL7V.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-8trZeYDC.js → TimelineRenderer-CUtj_KHA.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BUEEe9US.js → Tokens-DOg61zlq.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-COU97I7Y.js → Trigger-D4ldCewj.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DNWQChaP.js → Trust-3lk_Zd8j.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DWX_VtCp.js → UkeySign-kWhFR6Li.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-D9eIDhsp.js → VideoEditing-D3GOFVWg.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BOnCCgIL.js → Wallet-DzrZfBuh.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DN4dD-W8.js → WebAuthn-CyVJVp_g.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-ClvDb_jX.js → WorkflowEditor-i_b0wi9b.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DIxQ_Eeb.js → chat-DwPCK8I7.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BOX3WKcP.js → colors-D0vaVqZU.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Bny82VDi.js → compact-item-Don7nGW2.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-Cnoy3enS.js → createContext-D84R5qy2.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BBwWehQ7.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BJ7fkx3F.js → hasIn-BeUqVVKU.js} +1 -1
- package/src/assets/web-panel/assets/{icons-DEmcnxBf.js → icons-DG5UD-6K.js} +4 -4
- package/src/assets/web-panel/assets/{index-CZ6CHiCQ.js → index-4MkfPLGv.js} +1 -1
- package/src/assets/web-panel/assets/{index-7GLXvG8p.js → index-7QUwcGTg.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1nHEOFE.js → index-B57al0Z5.js} +1 -1
- package/src/assets/web-panel/assets/{index-Kio1ekYg.js → index-BER98o4P.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9QEc7Tq.js → index-BNpJf-sf.js} +1 -1
- package/src/assets/web-panel/assets/{index--CcGiwAI.js → index-BZ1N07Sp.js} +1 -1
- package/src/assets/web-panel/assets/{index-BWPey0Uf.js → index-BlfTf0ka.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7Fah_BX.js → index-BohrJpBf.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJ8-oLHI.js → index-Br3SOdER.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2qNdrNW.js → index-C0_sKVi7.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQ-xLCVb.js → index-CAdCTTBC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSRIjAWO.js → index-CExWtm98.js} +1 -1
- package/src/assets/web-panel/assets/{index--Xb2lq6E.js → index-CL71rIwo.js} +1 -1
- package/src/assets/web-panel/assets/{index-BDkN6YOI.js → index-CfYRv-zA.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bk5yST66.js → index-CnnxzIx0.js} +1 -1
- package/src/assets/web-panel/assets/{index-2N9Y1cLx.js → index-CrnVEEQO.js} +1 -1
- package/src/assets/web-panel/assets/{index-CW6n-y1L.js → index-CzT56x-_.js} +1 -1
- package/src/assets/web-panel/assets/{index-OaNFXV8N.js → index-D4b5XqIy.js} +1 -1
- package/src/assets/web-panel/assets/{index-CEPTiBWy.js → index-D84BnuFg.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZCTan8Q.js → index-D8r7WKk-.js} +1 -1
- package/src/assets/web-panel/assets/{index-rn6xd12s.js → index-DA0-EJKx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcmXbIwv.js → index-DBzBLX-K.js} +3 -3
- package/src/assets/web-panel/assets/{index-C--PPqwi.js → index-DFFzwrNs.js} +1 -1
- package/src/assets/web-panel/assets/index-DJ8etkBT.js +1 -0
- package/src/assets/web-panel/assets/{index-Ty4w18mR.js → index-DMT8n9vB.js} +4 -4
- package/src/assets/web-panel/assets/{index-qOBKrw-g.js → index-DQor_7NF.js} +1 -1
- package/src/assets/web-panel/assets/{index-CN6emwbq.js → index-DT40zd7K.js} +1 -1
- package/src/assets/web-panel/assets/{index-BM0mQhVv.js → index-De85D9Pc.js} +1 -1
- package/src/assets/web-panel/assets/{index-ceH9DsAi.js → index-IGGttsCv.js} +1 -1
- package/src/assets/web-panel/assets/{index-DBhkJqVF.js → index-L73NrmlW.js} +1 -1
- package/src/assets/web-panel/assets/{index-BbZynivw.js → index-LMo9bBLJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bvheiep2.js → index-MKZrJrP_.js} +1 -1
- package/src/assets/web-panel/assets/index-OeaK-zXZ.js +1 -0
- package/src/assets/web-panel/assets/{index-DxX8hhmL.js → index-WRw1pM5E.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6WoW8DJ.js → index-YKLCotoo.js} +1 -1
- package/src/assets/web-panel/assets/{index-BP7HFsAK.js → index-hjgrIX5I.js} +1 -1
- package/src/assets/web-panel/assets/{index-CnjtAKdw.js → index-m9bn0F1p.js} +1 -1
- package/src/assets/web-panel/assets/{index-CEq5Vh0i.js → index-wesQQ2cH.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1-h2QrC.js → index-x7VYlV41.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BmxMBuUZ.js → initDefaultProps-DGeU4vPS.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DnXNwo2k.js → motion-Dgqj9ab3.js} +1 -1
- package/src/assets/web-panel/assets/{move-WnIJwmZN.js → move-BFyEU6Lr.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DAto-c2w.js → mtc-parser-CueCXXPC.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BtvW9W0H.js → omit-D2pU5OyE.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DFqI9-_C.js → pickAttrs-CKTf6zD6.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Dax6wOK3.js → placementArrow-DieJaGCN.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BUFWWC83.js → responsiveObserve-Bxr-rmU6.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DyARyuSz.js → slide-CDEDk3Se.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BZSDugpi.js → statusUtils-icf2eCTx.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-B7o_K-4R.js → styleChecker-qKvgmBhE.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Dg7Bg8VG.js → useFlexGapSupport-1_UhiyJZ.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DQYXo5Rk.js → useFs-CFGLXDWh.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-E8NBAnkq.js → usePersonalDataHub-UafOXvQ6.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DD71_w1b.js → vnode-BfPqSx79.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-zc9EMd3q.js → zoom-CXsVcRB6.js} +1 -1
- package/src/assets/web-panel/index.html +2 -2
- package/src/command-manifest.json +22 -1
- package/src/commands/agenda.js +228 -0
- package/src/commands/agent.js +45 -4
- package/src/commands/artifacts.js +161 -0
- package/src/commands/batch.js +345 -0
- package/src/commands/browse.js +204 -2
- package/src/data/changelog.json +13 -2
- package/src/gateways/ws/artifact-protocol.js +219 -0
- package/src/gateways/ws/message-dispatcher.js +6 -0
- package/src/gateways/ws/ws-server.js +32 -0
- package/src/index.js +18 -0
- package/src/lib/accessibility.js +55 -0
- package/src/lib/agent-batch.js +235 -0
- package/src/lib/agent-notify.js +66 -0
- package/src/lib/agent-schedule-store.js +330 -0
- package/src/lib/artifact-store.js +236 -0
- package/src/lib/chrome-connector.js +314 -0
- package/src/lib/hook-runner.cjs +47 -7
- package/src/lib/ide-context.js +84 -0
- package/src/lib/permission-rules.cjs +26 -3
- package/src/lib/plugin-runtime/scopes.js +12 -0
- package/src/lib/safe-mode.js +40 -0
- package/src/lib/shell-selector.cjs +203 -0
- package/src/lib/skill-loader.js +19 -0
- package/src/lib/slash-dispatch.js +34 -0
- package/src/repl/agent-repl.js +99 -58
- package/src/runtime/agent-core.js +197 -8
- package/src/runtime/coding-agent-contract-shared.cjs +156 -0
- package/src/runtime/coding-agent-policy.cjs +43 -0
- package/src/runtime/headless-stream.js +51 -1
- package/src/runtime/policies/agent-policy.js +1 -0
- package/src/assets/web-panel/assets/AppLayout-C6WIrDZk.js +0 -9
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BFEnA8LN.js +0 -1
- package/src/assets/web-panel/assets/Inference-CoK8Ia0Z.js +0 -1
- package/src/assets/web-panel/assets/Marketplace-DFqhJMuY.js +0 -1
- package/src/assets/web-panel/assets/Reputation-CaRxB3Ep.js +0 -1
- package/src/assets/web-panel/assets/Search-BpjLBV8u.js +0 -1
- package/src/assets/web-panel/assets/Skills-DoTWIQOX.js +0 -1
- package/src/assets/web-panel/assets/Tasks-B3hEXNcI.js +0 -1
- package/src/assets/web-panel/assets/devWarning-Dr9dzlQ5.js +0 -1
- package/src/assets/web-panel/assets/index-B92M3t6B.js +0 -1
- 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 };
|
package/src/lib/hook-runner.cjs
CHANGED
|
@@ -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
|
-
|
|
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 (
|
|
216
|
-
|
|
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 = {
|
|
256
|
+
module.exports = {
|
|
257
|
+
runCommandHook,
|
|
258
|
+
runHooks,
|
|
259
|
+
tryParseDecision,
|
|
260
|
+
HOOK_DECISIONS,
|
|
261
|
+
_deps,
|
|
262
|
+
};
|
package/src/lib/ide-context.js
CHANGED
|
@@ -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",
|
|
362
|
-
"
|
|
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("/")
|
|
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) {
|
package/src/lib/safe-mode.js
CHANGED
|
@@ -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
|
+
}
|