chainlesschain 0.162.71 → 0.162.72
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 +3 -2
- package/src/assets/web-panel/assets/{AIOps-pes7_jGr.js → AIOps-1bl50Cen.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DiHWdeH1.js → ActionButton-Bw4kAx-1.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-C51UX-V_.js → Analytics-junq7r42.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BE6LJLw9.js → AppLayout-vf1TVGeu.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-CtTkLTe2.js → Audit-CBzpgcL9.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-lmpVwPxc.js → Backup-ChwGPeP4.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Dr7DR1E1.js → BaseInput-2G2KGE6M.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DXph6klA.js → Chat-Cr9NwfDA.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-RefrXxqp.js → ChatBubbleRenderer-CJvlo_ay.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BF0aS5R9.js → Checkbox-DN_5PBT7.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-B-oHWNTV.js → Codegen-D4bluAgT.js} +1 -1
- package/src/assets/web-panel/assets/{Col-B6KdyRTE.js → Col-DBP3UjYW.js} +1 -1
- package/src/assets/web-panel/assets/{Community-D5ac0KyO.js → Community-ge-SvR3c.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-Dc61O2eQ.js → Compact-DGXvlGNv.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-a48qvNmd.js → Compliance-Cn7nBsFQ.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CIF9lSRj.js → Cowork-DgHEO2de.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-C_gkf4xd.js → Cron-DUbYxPs0.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CJt15PzW.js → Crosschain-3J0MWPQq.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D8I_okEj.js → DID-BR_Jpkh4.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-O4q-qCF2.js → Dashboard-ffykLkAJ.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-flRziiaw.js → Dropdown-DaI38dXT.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CnnkfGPw.js → EmailListRenderer-CnIzkUVG.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-CYAJpGqd.js → FamilyGuardDashboard-BxrgAAZg.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-Dm3WqzQ_.js → Federation-BuS_FRh3.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CpAIter6.js → FormItemContext-DBG-8g1w.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-ChzXcyuN.js → GenericCardRenderer-CmEc8RgH.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DElmMsL4.js → Git-CK10BYwY.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-EWmgn9io.js → Governance-C8cABM_I.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-VtOR_sef.js → Inference-Dr4_DIw3.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-OMqlGkMR.js → KnowledgeGraph-0_bLuvvb.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CTgYDsTZ.js → Logs-PVGZxt9z.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-DZIUTbn8.js → Marketplace-BtVnS4HW.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DWeOErCA.js → McpTools-BVulusCC.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-DwFBp-ev.js → Memory-DEMtNbJ9.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-D42xBfE3.js → MobileBridge-5QL654FQ.js} +3 -3
- package/src/assets/web-panel/assets/{MobileProjects-BHCHauUl.js → MobileProjects-G5Du9Thr.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CabnNsyS.js → Mtc-CNFPJej0.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-Ddx9Awu5.js → MtcAudit-DAJ81Cej.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-CJpYBZ8f.js → Multisig-Cwj8tXfT.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BvPJskVG.js → NLProgramming-Di0ptaL6.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-92ELvJM2.js → Notes-Cse_4Ox6.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-CXz0SUzP.js → NotificationSettings-ntpoi5ec.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-4iWRkMr-.js → OrderTableRenderer-CbKsOM0D.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DblqJPeY.js → Organization-Bhjya5h9.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CHfS3HwD.js → Overflow-C0u3aive.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DIg0pLIG.js → P2P-BWrTwf_a.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BhD3DGjk.js → PdhVaultBrowser-pld2Qlqk.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-Ct7bwrz8.js → Permissions-CW5FG3mE.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DG9c1eas.js → PersonalDataHub-BuBmqfDa.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-BwQnEqG0.js → Pipeline-nZr06BNO.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BvWaf-Ba.js → Privacy-B_XvMHYv.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BFzMHtiW.js → ProjectInit-CbRHJy85.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DDpokSpH.js → ProjectSettings-CCH8SpEa.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-3IL8lyPl.js → Projects-D5Min2Fu.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-C6BsEzSR.js → Providers-DgCY5A8A.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Mh6690Ey.js → QuickAsk-DEcKTiTV.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CJbZ6wgA.js → Recommend-BRaq2oKc.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-GDcyl6iQ.js → Reputation-DzdbBdVv.js} +1 -1
- package/src/assets/web-panel/assets/{Row-6p4DKFSi.js → Row-Ds2JNh7c.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BLhrkoDq.js → RssFeed-Bk1-t4Ie.js} +3 -3
- package/src/assets/web-panel/assets/{Search-Ckiyt35t.js → Search-DaVhRwoq.js} +1 -1
- package/src/assets/web-panel/assets/{Security-qhh-B5Qt.js → Security-D9mXjPSu.js} +4 -4
- package/src/assets/web-panel/assets/{Services-D7a2OwKm.js → Services-D8sukL97.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CSMxupKd.js → Skeleton-COCJt30X.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CGpMng8w.js → Skills-DtFPSPSc.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CYtaU3Ar.js → Sla-DIab-wjR.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BTCsu1uM.js → SpeechSettings-xxIH9hgB.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Cnzx2L5I.js → SyncSettings-DrpxzJOH.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BhnGtqaZ.js → Tasks-maiJ3jAT.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Ccdx_C3w.js → Templates-COE-dSks.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-ZN8c86j3.js → Tenant-CLGObu4M.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-Bw8WBvW3.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-CjgirE9d.js → TimelineRenderer-D4gdk3Qb.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-QoRpi6Wf.js → Tokens-CSoL_uAw.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CEQKJkhs.js → Trigger-BCRcowFi.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-UTuAzV1q.js → Trust-D5QPZ198.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-JDs9I7fl.js → UkeySign-C7SsMqfZ.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BACYQQSd.js → VideoEditing-CDQ4HNZL.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-C-SKluhH.js → Wallet-VmG4P_Fm.js} +3 -3
- package/src/assets/web-panel/assets/{WebAuthn-9chizTFy.js → WebAuthn-qtC0dZxA.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-0mF-LAUo.js → WorkflowEditor-C_CSdk3l.js} +1 -1
- package/src/assets/web-panel/assets/{chat-Di7QINiP.js → chat-BGm8YCIo.js} +1 -1
- package/src/assets/web-panel/assets/{colors-SBmhHMM9.js → colors-BScOmAwk.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BmbETbNj.js → compact-item-HDwaT31n.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-C7hFnsJ7.js → createContext-CheSjPm8.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CjO6o19j.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CFtthKDD.js → hasIn-C1_NbX0O.js} +1 -1
- package/src/assets/web-panel/assets/index--jhpSxS1.js +1 -0
- package/src/assets/web-panel/assets/{index-D6vNUp6c.js → index-6C8mxO8j.js} +1 -1
- package/src/assets/web-panel/assets/{index-ClIp4Vin.js → index-B94_DZF-.js} +1 -1
- package/src/assets/web-panel/assets/{index-D134XFWR.js → index-BGROuzQG.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cib-RTws.js → index-BKQordyU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWDk3nDZ.js → index-BMxPL1oG.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bpo7UVJe.js → index-Bd2zS3jK.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dww6gCzI.js → index-ByoUjX8f.js} +1 -1
- package/src/assets/web-panel/assets/{index-PN02VlUB.js → index-C4FSaMAu.js} +1 -1
- package/src/assets/web-panel/assets/{index-CovTrPpI.js → index-C6WXnaG4.js} +1 -1
- package/src/assets/web-panel/assets/{index-C6tZzsb9.js → index-C71Z__li.js} +1 -1
- package/src/assets/web-panel/assets/{index-sz0w-D-C.js → index-CSaztnyZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-sA4vr5WV.js → index-CYkezxqK.js} +1 -1
- package/src/assets/web-panel/assets/{index-lRVjtXeH.js → index-Clqj2JTi.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNpb8m5a.js → index-Cm55PcYG.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2fhXmhW.js → index-CxoOEb-l.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2CN7coN.js → index-D2gy4IX0.js} +1 -1
- package/src/assets/web-panel/assets/{index-WjZVyLn7.js → index-D8xpncJ0.js} +1 -1
- package/src/assets/web-panel/assets/{index-_hO8-EnW.js → index-DB6gbgU2.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgXQwqXr.js → index-DDk0s3nO.js} +1 -1
- package/src/assets/web-panel/assets/{index-CcBhsVYn.js → index-DQ01dBAS.js} +1 -1
- package/src/assets/web-panel/assets/{index-D8CeUlN0.js → index-DSuWcpsv.js} +1 -1
- package/src/assets/web-panel/assets/{index-C07kpleB.js → index-DVyL9jbT.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dps9xdE8.js → index-DaATJtzu.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRBc1Ewn.js → index-DbfAaBxL.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dk4ez5rf.js → index-DhTELUKN.js} +1 -1
- package/src/assets/web-panel/assets/{index-DI2xH1fU.js → index-Dk2N6hxE.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcawAm_i.js → index-DkiIxTIS.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4etIqbf.js → index-DoZwBGUX.js} +1 -1
- package/src/assets/web-panel/assets/{index-CSnTUPQx.js → index-DppSbT8L.js} +3 -3
- package/src/assets/web-panel/assets/{index-BCcCs7LJ.js → index-DzE-95XH.js} +1 -1
- package/src/assets/web-panel/assets/index-J4Rzclyc.js +1 -0
- package/src/assets/web-panel/assets/{index-NxxQTlPJ.js → index-KG9xVANC.js} +1 -1
- package/src/assets/web-panel/assets/{index-_a2NG3iP.js → index-KXrUKw1h.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWfObC0n.js → index-eSBoP6E_.js} +1 -1
- package/src/assets/web-panel/assets/{index-D0yO3vWh.js → index-ggl9cj35.js} +1 -1
- package/src/assets/web-panel/assets/{index-BT2uOwmA.js → index-i-xYmSsZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DEYnWiq1.js → index-lB5kN9Yc.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcRTX_WO.js → index-yKmudyK7.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-B1zCwkvT.js → initDefaultProps-D7Q7zDzP.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BDGIV9KA.js → motion-B4DcaqPb.js} +1 -1
- package/src/assets/web-panel/assets/{move-BITqgluK.js → move-CYxmYrFY.js} +1 -1
- package/src/assets/web-panel/assets/{omit-uD97QKBF.js → omit-ze_9dKw3.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CZpXhSIE.js → pickAttrs-D24HrRSv.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-ByUvT_08.js → placementArrow-BWJZVYyS.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BQG9LlBs.js → responsiveObserve-CseJia_L.js} +1 -1
- package/src/assets/web-panel/assets/{slide-Bx9TyeEE.js → slide-CBWNEpey.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-XWaMqYM8.js → statusUtils-f4e6p7yd.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BZ-nMyDB.js → styleChecker-DZmfWZ8G.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-D_iwA3vx.js → useFlexGapSupport-4RBaBnVI.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DuvW_aRX.js → useFs-Cv6bNoEA.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BO7orS0d.js → usePersonalDataHub-DOTETJIA.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-Cc_fqAL4.js → vnode-DqS1K6-J.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BGkBp90M.js → zoom-C1MYzIJG.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/cowork.js +8 -0
- package/src/commands/crosschain.js +32 -4
- package/src/commands/init.js +10 -10
- package/src/commands/loop.js +9 -3
- package/src/commands/memory.js +6 -4
- package/src/commands/orchestrate.js +5 -2
- package/src/commands/video.js +5 -1
- package/src/lib/cowork-workflow.js +357 -136
- package/src/lib/micro-compact.js +52 -0
- package/src/lib/permission-rules.cjs +39 -0
- package/src/lib/skill-loader.js +62 -43
- package/src/repl/agent-repl.js +228 -20
- package/src/repl/chat-repl.js +4 -2
- package/src/repl/permission-tier.js +60 -0
- package/src/repl/stream-decision.js +16 -0
- package/src/repl/think-command.js +36 -0
- package/src/runtime/agent-core.js +42 -6
- package/src/runtime/file-ref-expander.js +209 -18
- package/src/runtime/headless-runner.js +3 -3
- package/src/runtime/headless-stream.js +16 -3
- package/src/assets/web-panel/assets/Terminal-BfgM0oyN.js +0 -3
- package/src/assets/web-panel/assets/devWarning-gNvbyy4j.js +0 -1
- package/src/assets/web-panel/assets/index-BNXpMnIY.js +0 -1
- package/src/assets/web-panel/assets/index-gfHxqT1Q.js +0 -1
package/src/lib/skill-loader.js
CHANGED
|
@@ -78,6 +78,10 @@ export const LAYER_NAMES = [
|
|
|
78
78
|
"workspace",
|
|
79
79
|
];
|
|
80
80
|
|
|
81
|
+
/** Max directory depth to descend when scanning a skill layer for nested
|
|
82
|
+
* `<group>/<skill>/SKILL.md` layouts (infinite-recursion / deep-tree guard). */
|
|
83
|
+
export const MAX_SKILL_NEST_DEPTH = 5;
|
|
84
|
+
|
|
81
85
|
/**
|
|
82
86
|
* Simple YAML frontmatter parser (no dependencies)
|
|
83
87
|
* Shared utility extracted from skill.js
|
|
@@ -348,54 +352,69 @@ export class CLISkillLoader {
|
|
|
348
352
|
*/
|
|
349
353
|
_loadFromDir(dir, layer) {
|
|
350
354
|
const skills = [];
|
|
351
|
-
|
|
355
|
+
this._collectSkills(dir, layer, skills, 0);
|
|
356
|
+
return skills;
|
|
357
|
+
}
|
|
352
358
|
|
|
359
|
+
/**
|
|
360
|
+
* Recursively collect skills under `dir`. A directory containing a SKILL.md
|
|
361
|
+
* is a skill (a leaf — we do NOT descend into its asset/script subdirs); a
|
|
362
|
+
* directory WITHOUT one is treated as a grouping/category folder and is
|
|
363
|
+
* descended into, so nested layouts like `.claude/skills/<group>/<skill>/`
|
|
364
|
+
* load too (Claude-Code 2.1.178 nested-skills parity). Depth-capped.
|
|
365
|
+
*/
|
|
366
|
+
_collectSkills(dir, layer, out, depth) {
|
|
367
|
+
if (!dir || !fs.existsSync(dir)) return;
|
|
368
|
+
let entries;
|
|
353
369
|
try {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
id: data.name || entry.name,
|
|
367
|
-
displayName: data.displayName || entry.name,
|
|
368
|
-
description: data.description || "",
|
|
369
|
-
version: data.version || "1.0.0",
|
|
370
|
-
category: data.category || "uncategorized",
|
|
371
|
-
activation: data.activation || "manual",
|
|
372
|
-
tags: data.tags || [],
|
|
373
|
-
userInvocable: data.userInvocable !== false,
|
|
374
|
-
handler: data.handler || null,
|
|
375
|
-
capabilities: data.capabilities || [],
|
|
376
|
-
os: data.os || [],
|
|
377
|
-
// CLI pack extended fields
|
|
378
|
-
executionMode: data.executionMode || null,
|
|
379
|
-
cliDomain: data.cliDomain || null,
|
|
380
|
-
cliVersionHash: data.cliVersionHash || null,
|
|
381
|
-
dirName: entry.name,
|
|
382
|
-
hasHandler: fs.existsSync(path.join(dir, entry.name, "handler.js")),
|
|
383
|
-
body,
|
|
384
|
-
// Skill-Embedded MCP: inline server declarations in a
|
|
385
|
-
// ```mcp-servers fenced code block. Empty array if absent.
|
|
386
|
-
mcpServers: parseSkillMcpServers(body),
|
|
387
|
-
source: layer,
|
|
388
|
-
skillDir: path.join(dir, entry.name),
|
|
389
|
-
});
|
|
390
|
-
} catch {
|
|
391
|
-
// Skip malformed skill files
|
|
370
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
371
|
+
} catch {
|
|
372
|
+
return; // Directory unreadable
|
|
373
|
+
}
|
|
374
|
+
for (const entry of entries) {
|
|
375
|
+
if (!entry.isDirectory()) continue;
|
|
376
|
+
const skillDir = path.join(dir, entry.name);
|
|
377
|
+
const skillMd = path.join(skillDir, "SKILL.md");
|
|
378
|
+
if (!fs.existsSync(skillMd)) {
|
|
379
|
+
// Grouping folder → descend (bounded) to find nested skills.
|
|
380
|
+
if (depth < MAX_SKILL_NEST_DEPTH) {
|
|
381
|
+
this._collectSkills(skillDir, layer, out, depth + 1);
|
|
392
382
|
}
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
try {
|
|
386
|
+
const content = fs.readFileSync(skillMd, "utf-8");
|
|
387
|
+
const { data, body } = parseSkillMd(content);
|
|
388
|
+
|
|
389
|
+
out.push({
|
|
390
|
+
id: data.name || entry.name,
|
|
391
|
+
displayName: data.displayName || entry.name,
|
|
392
|
+
description: data.description || "",
|
|
393
|
+
version: data.version || "1.0.0",
|
|
394
|
+
category: data.category || "uncategorized",
|
|
395
|
+
activation: data.activation || "manual",
|
|
396
|
+
tags: data.tags || [],
|
|
397
|
+
userInvocable: data.userInvocable !== false,
|
|
398
|
+
handler: data.handler || null,
|
|
399
|
+
capabilities: data.capabilities || [],
|
|
400
|
+
os: data.os || [],
|
|
401
|
+
// CLI pack extended fields
|
|
402
|
+
executionMode: data.executionMode || null,
|
|
403
|
+
cliDomain: data.cliDomain || null,
|
|
404
|
+
cliVersionHash: data.cliVersionHash || null,
|
|
405
|
+
dirName: entry.name,
|
|
406
|
+
hasHandler: fs.existsSync(path.join(skillDir, "handler.js")),
|
|
407
|
+
body,
|
|
408
|
+
// Skill-Embedded MCP: inline server declarations in a
|
|
409
|
+
// ```mcp-servers fenced code block. Empty array if absent.
|
|
410
|
+
mcpServers: parseSkillMcpServers(body),
|
|
411
|
+
source: layer,
|
|
412
|
+
skillDir,
|
|
413
|
+
});
|
|
414
|
+
} catch {
|
|
415
|
+
// Skip malformed skill files
|
|
393
416
|
}
|
|
394
|
-
} catch {
|
|
395
|
-
// Directory unreadable
|
|
396
417
|
}
|
|
397
|
-
|
|
398
|
-
return skills;
|
|
399
418
|
}
|
|
400
419
|
|
|
401
420
|
/**
|
package/src/repl/agent-repl.js
CHANGED
|
@@ -79,7 +79,7 @@ import {
|
|
|
79
79
|
listBackgroundShellTasks,
|
|
80
80
|
} from "../runtime/agent-core.js";
|
|
81
81
|
import { formatBackgroundTasks } from "./tasks-status.js";
|
|
82
|
-
import {
|
|
82
|
+
import { expandFileRefsAsync } from "../runtime/file-ref-expander.js";
|
|
83
83
|
import { composeSystemPrompt } from "../runtime/system-prompt.js";
|
|
84
84
|
import {
|
|
85
85
|
makeFallbackChatFn,
|
|
@@ -88,6 +88,13 @@ import {
|
|
|
88
88
|
import { resolveSlashMacro } from "./slash-macro.js";
|
|
89
89
|
import { expandMcpPrompt, renderMcpSurface } from "./mcp-prompt.js";
|
|
90
90
|
import { newCostStore, addUsage } from "./session-cost.js";
|
|
91
|
+
import { parseThinkCommand } from "./think-command.js";
|
|
92
|
+
import { shouldStreamLive } from "./stream-decision.js";
|
|
93
|
+
import {
|
|
94
|
+
parsePermissionTier,
|
|
95
|
+
describeTier,
|
|
96
|
+
nextTier,
|
|
97
|
+
} from "./permission-tier.js";
|
|
91
98
|
|
|
92
99
|
/**
|
|
93
100
|
* Reference to the runtime DB for hook execution (set during startAgentRepl)
|
|
@@ -230,6 +237,13 @@ async function agentLoop(messages, options) {
|
|
|
230
237
|
} else if (event.result?.success) {
|
|
231
238
|
process.stdout.write(chalk.green(` Done\n`));
|
|
232
239
|
}
|
|
240
|
+
} else if (event.type === "thinking") {
|
|
241
|
+
// Intermediate-step reasoning (before a tool call) — dimmed, inline.
|
|
242
|
+
if (process.env.CC_REPL_THINKING !== "0" && event.text) {
|
|
243
|
+
process.stdout.write(
|
|
244
|
+
"\n" + chalk.dim("💭 " + event.text.replace(/\n/g, "\n ")) + "\n",
|
|
245
|
+
);
|
|
246
|
+
}
|
|
233
247
|
} else if (event.type === "token-usage") {
|
|
234
248
|
usageEvents.push(event);
|
|
235
249
|
} else if (event.type === "iteration-warning") {
|
|
@@ -239,7 +253,7 @@ async function agentLoop(messages, options) {
|
|
|
239
253
|
chalk.red(`\n [Budget Exhausted] ${event.budget}\n`),
|
|
240
254
|
);
|
|
241
255
|
} else if (event.type === "response-complete") {
|
|
242
|
-
return { content: event.content, usageEvents };
|
|
256
|
+
return { content: event.content, usageEvents, thinking: event.thinking };
|
|
243
257
|
}
|
|
244
258
|
}
|
|
245
259
|
return { content: "", usageEvents };
|
|
@@ -252,10 +266,16 @@ export async function startAgentRepl(options = {}) {
|
|
|
252
266
|
let model = options.model || "qwen2.5:7b";
|
|
253
267
|
let provider = options.provider || "ollama";
|
|
254
268
|
// Extended thinking (Anthropic; opt-in via --think/--ultrathink). Carried from
|
|
255
|
-
// the runtime policy into the agent-loop options below.
|
|
269
|
+
// the runtime policy into the agent-loop options below. Mutable so the
|
|
270
|
+
// `/think` · `/ultrathink` slash commands can toggle it mid-session (the
|
|
271
|
+
// per-turn agentLoop call below reads the current value). thinkingBudget
|
|
256
272
|
// (--thinking-budget) is the companion legacy-model budget_tokens override.
|
|
257
|
-
|
|
273
|
+
let thinking = options.thinking || null;
|
|
258
274
|
const thinkingBudget = options.thinkingBudget || null;
|
|
275
|
+
// Current ApprovalGate session tier (strict|trusted|autopilot), mirrored here
|
|
276
|
+
// so Shift+Tab can cycle it and `/permissions <tier>` can set it. Kept in sync
|
|
277
|
+
// with _approvalGate.setSessionPolicy below.
|
|
278
|
+
let _sessionTier = "strict";
|
|
259
279
|
const baseUrl = options.baseUrl || "http://localhost:11434";
|
|
260
280
|
const apiKey = options.apiKey || null;
|
|
261
281
|
// Extra workspace roots (--add-dir): advertised in the system prompt and
|
|
@@ -325,6 +345,30 @@ export async function startAgentRepl(options = {}) {
|
|
|
325
345
|
// Set hook DB reference for tool pipeline
|
|
326
346
|
_hookDb = db;
|
|
327
347
|
|
|
348
|
+
// Live token streaming (Claude-Code parity): stream the answer (and reasoning)
|
|
349
|
+
// token-by-token as the LLM produces it, instead of replaying the finished
|
|
350
|
+
// text with a typewriter. ONLY safe when no AssistantResponse hook is
|
|
351
|
+
// registered — such a hook can rewrite/suppress the final answer, which is
|
|
352
|
+
// impossible once it's already on screen. CC_REPL_STREAM=0 forces the replay.
|
|
353
|
+
let _arHookCount = 0;
|
|
354
|
+
if (_hookDb) {
|
|
355
|
+
try {
|
|
356
|
+
const { listHooks } = await import("../lib/hook-manager.js");
|
|
357
|
+
_arHookCount = (
|
|
358
|
+
listHooks(_hookDb, {
|
|
359
|
+
event: "AssistantResponse",
|
|
360
|
+
enabledOnly: true,
|
|
361
|
+
}) || []
|
|
362
|
+
).length;
|
|
363
|
+
} catch {
|
|
364
|
+
_arHookCount = -1; // unknown → shouldStreamLive treats as unsafe
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const _streamLive = shouldStreamLive({
|
|
368
|
+
streamEnv: process.env.CC_REPL_STREAM,
|
|
369
|
+
arHookCount: _arHookCount,
|
|
370
|
+
});
|
|
371
|
+
|
|
328
372
|
// Wire the persistent ApprovalGate singleton (approval-policies.json) with
|
|
329
373
|
// a readline confirm prompt. agent-core's run_shell branch gates
|
|
330
374
|
// MEDIUM/HIGH-risk commands against the session's policy tier
|
|
@@ -698,6 +742,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
698
742
|
sessionId,
|
|
699
743
|
_bundleResolved.approvalPolicy.default,
|
|
700
744
|
);
|
|
745
|
+
// Mirror it so Shift+Tab cycling starts from the real tier.
|
|
746
|
+
const applied = parsePermissionTier(_bundleResolved.approvalPolicy.default);
|
|
747
|
+
if (applied) _sessionTier = applied;
|
|
701
748
|
} catch (_err) {
|
|
702
749
|
// Non-critical — invalid policy value is silently ignored
|
|
703
750
|
}
|
|
@@ -848,6 +895,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
848
895
|
"/ide",
|
|
849
896
|
"/mcp",
|
|
850
897
|
"/memory",
|
|
898
|
+
"/microcompact",
|
|
851
899
|
"/model",
|
|
852
900
|
"/output-style",
|
|
853
901
|
"/permissions",
|
|
@@ -869,6 +917,8 @@ export async function startAgentRepl(options = {}) {
|
|
|
869
917
|
"/tasks",
|
|
870
918
|
"/terminal-setup",
|
|
871
919
|
"/theme",
|
|
920
|
+
"/think",
|
|
921
|
+
"/ultrathink",
|
|
872
922
|
"/vim",
|
|
873
923
|
],
|
|
874
924
|
getIdeOpenFiles: async () => {
|
|
@@ -967,6 +1017,38 @@ export async function startAgentRepl(options = {}) {
|
|
|
967
1017
|
return;
|
|
968
1018
|
}
|
|
969
1019
|
|
|
1020
|
+
// 1.5) Shift+Tab cycles the session approval tier (Claude-Code mode
|
|
1021
|
+
// cycling): strict → trusted → autopilot → strict. Drives the existing
|
|
1022
|
+
// ApprovalGate.setSessionPolicy seam; intercepted before vim/completion.
|
|
1023
|
+
const isShiftTab =
|
|
1024
|
+
(k.name === "tab" && k.shift) || k.sequence === "\u001b[Z";
|
|
1025
|
+
if (isShiftTab) {
|
|
1026
|
+
if (
|
|
1027
|
+
_approvalGate &&
|
|
1028
|
+
sessionId &&
|
|
1029
|
+
typeof _approvalGate.setSessionPolicy === "function"
|
|
1030
|
+
) {
|
|
1031
|
+
const next = nextTier(_sessionTier);
|
|
1032
|
+
try {
|
|
1033
|
+
_approvalGate.setSessionPolicy(sessionId, next);
|
|
1034
|
+
_sessionTier = next;
|
|
1035
|
+
process.stdout.write(
|
|
1036
|
+
"\n" +
|
|
1037
|
+
chalk.cyan(`⇥ approval: ${next}`) +
|
|
1038
|
+
" " +
|
|
1039
|
+
chalk.gray(`(${describeTier(next)})`) +
|
|
1040
|
+
"\n",
|
|
1041
|
+
);
|
|
1042
|
+
if (!_turnAbort) prompt();
|
|
1043
|
+
} catch {
|
|
1044
|
+
process.stdout.write("\x07"); // bell on failure
|
|
1045
|
+
}
|
|
1046
|
+
} else {
|
|
1047
|
+
process.stdout.write("\x07"); // no gate this session
|
|
1048
|
+
}
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
970
1052
|
// 2) Vim mode: modal editing on the current input line.
|
|
971
1053
|
if (_vimEnabled && !_turnAbort) {
|
|
972
1054
|
if (!_vim) {
|
|
@@ -1208,6 +1290,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
1208
1290
|
` ${chalk.cyan("/model")} Show/change model (/model <name>)`,
|
|
1209
1291
|
);
|
|
1210
1292
|
logger.log(` ${chalk.cyan("/provider")} Show/change provider`);
|
|
1293
|
+
logger.log(
|
|
1294
|
+
` ${chalk.cyan("/think")} Extended thinking on/off (/think [on|off|ultra]; /ultrathink = max; Anthropic)`,
|
|
1295
|
+
);
|
|
1211
1296
|
logger.log(` ${chalk.cyan("/clear")} Clear conversation`);
|
|
1212
1297
|
logger.log(
|
|
1213
1298
|
` ${chalk.cyan("/vim")} Toggle vim-mode line editing (/vim [on|off]; Esc → NORMAL)`,
|
|
@@ -1240,7 +1325,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
1240
1325
|
` ${chalk.cyan("/cost")} Session token spend + estimated $ (per model & category)`,
|
|
1241
1326
|
);
|
|
1242
1327
|
logger.log(
|
|
1243
|
-
` ${chalk.cyan("/permissions")} Allow/ask/deny rules
|
|
1328
|
+
` ${chalk.cyan("/permissions")} Allow/ask/deny rules; set/cycle tier (/permissions <tier> · Shift+Tab cycles)`,
|
|
1244
1329
|
);
|
|
1245
1330
|
logger.log(
|
|
1246
1331
|
` ${chalk.cyan("/export")} Save this conversation to a Markdown file (/export [path])`,
|
|
@@ -1260,6 +1345,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
1260
1345
|
logger.log(
|
|
1261
1346
|
` ${chalk.cyan("/compact")} Smart compact (importance-based)`,
|
|
1262
1347
|
);
|
|
1348
|
+
logger.log(
|
|
1349
|
+
` ${chalk.cyan("/microcompact")} Trim large OLD tool results in place (keeps recent + flow)`,
|
|
1350
|
+
);
|
|
1263
1351
|
logger.log(
|
|
1264
1352
|
` ${chalk.cyan("/task")} Set task objective (/task <objective>)`,
|
|
1265
1353
|
);
|
|
@@ -1454,6 +1542,21 @@ export async function startAgentRepl(options = {}) {
|
|
|
1454
1542
|
return;
|
|
1455
1543
|
}
|
|
1456
1544
|
|
|
1545
|
+
// Extended-thinking toggle (Anthropic extended thinking; ignored by other
|
|
1546
|
+
// providers). Mutates `thinking`, read by the next turn's agentLoop call.
|
|
1547
|
+
{
|
|
1548
|
+
const think = parseThinkCommand(trimmed);
|
|
1549
|
+
if (think) {
|
|
1550
|
+
thinking = think.thinking;
|
|
1551
|
+
const note = think.anthropic
|
|
1552
|
+
? " " + chalk.gray("(Anthropic only; applies next turn)")
|
|
1553
|
+
: "";
|
|
1554
|
+
logger.info(`Extended thinking: ${chalk.cyan(think.label)}${note}`);
|
|
1555
|
+
prompt();
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1457
1560
|
if (trimmed === "/clear") {
|
|
1458
1561
|
messages.length = 1; // Keep system prompt
|
|
1459
1562
|
_checkpointMarks.length = 0; // checkpoint marks no longer map to anything
|
|
@@ -1780,6 +1883,27 @@ export async function startAgentRepl(options = {}) {
|
|
|
1780
1883
|
return;
|
|
1781
1884
|
}
|
|
1782
1885
|
|
|
1886
|
+
// Micro-compaction: surgically trim large OLD tool results in place (keeps
|
|
1887
|
+
// recent messages + the conversation flow). Safe (never orphans a tool
|
|
1888
|
+
// pair); cheaper + less lossy than a full /compact.
|
|
1889
|
+
if (trimmed === "/microcompact") {
|
|
1890
|
+
const { microCompact } = await import("../lib/micro-compact.js");
|
|
1891
|
+
const { messages: mc, stats } = microCompact(messages);
|
|
1892
|
+
if (stats.trimmed > 0) {
|
|
1893
|
+
messages.length = 0;
|
|
1894
|
+
messages.push(...mc);
|
|
1895
|
+
logger.info(
|
|
1896
|
+
`Micro-compacted: trimmed ${stats.trimmed} old tool result(s), ~${stats.saved} chars freed (recent messages kept).`,
|
|
1897
|
+
);
|
|
1898
|
+
} else {
|
|
1899
|
+
logger.info(
|
|
1900
|
+
"Nothing to micro-compact — no large old tool results in context.",
|
|
1901
|
+
);
|
|
1902
|
+
}
|
|
1903
|
+
prompt();
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1783
1907
|
// Task commands
|
|
1784
1908
|
if (trimmed.startsWith("/task")) {
|
|
1785
1909
|
const taskArg = trimmed.slice(5).trim();
|
|
@@ -2646,7 +2770,39 @@ export async function startAgentRepl(options = {}) {
|
|
|
2646
2770
|
|
|
2647
2771
|
// `/permissions` — allow/ask/deny rules in effect this session (Claude-Code
|
|
2648
2772
|
// parity): what the agent runs unprompted, asks about, or is blocked from.
|
|
2649
|
-
if (trimmed === "/permissions" || trimmed
|
|
2773
|
+
if (trimmed === "/permissions" || trimmed.startsWith("/permissions ")) {
|
|
2774
|
+
const arg = trimmed.slice("/permissions".length).trim();
|
|
2775
|
+
if (arg) {
|
|
2776
|
+
// Set this session's approval tier mid-session (Claude-Code
|
|
2777
|
+
// permission-mode / Shift+Tab parity; mirrors `cc session policy --set`).
|
|
2778
|
+
const tier = parsePermissionTier(arg);
|
|
2779
|
+
if (!tier) {
|
|
2780
|
+
logger.info(
|
|
2781
|
+
"Usage: /permissions [strict|trusted|autopilot] " +
|
|
2782
|
+
"(aliases: default · accept-edits · bypass). No arg = show rules.",
|
|
2783
|
+
);
|
|
2784
|
+
} else if (
|
|
2785
|
+
!_approvalGate ||
|
|
2786
|
+
!sessionId ||
|
|
2787
|
+
typeof _approvalGate.setSessionPolicy !== "function"
|
|
2788
|
+
) {
|
|
2789
|
+
logger.info(
|
|
2790
|
+
"Approval gate not available this session — can't change the tier.",
|
|
2791
|
+
);
|
|
2792
|
+
} else {
|
|
2793
|
+
try {
|
|
2794
|
+
_approvalGate.setSessionPolicy(sessionId, tier);
|
|
2795
|
+
_sessionTier = tier;
|
|
2796
|
+
logger.info(
|
|
2797
|
+
`Approval policy → ${chalk.cyan(tier)} ${chalk.gray(`(${describeTier(tier)})`)}`,
|
|
2798
|
+
);
|
|
2799
|
+
} catch (_err) {
|
|
2800
|
+
logger.info("Could not set the approval policy.");
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
prompt();
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2650
2806
|
let files = [];
|
|
2651
2807
|
try {
|
|
2652
2808
|
const { loadSettings } = await import("../lib/settings-loader.cjs");
|
|
@@ -2656,6 +2812,11 @@ export async function startAgentRepl(options = {}) {
|
|
|
2656
2812
|
}
|
|
2657
2813
|
const { renderPermissions } = await import("./permissions-status.js");
|
|
2658
2814
|
logger.log(renderPermissions(_permissionRules, { files }));
|
|
2815
|
+
logger.log(
|
|
2816
|
+
chalk.gray(
|
|
2817
|
+
" Set tier mid-session: /permissions <strict|trusted|autopilot>",
|
|
2818
|
+
),
|
|
2819
|
+
);
|
|
2659
2820
|
prompt();
|
|
2660
2821
|
return;
|
|
2661
2822
|
}
|
|
@@ -2821,7 +2982,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
2821
2982
|
// about and left as-is.
|
|
2822
2983
|
let userContent = effectivePrompt;
|
|
2823
2984
|
try {
|
|
2824
|
-
const fileRefs =
|
|
2985
|
+
const fileRefs = await expandFileRefsAsync(effectivePrompt, {
|
|
2986
|
+
cwd: process.cwd(),
|
|
2987
|
+
});
|
|
2825
2988
|
userContent = fileRefs.prompt;
|
|
2826
2989
|
for (const w of fileRefs.warnings) {
|
|
2827
2990
|
logger.info(chalk.yellow(`[@ref] ${w}`));
|
|
@@ -2951,7 +3114,35 @@ export async function startAgentRepl(options = {}) {
|
|
|
2951
3114
|
/* goal binding is best-effort — fall back to defaultPrepareCall */
|
|
2952
3115
|
}
|
|
2953
3116
|
_turnAbort = new AbortController();
|
|
2954
|
-
|
|
3117
|
+
// Live streaming hooks: write the answer token-by-token, and stream the
|
|
3118
|
+
// reasoning dimmed before it. Skipped (left undefined) in replay mode.
|
|
3119
|
+
let _liveStreamed = false;
|
|
3120
|
+
let _liveThinkStarted = false;
|
|
3121
|
+
const liveOpts = _streamLive
|
|
3122
|
+
? {
|
|
3123
|
+
onToken: (t) => {
|
|
3124
|
+
// Separate the answer from the dimmed reasoning above it (once).
|
|
3125
|
+
if (!_liveStreamed && _liveThinkStarted) process.stdout.write("\n");
|
|
3126
|
+
_liveStreamed = true;
|
|
3127
|
+
process.stdout.write(t);
|
|
3128
|
+
},
|
|
3129
|
+
onThinking: (t) => {
|
|
3130
|
+
if (process.env.CC_REPL_THINKING === "0") return;
|
|
3131
|
+
if (!_liveThinkStarted) {
|
|
3132
|
+
process.stdout.write(chalk.dim("💭 "));
|
|
3133
|
+
_liveThinkStarted = true;
|
|
3134
|
+
}
|
|
3135
|
+
process.stdout.write(chalk.dim(t));
|
|
3136
|
+
},
|
|
3137
|
+
}
|
|
3138
|
+
: {};
|
|
3139
|
+
if (_streamLive) process.stdout.write("\n");
|
|
3140
|
+
const {
|
|
3141
|
+
content: response,
|
|
3142
|
+
usageEvents,
|
|
3143
|
+
thinking: reasoning,
|
|
3144
|
+
} = await agentLoop(messages, {
|
|
3145
|
+
...liveOpts,
|
|
2955
3146
|
signal: _turnAbort.signal,
|
|
2956
3147
|
provider,
|
|
2957
3148
|
model: activeModel,
|
|
@@ -3032,19 +3223,36 @@ export async function startAgentRepl(options = {}) {
|
|
|
3032
3223
|
effectiveResponse = responseDirective.response;
|
|
3033
3224
|
}
|
|
3034
3225
|
|
|
3226
|
+
// Extended-thinking reasoning (Anthropic, when /think is on): shown dimmed
|
|
3227
|
+
// BEFORE the answer. Not subject to the AssistantResponse rewrite/suppress
|
|
3228
|
+
// hook (that governs the answer text only). CC_REPL_THINKING=0 hides it.
|
|
3229
|
+
// In live mode it already streamed via onThinking, so skip the replay.
|
|
3230
|
+
if (reasoning && !_streamLive && process.env.CC_REPL_THINKING !== "0") {
|
|
3231
|
+
process.stdout.write(
|
|
3232
|
+
"\n" + chalk.dim("💭 " + reasoning.replace(/\n/g, "\n ")) + "\n",
|
|
3233
|
+
);
|
|
3234
|
+
}
|
|
3235
|
+
|
|
3035
3236
|
if (effectiveResponse) {
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3237
|
+
if (_streamLive && _liveStreamed) {
|
|
3238
|
+
// Already streamed live token-by-token during the turn (no
|
|
3239
|
+
// AssistantResponse hook to rewrite it) — just terminate + record.
|
|
3240
|
+
process.stdout.write("\n\n");
|
|
3241
|
+
messages.push({ role: "assistant", content: effectiveResponse });
|
|
3242
|
+
} else {
|
|
3243
|
+
// Phase G #2 — route through StreamRouter so REPL / WS / future
|
|
3244
|
+
// streaming providers share one StreamEvent protocol.
|
|
3245
|
+
const { streamAgentResponse } = await import("../lib/agent-stream.js");
|
|
3246
|
+
process.stdout.write("\n");
|
|
3247
|
+
const noStream = options.noStream === true;
|
|
3248
|
+
const streamResult = await streamAgentResponse(effectiveResponse, {
|
|
3249
|
+
noStream,
|
|
3250
|
+
writer: noStream ? null : (chunk) => process.stdout.write(chunk),
|
|
3251
|
+
});
|
|
3252
|
+
if (noStream) process.stdout.write(streamResult.text);
|
|
3253
|
+
process.stdout.write("\n\n");
|
|
3254
|
+
messages.push({ role: "assistant", content: streamResult.text });
|
|
3255
|
+
}
|
|
3048
3256
|
} else if (!responseDirective.suppress) {
|
|
3049
3257
|
process.stdout.write("\n");
|
|
3050
3258
|
}
|
package/src/repl/chat-repl.js
CHANGED
|
@@ -12,7 +12,7 @@ import readline from "readline";
|
|
|
12
12
|
import chalk from "chalk";
|
|
13
13
|
import { logger } from "../lib/logger.js";
|
|
14
14
|
import { BUILT_IN_PROVIDERS } from "../lib/llm-providers.js";
|
|
15
|
-
import {
|
|
15
|
+
import { expandFileRefsAsync } from "../runtime/file-ref-expander.js";
|
|
16
16
|
import {
|
|
17
17
|
streamOllama,
|
|
18
18
|
streamOpenAI,
|
|
@@ -159,7 +159,9 @@ export async function startChatRepl(options = {}) {
|
|
|
159
159
|
// content; the JSONL log keeps the original line for readability.
|
|
160
160
|
let userContent = trimmed;
|
|
161
161
|
try {
|
|
162
|
-
const fileRefs =
|
|
162
|
+
const fileRefs = await expandFileRefsAsync(trimmed, {
|
|
163
|
+
cwd: process.cwd(),
|
|
164
|
+
});
|
|
163
165
|
userContent = fileRefs.prompt;
|
|
164
166
|
for (const w of fileRefs.warnings) {
|
|
165
167
|
logger.info(chalk.yellow(`[@ref] ${w}`));
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map a `/permissions <arg>` tier alias to an ApprovalGate session-policy tier
|
|
3
|
+
* (strict | trusted | autopilot), or null when the arg is not a known tier.
|
|
4
|
+
* Pure → unit-testable. Mirrors `cc session policy --set` and the headless
|
|
5
|
+
* --permission-mode mapping (default → strict, acceptEdits → trusted,
|
|
6
|
+
* bypassPermissions → autopilot), giving the interactive REPL a mid-session
|
|
7
|
+
* permission-mode toggle (Claude-Code Shift+Tab mode-cycling parity).
|
|
8
|
+
*/
|
|
9
|
+
const TIER_ALIASES = {
|
|
10
|
+
// strict — every risky action asks (the default)
|
|
11
|
+
strict: "strict",
|
|
12
|
+
default: "strict",
|
|
13
|
+
normal: "strict",
|
|
14
|
+
off: "strict",
|
|
15
|
+
// trusted — low/medium-risk auto-approved, high-risk still asks (acceptEdits)
|
|
16
|
+
trusted: "trusted",
|
|
17
|
+
accept: "trusted",
|
|
18
|
+
"accept-edits": "trusted",
|
|
19
|
+
acceptedits: "trusted",
|
|
20
|
+
// autopilot — everything auto-approved (bypassPermissions)
|
|
21
|
+
autopilot: "autopilot",
|
|
22
|
+
bypass: "autopilot",
|
|
23
|
+
bypasspermissions: "autopilot",
|
|
24
|
+
yolo: "autopilot",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** Cycle order for Shift+Tab mode cycling (Claude-Code parity). */
|
|
28
|
+
export const TIER_CYCLE = Object.freeze(["strict", "trusted", "autopilot"]);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Next tier in the Shift+Tab cycle: strict → trusted → autopilot → strict.
|
|
32
|
+
* An unrecognized current tier resets to the first (strict).
|
|
33
|
+
*/
|
|
34
|
+
export function nextTier(current) {
|
|
35
|
+
const i = TIER_CYCLE.indexOf(current);
|
|
36
|
+
return TIER_CYCLE[(i + 1) % TIER_CYCLE.length];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function parsePermissionTier(arg) {
|
|
40
|
+
const a = String(arg == null ? "" : arg)
|
|
41
|
+
.trim()
|
|
42
|
+
.toLowerCase();
|
|
43
|
+
return Object.prototype.hasOwnProperty.call(TIER_ALIASES, a)
|
|
44
|
+
? TIER_ALIASES[a]
|
|
45
|
+
: null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** One-line description of what a tier auto-approves. */
|
|
49
|
+
export function describeTier(tier) {
|
|
50
|
+
switch (tier) {
|
|
51
|
+
case "autopilot":
|
|
52
|
+
return "everything auto-approved (no prompts)";
|
|
53
|
+
case "trusted":
|
|
54
|
+
return "low/medium-risk auto-approved; high-risk still asks";
|
|
55
|
+
case "strict":
|
|
56
|
+
return "every risky action asks";
|
|
57
|
+
default:
|
|
58
|
+
return "";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decide whether the REPL may stream the answer live (token-by-token) instead of
|
|
3
|
+
* replaying the finished text. Pure → unit-testable.
|
|
4
|
+
*
|
|
5
|
+
* Safety invariant: live streaming is ONLY allowed when no AssistantResponse
|
|
6
|
+
* hook is registered. Such a hook can rewrite or suppress the final answer, and
|
|
7
|
+
* once tokens are on screen they can't be un-printed. If we can't determine the
|
|
8
|
+
* hook count (a query error → pass arHookCount < 0), we stay safe (no streaming).
|
|
9
|
+
* `CC_REPL_STREAM=0` forces the replay regardless.
|
|
10
|
+
*/
|
|
11
|
+
export function shouldStreamLive({ streamEnv, arHookCount = 0 } = {}) {
|
|
12
|
+
if (streamEnv === "0") return false;
|
|
13
|
+
// 0 → no rewrite/suppress hook → safe. Anything else (hooks present, or -1 for
|
|
14
|
+
// "unknown") → fall back to the replay.
|
|
15
|
+
return arHookCount === 0;
|
|
16
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure parser for the REPL `/think` · `/ultrathink` extended-thinking toggle.
|
|
3
|
+
* Maps the slash-command string to the next `thinking` value the agent loop
|
|
4
|
+
* reads (true | "ultra" | <level> | null) plus a human label. Returns null when
|
|
5
|
+
* the input is not a think command, so the REPL falls through to its other
|
|
6
|
+
* handlers. vscode/readline-free → unit-testable (mirrors the chat panel's
|
|
7
|
+
* /think; extended thinking is Anthropic-only, ignored by other providers).
|
|
8
|
+
*
|
|
9
|
+
* /think → on (default budget) /think off → off
|
|
10
|
+
* /think ultra → max budget /think <level> → that level
|
|
11
|
+
* /think-off → off (panel-style alias)
|
|
12
|
+
* /ultrathink → max budget (alias)
|
|
13
|
+
*/
|
|
14
|
+
export function parseThinkCommand(trimmed) {
|
|
15
|
+
const t = String(trimmed == null ? "" : trimmed).trim();
|
|
16
|
+
const isThink =
|
|
17
|
+
t === "/think" || t.startsWith("/think ") || t.startsWith("/think-");
|
|
18
|
+
const isUltra = t === "/ultrathink" || t.startsWith("/ultrathink ");
|
|
19
|
+
if (!isThink && !isUltra) return null;
|
|
20
|
+
|
|
21
|
+
const arg = isUltra
|
|
22
|
+
? "ultra"
|
|
23
|
+
: t.slice(6).replace(/^-+/, "").trim().toLowerCase();
|
|
24
|
+
|
|
25
|
+
if (arg === "ultra") {
|
|
26
|
+
return { thinking: "ultra", label: "ultra (max budget)", anthropic: true };
|
|
27
|
+
}
|
|
28
|
+
if (arg === "off" || arg === "false" || arg === "0" || arg === "none") {
|
|
29
|
+
return { thinking: null, label: "off", anthropic: false };
|
|
30
|
+
}
|
|
31
|
+
if (!arg || arg === "on" || arg === "true") {
|
|
32
|
+
return { thinking: true, label: "on", anthropic: true };
|
|
33
|
+
}
|
|
34
|
+
// An explicit effort level (low/medium/high/…) — passed through to the engine.
|
|
35
|
+
return { thinking: arg, label: arg, anthropic: true };
|
|
36
|
+
}
|