chainlesschain 0.162.108 → 0.162.110
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-B8T7xeuV.js → AIOps-BxhyZq9J.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BDxBsVYw.js → ActionButton-BNUhIcPN.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-5cYrjLzN.js → Analytics-DpE1LQXz.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DkanJ7a5.js → AppLayout-D-vd98us.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-8r3yG5R0.js → Audit-Bnfg0nUP.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DmdJpAwc.js → Backup-84Ryr92i.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CCTC_ROJ.js → BaseInput-BHl2VN49.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BE4xBoCH.js → Chat-C-XyRbZ_.js} +4 -4
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-ita18CO8.js → ChatBubbleRenderer-BQuS_MfK.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-DVUDDyL5.js → Checkbox-gJpyQ5hV.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-C07Q688Z.js → Codegen-NIwk76Jx.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Bfmb67bb.js → Col-Dy7-G7ei.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DvUS6LOy.js → Community-DGPF1QLB.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-B2jvuCf2.js → Compact-Dg2jm5mx.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-B4VnGZOX.js → Compliance-BGaJDYow.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Bf1UR9uf.js → Cowork-CZiec6-_.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-BYU0OhXx.js → Cron-C5plyjhD.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DhtbpJ8Y.js → Crosschain-CGMz-rIE.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DFtdsXjV.js → DID-BLMcsumG.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BOqMlucS.js → Dashboard-DEntKWjF.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DWcGi1lU.js → Dropdown-DyRaZqrS.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-smcpeq18.js → EmailListRenderer-DX_Wu_uV.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-_qKQM8YZ.js → FamilyGuardDashboard-PX0U_pH5.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DxaaO_Pn.js → Federation-DVgF8ZdM.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Bijk4-hV.js → FormItemContext-BRaX9vyC.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CXs5JGAs.js → GenericCardRenderer-Dy_c9ccI.js} +1 -1
- package/src/assets/web-panel/assets/{Git-vopq_XAi.js → Git-Dv8Fh1_w.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-D6lEr1e2.js → Governance-D5vw7D8q.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-D716knRp.js → Inference-Dbt861U-.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Mt7L5B8p.js → KnowledgeGraph-DgkYyYYk.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BmY4Aozy.js → Logs-Bi7BMh2S.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-Bk7yghOV.js → Marketplace-Cu4tyI-N.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BdEGNK8B.js → McpTools-DhOo6KRf.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-DaGxWezP.js → Memory-M6jr41rl.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-D2QqiIXV.js → MobileBridge-mB45yYre.js} +2 -2
- package/src/assets/web-panel/assets/{MobileProjects-JQQ60jth.js → MobileProjects-pQO0ZS_8.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-DHp0q17f.js → Mtc-DIT60RG6.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-RWrCriuc.js → MtcAudit-DV16w59B.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-BR_5j51g.js → Multisig-DzXEXej1.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-MF3ZkPYq.js → NLProgramming-ClR96m-f.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BDWwfQ-t.js → Notes-Cqd3TQ9N.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-Xk8BtRL5.js → NotificationSettings-Ctzz5cx5.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DaVlfAsv.js → OrderTableRenderer-DlEZYyLt.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-CNdU6Jxx.js → Organization-0HyOJovw.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DOf8iK6T.js → Overflow-BInoQKwn.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DvOwefeB.js → P2P-DSMX8d_q.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BmrU5B5U.js → PdhVaultBrowser-Dn8Utbaw.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DM_Cha_-.js → Permissions-Dd_GAUT-.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DGOiN1md.js → PersonalDataHub-CgZ2qfLH.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-BMZcrOpC.js → Pipeline-Bh4OTBfH.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CcBD3ZJ_.js → Privacy-DRdhoeaJ.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BQmkVkFp.js → ProjectInit-bifrkI6i.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BL0iqE5h.js → ProjectSettings-RJ2xbAx3.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-7iyoLF6T.js → Projects-DPMre_Vt.js} +1 -1
- package/src/assets/web-panel/assets/Providers-DXR9-uVd.js +1 -0
- package/src/assets/web-panel/assets/{QuickAsk-Cp0Lpic1.js → QuickAsk-VkUau5yj.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DN-26aef.js → Recommend-D9NMPNb6.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-ChdhhVGu.js → Reputation-D7-6naYX.js} +1 -1
- package/src/assets/web-panel/assets/{Row-2RBgBLEz.js → Row-BX2i0Cmv.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CfogWRiS.js → RssFeed-Byb5WDNx.js} +3 -3
- package/src/assets/web-panel/assets/Search-CZyeEQAc.js +1 -0
- package/src/assets/web-panel/assets/{Security-CcDdV-6d.js → Security-BWR_hUCv.js} +4 -4
- package/src/assets/web-panel/assets/{Services-qG5L2PHE.js → Services-NSc_9EF_.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-7CVgHJwr.js → Skeleton-BlAIlPnB.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BEbdNS7s.js → Skills-CaU0R1CL.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-C4ENvrVM.js → Sla-BnV-Fro4.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DuvM2AsQ.js → SpeechSettings-CBHGfr4p.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Bm9Np5Yt.js → SyncSettings-BwthnPiP.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-KdXnN3nt.js +1 -0
- package/src/assets/web-panel/assets/{Templates-C3GJa4ya.js → Templates-BN4WwLUS.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BLQnLb8K.js → Tenant-CZ24n54K.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BGgZIwb0.js → Terminal-BelzaRsd.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BD8NwNAK.js → TimelineRenderer-DhkqTGFa.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BIsO2iiX.js → Tokens-46_v1nkV.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-FJv5zeym.js → Trigger-DogUmIqL.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-9FRFEAhW.js → Trust-BRByKmic.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-aOdZVwgi.js → UkeySign-BZmDgBR1.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-C1KyY7ZB.js → VideoEditing-DVvG6VSG.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CH5edY0l.js → Wallet-ChxdrDjz.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-BUPlkFe0.js → WebAuthn-BkX-jd9_.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-BoH8bG7y.js → WorkflowEditor-DtWPAhrj.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DRS34SZq.js → chat-FLdS_X89.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DxdhROQh.js → colors-CdqVIbAc.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-C5f4y3lM.js → compact-item-B1em0FOh.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DkyzEt2y.js → createContext-xqQveKVc.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-FuXjMk0L.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CIRkRQ69.js → hasIn-C2Ooy3F5.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfZal2SO.js → index-2N63y-3V.js} +1 -1
- package/src/assets/web-panel/assets/{index-1Vd3bgPH.js → index-6x_TEnAx.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_ytTrAE.js → index-7hThnUln.js} +1 -1
- package/src/assets/web-panel/assets/{index-Kyd48UEO.js → index-B4vllS2j.js} +1 -1
- package/src/assets/web-panel/assets/{index-BrNCtBLG.js → index-B7MunZX_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgJf60U0.js → index-BENOufkM.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cv9YEv3M.js → index-BI7hFUhB.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dz-Xl9WT.js → index-BIDq5ivM.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5r9kCHD.js → index-BINmnZLO.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXE4fjlC.js → index-BP7jquLi.js} +1 -1
- package/src/assets/web-panel/assets/index-B_D6g2oc.js +1 -0
- package/src/assets/web-panel/assets/{index-DSpV3T2E.js → index-BcWe6vyc.js} +1 -1
- package/src/assets/web-panel/assets/{index-wdXKonKr.js → index-BfcxX38y.js} +1 -1
- package/src/assets/web-panel/assets/{index-fgBegFCK.js → index-BhuXJoB0.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtOM1jbe.js → index-Bkyb8UQL.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bc59VV8o.js → index-BqXBDJNB.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpHZisZn.js → index-Br134Y_p.js} +1 -1
- package/src/assets/web-panel/assets/index-C-2_nxKI.js +1 -0
- package/src/assets/web-panel/assets/{index-D3WhDg1s.js → index-C0YSl1cy.js} +3 -3
- package/src/assets/web-panel/assets/{index-DV5W06OY.js → index-CDK13Wi9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BStiYEJX.js → index-CPsgpARa.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLLkL7YU.js → index-C_4O2sqg.js} +1 -1
- package/src/assets/web-panel/assets/{index-5Mph6K8H.js → index-CevlY-ti.js} +1 -1
- package/src/assets/web-panel/assets/{index-dtUUXiuv.js → index-CtJcdcSi.js} +1 -1
- package/src/assets/web-panel/assets/{index-CyMVWOwq.js → index-Cw90dIiq.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLjIL9vZ.js → index-D1Y0w9Bp.js} +1 -1
- package/src/assets/web-panel/assets/{index-btOwnCpT.js → index-D62oOPYQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0VYKzdU.js → index-DBcAYh1Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bskk24yp.js → index-DBemh8BC.js} +1 -1
- package/src/assets/web-panel/assets/{index-CSHsX3UZ.js → index-DC2j3ZH1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHACYX-h.js → index-DDoQxg20.js} +1 -1
- package/src/assets/web-panel/assets/{index-CO4PAGWX.js → index-DJsz-ISP.js} +1 -1
- package/src/assets/web-panel/assets/{index-X7QcpsDO.js → index-DaiNFrXW.js} +1 -1
- package/src/assets/web-panel/assets/{index-BzC5O6l6.js → index-I3PgXyCp.js} +1 -1
- package/src/assets/web-panel/assets/{index-2uQYIxL1.js → index-XI6xtz_p.js} +1 -1
- package/src/assets/web-panel/assets/{index-DnAbauX-.js → index-dZPaczgC.js} +1 -1
- package/src/assets/web-panel/assets/{index-BdzFfdXs.js → index-vPuKHNG_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdLAPzIl.js → index-wAA_52rV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DR_uUG6x.js → index-yjlraVSz.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-4uTrrqjb.js → initDefaultProps-DAa9deN1.js} +1 -1
- package/src/assets/web-panel/assets/{motion-gkRvxGHW.js → motion-DrpdeaZb.js} +1 -1
- package/src/assets/web-panel/assets/{move-CIgnaWNe.js → move-Tqv8fQ0W.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DB-Ux8Gg.js → omit-DHA5fID2.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-ree-Itb0.js → pickAttrs-bsvBPVv5.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-EdAP6bCf.js → placementArrow-Cl6kkCqO.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-B1DqSgFh.js → responsiveObserve-BEFo1aZl.js} +1 -1
- package/src/assets/web-panel/assets/{slide-D_9NP_KE.js → slide-J59l_i_x.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CAytzvoR.js → statusUtils-CgXZ-Y0K.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-zb3gob1_.js → styleChecker-CGQqxIef.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BQWuGrL0.js → useFlexGapSupport-CgO0iWFX.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-QMEsYkGX.js → useFs-DzTO2udE.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-Ccrz7-au.js → usePersonalDataHub-Brihie_5.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DZVY4QM-.js → vnode-BSsmn1zK.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Dii_0V_X.js → zoom-Dofbccho.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +6 -0
- package/src/commands/hub.js +6 -2
- package/src/commands/mcp.js +42 -10
- package/src/commands/pdh.js +4 -1
- package/src/constants.js +2 -2
- package/src/gateways/ws/video-protocol.js +7 -0
- package/src/harness/feature-flags.js +19 -6
- package/src/harness/prompt-compressor.js +1 -0
- package/src/lib/agent-core.js +2 -0
- package/src/lib/config-manager.js +42 -1
- package/src/lib/git-integration.js +1 -2
- package/src/lib/learning/learning-hooks.js +6 -0
- package/src/lib/llm-providers.js +2 -0
- package/src/lib/mcp-auth-target.js +53 -0
- package/src/lib/repl-bang-memorize.js +27 -1
- package/src/lib/runnable-provider.js +12 -0
- package/src/lib/settings-loader.cjs +29 -1
- package/src/lib/shell-approval.js +7 -12
- package/src/lib/skill-loader.js +6 -2
- package/src/lib/suggest-name.js +91 -0
- package/src/lib/task-model-selector.js +5 -5
- package/src/repl/agent-repl.js +31 -3
- package/src/repl/slash-macro.js +7 -1
- package/src/runtime/agent-core.js +62 -3
- package/src/runtime/coding-agent-contract-shared.cjs +11 -1
- package/src/runtime/headless-runner.js +57 -4
- package/src/runtime/headless-stream.js +50 -2
- package/src/session/index.js +6 -2
- package/src/assets/web-panel/assets/Providers-IQqLZ_11.js +0 -1
- package/src/assets/web-panel/assets/Search-CrREUZpz.js +0 -1
- package/src/assets/web-panel/assets/Tasks-K2ZtJ47C.js +0 -1
- package/src/assets/web-panel/assets/devWarning-vwZx8E0a.js +0 -1
- package/src/assets/web-panel/assets/index-Dhn6EWMQ.js +0 -1
- package/src/assets/web-panel/assets/index-dGvDljur.js +0 -1
|
@@ -187,7 +187,9 @@ function addRule({ cwd = process.cwd(), kind, rule, scope = "project" } = {}) {
|
|
|
187
187
|
try {
|
|
188
188
|
data = JSON.parse(text) || {};
|
|
189
189
|
} catch (err) {
|
|
190
|
-
throw new Error(
|
|
190
|
+
throw new Error(
|
|
191
|
+
`refusing to overwrite malformed ${file} (${err.message})`,
|
|
192
|
+
);
|
|
191
193
|
}
|
|
192
194
|
}
|
|
193
195
|
if (!data.permissions || typeof data.permissions !== "object") {
|
|
@@ -237,10 +239,36 @@ function loadSettingsConfig(opts = {}) {
|
|
|
237
239
|
return { model, env, files };
|
|
238
240
|
}
|
|
239
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Read a top-level boolean setting across the layered `.claude/settings.json`
|
|
244
|
+
* files (last/closest layer wins — same precedence as the permission rules).
|
|
245
|
+
* Non-boolean values are ignored. Returns `undefined` when no layer sets it, so
|
|
246
|
+
* a caller can distinguish "unset" from an explicit `false`.
|
|
247
|
+
*
|
|
248
|
+
* @param {string} key top-level settings key (e.g. "respondToBashCommands")
|
|
249
|
+
* @param {object} [opts]
|
|
250
|
+
* @param {string} [opts.cwd=process.cwd()]
|
|
251
|
+
* @param {string} [opts.settingsFile]
|
|
252
|
+
* @param {(msg:string)=>void} [opts.onWarn]
|
|
253
|
+
* @returns {boolean|undefined}
|
|
254
|
+
*/
|
|
255
|
+
function readBooleanSetting(key, opts = {}) {
|
|
256
|
+
const cwd = opts.cwd || process.cwd();
|
|
257
|
+
let value;
|
|
258
|
+
for (const file of settingsPaths(cwd, opts.settingsFile)) {
|
|
259
|
+
const data = readSettingsFile(file, { onWarn: opts.onWarn });
|
|
260
|
+
if (data && typeof data[key] === "boolean") {
|
|
261
|
+
value = data[key]; // last (closest) layer wins
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return value;
|
|
265
|
+
}
|
|
266
|
+
|
|
240
267
|
module.exports = {
|
|
241
268
|
loadSettings,
|
|
242
269
|
loadSettingsConfig,
|
|
243
270
|
readSettingsFile,
|
|
271
|
+
readBooleanSetting,
|
|
244
272
|
settingsPaths,
|
|
245
273
|
parseEnvList,
|
|
246
274
|
ruleSource,
|
|
@@ -19,20 +19,20 @@
|
|
|
19
19
|
* riskLevel, policy }` so callers don't have to juggle two decision types.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
APPROVAL_RISK
|
|
25
|
-
APPROVAL_DECISION
|
|
26
|
-
}
|
|
22
|
+
import sharedShellPolicy from "../runtime/coding-agent-shell-policy.cjs";
|
|
23
|
+
import {
|
|
24
|
+
APPROVAL_RISK as RISK,
|
|
25
|
+
APPROVAL_DECISION as DECISION,
|
|
26
|
+
} from "@chainlesschain/session-core";
|
|
27
27
|
|
|
28
|
-
const SHELL_TO_RISK = {
|
|
28
|
+
export const SHELL_TO_RISK = {
|
|
29
29
|
allow: RISK.LOW,
|
|
30
30
|
warn: RISK.MEDIUM,
|
|
31
31
|
deny: RISK.HIGH,
|
|
32
32
|
reroute: RISK.HIGH,
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
async function evaluateShellCommandWithApproval({
|
|
35
|
+
export async function evaluateShellCommandWithApproval({
|
|
36
36
|
command,
|
|
37
37
|
sessionId = null,
|
|
38
38
|
approvalGate = null,
|
|
@@ -89,8 +89,3 @@ async function evaluateShellCommandWithApproval({
|
|
|
89
89
|
policy: gateResult.policy,
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
|
-
|
|
93
|
-
module.exports = {
|
|
94
|
-
evaluateShellCommandWithApproval,
|
|
95
|
-
SHELL_TO_RISK,
|
|
96
|
-
};
|
package/src/lib/skill-loader.js
CHANGED
|
@@ -129,8 +129,12 @@ export function parseSkillMd(content) {
|
|
|
129
129
|
const key = trimmed.slice(0, colonIndex).trim();
|
|
130
130
|
let value = trimmed.slice(colonIndex + 1).trim();
|
|
131
131
|
|
|
132
|
-
//
|
|
133
|
-
|
|
132
|
+
// Normalize frontmatter key case to camelCase. Claude-Code skills
|
|
133
|
+
// (auto-loaded from .claude/skills) may author keys in kebab-case,
|
|
134
|
+
// snake_case, or camelCase interchangeably (Claude Code 2.1.186), e.g.
|
|
135
|
+
// `display-name` / `display_name` / `displayName`. Treat all three the
|
|
136
|
+
// same so the field reads below (data.displayName, …) pick them up.
|
|
137
|
+
const camelKey = key.replace(/[-_]([a-z])/g, (_, c) => c.toUpperCase());
|
|
134
138
|
|
|
135
139
|
if (value === "") {
|
|
136
140
|
currentKey = camelKey;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* suggest-name — "did you mean?" suggestions for a mistyped identifier.
|
|
3
|
+
*
|
|
4
|
+
* Combines substring containment (a partial name) with bounded Levenshtein edit
|
|
5
|
+
* distance (single-char typos / transpositions) so `cc mcp remove cotnext7`
|
|
6
|
+
* suggests `context7`. Pure + dependency-free; reusable by any command that
|
|
7
|
+
* resolves a user-supplied name against a known set (MCP servers, skills, …).
|
|
8
|
+
*
|
|
9
|
+
* Claude-Code 2.1.186 parity: `mcp get/remove` typo suggestions.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Levenshtein edit distance between two strings (iterative two-row, O(n*m)). */
|
|
13
|
+
export function editDistance(a, b) {
|
|
14
|
+
a = String(a ?? "");
|
|
15
|
+
b = String(b ?? "");
|
|
16
|
+
if (a === b) return 0;
|
|
17
|
+
if (!a.length) return b.length;
|
|
18
|
+
if (!b.length) return a.length;
|
|
19
|
+
let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
20
|
+
let curr = new Array(b.length + 1);
|
|
21
|
+
for (let i = 1; i <= a.length; i++) {
|
|
22
|
+
curr[0] = i;
|
|
23
|
+
for (let j = 1; j <= b.length; j++) {
|
|
24
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
25
|
+
curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
|
|
26
|
+
}
|
|
27
|
+
const tmp = prev;
|
|
28
|
+
prev = curr;
|
|
29
|
+
curr = tmp;
|
|
30
|
+
}
|
|
31
|
+
return prev[b.length];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Rank candidates by closeness to `target` and return up to `limit` names.
|
|
36
|
+
* A candidate qualifies if it contains `target` (or vice-versa) as a
|
|
37
|
+
* case-insensitive substring, OR is within `maxDistance` edits (default scales
|
|
38
|
+
* with target length, capped at 3). Exact matches are excluded (not a typo).
|
|
39
|
+
* Output is ordered: substring matches first, then by edit distance, then
|
|
40
|
+
* alphabetically (stable).
|
|
41
|
+
*
|
|
42
|
+
* @param {string} target
|
|
43
|
+
* @param {string[]} candidates
|
|
44
|
+
* @param {{ limit?: number, maxDistance?: number }} [opts]
|
|
45
|
+
* @returns {string[]}
|
|
46
|
+
*/
|
|
47
|
+
export function suggestNames(target, candidates, opts = {}) {
|
|
48
|
+
const t = String(target ?? "")
|
|
49
|
+
.trim()
|
|
50
|
+
.toLowerCase();
|
|
51
|
+
if (!t || !Array.isArray(candidates)) return [];
|
|
52
|
+
const limit = opts.limit ?? 3;
|
|
53
|
+
const maxDistance =
|
|
54
|
+
opts.maxDistance ?? Math.min(3, Math.max(1, Math.floor(t.length / 3)));
|
|
55
|
+
const scored = [];
|
|
56
|
+
for (const raw of candidates) {
|
|
57
|
+
const name = String(raw ?? "");
|
|
58
|
+
if (!name) continue;
|
|
59
|
+
const lc = name.toLowerCase();
|
|
60
|
+
if (lc === t) continue; // exact match isn't a "did you mean"
|
|
61
|
+
const contains = lc.includes(t) || t.includes(lc);
|
|
62
|
+
const dist = editDistance(t, lc);
|
|
63
|
+
if (contains || dist <= maxDistance) {
|
|
64
|
+
scored.push({ name, contains: contains ? 0 : 1, dist });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
scored.sort((a, b) => {
|
|
68
|
+
if (a.contains !== b.contains) return a.contains - b.contains;
|
|
69
|
+
if (a.dist !== b.dist) return a.dist - b.dist;
|
|
70
|
+
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
|
71
|
+
});
|
|
72
|
+
return scored.slice(0, limit).map((s) => s.name);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Build a `<noun> "<target>" not found` message, appending a `— did you mean
|
|
77
|
+
* "x", "y"?` hint when there are close candidates. Returns just the base when
|
|
78
|
+
* there are none.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} target
|
|
81
|
+
* @param {string[]} candidates
|
|
82
|
+
* @param {{ noun?: string, limit?: number, maxDistance?: number }} [opts]
|
|
83
|
+
* @returns {string}
|
|
84
|
+
*/
|
|
85
|
+
export function notFoundWithSuggestion(target, candidates, opts = {}) {
|
|
86
|
+
const noun = opts.noun || "item";
|
|
87
|
+
const base = `${noun} "${target}" not found`;
|
|
88
|
+
const hints = suggestNames(target, candidates, opts);
|
|
89
|
+
if (!hints.length) return base;
|
|
90
|
+
return `${base} — did you mean ${hints.map((h) => `"${h}"`).join(", ")}?`;
|
|
91
|
+
}
|
|
@@ -24,7 +24,7 @@ export const TaskType = {
|
|
|
24
24
|
*/
|
|
25
25
|
const TASK_MODEL_MAP = {
|
|
26
26
|
[TaskType.CHAT]: {
|
|
27
|
-
volcengine: "doubao-seed-1-
|
|
27
|
+
volcengine: "doubao-seed-2-1-pro-260628",
|
|
28
28
|
openai: "gpt-4o-mini",
|
|
29
29
|
anthropic: "claude-sonnet-4-6",
|
|
30
30
|
deepseek: "deepseek-chat",
|
|
@@ -36,7 +36,7 @@ const TASK_MODEL_MAP = {
|
|
|
36
36
|
ollama: "qwen2.5:7b",
|
|
37
37
|
},
|
|
38
38
|
[TaskType.CODE]: {
|
|
39
|
-
volcengine: "doubao-seed-1-
|
|
39
|
+
volcengine: "doubao-seed-2-1-pro-260628",
|
|
40
40
|
openai: "gpt-4o",
|
|
41
41
|
anthropic: "claude-sonnet-4-6",
|
|
42
42
|
deepseek: "deepseek-coder",
|
|
@@ -48,7 +48,7 @@ const TASK_MODEL_MAP = {
|
|
|
48
48
|
ollama: "qwen2.5-coder:14b",
|
|
49
49
|
},
|
|
50
50
|
[TaskType.REASONING]: {
|
|
51
|
-
volcengine: "doubao-seed-1-
|
|
51
|
+
volcengine: "doubao-seed-2-1-pro-260628",
|
|
52
52
|
openai: "o1",
|
|
53
53
|
anthropic: "claude-opus-4-6",
|
|
54
54
|
deepseek: "deepseek-reasoner",
|
|
@@ -72,7 +72,7 @@ const TASK_MODEL_MAP = {
|
|
|
72
72
|
ollama: "qwen2:7b",
|
|
73
73
|
},
|
|
74
74
|
[TaskType.TRANSLATE]: {
|
|
75
|
-
volcengine: "doubao-seed-1-
|
|
75
|
+
volcengine: "doubao-seed-2-1-pro-260628",
|
|
76
76
|
openai: "gpt-4o",
|
|
77
77
|
anthropic: "claude-sonnet-4-6",
|
|
78
78
|
deepseek: "deepseek-chat",
|
|
@@ -84,7 +84,7 @@ const TASK_MODEL_MAP = {
|
|
|
84
84
|
ollama: "qwen2:7b",
|
|
85
85
|
},
|
|
86
86
|
[TaskType.CREATIVE]: {
|
|
87
|
-
volcengine: "doubao-seed-1-
|
|
87
|
+
volcengine: "doubao-seed-2-1-pro-260628",
|
|
88
88
|
openai: "gpt-4o",
|
|
89
89
|
anthropic: "claude-opus-4-6",
|
|
90
90
|
deepseek: "deepseek-chat",
|
package/src/repl/agent-repl.js
CHANGED
|
@@ -112,6 +112,10 @@ let _permissionRules = null;
|
|
|
112
112
|
let _permissionConfirm = null;
|
|
113
113
|
// .claude/settings.json `hooks` block (decision-capable PreToolUse/PostToolUse).
|
|
114
114
|
let _settingsHooks = null;
|
|
115
|
+
// .claude/settings.json `respondToBashCommands` (Claude-Code 2.1.186): whether a
|
|
116
|
+
// `!command` auto-triggers an assistant response to its output. undefined =
|
|
117
|
+
// unset → defaults OFF (opt-in) in shouldRespondToBashCommands.
|
|
118
|
+
let _respondToBash;
|
|
115
119
|
|
|
116
120
|
/**
|
|
117
121
|
* Fire settings.json Notification hooks (observe-only) — the agent needs the
|
|
@@ -444,8 +448,13 @@ export async function startAgentRepl(options = {}) {
|
|
|
444
448
|
// Load .claude/settings.json permission rules + wire an interactive confirmer
|
|
445
449
|
// so `ask` rules prompt (rather than fall closed like headless does).
|
|
446
450
|
try {
|
|
447
|
-
const { loadSettings } =
|
|
451
|
+
const { loadSettings, readBooleanSetting } =
|
|
452
|
+
await import("../lib/settings-loader.cjs");
|
|
448
453
|
const loaded = loadSettings({ cwd: process.cwd() });
|
|
454
|
+
// Claude-Code 2.1.186 respondToBashCommands (default OFF / opt-in when unset).
|
|
455
|
+
_respondToBash = readBooleanSetting("respondToBashCommands", {
|
|
456
|
+
cwd: process.cwd(),
|
|
457
|
+
});
|
|
449
458
|
const total =
|
|
450
459
|
loaded.rules.allow.length +
|
|
451
460
|
loaded.rules.ask.length +
|
|
@@ -1253,7 +1262,8 @@ export async function startAgentRepl(options = {}) {
|
|
|
1253
1262
|
// no LLM round-trip — and fold the output into the conversation context.
|
|
1254
1263
|
if (trimmed.startsWith("!") && trimmed.slice(1).trim()) {
|
|
1255
1264
|
try {
|
|
1256
|
-
const { runBangCommand } =
|
|
1265
|
+
const { runBangCommand, shouldRespondToBashCommands } =
|
|
1266
|
+
await import("../lib/repl-bang-memorize.js");
|
|
1257
1267
|
const res = runBangCommand(trimmed, { cwd: process.cwd() });
|
|
1258
1268
|
logger.log(chalk.gray(`$ ${res.cmd}`));
|
|
1259
1269
|
if (res.stdout)
|
|
@@ -1268,6 +1278,16 @@ export async function startAgentRepl(options = {}) {
|
|
|
1268
1278
|
);
|
|
1269
1279
|
if (res.error) logger.error(`shell error: ${res.error.message}`);
|
|
1270
1280
|
logger.log(chalk.gray(`(exit ${res.exitCode})`));
|
|
1281
|
+
// Claude-Code 2.1.186 `respondToBashCommands` (opt-in, default OFF):
|
|
1282
|
+
// when enabled, the assistant automatically responds to the command
|
|
1283
|
+
// output. Re-dispatch the captured <bash-input>/<bash-output> as a
|
|
1284
|
+
// normal user turn so the FULL turn machinery (streaming render, tools,
|
|
1285
|
+
// session persistence) is reused — the bash-tagged content can't
|
|
1286
|
+
// re-trigger the `!` branch.
|
|
1287
|
+
if (shouldRespondToBashCommands({ settingValue: _respondToBash })) {
|
|
1288
|
+
await handleLine(res.contextMessage.content);
|
|
1289
|
+
return; // the nested turn already re-prompted
|
|
1290
|
+
}
|
|
1271
1291
|
messages.push(res.contextMessage);
|
|
1272
1292
|
} catch (err) {
|
|
1273
1293
|
logger.error(`! command failed: ${err.message}`);
|
|
@@ -1317,7 +1337,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
1317
1337
|
logger.log(
|
|
1318
1338
|
` ${chalk.cyan("… \\")} End a line with \\ to continue input onto the next line`,
|
|
1319
1339
|
);
|
|
1320
|
-
logger.log(
|
|
1340
|
+
logger.log(
|
|
1341
|
+
` ${chalk.cyan("/exit")} Exit the agent (alias: /quit)`,
|
|
1342
|
+
);
|
|
1321
1343
|
logger.log(
|
|
1322
1344
|
` ${chalk.cyan("/model")} Show/change model (/model <name>)`,
|
|
1323
1345
|
);
|
|
@@ -1338,6 +1360,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
1338
1360
|
logger.log(
|
|
1339
1361
|
` ${chalk.cyan("/theme")} Color theme (/theme <auto|dark|light|mono>; mono = no color)`,
|
|
1340
1362
|
);
|
|
1363
|
+
logger.log(
|
|
1364
|
+
` ${chalk.cyan("/output-style")} Response persona (/output-style <name|list>; explanatory/learning built-in)`,
|
|
1365
|
+
);
|
|
1341
1366
|
logger.log(
|
|
1342
1367
|
` ${chalk.cyan("/config")} Show config; ${chalk.cyan("/config <key>")} read, ${chalk.cyan("/config <key>=<val>")} set, ${chalk.cyan("/config --help")} keys`,
|
|
1343
1368
|
);
|
|
@@ -1423,6 +1448,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
1423
1448
|
logger.log(
|
|
1424
1449
|
` ${chalk.cyan("/ide")} IDE bridge status (connected editor, tools, or why not)`,
|
|
1425
1450
|
);
|
|
1451
|
+
logger.log(
|
|
1452
|
+
` ${chalk.cyan("/mcp")} MCP server status + tools (/mcp <name> for one server)`,
|
|
1453
|
+
);
|
|
1426
1454
|
logger.log(chalk.bold("\nCapabilities:"));
|
|
1427
1455
|
logger.log(" Read, write, and edit files");
|
|
1428
1456
|
logger.log(" Run shell commands (git, npm, etc.)");
|
package/src/repl/slash-macro.js
CHANGED
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
* @param {string} input raw REPL line
|
|
16
16
|
* @param {object} [opts] { cwd, deps:{ getCommand, expandCommand } }
|
|
17
17
|
* @returns {Promise<{matched:boolean, promptText:string, warnings:string[],
|
|
18
|
-
* name?:string, scope?:string
|
|
18
|
+
* name?:string, scope?:string, model?:string|null,
|
|
19
|
+
* allowedTools?:string|null}>}
|
|
20
|
+
* `model` / `allowedTools` are the matched command's frontmatter (or null),
|
|
21
|
+
* so a caller running the macro as a whole turn can scope the run the same
|
|
22
|
+
* way `cc command run` does.
|
|
19
23
|
*/
|
|
20
24
|
export async function resolveSlashMacro(input, opts = {}) {
|
|
21
25
|
const cwd = opts.cwd || process.cwd();
|
|
@@ -41,5 +45,7 @@ export async function resolveSlashMacro(input, opts = {}) {
|
|
|
41
45
|
warnings,
|
|
42
46
|
name: macro.name,
|
|
43
47
|
scope: macro.scope,
|
|
48
|
+
model: macro.model || null,
|
|
49
|
+
allowedTools: macro.allowedTools || null,
|
|
44
50
|
};
|
|
45
51
|
}
|
|
@@ -1546,17 +1546,43 @@ async function executeToolInner(
|
|
|
1546
1546
|
}
|
|
1547
1547
|
// Hashline mode: prefix each line with a 6-char content hash tag
|
|
1548
1548
|
// so downstream edit_file_hashed calls can anchor by hash.
|
|
1549
|
-
|
|
1549
|
+
let rendered = args.hashed === true ? annotateLines(content) : content;
|
|
1550
|
+
|
|
1551
|
+
// Line-range slice (Claude-Code Read offset/limit parity): `offset` is the
|
|
1552
|
+
// 1-based first line, `limit` the max line count — so a file larger than
|
|
1553
|
+
// the size cap can be paged through instead of being stuck at its head.
|
|
1554
|
+
// Coerces numeric strings the model may emit ("10" → 10).
|
|
1555
|
+
const toPos = (v) => {
|
|
1556
|
+
const n = typeof v === "number" ? v : parseInt(v, 10);
|
|
1557
|
+
return Number.isInteger(n) && n > 0 ? n : null;
|
|
1558
|
+
};
|
|
1559
|
+
const offset = toPos(args.offset);
|
|
1560
|
+
const limit = toPos(args.limit);
|
|
1561
|
+
let range = null;
|
|
1562
|
+
if (offset || limit) {
|
|
1563
|
+
const lines = rendered.split("\n");
|
|
1564
|
+
const start = offset ? offset - 1 : 0;
|
|
1565
|
+
const end = limit != null ? start + limit : lines.length;
|
|
1566
|
+
rendered = lines.slice(start, end).join("\n");
|
|
1567
|
+
range = {
|
|
1568
|
+
startLine: Math.min(start + 1, lines.length),
|
|
1569
|
+
endLine: Math.min(end, lines.length),
|
|
1570
|
+
totalLines: lines.length,
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1550
1574
|
if (rendered.length > 50000) {
|
|
1551
1575
|
return attachDescriptor({
|
|
1552
1576
|
content: rendered.substring(0, 50000) + "\n...(truncated)",
|
|
1553
1577
|
size: rendered.length,
|
|
1554
1578
|
hashed: args.hashed === true,
|
|
1579
|
+
...(range ? { range } : {}),
|
|
1555
1580
|
});
|
|
1556
1581
|
}
|
|
1557
1582
|
return attachDescriptor({
|
|
1558
1583
|
content: rendered,
|
|
1559
1584
|
hashed: args.hashed === true,
|
|
1585
|
+
...(range ? { range } : {}),
|
|
1560
1586
|
});
|
|
1561
1587
|
}
|
|
1562
1588
|
|
|
@@ -2633,6 +2659,36 @@ async function _executeRunCode(args, cwd) {
|
|
|
2633
2659
|
*/
|
|
2634
2660
|
export const MAX_SUB_AGENT_DEPTH = 5;
|
|
2635
2661
|
|
|
2662
|
+
/**
|
|
2663
|
+
* Per-tool-result character cap fed back to the model. One giant tool output (a
|
|
2664
|
+
* huge file, a verbose command, an MCP blob) must not blow the context window —
|
|
2665
|
+
* but the model is TOLD when output is cut, instead of the old silent
|
|
2666
|
+
* `substring(0, 5000)` that sliced mid-content with no marker and undercut even
|
|
2667
|
+
* read_file's own 50k self-limit. Tools that self-limit (read_file, notebook
|
|
2668
|
+
* render) stay below this; it is the final safety net for the ones that don't
|
|
2669
|
+
* (run_shell, search_files, run_code, MCP). Override with CC_MAX_TOOL_RESULT_CHARS.
|
|
2670
|
+
*/
|
|
2671
|
+
export const MAX_TOOL_RESULT_CHARS = (() => {
|
|
2672
|
+
const n = Number(process.env.CC_MAX_TOOL_RESULT_CHARS);
|
|
2673
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : 50000;
|
|
2674
|
+
})();
|
|
2675
|
+
|
|
2676
|
+
/**
|
|
2677
|
+
* Cap a serialized tool result to `max` chars, appending a visible truncation
|
|
2678
|
+
* marker (with the original length + how to get the rest) when it overflows —
|
|
2679
|
+
* so the model never silently receives a mid-content slice. Pure; exported for
|
|
2680
|
+
* tests.
|
|
2681
|
+
*/
|
|
2682
|
+
export function capToolResultString(serialized, max = MAX_TOOL_RESULT_CHARS) {
|
|
2683
|
+
const s = String(serialized ?? "");
|
|
2684
|
+
if (s.length <= max) return s;
|
|
2685
|
+
return (
|
|
2686
|
+
s.slice(0, max) +
|
|
2687
|
+
`\n…[tool output truncated: showing the first ${max} of ${s.length} chars` +
|
|
2688
|
+
` — narrow the request (read a line range, grep, or paginate) to see the rest]`
|
|
2689
|
+
);
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2636
2692
|
/**
|
|
2637
2693
|
* Execute a spawn_sub_agent tool call.
|
|
2638
2694
|
* Creates an isolated SubAgentContext, runs it, and returns only the summary.
|
|
@@ -3112,7 +3168,7 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
3112
3168
|
dashscope: "qwen-turbo",
|
|
3113
3169
|
mistral: "mistral-large-latest",
|
|
3114
3170
|
gemini: "gemini-2.0-flash",
|
|
3115
|
-
volcengine: "doubao-seed-1-
|
|
3171
|
+
volcengine: "doubao-seed-2-1-pro-260628",
|
|
3116
3172
|
};
|
|
3117
3173
|
|
|
3118
3174
|
// Real token streaming (--include-partial-messages) for every OpenAI-compatible
|
|
@@ -4586,7 +4642,10 @@ export async function* agentLoop(messages, options) {
|
|
|
4586
4642
|
|
|
4587
4643
|
// Append budget warning to tool result so the LLM sees it
|
|
4588
4644
|
const warningMsg = budget.toWarningMessage();
|
|
4589
|
-
|
|
4645
|
+
// Cap an individual tool result so one giant output can't blow the
|
|
4646
|
+
// context — but tell the model when we cut it (no more silent
|
|
4647
|
+
// mid-content slice). See MAX_TOOL_RESULT_CHARS / capToolResultString.
|
|
4648
|
+
const resultStr = capToolResultString(JSON.stringify(toolResult));
|
|
4590
4649
|
const toolContent = warningMsg
|
|
4591
4650
|
? `${resultStr}\n\n${warningMsg}`
|
|
4592
4651
|
: resultStr;
|
|
@@ -28,11 +28,21 @@ const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
|
|
|
28
28
|
kind: "filesystem",
|
|
29
29
|
tier: "mvp",
|
|
30
30
|
description:
|
|
31
|
-
"Read a file's content. Jupyter notebooks (.ipynb) are rendered as a compact cell listing (index/id/type/source, outputs hidden) for use with notebook_edit — pass raw:true for the underlying JSON.",
|
|
31
|
+
"Read a file's content. For a large file, page through it with offset+limit (line range) instead of re-reading the head. Jupyter notebooks (.ipynb) are rendered as a compact cell listing (index/id/type/source, outputs hidden) for use with notebook_edit — pass raw:true for the underlying JSON.",
|
|
32
32
|
inputSchema: {
|
|
33
33
|
type: "object",
|
|
34
34
|
properties: {
|
|
35
35
|
path: { type: "string", description: "File path to read" },
|
|
36
|
+
offset: {
|
|
37
|
+
type: "integer",
|
|
38
|
+
description:
|
|
39
|
+
"1-based line number to start reading from (omit to read from the top). Use with limit to page a large file.",
|
|
40
|
+
},
|
|
41
|
+
limit: {
|
|
42
|
+
type: "integer",
|
|
43
|
+
description:
|
|
44
|
+
"Maximum number of lines to return starting at offset (omit to read to end, subject to the size cap).",
|
|
45
|
+
},
|
|
36
46
|
hashed: {
|
|
37
47
|
type: "boolean",
|
|
38
48
|
description:
|
|
@@ -250,7 +250,10 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
250
250
|
);
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
-
const
|
|
253
|
+
// `let` (not const): a custom-command macro's `model:` frontmatter may
|
|
254
|
+
// override it below (when the user passed no explicit --model), mirroring
|
|
255
|
+
// `cc command run`.
|
|
256
|
+
let model = options.model || "qwen2.5:7b";
|
|
254
257
|
const provider = options.provider || "ollama";
|
|
255
258
|
const baseUrl = options.baseUrl || "http://localhost:11434";
|
|
256
259
|
const apiKey = options.apiKey || null;
|
|
@@ -369,12 +372,60 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
369
372
|
return { exitCode: isError ? 1 : 0, result: text, isError };
|
|
370
373
|
}
|
|
371
374
|
|
|
375
|
+
// ── Custom slash-command macros (Claude-Code parity: a .claude/commands/*
|
|
376
|
+
// command runs in `-p` mode too, not just the interactive REPL). A leading
|
|
377
|
+
// `/name …` that resolves to a user/project command is expanded into its
|
|
378
|
+
// prompt template ($ARGUMENTS / $1.. + !`bang` + @file) before the turn; an
|
|
379
|
+
// unknown `/...` (or plain text) is left untouched so it reaches the LLM
|
|
380
|
+
// verbatim. expandCommand already runs @file expansion, so the @-ref pass
|
|
381
|
+
// below is skipped when a macro matched. Opt out with options.slashMacros:false.
|
|
382
|
+
let userContent = prompt;
|
|
383
|
+
let slashExpanded = false;
|
|
384
|
+
// A matched command's `allowed-tools:` frontmatter scopes the run (parsed
|
|
385
|
+
// below into enabledToolNames) the same way `cc command run` does.
|
|
386
|
+
let macroAllowedTools = null;
|
|
387
|
+
if (options.slashMacros !== false && prompt.startsWith("/")) {
|
|
388
|
+
try {
|
|
389
|
+
const doMacro =
|
|
390
|
+
deps.resolveSlashMacro ||
|
|
391
|
+
(await import("../repl/slash-macro.js")).resolveSlashMacro;
|
|
392
|
+
const macro = await doMacro(prompt, { cwd });
|
|
393
|
+
if (macro && macro.matched) {
|
|
394
|
+
userContent = macro.promptText;
|
|
395
|
+
slashExpanded = true;
|
|
396
|
+
for (const w of macro.warnings || []) {
|
|
397
|
+
writeErr(` /${macro.name}: ${w}\n`);
|
|
398
|
+
}
|
|
399
|
+
writeErr(` command: /${macro.name} [${macro.scope}]\n`);
|
|
400
|
+
// Frontmatter `model:` / `allowed-tools:` scope the run exactly like
|
|
401
|
+
// `cc command run` — but an explicit --model / --allowed-tools still
|
|
402
|
+
// wins (those arrive as a set options.model / options.allowedTools).
|
|
403
|
+
if (macro.model && !options.model) {
|
|
404
|
+
model = macro.model;
|
|
405
|
+
writeErr(` command: model → ${model}\n`);
|
|
406
|
+
try {
|
|
407
|
+
const { maybeWarnDeprecatedModel } =
|
|
408
|
+
await import("../lib/model-deprecation.js");
|
|
409
|
+
maybeWarnDeprecatedModel({ model });
|
|
410
|
+
} catch {
|
|
411
|
+
// deprecation notice is best-effort
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (macro.allowedTools && !options.allowedTools) {
|
|
415
|
+
macroAllowedTools = parseToolList(macro.allowedTools);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
} catch {
|
|
419
|
+
// macro resolution is best-effort — fall back to the literal prompt
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
372
423
|
// ── Expand @file references in the prompt (Claude-Code parity) ─────────
|
|
373
424
|
// `@path/to/file` tokens are augmented with the referenced file contents (or
|
|
374
425
|
// a dir listing) so `cc agent -p "review @src/x.js"` works without a manual
|
|
375
426
|
// cat-pipe. Opt out with `--no-file-refs` (options.expandFileRefs === false).
|
|
376
|
-
|
|
377
|
-
if (options.expandFileRefs !== false) {
|
|
427
|
+
// Skipped when a slash macro already expanded (expandCommand ran @refs).
|
|
428
|
+
if (!slashExpanded && options.expandFileRefs !== false) {
|
|
378
429
|
const doExpand = deps.expandFileRefs || expandFileRefsAsync;
|
|
379
430
|
const expanded = await doExpand(prompt, { cwd });
|
|
380
431
|
userContent = expanded.prompt;
|
|
@@ -388,7 +439,9 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
388
439
|
// ── Permission + tool resolution ──────────────────────────────────────
|
|
389
440
|
const perm = resolvePermissionMode(options.permissionMode);
|
|
390
441
|
const enabledToolNames = resolveEnabledTools({
|
|
391
|
-
|
|
442
|
+
// An explicit --allowed-tools wins; otherwise a matched command's
|
|
443
|
+
// `allowed-tools:` frontmatter scopes the run (null when neither applies).
|
|
444
|
+
allowedTools: options.allowedTools || macroAllowedTools,
|
|
392
445
|
readOnly: perm.readOnly,
|
|
393
446
|
});
|
|
394
447
|
const disabledTools = options.disallowedTools || [];
|
|
@@ -31,6 +31,7 @@ import { CostBudget } from "../lib/cost-budget.js";
|
|
|
31
31
|
import {
|
|
32
32
|
resolvePermissionMode,
|
|
33
33
|
resolveEnabledTools,
|
|
34
|
+
parseToolList,
|
|
34
35
|
} from "./headless-runner.js";
|
|
35
36
|
import {
|
|
36
37
|
startSession as jsonlStartSession,
|
|
@@ -1078,9 +1079,49 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
1078
1079
|
})
|
|
1079
1080
|
: new IterationBudget();
|
|
1080
1081
|
|
|
1081
|
-
//
|
|
1082
|
+
// Custom slash-command macro expansion per user event (Claude-Code parity:
|
|
1083
|
+
// a /name from .claude/commands runs in panel / stream mode too, not just
|
|
1084
|
+
// the REPL). expandCommand already runs @file expansion, so the @-ref pass
|
|
1085
|
+
// below is skipped when a macro matched. Opt out: options.slashMacros:false.
|
|
1082
1086
|
let userContent = parsed.text;
|
|
1083
|
-
|
|
1087
|
+
let slashExpanded = false;
|
|
1088
|
+
// A matched command's `model:` / `allowed-tools:` frontmatter scopes THIS
|
|
1089
|
+
// turn's loopOptions below (parity with `cc command run` / headless -p).
|
|
1090
|
+
let turnMacroModel = null;
|
|
1091
|
+
let turnMacroTools = null;
|
|
1092
|
+
if (
|
|
1093
|
+
options.slashMacros !== false &&
|
|
1094
|
+
typeof parsed.text === "string" &&
|
|
1095
|
+
parsed.text.startsWith("/")
|
|
1096
|
+
) {
|
|
1097
|
+
try {
|
|
1098
|
+
const doMacro =
|
|
1099
|
+
deps.resolveSlashMacro ||
|
|
1100
|
+
(await import("../repl/slash-macro.js")).resolveSlashMacro;
|
|
1101
|
+
const macro = await doMacro(parsed.text, { cwd });
|
|
1102
|
+
if (macro && macro.matched) {
|
|
1103
|
+
userContent = macro.promptText;
|
|
1104
|
+
slashExpanded = true;
|
|
1105
|
+
for (const w of macro.warnings || []) {
|
|
1106
|
+
writeErr(` /${macro.name}: ${w}\n`);
|
|
1107
|
+
}
|
|
1108
|
+
writeErr(` command: /${macro.name} [${macro.scope}]\n`);
|
|
1109
|
+
if (macro.model) {
|
|
1110
|
+
turnMacroModel = macro.model;
|
|
1111
|
+
writeErr(` command: model → ${turnMacroModel}\n`);
|
|
1112
|
+
}
|
|
1113
|
+
if (macro.allowedTools) {
|
|
1114
|
+
turnMacroTools = parseToolList(macro.allowedTools);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
} catch {
|
|
1118
|
+
// macro resolution is best-effort — fall back to the literal text
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// @file expansion per user event (parity with single-turn headless).
|
|
1123
|
+
// Skipped when a slash macro already expanded (expandCommand ran @refs).
|
|
1124
|
+
if (!slashExpanded && options.expandFileRefs !== false) {
|
|
1084
1125
|
const expanded = await doExpand(parsed.text, { cwd });
|
|
1085
1126
|
userContent = expanded.prompt;
|
|
1086
1127
|
for (const w of expanded.warnings) writeErr(` @ref: ${w}\n`);
|
|
@@ -1214,6 +1255,13 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
1214
1255
|
messages,
|
|
1215
1256
|
{
|
|
1216
1257
|
...loopOptionsBase,
|
|
1258
|
+
// Custom-command frontmatter scopes THIS turn (parity with `cc command
|
|
1259
|
+
// run` / headless -p): a matched /name applies its model: and
|
|
1260
|
+
// allowed-tools:. Lowest-priority override — an explicit per-turn
|
|
1261
|
+
// privacy (turnLlmOverride) or vision (turnVisionLlm) switch below
|
|
1262
|
+
// still wins on model.
|
|
1263
|
+
...(turnMacroModel ? { model: turnMacroModel } : {}),
|
|
1264
|
+
...(turnMacroTools ? { enabledToolNames: turnMacroTools } : {}),
|
|
1217
1265
|
// §3.5.10 接线6: explicit per-turn LLM override (privacy-tier switch);
|
|
1218
1266
|
// only the fields provided are overridden (e.g. provider+model+baseUrl
|
|
1219
1267
|
// to route to your own PC Ollama for one turn).
|
package/src/session/index.js
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
* CLI session module — thin re-export of @chainlesschain/session-core
|
|
3
3
|
*
|
|
4
4
|
* Managed Agents parity Phase A+B.
|
|
5
|
-
* Consume via:
|
|
5
|
+
* Consume via: import { ... } from "../session/index.js" inside packages/cli/src/.
|
|
6
|
+
*
|
|
7
|
+
* NOTE: packages/cli is ESM ("type": "module"). The previous
|
|
8
|
+
* `module.exports = require(...)` form silently broke here (module/require are
|
|
9
|
+
* undefined in ESM); use an ESM re-export.
|
|
6
10
|
*/
|
|
7
|
-
|
|
11
|
+
export * from "@chainlesschain/session-core";
|