chainlesschain 0.162.162 → 0.162.163
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/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-ClI2ALgv.js → AIOps-BQ0kgfA1.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BDjRcJaK.js → ActionButton-Dr1MDANe.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DikSRsaT.js → Analytics-CZ5vsaXW.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-9mLhMFmz.js → AppLayout-BIBga881.js} +3 -3
- package/src/assets/web-panel/assets/{Artifacts-BSr88BrH.js → Artifacts-vpXHRU3u.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CdvyimOF.js → Audit-B8exuoub.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-D3_hCedh.js → BackgroundAgents-923evRRN.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Cyvv9RHf.js → Backup-sr_sswlQ.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BZM14kt8.js → BaseInput-CnW-4XKT.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DrQSokVz.js → Chat-FZCsTZIL.js} +6 -6
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DDPd9qT0.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-bFLYeWMV.js → Checkbox-DFrxB59H.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-7mN7xJXT.js → Codegen-_b6WEel8.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DAB5ZWm7.js → Col-CkbnnZ-K.js} +1 -1
- package/src/assets/web-panel/assets/{Community-B_1TymaK.js → Community-BxM_82zW.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-Bq1uSWLJ.js → Compact-DYinP45R.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CyiE1jEe.js → Compliance-DyXp3sSP.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Cpk0D7vm.js → Cowork-BEr2DhW7.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-d_Lv3rdB.js → Cron-B-Vwo1hN.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DMqhHjMR.js → Crosschain-DbLxKddd.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Br-oPuBw.js → DID-BsQ_LFw9.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CfLW_RaP.js → Dashboard-lVlXQHu5.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DZnTOExV.js → Dropdown-Beo_foO7.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DHIUHDv1.js → EmailListRenderer-DszHIXED.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Bw3t3myu.js → FamilyGuardDashboard-DcAwoh81.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-D9cpQPAY.js → Federation-CuJLa2eb.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Jdczr9Mr.js → FormItemContext-Cxd1lxhc.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BT8ZMpC5.js → GenericCardRenderer-BBvq5Jsw.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DjARip2b.js → Git-WqPKetx6.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BgWbSRcj.js → Governance-CWYtY3ke.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CfouJciM.js → Inference-BnX1iiNV.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-50l7mrAr.js → KnowledgeGraph-BJuM8GN5.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-rq50H84B.js → Logs-DqLMNntm.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-DUPIEdQH.js → MarkdownRenderer-BqYwEYou.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BYgPDmHU.js → Marketplace-_vGfmhNl.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-5DVgcFdx.js → McpTools-BeJDOkDj.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-B70LPk7f.js → Memory-Da0EEZDO.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-De8ssA5Y.js → MobileBridge-DSuzcCCy.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-D0RAk4e8.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-RJsOTsIA.js → Mtc-BE9YgdA0.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-Crh-6BEK.js → MtcAudit-fC2BpO5S.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-wuoR5l3x.js → Multisig-D24Xxqw3.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-U2Mji4fZ.js → NLProgramming-Bs3oGvS8.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CdTZAyDH.js → Notes-BFjmAA-3.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-C-FucWE7.js → NotificationSettings-BX_9Bhv0.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DCYFTeaI.js → OrderTableRenderer-wjkZwJXI.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-an3OF85L.js → Organization-DBcdsVEP.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-C-1vmYFe.js → Overflow-DDyaF9vC.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Cwh47xKg.js → P2P-BB78L4he.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Dm38GT0k.js → PdhVaultBrowser-DqTiZFum.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DS2Y8awD.js → Permissions-INbnWv4j.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BQNziA0I.js → PersonalDataHub-BST-VmTq.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-DF85L1ue.js → Pipeline-CE7_tS2k.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BuLornF-.js → Privacy-hK5L8dOB.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-C3Y31IwX.js → ProjectInit-BvkkUoDn.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BCPiI8aD.js → ProjectSettings-u7_EUtkm.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BZ6s4k6g.js → Projects-5Fmt_8Nw.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-D3Wp022X.js → Providers-B9eanarR.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CUJfQkH4.js → QrScannerModal-C0dX5N2f.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-UAc0EO8l.js → QuickAsk-CKTpG-PZ.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BaxKupcs.js → Recommend-Px_w_XQM.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C75NNnXZ.js → RemoteSession-Di0fnYkM.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-BVXynZAk.js → Reputation-vesnWjXt.js} +1 -1
- package/src/assets/web-panel/assets/{Row-C5MRZ0bi.js → Row-exbY6LkT.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DAKkEYka.js → RssFeed-B3O3x70t.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DvWv9YOA.js → Search-uRA0DhNz.js} +1 -1
- package/src/assets/web-panel/assets/{Security-B5peafW1.js → Security-CViqdH6K.js} +4 -4
- package/src/assets/web-panel/assets/{Services-UQmvPEaG.js → Services-CkXS97WN.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-C3bhWpuF.js → Skeleton-DxW5WYnE.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DfkKf9_H.js → Skills-CLxmGFse.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DqMSjlBM.js → Sla-Cr5fziXq.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-C-WbxWUX.js → SpeechSettings-H0JSb3cp.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CYuHZ4N2.js → SyncSettings-DMYWMae7.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CyeAyB9d.js → Tasks-CbYyDdF4.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BTCeQnpq.js → Templates-Cpc8JjiK.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BifQuA9Y.js → Tenant-CRh77Jda.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-Bx_9Bocf.js → Terminal-4D8tNBO5.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BX0RFCOm.js → TimelineRenderer-D41jzzbG.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CGHrCmUS.js → Tokens-C_jWEy6b.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DlqaY4SO.js → Trigger-Dk1ZUhsy.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DfOmj-ay.js → Trust-CLVy4l7m.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B72jH5fE.js → UkeySign-C-FeqFDj.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BT-63fqv.js → VideoEditing-CqVZzyMm.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Dsul3sHr.js → Wallet-DX9BW9pu.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-Izkwwdgy.js → WebAuthn-7hQDpWw_.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-D3FHSiH1.js → WorkflowEditor-6XqUAO0j.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CmVz5z3U.js → chat-5R_vkaJG.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CuLgyJBp.js → colors-huUPZdC-.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Ml1YkXpw.js → compact-item-CfJlgisE.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BvjJ48bs.js → createContext-jO8heofr.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DeIrCfH-.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DVC0157C.js → hasIn-DY8Vxqx7.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJyN28Et.js → index-A48PtXw9.js} +1 -1
- package/src/assets/web-panel/assets/{index-CudmwNBV.js → index-B300RPnT.js} +3 -3
- package/src/assets/web-panel/assets/{index-BbepULSc.js → index-B74OXFxC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DYXHFr4r.js → index-BAON7TzU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BzG-x9p4.js → index-BCqxyvqp.js} +1 -1
- package/src/assets/web-panel/assets/{index-wvb_FtaV.js → index-BHE8OIls.js} +1 -1
- package/src/assets/web-panel/assets/{index-QXzlT9KD.js → index-BQS-uPOK.js} +1 -1
- package/src/assets/web-panel/assets/{index-DukDVXCv.js → index-BRXOP09X.js} +1 -1
- package/src/assets/web-panel/assets/{index-CFYVQMvu.js → index-BYLBPpt8.js} +1 -1
- package/src/assets/web-panel/assets/{index-BydjdZnl.js → index-BfCx4pFT.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmFNvNLZ.js → index-BliaT02i.js} +1 -1
- package/src/assets/web-panel/assets/{index-KMqhcF7l.js → index-BmqGerpC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ck_52dz_.js → index-BsBtRWbA.js} +1 -1
- package/src/assets/web-panel/assets/{index-C229ABfy.js → index-BtEXSoPL.js} +1 -1
- package/src/assets/web-panel/assets/index-BxUKMRpP.js +1 -0
- package/src/assets/web-panel/assets/{index-C_H_S-DY.js → index-Bxax9vQ6.js} +1 -1
- package/src/assets/web-panel/assets/{index-B0S8NzO6.js → index-C2iVSFpy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BjHBpRYZ.js → index-CA6sO5VD.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bzr0rB5T.js → index-CA90TZxG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DDGaOWBV.js → index-CLhBh-qE.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dt5KMjwJ.js → index-CRElRhyS.js} +1 -1
- package/src/assets/web-panel/assets/{index-BbPtzB8M.js → index-Cbe9G0V4.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_vRgTl9.js → index-ChvHubX3.js} +1 -1
- package/src/assets/web-panel/assets/{index-BpFJHiTv.js → index-Cis_Ip7H.js} +1 -1
- package/src/assets/web-panel/assets/{index-KjgKdZe-.js → index-Cjxbu68P.js} +1 -1
- package/src/assets/web-panel/assets/{index-vG4XSJ1d.js → index-CzKmteNS.js} +1 -1
- package/src/assets/web-panel/assets/{index-BUt8kTIB.js → index-D0pet5Uy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfuKP4qe.js → index-D1VLuww0.js} +1 -1
- package/src/assets/web-panel/assets/{index-4SBta3d7.js → index-DXBcvUnf.js} +1 -1
- package/src/assets/web-panel/assets/{index-DC_OI9H2.js → index-Db1cLWir.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuGszaWY.js → index-DoI2sZm7.js} +1 -1
- package/src/assets/web-panel/assets/index-DscU9iPI.js +1 -0
- package/src/assets/web-panel/assets/{index-4wcgvPoF.js → index-KRHroryI.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3lD5qap.js → index-d1xkbtNz.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_EvG_GW.js → index-dKx57io7.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXkJH-lK.js → index-j3O2EEJe.js} +1 -1
- package/src/assets/web-panel/assets/{index-BiC6dQZG.js → index-p85YaaKw.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cw0Rjy87.js → index-v3pTaEQm.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_XuRgGn.js → index-vXs3B8GJ.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BDgd2YZ8.js → initDefaultProps-MCB7-raZ.js} +1 -1
- package/src/assets/web-panel/assets/{motion-ErtrXdYe.js → motion-UWDF9swV.js} +1 -1
- package/src/assets/web-panel/assets/{move-_GPPNCgs.js → move-CsTmgXZU.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DRLL-8V2.js → mtc-parser-fQy6KGLg.js} +1 -1
- package/src/assets/web-panel/assets/{omit-p86sl023.js → omit-woPnEjd1.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BTu7cMLH.js → pickAttrs-Bs2dZ1gf.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BEKz6Isr.js → placementArrow-C7EVgPkg.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Ck42c9MP.js → responsiveObserve-BH2kYCTN.js} +1 -1
- package/src/assets/web-panel/assets/{slide-1CEvuPsB.js → slide-BFBP2V_e.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BDfdx1GJ.js → statusUtils-xdDxDYse.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-B4K46m5w.js → styleChecker-aAWgPPQf.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DoIVpJNq.js → useFlexGapSupport-DkGSAWoO.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DdAWi-ln.js → useFs-BrOih40N.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-iUu6k8dx.js → usePersonalDataHub-eIYrgMqO.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DZz4R6Tw.js → vnode-B8lutNSE.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DXlf9sA3.js → zoom-CffNre7V.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +254 -23
- package/src/commands/agent.js +105 -11
- package/src/commands/context.js +125 -14
- package/src/commands/hook.js +181 -7
- package/src/commands/plugin.js +239 -1
- package/src/commands/review.js +347 -2
- package/src/data/changelog.json +9 -4
- package/src/gateways/ws/remote-session-protocol.js +39 -0
- package/src/gateways/ws/session-protocol.js +5 -2
- package/src/lib/agent-schedule-store.js +289 -10
- package/src/lib/channels/channel-manager.js +54 -2
- package/src/lib/cli-context-engineering.js +9 -2
- package/src/lib/context-breakdown.js +116 -0
- package/src/lib/doctor-checkup.js +52 -0
- package/src/lib/hook-event-log.cjs +193 -0
- package/src/lib/interaction-adapter.js +34 -4
- package/src/lib/json-schema-output.js +46 -12
- package/src/lib/json-schema-validate.js +143 -3
- package/src/lib/plugin-runtime/capability-consent.js +175 -0
- package/src/lib/plugin-runtime/policy.js +62 -0
- package/src/lib/plugin-runtime/scopes.js +20 -4
- package/src/lib/project-instructions.js +128 -0
- package/src/lib/repl-goal.js +132 -0
- package/src/lib/repl-rewind.js +52 -0
- package/src/lib/schedule-planner.js +17 -1
- package/src/lib/settings-hook-events.cjs +13 -0
- package/src/lib/settings-loader.cjs +42 -0
- package/src/lib/side-effect-ledger-store.js +88 -0
- package/src/lib/side-effect-ledger.js +296 -0
- package/src/lib/sub-agent-context.js +74 -5
- package/src/lib/subagent-inheritance.js +112 -0
- package/src/lib/turn-binding-store.js +119 -0
- package/src/repl/agent-repl.js +136 -0
- package/src/repl/permission-tier.js +28 -0
- package/src/runtime/agent-core.js +376 -10
- package/src/runtime/coding-agent-contract-shared.cjs +60 -1
- package/src/runtime/headless-runner.js +267 -0
- package/src/runtime/headless-stream.js +60 -1
- package/src/assets/web-panel/assets/ChatBubbleRenderer-HwJR-C6B.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-D10VISps.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DhiGrbsh.js +0 -1
- package/src/assets/web-panel/assets/index-B7a1TrU6.js +0 -1
- package/src/assets/web-panel/assets/index-C2B1KRja.js +0 -1
package/src/commands/context.js
CHANGED
|
@@ -66,22 +66,21 @@ export function registerContextCommand(program) {
|
|
|
66
66
|
.argument("[id]", "Session ID (omit for the most-recent headless session)")
|
|
67
67
|
.option("--model <model>", "Size against this model's context window")
|
|
68
68
|
.option("--provider <provider>", "Provider (for the window default)")
|
|
69
|
+
.option(
|
|
70
|
+
"--sources",
|
|
71
|
+
"Also break tokens down by source (project-memory instruction files + message classes)",
|
|
72
|
+
)
|
|
69
73
|
.option("--json", "Output as JSON")
|
|
70
74
|
.action(async (id, options) => {
|
|
71
|
-
const {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
readEvents,
|
|
76
|
-
} = await import("../harness/jsonl-session-store.js");
|
|
77
|
-
const { estimateTokens, getContextWindow } = await import(
|
|
78
|
-
"../harness/prompt-compressor.js"
|
|
79
|
-
);
|
|
75
|
+
const { rebuildMessages, getLastSessionId, sessionExists, readEvents } =
|
|
76
|
+
await import("../harness/jsonl-session-store.js");
|
|
77
|
+
const { estimateTokens, getContextWindow } =
|
|
78
|
+
await import("../harness/prompt-compressor.js");
|
|
80
79
|
|
|
81
80
|
const sessionId = id || getLastSessionId();
|
|
82
81
|
if (!sessionId) {
|
|
83
82
|
logger.error(
|
|
84
|
-
|
|
83
|
+
"No session found. Run a headless agent with `--session <id>` first, or pass a session id.",
|
|
85
84
|
);
|
|
86
85
|
process.exitCode = 1;
|
|
87
86
|
return;
|
|
@@ -121,6 +120,55 @@ export function registerContextCommand(program) {
|
|
|
121
120
|
const used = window > 0 ? total / window : 0;
|
|
122
121
|
const remaining = Math.max(0, window - total);
|
|
123
122
|
|
|
123
|
+
// --sources (opt-in): attribute the project-memory instruction files that
|
|
124
|
+
// WOULD load for the current cwd (same instructionExcludes the agent
|
|
125
|
+
// honors), folded in with the session's message-role buckets. The base
|
|
126
|
+
// view is byte-for-byte unchanged when the flag is absent.
|
|
127
|
+
let sourceReport = null;
|
|
128
|
+
if (options.sources) {
|
|
129
|
+
try {
|
|
130
|
+
const { loadProjectInstructions } =
|
|
131
|
+
await import("../lib/project-instructions.js");
|
|
132
|
+
const { breakdownInstructionSources, rankContextSources } =
|
|
133
|
+
await import("../lib/context-breakdown.js");
|
|
134
|
+
const cwd = process.cwd();
|
|
135
|
+
let instructionExcludes;
|
|
136
|
+
try {
|
|
137
|
+
const { readStringArraySetting } =
|
|
138
|
+
await import("../lib/settings-loader.cjs");
|
|
139
|
+
instructionExcludes = readStringArraySetting(
|
|
140
|
+
"instructionExcludes",
|
|
141
|
+
{
|
|
142
|
+
cwd,
|
|
143
|
+
},
|
|
144
|
+
);
|
|
145
|
+
} catch {
|
|
146
|
+
instructionExcludes = undefined; // fail-open — load everything
|
|
147
|
+
}
|
|
148
|
+
const loaded = loadProjectInstructions({ cwd, instructionExcludes });
|
|
149
|
+
const instr = breakdownInstructionSources(
|
|
150
|
+
loaded.files,
|
|
151
|
+
estimateTokens,
|
|
152
|
+
cwd,
|
|
153
|
+
);
|
|
154
|
+
const ranked = rankContextSources({
|
|
155
|
+
instructionTotal: instr.total,
|
|
156
|
+
buckets,
|
|
157
|
+
counts,
|
|
158
|
+
});
|
|
159
|
+
sourceReport = {
|
|
160
|
+
cwd,
|
|
161
|
+
instructions: instr.sources,
|
|
162
|
+
instructionTokens: instr.total,
|
|
163
|
+
ranked: ranked.sources,
|
|
164
|
+
combinedTotal: ranked.total,
|
|
165
|
+
};
|
|
166
|
+
} catch {
|
|
167
|
+
// Best-effort — a source breakdown must never break the base view.
|
|
168
|
+
sourceReport = null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
124
172
|
if (options.json) {
|
|
125
173
|
console.log(
|
|
126
174
|
JSON.stringify(
|
|
@@ -136,6 +184,15 @@ export function registerContextCommand(program) {
|
|
|
136
184
|
breakdown: buckets,
|
|
137
185
|
counts,
|
|
138
186
|
overflows: total > window,
|
|
187
|
+
...(sourceReport
|
|
188
|
+
? {
|
|
189
|
+
sources: sourceReport.ranked,
|
|
190
|
+
instructions: sourceReport.instructions,
|
|
191
|
+
instructionTokens: sourceReport.instructionTokens,
|
|
192
|
+
combinedTotal: sourceReport.combinedTotal,
|
|
193
|
+
cwd: sourceReport.cwd,
|
|
194
|
+
}
|
|
195
|
+
: {}),
|
|
139
196
|
},
|
|
140
197
|
null,
|
|
141
198
|
2,
|
|
@@ -144,7 +201,9 @@ export function registerContextCommand(program) {
|
|
|
144
201
|
return;
|
|
145
202
|
}
|
|
146
203
|
|
|
147
|
-
logger.log(
|
|
204
|
+
logger.log(
|
|
205
|
+
chalk.bold(`Context — session ${chalk.gray(sessionId.slice(0, 28))}`),
|
|
206
|
+
);
|
|
148
207
|
logger.log(
|
|
149
208
|
chalk.gray(
|
|
150
209
|
` model ${model || "(default)"} · provider ${provider} · ` +
|
|
@@ -172,17 +231,69 @@ export function registerContextCommand(program) {
|
|
|
172
231
|
|
|
173
232
|
logger.log("");
|
|
174
233
|
const pct = (used * 100).toFixed(1);
|
|
175
|
-
const color =
|
|
234
|
+
const color =
|
|
235
|
+
used > 0.9 ? chalk.red : used > 0.7 ? chalk.yellow : chalk.green;
|
|
176
236
|
logger.log(
|
|
177
237
|
` ${chalk.bold("total".padEnd(11))} ${String(total).padStart(7)} ` +
|
|
178
238
|
`${color(bar(used))} ${color(`${pct}% of window`)}`,
|
|
179
239
|
);
|
|
180
240
|
logger.log(
|
|
181
|
-
chalk.gray(
|
|
241
|
+
chalk.gray(
|
|
242
|
+
` headroom ${String(remaining).padStart(7)} tokens remaining`,
|
|
243
|
+
),
|
|
182
244
|
);
|
|
183
245
|
if (total > window) {
|
|
184
246
|
logger.log(
|
|
185
|
-
chalk.red(
|
|
247
|
+
chalk.red(
|
|
248
|
+
" ⚠ exceeds the model context window — compaction required",
|
|
249
|
+
),
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (sourceReport) {
|
|
254
|
+
logger.log("");
|
|
255
|
+
logger.log(
|
|
256
|
+
chalk.bold("By source") +
|
|
257
|
+
chalk.gray(` (project memory for ${sourceReport.cwd})`),
|
|
258
|
+
);
|
|
259
|
+
if (sourceReport.instructions.length) {
|
|
260
|
+
for (const s of sourceReport.instructions) {
|
|
261
|
+
logger.log(
|
|
262
|
+
chalk.gray(` · ${String(s.scope).padEnd(7)} `) +
|
|
263
|
+
`${String(s.tokens).padStart(7)} ${s.source}` +
|
|
264
|
+
(s.truncated ? chalk.yellow(" (truncated)") : ""),
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
logger.log(
|
|
268
|
+
chalk.gray(" project-memory subtotal ") +
|
|
269
|
+
`${String(sourceReport.instructionTokens).padStart(7)} tokens`,
|
|
270
|
+
);
|
|
271
|
+
} else {
|
|
272
|
+
logger.log(
|
|
273
|
+
chalk.gray(" (no project-memory instruction files for this cwd)"),
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
logger.log("");
|
|
277
|
+
for (const s of sourceReport.ranked) {
|
|
278
|
+
logger.log(
|
|
279
|
+
` ${String(s.source).padEnd(30)} ${String(s.tokens).padStart(7)} ` +
|
|
280
|
+
`${chalk.cyan(bar(s.share))} ${String(Math.round(s.share * 100)).padStart(3)}%`,
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
logger.log(
|
|
284
|
+
chalk.gray(
|
|
285
|
+
" note: instructions reflect the current cwd (what a new run here",
|
|
286
|
+
),
|
|
287
|
+
);
|
|
288
|
+
logger.log(
|
|
289
|
+
chalk.gray(
|
|
290
|
+
" would load); messages are this session's stored turns. Skill / MCP",
|
|
291
|
+
),
|
|
292
|
+
);
|
|
293
|
+
logger.log(
|
|
294
|
+
chalk.gray(
|
|
295
|
+
" tool schemas load at runtime and are not counted here.",
|
|
296
|
+
),
|
|
186
297
|
);
|
|
187
298
|
}
|
|
188
299
|
});
|
package/src/commands/hook.js
CHANGED
|
@@ -101,10 +101,7 @@ export function registerHookCommand(program) {
|
|
|
101
101
|
}
|
|
102
102
|
const events = Object.keys(settingsHooks);
|
|
103
103
|
if (events.length > 0) {
|
|
104
|
-
const n = events.reduce(
|
|
105
|
-
(a, e) => a + settingsHooks[e].length,
|
|
106
|
-
0,
|
|
107
|
-
);
|
|
104
|
+
const n = events.reduce((a, e) => a + settingsHooks[e].length, 0);
|
|
108
105
|
logger.log(chalk.bold(`\n.claude/settings.json hooks (${n}):`));
|
|
109
106
|
for (const ev of events) {
|
|
110
107
|
if (options.event && ev !== options.event) continue;
|
|
@@ -145,7 +142,8 @@ export function registerHookCommand(program) {
|
|
|
145
142
|
settingsFile: options.settings,
|
|
146
143
|
});
|
|
147
144
|
const matched = collectHooks(hooks, event, tool);
|
|
148
|
-
const toolInput =
|
|
145
|
+
const toolInput =
|
|
146
|
+
args && args.length ? { command: args.join(" "), args } : {};
|
|
149
147
|
const payload = {
|
|
150
148
|
hook_event_name: event,
|
|
151
149
|
tool_name: tool,
|
|
@@ -156,14 +154,18 @@ export function registerHookCommand(program) {
|
|
|
156
154
|
|
|
157
155
|
if (!options.run) {
|
|
158
156
|
if (options.json) {
|
|
159
|
-
console.log(
|
|
157
|
+
console.log(
|
|
158
|
+
JSON.stringify({ event, tool, matched, payload }, null, 2),
|
|
159
|
+
);
|
|
160
160
|
} else if (matched.length === 0) {
|
|
161
161
|
logger.log(
|
|
162
162
|
chalk.gray(`no settings.json hooks match ${event} / ${tool}`),
|
|
163
163
|
);
|
|
164
164
|
} else {
|
|
165
165
|
logger.log(
|
|
166
|
-
chalk.bold(
|
|
166
|
+
chalk.bold(
|
|
167
|
+
`${matched.length} hook(s) would fire for ${event} / ${tool}:`,
|
|
168
|
+
),
|
|
167
169
|
);
|
|
168
170
|
for (const h of matched) logger.log(` ${chalk.gray(h.command)}`);
|
|
169
171
|
logger.log(chalk.dim(" (use --run to execute and see decisions)"));
|
|
@@ -201,6 +203,178 @@ export function registerHookCommand(program) {
|
|
|
201
203
|
}
|
|
202
204
|
});
|
|
203
205
|
|
|
206
|
+
// hook replay — replay a recorded hook delivery by event id (P2 event bus).
|
|
207
|
+
// Dry-run by default; --run executes observe-only hooks. A DECISION hook can
|
|
208
|
+
// re-gate control flow, so its replay is refused without an explicit sandbox
|
|
209
|
+
// (and, since a real sandbox executor is not yet available, is never executed
|
|
210
|
+
// — only its dry-run plan is shown).
|
|
211
|
+
hook
|
|
212
|
+
.command("replay <event-id>")
|
|
213
|
+
.description(
|
|
214
|
+
"Replay a recorded hook delivery (needs CC_HOOK_EVENT_LOG); dry-run by default, --run executes observe-only hooks",
|
|
215
|
+
)
|
|
216
|
+
.option("--run", "Execute the replayed hooks (observe-only events only)")
|
|
217
|
+
.option(
|
|
218
|
+
"--sandbox",
|
|
219
|
+
"Assert an explicit sandbox for a decision-hook replay",
|
|
220
|
+
)
|
|
221
|
+
.option("--file <path>", "Read from a specific hook event log file")
|
|
222
|
+
.option("--json", "Output as JSON")
|
|
223
|
+
.action(async (eventId, options) => {
|
|
224
|
+
try {
|
|
225
|
+
const { findHookEvent } = await import("../lib/hook-event-log.cjs");
|
|
226
|
+
const { planHookReplay } = await import("../lib/hook-event-bus.cjs");
|
|
227
|
+
const envelope = findHookEvent(eventId, { filePath: options.file });
|
|
228
|
+
if (!envelope) {
|
|
229
|
+
logger.error(
|
|
230
|
+
`hook event "${eventId}" not found` +
|
|
231
|
+
(options.file
|
|
232
|
+
? ""
|
|
233
|
+
: " (enable recording with CC_HOOK_EVENT_LOG=1)"),
|
|
234
|
+
);
|
|
235
|
+
process.exitCode = 1;
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const plan = planHookReplay(envelope, {
|
|
239
|
+
sandbox: Boolean(options.sandbox),
|
|
240
|
+
});
|
|
241
|
+
if (!plan.ok) {
|
|
242
|
+
if (options.json) {
|
|
243
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
244
|
+
} else {
|
|
245
|
+
logger.error(`cannot replay: ${plan.reason}`);
|
|
246
|
+
}
|
|
247
|
+
process.exitCode = 1;
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
// Decision events re-gate flow; a real sandbox executor is still owed,
|
|
251
|
+
// so we never EXECUTE them — the dry-run plan is shown instead.
|
|
252
|
+
if (options.run && plan.requiresSandbox) {
|
|
253
|
+
const note =
|
|
254
|
+
"decision-hook execution needs a real sandbox executor (not yet available); showing dry-run plan";
|
|
255
|
+
if (options.json) {
|
|
256
|
+
console.log(
|
|
257
|
+
JSON.stringify({ ...plan, executed: false, note }, null, 2),
|
|
258
|
+
);
|
|
259
|
+
} else {
|
|
260
|
+
logger.warn(`${note}:`);
|
|
261
|
+
logger.log(JSON.stringify(plan.payload, null, 2));
|
|
262
|
+
}
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (!options.run) {
|
|
266
|
+
if (options.json) {
|
|
267
|
+
console.log(JSON.stringify({ ...plan, executed: false }, null, 2));
|
|
268
|
+
} else {
|
|
269
|
+
logger.log(
|
|
270
|
+
chalk.bold(
|
|
271
|
+
`replay (dry-run) of ${plan.event_type} [${plan.event_id}]`,
|
|
272
|
+
),
|
|
273
|
+
);
|
|
274
|
+
if (plan.requiresSandbox)
|
|
275
|
+
logger.log(chalk.dim(" (decision event — sandboxed)"));
|
|
276
|
+
logger.log(JSON.stringify(plan.payload, null, 2));
|
|
277
|
+
logger.log(
|
|
278
|
+
chalk.dim(" (use --run to execute observe-only hooks)"),
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
// observe-only + --run: re-collect and execute the matching hooks.
|
|
284
|
+
const { loadHooks, collectHooks } =
|
|
285
|
+
await import("../lib/settings-hooks.cjs");
|
|
286
|
+
const { runHooks } = await import("../lib/hook-runner.cjs");
|
|
287
|
+
const { hooks } = loadHooks({ cwd: process.cwd() });
|
|
288
|
+
const matched = collectHooks(
|
|
289
|
+
hooks,
|
|
290
|
+
plan.event_type,
|
|
291
|
+
plan.payload.tool_name || "",
|
|
292
|
+
);
|
|
293
|
+
const outcome = runHooks(matched, plan.payload, {
|
|
294
|
+
cwd: process.cwd(),
|
|
295
|
+
event: plan.event_type,
|
|
296
|
+
});
|
|
297
|
+
if (options.json) {
|
|
298
|
+
console.log(
|
|
299
|
+
JSON.stringify({ ...plan, executed: true, outcome }, null, 2),
|
|
300
|
+
);
|
|
301
|
+
} else {
|
|
302
|
+
logger.log(
|
|
303
|
+
chalk.bold(
|
|
304
|
+
`replayed ${plan.event_type} [${plan.event_id}] → ${outcome.results.length} hook(s)`,
|
|
305
|
+
),
|
|
306
|
+
);
|
|
307
|
+
for (const r of outcome.results) {
|
|
308
|
+
logger.log(
|
|
309
|
+
` ${chalk.gray(r.command)} → ${r.decision}` +
|
|
310
|
+
(r.exitCode != null ? ` (exit ${r.exitCode})` : ""),
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
} catch (err) {
|
|
315
|
+
logger.error(`hook replay failed: ${err.message}`);
|
|
316
|
+
process.exitCode = 1;
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// hook events-log — list / verify the recorded hook delivery log.
|
|
321
|
+
hook
|
|
322
|
+
.command("events-log")
|
|
323
|
+
.description(
|
|
324
|
+
"List or verify the recorded hook delivery log (CC_HOOK_EVENT_LOG)",
|
|
325
|
+
)
|
|
326
|
+
.option("--limit <n>", "Max entries to show", "20")
|
|
327
|
+
.option("--verify", "Verify the hash chain instead of listing")
|
|
328
|
+
.option("--file <path>", "Read from a specific hook event log file")
|
|
329
|
+
.option("--json", "Output as JSON")
|
|
330
|
+
.action(async (options) => {
|
|
331
|
+
try {
|
|
332
|
+
const { listHookEvents, verifyHookEventChain, hookEventLogPath } =
|
|
333
|
+
await import("../lib/hook-event-log.cjs");
|
|
334
|
+
if (options.verify) {
|
|
335
|
+
const result = verifyHookEventChain({ filePath: options.file });
|
|
336
|
+
if (options.json) {
|
|
337
|
+
console.log(JSON.stringify(result, null, 2));
|
|
338
|
+
} else if (result.ok) {
|
|
339
|
+
logger.success(
|
|
340
|
+
`hook event log chain OK (${result.length} record(s))`,
|
|
341
|
+
);
|
|
342
|
+
} else {
|
|
343
|
+
logger.error(
|
|
344
|
+
`hook event log chain BROKEN at #${result.brokenAt}: ${result.reason}`,
|
|
345
|
+
);
|
|
346
|
+
process.exitCode = 1;
|
|
347
|
+
}
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
const events = listHookEvents({
|
|
351
|
+
limit: parseInt(options.limit, 10) || 20,
|
|
352
|
+
filePath: options.file,
|
|
353
|
+
});
|
|
354
|
+
const logPath = options.file || hookEventLogPath();
|
|
355
|
+
if (options.json) {
|
|
356
|
+
console.log(JSON.stringify({ events, path: logPath }, null, 2));
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
if (events.length === 0) {
|
|
360
|
+
logger.info(
|
|
361
|
+
`No recorded hook events (enable with CC_HOOK_EVENT_LOG=1). Log: ${logPath}`,
|
|
362
|
+
);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
logger.log(chalk.bold(`Recorded hook events (${events.length}):\n`));
|
|
366
|
+
for (const e of events) {
|
|
367
|
+
logger.log(
|
|
368
|
+
` ${chalk.cyan(e.event_id)} ${chalk.yellow(e.event_type)}` +
|
|
369
|
+
(e.session_id ? ` session=${e.session_id}` : ""),
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
} catch (err) {
|
|
373
|
+
logger.error(`hook events-log failed: ${err.message}`);
|
|
374
|
+
process.exitCode = 1;
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
|
|
204
378
|
// hook add
|
|
205
379
|
hook
|
|
206
380
|
.command("add")
|
package/src/commands/plugin.js
CHANGED
|
@@ -30,6 +30,59 @@ import {
|
|
|
30
30
|
getPluginSkills,
|
|
31
31
|
} from "../harness/plugin-manager.js";
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* After an install/upgrade, resolve the just-installed plugin's DECLARED
|
|
35
|
+
* capabilities and its current consent status, so `cc plugin add`/`upgrade`
|
|
36
|
+
* surface a capability list + diff and tell the user when a WIDENING requires
|
|
37
|
+
* re-consent (mirroring `cc plugin consent`). Best-effort: capability rendering
|
|
38
|
+
* must never fail an otherwise-successful install. Returns a structured summary
|
|
39
|
+
* (for --json folding) or null when the plugin declares nothing / can't be
|
|
40
|
+
* resolved.
|
|
41
|
+
*/
|
|
42
|
+
async function resolvePluginCapabilityNotice(name, scope, cwd) {
|
|
43
|
+
try {
|
|
44
|
+
const { discoverPlugins } = await import("../lib/plugin-runtime/scopes.js");
|
|
45
|
+
const { describeCapabilities } =
|
|
46
|
+
await import("../lib/plugin-runtime/capabilities.js");
|
|
47
|
+
const consent = await import("../lib/plugin-runtime/capability-consent.js");
|
|
48
|
+
const installed = discoverPlugins({ cwd, skipPolicy: true }).find(
|
|
49
|
+
(p) => p.name === name && p.scope === scope,
|
|
50
|
+
);
|
|
51
|
+
if (!installed) return null;
|
|
52
|
+
const declared = installed.manifest?.capabilities;
|
|
53
|
+
if (!declared || consent.capabilitiesAreEmpty(declared)) return null;
|
|
54
|
+
const entry = consent.loadConsentStore()[`${scope}:${name}`] || null;
|
|
55
|
+
const status = consent.capabilityConsentStatus(declared, entry);
|
|
56
|
+
return {
|
|
57
|
+
declared: describeCapabilities(declared),
|
|
58
|
+
consented: status.consented,
|
|
59
|
+
reason: status.reason,
|
|
60
|
+
added: status.added,
|
|
61
|
+
};
|
|
62
|
+
} catch {
|
|
63
|
+
return null; // capability rendering is advisory — never break an install
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Print the capability notice (text mode) after an install/upgrade. */
|
|
68
|
+
function printPluginCapabilityNotice(name, notice) {
|
|
69
|
+
if (!notice) return;
|
|
70
|
+
logger.log(chalk.bold("\nCapabilities (declared):"));
|
|
71
|
+
for (const l of notice.declared) logger.log(` ${chalk.magenta(l)}`);
|
|
72
|
+
if (notice.consented) {
|
|
73
|
+
logger.log(chalk.gray(` capability consent: ${notice.reason}`));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
logger.log(
|
|
77
|
+
chalk.yellow(` ⚠ capability consent required (${notice.reason})`),
|
|
78
|
+
);
|
|
79
|
+
if (notice.added.length)
|
|
80
|
+
logger.log(chalk.yellow(` new: ${notice.added.join(", ")}`));
|
|
81
|
+
logger.log(
|
|
82
|
+
chalk.dim(` run \`cc plugin consent ${name} --grant\` to allow them`),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
33
86
|
export function registerPluginCommand(program) {
|
|
34
87
|
const plugin = program
|
|
35
88
|
.command("plugin")
|
|
@@ -632,6 +685,17 @@ export function registerPluginCommand(program) {
|
|
|
632
685
|
logger.log(chalk.yellow(` ⚠ ${issue}`));
|
|
633
686
|
}
|
|
634
687
|
}
|
|
688
|
+
// Declared capabilities (Phase 3 gap): show what the plugin says it needs
|
|
689
|
+
// so a user can compare against the components it ships (the audit findings
|
|
690
|
+
// for an under-declared manifest already surface in manifest.warnings).
|
|
691
|
+
if (manifest.capabilitiesDeclared) {
|
|
692
|
+
const { describeCapabilities } =
|
|
693
|
+
await import("../lib/plugin-runtime/capabilities.js");
|
|
694
|
+
const capLines = describeCapabilities(manifest.capabilities);
|
|
695
|
+
logger.log(chalk.bold("\nCapabilities (declared):"));
|
|
696
|
+
if (capLines.length === 0) logger.log(chalk.gray(" (none)"));
|
|
697
|
+
for (const l of capLines) logger.log(` ${chalk.magenta(l)}`);
|
|
698
|
+
}
|
|
635
699
|
for (const w of manifest.warnings) logger.log(chalk.yellow(` ⚠ ${w}`));
|
|
636
700
|
for (const e of manifest.errors) logger.log(chalk.red(` ✖ ${e}`));
|
|
637
701
|
if (manifest.ok) {
|
|
@@ -736,8 +800,15 @@ export function registerPluginCommand(program) {
|
|
|
736
800
|
force: options.force === true,
|
|
737
801
|
signature,
|
|
738
802
|
});
|
|
803
|
+
const capNotice = await resolvePluginCapabilityNotice(
|
|
804
|
+
res.name,
|
|
805
|
+
res.scope,
|
|
806
|
+
process.cwd(),
|
|
807
|
+
);
|
|
739
808
|
if (options.json) {
|
|
740
|
-
console.log(
|
|
809
|
+
console.log(
|
|
810
|
+
JSON.stringify({ ...res, capabilities: capNotice }, null, 2),
|
|
811
|
+
);
|
|
741
812
|
} else {
|
|
742
813
|
logger.success(
|
|
743
814
|
`Installed ${res.name} v${res.version} (${res.scope} scope)` +
|
|
@@ -746,6 +817,7 @@ export function registerPluginCommand(program) {
|
|
|
746
817
|
logger.log(chalk.gray(` → ${res.dir}`));
|
|
747
818
|
for (const w of res.warnings || [])
|
|
748
819
|
logger.log(chalk.yellow(` ⚠ ${w}`));
|
|
820
|
+
printPluginCapabilityNotice(res.name, capNotice);
|
|
749
821
|
}
|
|
750
822
|
} catch (err) {
|
|
751
823
|
logger.error(`Install failed: ${err.message}`);
|
|
@@ -905,6 +977,161 @@ export function registerPluginCommand(program) {
|
|
|
905
977
|
}
|
|
906
978
|
});
|
|
907
979
|
|
|
980
|
+
// plugin consent [name] — view / grant a plugin's CAPABILITY consent
|
|
981
|
+
// (process / network / filesystem / mcp / monitor / credential). Distinct from
|
|
982
|
+
// `trust`, which pins the code VERSION: consent pins the capability SET, and
|
|
983
|
+
// any widening re-prompts even for an already-trusted plugin.
|
|
984
|
+
plugin
|
|
985
|
+
.command("consent [name]")
|
|
986
|
+
.description(
|
|
987
|
+
"View or grant a plugin's capability consent (--grant / --revoke / --list)",
|
|
988
|
+
)
|
|
989
|
+
.option("--scope <scope>", "Scope of the plugin", "project")
|
|
990
|
+
.option(
|
|
991
|
+
"--grant",
|
|
992
|
+
"Grant consent for the plugin's currently-declared capabilities",
|
|
993
|
+
)
|
|
994
|
+
.option("--revoke", "Revoke capability consent")
|
|
995
|
+
.option("--list", "List all capability consent entries")
|
|
996
|
+
.option("--json", "Output as JSON")
|
|
997
|
+
.action(async (name, options) => {
|
|
998
|
+
const consent =
|
|
999
|
+
await import("../lib/plugin-runtime/capability-consent.js");
|
|
1000
|
+
const { describeCapabilities } =
|
|
1001
|
+
await import("../lib/plugin-runtime/capabilities.js");
|
|
1002
|
+
|
|
1003
|
+
if (!name || options.list) {
|
|
1004
|
+
const rows = consent.listCapabilityConsent();
|
|
1005
|
+
if (options.json) {
|
|
1006
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
if (rows.length === 0) {
|
|
1010
|
+
logger.info("No capability consent recorded.");
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
logger.log(chalk.bold(`Capability consent (${rows.length}):`));
|
|
1014
|
+
for (const r of rows) {
|
|
1015
|
+
logger.log(` ${chalk.cyan(r.name)} v${r.version} [${r.scope}]`);
|
|
1016
|
+
for (const c of r.capabilities) logger.log(` ${chalk.magenta(c)}`);
|
|
1017
|
+
}
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
if (options.revoke) {
|
|
1022
|
+
const res = consent.revokeCapabilityConsent(name, {
|
|
1023
|
+
scope: options.scope,
|
|
1024
|
+
});
|
|
1025
|
+
if (res.removed) {
|
|
1026
|
+
logger.success(
|
|
1027
|
+
`Revoked capability consent for ${name} (${options.scope} scope)`,
|
|
1028
|
+
);
|
|
1029
|
+
} else {
|
|
1030
|
+
logger.info(
|
|
1031
|
+
`${name} had no capability consent at ${options.scope} scope`,
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// Resolve the installed plugin + its declared capabilities.
|
|
1038
|
+
const { discoverPlugins } =
|
|
1039
|
+
await import("../lib/plugin-runtime/scopes.js");
|
|
1040
|
+
let installed = null;
|
|
1041
|
+
try {
|
|
1042
|
+
installed = discoverPlugins({
|
|
1043
|
+
cwd: process.cwd(),
|
|
1044
|
+
skipPolicy: true,
|
|
1045
|
+
}).find((p) => p.name === name && p.scope === options.scope);
|
|
1046
|
+
} catch {
|
|
1047
|
+
/* discovery best-effort */
|
|
1048
|
+
}
|
|
1049
|
+
if (!installed) {
|
|
1050
|
+
logger.error(`${name} is not installed at ${options.scope} scope`);
|
|
1051
|
+
process.exitCode = 1;
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
const declared = installed.manifest?.capabilities;
|
|
1055
|
+
const entry =
|
|
1056
|
+
consent.loadConsentStore()[`${options.scope}:${name}`] || null;
|
|
1057
|
+
const status = consent.capabilityConsentStatus(declared, entry);
|
|
1058
|
+
|
|
1059
|
+
if (options.grant) {
|
|
1060
|
+
consent.consentPluginCapabilities(name, {
|
|
1061
|
+
scope: options.scope,
|
|
1062
|
+
version: installed.version,
|
|
1063
|
+
capabilities: declared,
|
|
1064
|
+
});
|
|
1065
|
+
const capLines = describeCapabilities(declared);
|
|
1066
|
+
if (options.json) {
|
|
1067
|
+
console.log(
|
|
1068
|
+
JSON.stringify(
|
|
1069
|
+
{
|
|
1070
|
+
granted: true,
|
|
1071
|
+
name,
|
|
1072
|
+
scope: options.scope,
|
|
1073
|
+
version: installed.version,
|
|
1074
|
+
capabilities: capLines,
|
|
1075
|
+
},
|
|
1076
|
+
null,
|
|
1077
|
+
2,
|
|
1078
|
+
),
|
|
1079
|
+
);
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
logger.success(
|
|
1083
|
+
`Granted capability consent for ${name} v${installed.version} (${options.scope} scope)`,
|
|
1084
|
+
);
|
|
1085
|
+
for (const l of capLines) logger.log(` ${chalk.magenta(l)}`);
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// Show status.
|
|
1090
|
+
const capLines = describeCapabilities(declared);
|
|
1091
|
+
if (options.json) {
|
|
1092
|
+
console.log(
|
|
1093
|
+
JSON.stringify(
|
|
1094
|
+
{
|
|
1095
|
+
name,
|
|
1096
|
+
scope: options.scope,
|
|
1097
|
+
version: installed.version,
|
|
1098
|
+
declared: capLines,
|
|
1099
|
+
...status,
|
|
1100
|
+
},
|
|
1101
|
+
null,
|
|
1102
|
+
2,
|
|
1103
|
+
),
|
|
1104
|
+
);
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
logger.log(
|
|
1108
|
+
chalk.bold("Capability consent: ") +
|
|
1109
|
+
`${chalk.cyan(name)} v${installed.version} [${options.scope}]`,
|
|
1110
|
+
);
|
|
1111
|
+
logger.log(chalk.bold("Declared capabilities:"));
|
|
1112
|
+
if (capLines.length === 0) logger.log(chalk.gray(" (none)"));
|
|
1113
|
+
for (const l of capLines) logger.log(` ${chalk.magenta(l)}`);
|
|
1114
|
+
logger.log(
|
|
1115
|
+
chalk.bold("Status: ") +
|
|
1116
|
+
(status.consented
|
|
1117
|
+
? chalk.green("consented")
|
|
1118
|
+
: chalk.yellow("NEEDS CONSENT")),
|
|
1119
|
+
);
|
|
1120
|
+
logger.log(chalk.gray(` ${status.reason}`));
|
|
1121
|
+
if (status.added.length) {
|
|
1122
|
+
logger.log(
|
|
1123
|
+
chalk.yellow(` new since consent: ${status.added.join(", ")}`),
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
if (!status.consented) {
|
|
1127
|
+
logger.log(
|
|
1128
|
+
chalk.dim(
|
|
1129
|
+
` run \`cc plugin consent ${name} --grant\` to allow them`,
|
|
1130
|
+
),
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1134
|
+
|
|
908
1135
|
// plugin monitors — list (and optionally run) trusted plugins' background
|
|
909
1136
|
// monitors. `--run --seconds N` actually starts the supervisor for N seconds,
|
|
910
1137
|
// prints captured output, then reaps everything (verifies no leaked process).
|
|
@@ -1012,6 +1239,17 @@ export function registerPluginCommand(program) {
|
|
|
1012
1239
|
`${res.name} is already up to date at v${res.version} (use --force to reinstall)`,
|
|
1013
1240
|
);
|
|
1014
1241
|
}
|
|
1242
|
+
// Surface the (possibly widened) capability set + re-consent hint. An
|
|
1243
|
+
// upgrade that adds a capability shows a `⚠ capability consent required`
|
|
1244
|
+
// notice diffed against the prior consent.
|
|
1245
|
+
printPluginCapabilityNotice(
|
|
1246
|
+
res.name,
|
|
1247
|
+
await resolvePluginCapabilityNotice(
|
|
1248
|
+
res.name,
|
|
1249
|
+
options.scope,
|
|
1250
|
+
process.cwd(),
|
|
1251
|
+
),
|
|
1252
|
+
);
|
|
1015
1253
|
} catch (err) {
|
|
1016
1254
|
logger.error(`Upgrade failed: ${err.message}`);
|
|
1017
1255
|
process.exitCode = 1;
|