chainlesschain 0.162.33 → 0.162.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-3TazCYWE.js → AIOps-BYfi9NYS.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-DUPN0PST.js → ActionButton-BiS_tAN7.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-CemvhkzD.js → Analytics-jiWl_p-B.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-BL_tAU3M.js → AppLayout-m4sIzDot.js} +3 -3
  6. package/src/assets/web-panel/assets/{Audit-Dl9l-cxF.js → Audit-CPla3Erm.js} +1 -1
  7. package/src/assets/web-panel/assets/{Backup-BKDDX75m.js → Backup-BGeQzTaB.js} +1 -1
  8. package/src/assets/web-panel/assets/{BaseInput-CDYePvMI.js → BaseInput-DTf7Z1iU.js} +1 -1
  9. package/src/assets/web-panel/assets/{Chat-CGtR0sg3.js → Chat-DPTlQlD-.js} +4 -4
  10. package/src/assets/web-panel/assets/ChatBubbleRenderer-BgRXce4e.js +1 -0
  11. package/src/assets/web-panel/assets/{Checkbox-CwYIHOOo.js → Checkbox-DY-XuQMu.js} +1 -1
  12. package/src/assets/web-panel/assets/{Codegen-CIF5tbtd.js → Codegen-B6oxPiZI.js} +1 -1
  13. package/src/assets/web-panel/assets/{Col-z7d4kxeP.js → Col-Dqxb4wSE.js} +1 -1
  14. package/src/assets/web-panel/assets/{Community-DUlDrqF7.js → Community-DCIX514p.js} +1 -1
  15. package/src/assets/web-panel/assets/{Compact-CJ1o8QQR.js → Compact-BGtCzDoJ.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compliance-D3i9d_uO.js → Compliance-zcOYd55o.js} +1 -1
  17. package/src/assets/web-panel/assets/{Cowork-Wm7JTkfB.js → Cowork-DVTtdIdM.js} +4 -4
  18. package/src/assets/web-panel/assets/{Cron-B0QnHhZx.js → Cron-CPUaR69k.js} +2 -2
  19. package/src/assets/web-panel/assets/{Crosschain-3yPrnNgd.js → Crosschain-DnjUS6QH.js} +1 -1
  20. package/src/assets/web-panel/assets/{DID-cfdkiDWF.js → DID-Dnz8VDmx.js} +2 -2
  21. package/src/assets/web-panel/assets/{Dashboard-DFkgM4gT.js → Dashboard-CtWf27j7.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dropdown-YYWE81DL.js → Dropdown-B4GC1ZV4.js} +1 -1
  23. package/src/assets/web-panel/assets/{EmailListRenderer-BXfHK1Bn.js → EmailListRenderer-wjij3kzr.js} +1 -1
  24. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DInUxJ2G.js → FamilyGuardDashboard-rS-2W4u5.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-DNUYeFsv.js → Federation-90p5Tnoz.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-Cr7eVEBB.js → FormItemContext-Cnrw7gzq.js} +1 -1
  27. package/src/assets/web-panel/assets/{GenericCardRenderer-_gF4cmDa.js → GenericCardRenderer-C85NsWa3.js} +1 -1
  28. package/src/assets/web-panel/assets/{Git-BqldmUbO.js → Git-BFAVM9F8.js} +2 -2
  29. package/src/assets/web-panel/assets/{Governance-BF59ZiQ8.js → Governance-DBoRonpq.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-Cy7y1eb9.js → Inference-DHRyD66j.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-B3fVocTO.js → KnowledgeGraph-CTvUKecD.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-BDirsUVk.js → Logs-CB0dv_Ts.js} +2 -2
  33. package/src/assets/web-panel/assets/{Marketplace-GhXpZgp2.js → Marketplace-CN7Hm5Uw.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-0VvfIhKx.js → McpTools-q5H25_8L.js} +5 -5
  35. package/src/assets/web-panel/assets/{Memory-CJLBgAUT.js → Memory-BCV3pZ1d.js} +2 -2
  36. package/src/assets/web-panel/assets/{MobileBridge-BMedY9Yg.js → MobileBridge-C04Mngt4.js} +2 -2
  37. package/src/assets/web-panel/assets/MobileProjects-CUxONYre.js +1 -0
  38. package/src/assets/web-panel/assets/{Mtc-CgEuUg0g.js → Mtc-ByAMz2DN.js} +2 -2
  39. package/src/assets/web-panel/assets/{MtcAudit-1pWNe_xi.js → MtcAudit-B7V7byJq.js} +2 -2
  40. package/src/assets/web-panel/assets/{Multisig-DPIQ7oZL.js → Multisig-DtKmcVQV.js} +3 -3
  41. package/src/assets/web-panel/assets/{NLProgramming-W__P_P4Z.js → NLProgramming-CaMbT5SC.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-C_MCDhFk.js → Notes-DRjbSTCU.js} +4 -4
  43. package/src/assets/web-panel/assets/{NotificationSettings-CDFotapL.js → NotificationSettings-B9YbJID5.js} +1 -1
  44. package/src/assets/web-panel/assets/OrderTableRenderer-BcI_-vGS.js +1 -0
  45. package/src/assets/web-panel/assets/{Organization-D6lMumhD.js → Organization-oTask4BE.js} +4 -4
  46. package/src/assets/web-panel/assets/{Overflow-BMOvUMW6.js → Overflow-Bab06ey7.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P-DsQTEw1t.js → P2P--wlBeU0N.js} +2 -2
  48. package/src/assets/web-panel/assets/{PdhVaultBrowser-CncRtN1Z.js → PdhVaultBrowser-D4t77Pwc.js} +3 -3
  49. package/src/assets/web-panel/assets/{Permissions-DDC-DkUl.js → Permissions-B3sf6CJ3.js} +4 -4
  50. package/src/assets/web-panel/assets/{PersonalDataHub-DVKY_NnT.js → PersonalDataHub-BXOojk63.js} +2 -2
  51. package/src/assets/web-panel/assets/{Pipeline-C7oDVTl-.js → Pipeline-DReqtBFN.js} +1 -1
  52. package/src/assets/web-panel/assets/{Privacy-DReGvTEJ.js → Privacy-cT1GwKLx.js} +1 -1
  53. package/src/assets/web-panel/assets/{ProjectInit-C-j2dzxJ.js → ProjectInit-BhTAzVhH.js} +2 -2
  54. package/src/assets/web-panel/assets/{ProjectSettings-DcUsvFnc.js → ProjectSettings-CK-D8Fyj.js} +2 -2
  55. package/src/assets/web-panel/assets/Projects-CbHiwen6.js +1 -0
  56. package/src/assets/web-panel/assets/{Providers-DIpohWG5.js → Providers-B-ftiXa8.js} +1 -1
  57. package/src/assets/web-panel/assets/{QuickAsk-DdvLtpEU.js → QuickAsk-CT5XPwTF.js} +1 -1
  58. package/src/assets/web-panel/assets/{Recommend-DPAi2zo3.js → Recommend-CohhlBZ_.js} +1 -1
  59. package/src/assets/web-panel/assets/{Reputation-DJD7qXSI.js → Reputation-CrgbixFz.js} +1 -1
  60. package/src/assets/web-panel/assets/{Row-XERdPDHk.js → Row-ClExmBn3.js} +1 -1
  61. package/src/assets/web-panel/assets/{RssFeed-Cl_VlCLg.js → RssFeed-VV0qizCJ.js} +2 -2
  62. package/src/assets/web-panel/assets/{Search-C-poG9P5.js → Search-CqJapSiL.js} +1 -1
  63. package/src/assets/web-panel/assets/{Security-DjjCrw8v.js → Security-DY66Zie6.js} +2 -2
  64. package/src/assets/web-panel/assets/{Services-BuWeB4YJ.js → Services-RQwxat7-.js} +2 -2
  65. package/src/assets/web-panel/assets/{Skeleton-VZXOKwC_.js → Skeleton-0v37UTU_.js} +1 -1
  66. package/src/assets/web-panel/assets/{Skills-B76ONTfP.js → Skills-B4Vm4DxN.js} +1 -1
  67. package/src/assets/web-panel/assets/{Sla-DIj1KREq.js → Sla-CggphTlo.js} +1 -1
  68. package/src/assets/web-panel/assets/{SpeechSettings-BrAp3Yk3.js → SpeechSettings-BAOU08C7.js} +1 -1
  69. package/src/assets/web-panel/assets/{SyncSettings--mJcpccF.js → SyncSettings-DmtC4J1w.js} +2 -2
  70. package/src/assets/web-panel/assets/{Tasks-DM8cMr83.js → Tasks-CExqxzL6.js} +1 -1
  71. package/src/assets/web-panel/assets/{Templates-kOBK6m1Z.js → Templates-C1QK0YoU.js} +1 -1
  72. package/src/assets/web-panel/assets/{Tenant-BjSzYPzn.js → Tenant-CieOfmqp.js} +1 -1
  73. package/src/assets/web-panel/assets/{Terminal-DwpY-Ay7.js → Terminal-DWdhrxRq.js} +2 -2
  74. package/src/assets/web-panel/assets/{TimelineRenderer-aoI0DazM.js → TimelineRenderer-CjFVUUDU.js} +1 -1
  75. package/src/assets/web-panel/assets/{Tokens-YwE0LqSZ.js → Tokens-Bwbk3id9.js} +1 -1
  76. package/src/assets/web-panel/assets/{Trigger-CwSKzvlX.js → Trigger-uJle_yj4.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trust-B__Jqdzn.js → Trust-BcOuxAA5.js} +1 -1
  78. package/src/assets/web-panel/assets/{UkeySign-mty0jwmx.js → UkeySign-DUu7Ufg6.js} +1 -1
  79. package/src/assets/web-panel/assets/{VideoEditing-Ddsx_OQ6.js → VideoEditing-Ck8JtQ2n.js} +1 -1
  80. package/src/assets/web-panel/assets/{Wallet-D4Q8yXZm.js → Wallet-B3jw43on.js} +2 -2
  81. package/src/assets/web-panel/assets/{WebAuthn-CLUaKUr5.js → WebAuthn-Baf9K0y7.js} +3 -3
  82. package/src/assets/web-panel/assets/{WorkflowEditor-Di5pOaeC.js → WorkflowEditor-CTEDl_83.js} +1 -1
  83. package/src/assets/web-panel/assets/{chat-CELatHkT.js → chat-CKV51quV.js} +1 -1
  84. package/src/assets/web-panel/assets/{colors-CawDLjXV.js → colors-BO_RP_yz.js} +1 -1
  85. package/src/assets/web-panel/assets/{compact-item-DeMp-K0j.js → compact-item-BZsxw_ZG.js} +1 -1
  86. package/src/assets/web-panel/assets/{createContext-zY9kXivd.js → createContext-CAbvtzVL.js} +1 -1
  87. package/src/assets/web-panel/assets/devWarning-DQYatsRR.js +1 -0
  88. package/src/assets/web-panel/assets/{hasIn-VEBMW8E4.js → hasIn-QmHT8zDz.js} +1 -1
  89. package/src/assets/web-panel/assets/{index-BjctklSd.js → index-5hlO2-JQ.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-DClGYjBM.js → index-8BMLlHCv.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-KcOEkUCM.js → index-9IqJODII.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-fBNVDEf2.js → index-B2aiE8jk.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-BqJ2r12F.js → index-B3fwyCjJ.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-VJnHvkv2.js → index-B5zhcul9.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CHqvj9uz.js → index-B9Z83FTS.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-8kqE_cVD.js → index-BCsZiq4i.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-Cr7lnIeI.js → index-BEJa1FiF.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DSiL_W2n.js → index-BL7gQAuB.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DPHe9NYG.js → index-BNvTNZ1V.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-B8AZpx7d.js → index-BPZHeug4.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DALuVdhu.js → index-BRNYA0BV.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CHxHLv2b.js → index-BnPBG3Tr.js} +1 -1
  103. package/src/assets/web-panel/assets/index-Bv_y1Ud7.js +1 -0
  104. package/src/assets/web-panel/assets/{index-CbJZzK9B.js → index-C3K1eHDd.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-V3K9gvKR.js → index-C6AA-xB2.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BfGGKoo8.js → index-C6i3reUS.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-GRNVdvoA.js → index-CEh2Ry_A.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-TfXODan7.js → index-CSaI8R_7.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CWbbB1MI.js → index-CVoYeZ5Q.js} +1 -1
  110. package/src/assets/web-panel/assets/index-CZZnSJEX.js +1 -0
  111. package/src/assets/web-panel/assets/{index-u_1aiNTA.js → index-CqiKnXtL.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CtoauqWt.js → index-CsBx0u5G.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-b3ZuAreb.js → index-D8CHQnPl.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BVkrfyuk.js → index-DBCYOypV.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DXNe_zIP.js → index-DC1CFfQU.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-S9JZDSaa.js → index-DKnngF_f.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-XFyv3Sg_.js → index-DKquNxL2.js} +3 -3
  118. package/src/assets/web-panel/assets/{index-vaD1iHg5.js → index-DRK0oAV5.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-SrQIPYq8.js → index-DeC7lehI.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BFc0vBN9.js → index-DjrDGJP2.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CfZV3FXN.js → index-Dln_vjSY.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-C0GhuYLk.js → index-Dob6B6qS.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-JseP3-5X.js → index-GPY0LjCu.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DhsfyHcg.js → index-Ha2_56mf.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-CtLZammH.js → index-fnDgExTu.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CyeYs7SG.js → index-jd2r-T4p.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-Dna2psGz.js → index-qPafbZmr.js} +1 -1
  128. package/src/assets/web-panel/assets/{initDefaultProps-Sd7Eayz4.js → initDefaultProps-Bc2GWeWe.js} +1 -1
  129. package/src/assets/web-panel/assets/{motion-DlToY72q.js → motion-BI-Rxw6o.js} +1 -1
  130. package/src/assets/web-panel/assets/{move-DvS7EmAP.js → move-DRPdwDQB.js} +1 -1
  131. package/src/assets/web-panel/assets/{omit-CzLq4QKW.js → omit-B4XTl3jW.js} +1 -1
  132. package/src/assets/web-panel/assets/{pickAttrs-BcM75Jx_.js → pickAttrs-Do5d86Wr.js} +1 -1
  133. package/src/assets/web-panel/assets/{placementArrow-B7xXXiwd.js → placementArrow-B8VGZ0ZF.js} +1 -1
  134. package/src/assets/web-panel/assets/{responsiveObserve-CrYPRB-g.js → responsiveObserve-Cf0kI_vN.js} +1 -1
  135. package/src/assets/web-panel/assets/{slide-CSYTtsRt.js → slide-Cb0psjSL.js} +1 -1
  136. package/src/assets/web-panel/assets/{statusUtils-CeSuOVT_.js → statusUtils-Bjuo5Oal.js} +1 -1
  137. package/src/assets/web-panel/assets/{styleChecker-KiQethca.js → styleChecker-BLMhoHJ5.js} +1 -1
  138. package/src/assets/web-panel/assets/{useFlexGapSupport-CSQnQdiv.js → useFlexGapSupport-BdCwAfNU.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFs-Br8Kr1pr.js → useFs-9Jhaz5gG.js} +1 -1
  140. package/src/assets/web-panel/assets/{usePersonalDataHub-DGJtDcMm.js → usePersonalDataHub-xYFyXKwD.js} +1 -1
  141. package/src/assets/web-panel/assets/{vnode-C-jVtGka.js → vnode-CVhepE6Z.js} +1 -1
  142. package/src/assets/web-panel/assets/{zoom-CeWySTPF.js → zoom-IbbtJ4Zr.js} +1 -1
  143. package/src/assets/web-panel/index.html +1 -1
  144. package/src/commands/agent.js +19 -0
  145. package/src/commands/command.js +187 -0
  146. package/src/commands/context.js +189 -0
  147. package/src/index.js +4 -0
  148. package/src/lib/slash-commands.js +197 -0
  149. package/src/repl/agent-repl.js +42 -1
  150. package/src/runtime/agent-core.js +253 -0
  151. package/src/runtime/headless-runner.js +30 -1
  152. package/src/runtime/headless-stream.js +65 -0
  153. package/src/runtime/mcp-config.js +100 -0
  154. package/src/runtime/policies/agent-policy.js +1 -0
  155. package/src/assets/web-panel/assets/ChatBubbleRenderer-DZjc9uKn.js +0 -1
  156. package/src/assets/web-panel/assets/MobileProjects-mdohgRlL.js +0 -1
  157. package/src/assets/web-panel/assets/OrderTableRenderer-Dtht0cEs.js +0 -1
  158. package/src/assets/web-panel/assets/Projects-jSjWnmr6.js +0 -1
  159. package/src/assets/web-panel/assets/devWarning-zLjV7g6r.js +0 -1
  160. package/src/assets/web-panel/assets/index-CDtUWCtX.js +0 -1
  161. package/src/assets/web-panel/assets/index-d_RPqH7u.js +0 -1
@@ -0,0 +1,187 @@
1
+ /**
2
+ * cc command — user-defined slash-command macros (Claude-Code parity).
3
+ *
4
+ * cc command list [--json] list discovered commands
5
+ * cc command show <name> show metadata + template body
6
+ * cc command run <name> [args...] [opts] expand the template and run it
7
+ * headlessly (or --print-prompt)
8
+ * cc command new <name> [--description <d>] scaffold a command file
9
+ *
10
+ * Commands live in `.claude/commands/` (project, recursive) or
11
+ * `~/.claude/commands/` (personal). A file `git/commit.md` → command
12
+ * `git:commit`. Template body
13
+ * supports $ARGUMENTS / $1..$9, !`bash` bang execution, and @path file refs.
14
+ * Distinct from skills (AI-invoked); these are macros you run explicitly.
15
+ */
16
+
17
+ import chalk from "chalk";
18
+ import { logger } from "../lib/logger.js";
19
+
20
+ export function registerCommandCommand(program) {
21
+ const cmd = program
22
+ .command("command")
23
+ .alias("cmd")
24
+ .description("User-defined slash-command macros (.claude/commands/*.md)");
25
+
26
+ // ── list ──────────────────────────────────────────────────────────────
27
+ cmd
28
+ .command("list")
29
+ .alias("ls")
30
+ .description("List discovered command macros (project + personal)")
31
+ .option("--json", "Output as JSON")
32
+ .action(async (options) => {
33
+ try {
34
+ const { discoverCommands } = await import("../lib/slash-commands.js");
35
+ const all = discoverCommands(process.cwd());
36
+ if (options.json) {
37
+ console.log(JSON.stringify(all, null, 2));
38
+ return;
39
+ }
40
+ if (all.length === 0) {
41
+ logger.log(
42
+ chalk.gray(
43
+ "No command macros. Create one: cc command new <name> " +
44
+ "(or add .claude/commands/<name>.md)",
45
+ ),
46
+ );
47
+ return;
48
+ }
49
+ for (const c of all) {
50
+ const tag =
51
+ c.scope === "project" ? chalk.cyan("[proj]") : chalk.gray("[pers]");
52
+ logger.log(
53
+ `${chalk.bold("/" + c.name.padEnd(22))} ${tag} ${chalk.gray(c.description || "")}` +
54
+ (c.argumentHint ? chalk.dim(` ${c.argumentHint}`) : ""),
55
+ );
56
+ }
57
+ } catch (err) {
58
+ logger.error(chalk.red(`command list failed: ${err.message}`));
59
+ process.exitCode = 1;
60
+ }
61
+ });
62
+
63
+ // ── show ──────────────────────────────────────────────────────────────
64
+ cmd
65
+ .command("show <name>")
66
+ .description("Show a command's metadata and template body")
67
+ .option("--json", "Output as JSON")
68
+ .action(async (name, options) => {
69
+ try {
70
+ const { getCommand } = await import("../lib/slash-commands.js");
71
+ const c = getCommand(name, process.cwd());
72
+ if (!c) {
73
+ logger.error(chalk.red(`no such command: ${name}`));
74
+ process.exitCode = 1;
75
+ return;
76
+ }
77
+ if (options.json) {
78
+ console.log(JSON.stringify(c, null, 2));
79
+ return;
80
+ }
81
+ logger.log(
82
+ chalk.bold(`/${c.name}`) + ` ${chalk.gray(`[${c.scope}]`)}`,
83
+ );
84
+ if (c.description) logger.log(chalk.gray(` ${c.description}`));
85
+ if (c.argumentHint) logger.log(chalk.gray(` args: ${c.argumentHint}`));
86
+ if (c.model) logger.log(chalk.gray(` model: ${c.model}`));
87
+ if (c.allowedTools)
88
+ logger.log(chalk.gray(` allowed-tools: ${c.allowedTools}`));
89
+ logger.log(chalk.gray(` file: ${c.file}`));
90
+ logger.log(chalk.dim(" ───"));
91
+ logger.log(c.body);
92
+ } catch (err) {
93
+ logger.error(chalk.red(`command show failed: ${err.message}`));
94
+ process.exitCode = 1;
95
+ }
96
+ });
97
+
98
+ // ── run ───────────────────────────────────────────────────────────────
99
+ cmd
100
+ .command("run <name> [args...]")
101
+ .description("Expand a command template and run it headlessly")
102
+ .option("--print-prompt", "Print the expanded prompt without running it")
103
+ .option("--no-bang", "Do not execute !`cmd` bang segments")
104
+ .option("--output-format <fmt>", "text | json | stream-json", "text")
105
+ .option("--model <model>", "Override the model")
106
+ .option("--permission-mode <mode>", "ApprovalGate tier (see cc agent)")
107
+ .action(async (name, args, options) => {
108
+ try {
109
+ const { getCommand, expandCommand } =
110
+ await import("../lib/slash-commands.js");
111
+ const c = getCommand(name, process.cwd());
112
+ if (!c) {
113
+ logger.error(chalk.red(`no such command: ${name}`));
114
+ process.exitCode = 1;
115
+ return;
116
+ }
117
+ const { prompt, warnings } = expandCommand(c, args, {
118
+ cwd: process.cwd(),
119
+ allowBang: options.bang !== false, // commander maps --no-bang → bang:false
120
+ });
121
+ for (const w of warnings) process.stderr.write(` @ref: ${w}\n`);
122
+
123
+ if (options.printPrompt) {
124
+ console.log(prompt);
125
+ return;
126
+ }
127
+
128
+ const { runAgentHeadless, parseToolList } =
129
+ await import("../runtime/headless-runner.js");
130
+ const outcome = await runAgentHeadless({
131
+ prompt,
132
+ outputFormat: options.outputFormat,
133
+ model: options.model || c.model || undefined,
134
+ permissionMode: options.permissionMode,
135
+ // A command's frontmatter allowed-tools scopes the run.
136
+ allowedTools: parseToolList(c.allowedTools),
137
+ });
138
+ process.exit(outcome.exitCode);
139
+ } catch (err) {
140
+ logger.error(chalk.red(`command run failed: ${err.message}`));
141
+ process.exitCode = 1;
142
+ }
143
+ });
144
+
145
+ // ── new (scaffold) ────────────────────────────────────────────────────
146
+ cmd
147
+ .command("new <name>")
148
+ .description("Scaffold a new command file under .claude/commands/")
149
+ .option("--description <d>", "Frontmatter description")
150
+ .option("--personal", "Create under ~/.claude/commands instead of project")
151
+ .action(async (name, options) => {
152
+ try {
153
+ const fs = await import("node:fs");
154
+ const path = await import("node:path");
155
+ const { homedir } = await import("node:os");
156
+ const safe = String(name).replace(/^\//, "").replace(/:/g, "/");
157
+ const root = options.personal
158
+ ? path.join(homedir(), ".claude", "commands")
159
+ : path.join(process.cwd(), ".claude", "commands");
160
+ const file = path.join(root, `${safe}.md`);
161
+ if (fs.existsSync(file)) {
162
+ logger.error(chalk.red(`already exists: ${file}`));
163
+ process.exitCode = 1;
164
+ return;
165
+ }
166
+ fs.mkdirSync(path.dirname(file), { recursive: true });
167
+ const tpl = `---
168
+ description: ${options.description || name}
169
+ argument-hint: "[args]"
170
+ ---
171
+
172
+ Describe the task here. Use $ARGUMENTS for all args, $1/$2 for positional,
173
+ @path to inline file contents, and !\`git status\` to splice in command output.
174
+ `;
175
+ fs.writeFileSync(file, tpl, "utf-8");
176
+ logger.log(chalk.green(`✓ created ${file}`));
177
+ logger.log(
178
+ chalk.gray(
179
+ ` run it with: cc command run ${safe.replace(/\//g, ":")} <args>`,
180
+ ),
181
+ );
182
+ } catch (err) {
183
+ logger.error(chalk.red(`command new failed: ${err.message}`));
184
+ process.exitCode = 1;
185
+ }
186
+ });
187
+ }
@@ -0,0 +1,189 @@
1
+ /**
2
+ * cc context [sessionId] — context-window token breakdown for a session.
3
+ *
4
+ * Shows how a session's stored conversation fills the model context window,
5
+ * grouped by role (system / user / assistant / tool), with the share each takes
6
+ * and the headroom remaining. Reuses the SAME token estimator + window table as
7
+ * the headless auto-compactor (prompt-compressor.js) and the JSONL session store
8
+ * (rebuildMessages) — no new data is collected, this is purely a reporting view.
9
+ *
10
+ * Complements `cc cost` ($ spend) and `cc session usage` (raw token counts):
11
+ * this is the "how full is the window right now" view (Claude-Code `/context`).
12
+ *
13
+ * cc context # most-recent headless session
14
+ * cc context <sessionId>
15
+ * cc context --model claude-sonnet-4-6 # size against a specific window
16
+ * cc context --json
17
+ */
18
+
19
+ import chalk from "chalk";
20
+ import { logger } from "../lib/logger.js";
21
+
22
+ /**
23
+ * Bucket message tokens by role. tool_calls carried on assistant messages are
24
+ * counted separately so the breakdown reflects what actually fills the window.
25
+ */
26
+ export function categorizeContext(messages, estimateTokens) {
27
+ const buckets = { system: 0, user: 0, assistant: 0, tool: 0, toolCalls: 0 };
28
+ const counts = { system: 0, user: 0, assistant: 0, tool: 0 };
29
+ for (const m of messages) {
30
+ if (!m) continue;
31
+ const role =
32
+ m.role === "system" || m.role === "user" || m.role === "tool"
33
+ ? m.role
34
+ : "assistant";
35
+ const content =
36
+ typeof m.content === "string"
37
+ ? m.content
38
+ : JSON.stringify(m.content || "");
39
+ buckets[role] += estimateTokens(content);
40
+ counts[role] += 1;
41
+ if (Array.isArray(m.tool_calls) && m.tool_calls.length) {
42
+ buckets.toolCalls += estimateTokens(JSON.stringify(m.tool_calls));
43
+ }
44
+ }
45
+ const total =
46
+ buckets.system +
47
+ buckets.user +
48
+ buckets.assistant +
49
+ buckets.tool +
50
+ buckets.toolCalls;
51
+ return { buckets, counts, total };
52
+ }
53
+
54
+ function bar(frac, width = 24) {
55
+ const f = Math.max(0, Math.min(1, frac || 0));
56
+ const filled = Math.round(f * width);
57
+ return "█".repeat(filled) + "░".repeat(width - filled);
58
+ }
59
+
60
+ export function registerContextCommand(program) {
61
+ program
62
+ .command("context")
63
+ .description(
64
+ "Context-window token breakdown for a session (Claude-Code /context parity)",
65
+ )
66
+ .argument("[id]", "Session ID (omit for the most-recent headless session)")
67
+ .option("--model <model>", "Size against this model's context window")
68
+ .option("--provider <provider>", "Provider (for the window default)")
69
+ .option("--json", "Output as JSON")
70
+ .action(async (id, options) => {
71
+ const {
72
+ rebuildMessages,
73
+ getLastSessionId,
74
+ sessionExists,
75
+ readEvents,
76
+ } = await import("../harness/jsonl-session-store.js");
77
+ const { estimateTokens, getContextWindow } = await import(
78
+ "../harness/prompt-compressor.js"
79
+ );
80
+
81
+ const sessionId = id || getLastSessionId();
82
+ if (!sessionId) {
83
+ logger.error(
84
+ 'No session found. Run a headless agent with `--session <id>` first, or pass a session id.',
85
+ );
86
+ process.exitCode = 1;
87
+ return;
88
+ }
89
+ if (!sessionExists(sessionId)) {
90
+ logger.error(
91
+ `No headless transcript for session "${sessionId}" (headless sessions are JSONL-only).`,
92
+ );
93
+ process.exitCode = 1;
94
+ return;
95
+ }
96
+
97
+ const messages = rebuildMessages(sessionId) || [];
98
+
99
+ // Auto-detect the session's model/provider from its session_start header,
100
+ // overridable by flags so you can size the same transcript against a
101
+ // different window.
102
+ let recordedModel = "";
103
+ let recordedProvider = "";
104
+ try {
105
+ const start = (readEvents(sessionId) || []).find(
106
+ (e) => e.type === "session_start",
107
+ );
108
+ recordedModel = start?.data?.model || "";
109
+ recordedProvider = start?.data?.provider || "";
110
+ } catch {
111
+ // header optional — fall through to flags/defaults
112
+ }
113
+ const model = options.model || recordedModel || null;
114
+ const provider = options.provider || recordedProvider || "ollama";
115
+ const window = getContextWindow(model, provider);
116
+
117
+ const { buckets, counts, total } = categorizeContext(
118
+ messages,
119
+ estimateTokens,
120
+ );
121
+ const used = window > 0 ? total / window : 0;
122
+ const remaining = Math.max(0, window - total);
123
+
124
+ if (options.json) {
125
+ console.log(
126
+ JSON.stringify(
127
+ {
128
+ sessionId,
129
+ model: model || null,
130
+ provider,
131
+ contextWindow: window,
132
+ totalTokens: total,
133
+ usedFraction: Number(used.toFixed(4)),
134
+ remainingTokens: remaining,
135
+ messageCount: messages.length,
136
+ breakdown: buckets,
137
+ counts,
138
+ overflows: total > window,
139
+ },
140
+ null,
141
+ 2,
142
+ ),
143
+ );
144
+ return;
145
+ }
146
+
147
+ logger.log(chalk.bold(`Context — session ${chalk.gray(sessionId.slice(0, 28))}`));
148
+ logger.log(
149
+ chalk.gray(
150
+ ` model ${model || "(default)"} · provider ${provider} · ` +
151
+ `window ${window.toLocaleString()} tokens · ${messages.length} messages`,
152
+ ),
153
+ );
154
+ logger.log("");
155
+
156
+ const rows = [
157
+ ["system", buckets.system, counts.system],
158
+ ["user", buckets.user, counts.user],
159
+ ["assistant", buckets.assistant, counts.assistant],
160
+ ["tool results", buckets.tool, counts.tool],
161
+ ["tool calls", buckets.toolCalls, null],
162
+ ];
163
+ for (const [label, tok, cnt] of rows) {
164
+ if (!tok) continue;
165
+ const frac = total ? tok / total : 0;
166
+ logger.log(
167
+ ` ${label.padEnd(13)} ${String(tok).padStart(7)} ` +
168
+ `${chalk.cyan(bar(frac))} ${String(Math.round(frac * 100)).padStart(3)}%` +
169
+ `${cnt != null ? chalk.gray(` (${cnt})`) : ""}`,
170
+ );
171
+ }
172
+
173
+ logger.log("");
174
+ const pct = (used * 100).toFixed(1);
175
+ const color = used > 0.9 ? chalk.red : used > 0.7 ? chalk.yellow : chalk.green;
176
+ logger.log(
177
+ ` ${chalk.bold("total".padEnd(11))} ${String(total).padStart(7)} ` +
178
+ `${color(bar(used))} ${color(`${pct}% of window`)}`,
179
+ );
180
+ logger.log(
181
+ chalk.gray(` headroom ${String(remaining).padStart(7)} tokens remaining`),
182
+ );
183
+ if (total > window) {
184
+ logger.log(
185
+ chalk.red(" ⚠ exceeds the model context window — compaction required"),
186
+ );
187
+ }
188
+ });
189
+ }
package/src/index.js CHANGED
@@ -57,8 +57,10 @@ import { registerPermMemCommand } from "./commands/permmem.js";
57
57
  import { registerRCacheCommand } from "./commands/rcache.js";
58
58
  import { registerSessionCommand } from "./commands/session.js";
59
59
  import { registerCostCommand } from "./commands/cost.js";
60
+ import { registerContextCommand } from "./commands/context.js";
60
61
  import { registerCheckpointCommand } from "./commands/checkpoint.js";
61
62
  import { registerGoalCommand } from "./commands/goal.js";
63
+ import { registerCommandCommand } from "./commands/command.js";
62
64
  import { registerCompactCommand } from "./commands/compact.js";
63
65
  import { registerConsolCommand } from "./commands/consol.js";
64
66
  import { registerImportCommand } from "./commands/import.js";
@@ -453,8 +455,10 @@ export function createProgram(opts = {}) {
453
455
  registerRCacheCommand(program);
454
456
  registerSessionCommand(program);
455
457
  registerCostCommand(program);
458
+ registerContextCommand(program);
456
459
  registerCheckpointCommand(program);
457
460
  registerGoalCommand(program);
461
+ registerCommandCommand(program);
458
462
  registerCompactCommand(program);
459
463
  registerConsolCommand(program);
460
464
 
@@ -0,0 +1,197 @@
1
+ /**
2
+ * slash-commands — user-defined command templates (Claude-Code parity).
3
+ *
4
+ * Markdown files under `.claude/commands/` (project) or `~/.claude/commands/`
5
+ * (personal) become reusable command macros. Distinct from skills (skills are
6
+ * AI-invoked capability bundles; these are user-authored prompt macros you run
7
+ * explicitly). A file `git/commit.md` is the command `git:commit`.
8
+ *
9
+ * Frontmatter (all optional): `description`, `argument-hint`, `allowed-tools`,
10
+ * `model`. Body is the prompt template, with substitutions applied at run time:
11
+ * $ARGUMENTS → all args joined by space
12
+ * $1 $2 … $9 → positional args (missing → empty string)
13
+ * !`<cmd>` → run <cmd> in a shell, splice in its stdout (bang exec)
14
+ * @path → splice in file/dir contents (via file-ref-expander)
15
+ *
16
+ * Project scope shadows personal scope on a name clash. Discovery + parse +
17
+ * expand are pure (inject fs/exec/cwd) so the whole thing is unit-testable.
18
+ */
19
+
20
+ import fsDefault from "node:fs";
21
+ import pathDefault from "node:path";
22
+ import { homedir } from "node:os";
23
+ import { execSync as execSyncDefault } from "node:child_process";
24
+ import yaml from "js-yaml";
25
+ import { expandFileRefs } from "../runtime/file-ref-expander.js";
26
+
27
+ const _deps = { fs: fsDefault, path: pathDefault, execSync: execSyncDefault };
28
+
29
+ /**
30
+ * Split `--- ... ---` YAML frontmatter from the body and camelCase the keys
31
+ * (so `argument-hint` → `argumentHint`). Self-contained (no skill-loader, whose
32
+ * import chain drags in heavy native deps). Returns `{ data, body }`.
33
+ */
34
+ function parseFrontmatter(content) {
35
+ const text = String(content || "");
36
+ const m = text.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
37
+ if (!m) return { data: {}, body: text.trim() };
38
+ let raw = {};
39
+ try {
40
+ raw = yaml.load(m[1]) || {};
41
+ } catch {
42
+ raw = {};
43
+ }
44
+ const data = {};
45
+ for (const [k, v] of Object.entries(raw)) {
46
+ const camel = k.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
47
+ data[camel] = v;
48
+ }
49
+ return { data, body: (m[2] || "").trim() };
50
+ }
51
+
52
+ /** Default shell timeout for `!`cmd`` bang execution. */
53
+ export const BANG_TIMEOUT_MS = 10_000;
54
+
55
+ /**
56
+ * The directories scanned for command files, project first (it shadows personal
57
+ * on a name clash). `opts.home` overrides the personal root for tests.
58
+ */
59
+ export function commandDirs(cwd = process.cwd(), opts = {}) {
60
+ const path = opts.deps?.path || _deps.path;
61
+ const home = opts.home || homedir();
62
+ return [
63
+ { dir: path.join(cwd, ".claude", "commands"), scope: "project" },
64
+ { dir: path.join(home, ".claude", "commands"), scope: "personal" },
65
+ ];
66
+ }
67
+
68
+ /** Recursively collect `*.md` files under `dir` as `{file, rel}` (rel uses /). */
69
+ function walkMd(dir, { fs, path }, base = dir, acc = []) {
70
+ let entries;
71
+ try {
72
+ entries = fs.readdirSync(dir, { withFileTypes: true });
73
+ } catch {
74
+ return acc;
75
+ }
76
+ for (const e of entries) {
77
+ const full = path.join(dir, e.name);
78
+ if (e.isDirectory()) {
79
+ walkMd(full, { fs, path }, base, acc);
80
+ } else if (e.isFile() && e.name.endsWith(".md")) {
81
+ const rel = path.relative(base, full).replace(/\\/g, "/");
82
+ acc.push({ file: full, rel });
83
+ }
84
+ }
85
+ return acc;
86
+ }
87
+
88
+ /** Command name from a relative path: `git/commit.md` → `git:commit`. */
89
+ function nameFromRel(rel) {
90
+ return rel.replace(/\.md$/, "").replace(/\//g, ":");
91
+ }
92
+
93
+ /** Parse one command file into its metadata + body (no expansion yet). */
94
+ export function parseCommandFile(file, scope, opts = {}) {
95
+ const fs = opts.deps?.fs || _deps.fs;
96
+ let content;
97
+ try {
98
+ content = fs.readFileSync(file, "utf-8");
99
+ } catch {
100
+ return null;
101
+ }
102
+ const { data, body } = parseFrontmatter(content);
103
+ return {
104
+ file,
105
+ scope,
106
+ description: data.description || "",
107
+ argumentHint: data.argumentHint || "",
108
+ allowedTools: data.allowedTools || null,
109
+ model: data.model || null,
110
+ body: body || "",
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Discover all commands across both scopes. Project shadows personal by name.
116
+ * @returns {Array<{name, scope, file, description, argumentHint, allowedTools, model}>}
117
+ */
118
+ export function discoverCommands(cwd = process.cwd(), opts = {}) {
119
+ const fs = opts.deps?.fs || _deps.fs;
120
+ const path = opts.deps?.path || _deps.path;
121
+ const byName = new Map();
122
+ // Personal first, then project — so project overwrites on clash.
123
+ const dirs = commandDirs(cwd, opts).reverse();
124
+ for (const { dir, scope } of dirs) {
125
+ for (const { file, rel } of walkMd(dir, { fs, path })) {
126
+ const meta = parseCommandFile(file, scope, opts);
127
+ if (!meta) continue;
128
+ const name = nameFromRel(rel);
129
+ byName.set(name, { name, ...meta });
130
+ }
131
+ }
132
+ return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
133
+ }
134
+
135
+ /** Look up one command by name (accepts `git:commit` or `git/commit`). */
136
+ export function getCommand(name, cwd = process.cwd(), opts = {}) {
137
+ const wanted = String(name || "")
138
+ .replace(/^\//, "")
139
+ .replace(/\//g, ":");
140
+ return discoverCommands(cwd, opts).find((c) => c.name === wanted) || null;
141
+ }
142
+
143
+ /** Substitute $ARGUMENTS and $1..$9 in `text`. */
144
+ export function substituteArgs(text, args = []) {
145
+ const list = Array.isArray(args) ? args : [];
146
+ let out = text.replace(/\$ARGUMENTS/g, list.join(" "));
147
+ out = out.replace(/\$([1-9])/g, (_, d) => list[Number(d) - 1] ?? "");
148
+ return out;
149
+ }
150
+
151
+ /** Run every `!`cmd`` and replace it with the command's stdout (best-effort). */
152
+ function runBangs(text, { cwd, execSync }) {
153
+ return text.replace(/!`([^`]+)`/g, (_, cmd) => {
154
+ try {
155
+ const out = execSync(cmd, {
156
+ cwd,
157
+ encoding: "utf-8",
158
+ timeout: BANG_TIMEOUT_MS,
159
+ stdio: ["ignore", "pipe", "pipe"],
160
+ });
161
+ return String(out).trim();
162
+ } catch (err) {
163
+ return `[command failed: ${cmd} — ${err.message?.split("\n")[0] || err}]`;
164
+ }
165
+ });
166
+ }
167
+
168
+ /**
169
+ * Expand a command into a final prompt string.
170
+ * 1. $ARGUMENTS / $1..$9 substitution
171
+ * 2. !`cmd` bang execution (skipped when opts.allowBang === false)
172
+ * 3. @path file references (via file-ref-expander)
173
+ *
174
+ * @param {object} command output of getCommand/parseCommandFile (needs .body)
175
+ * @param {string[]} args
176
+ * @param {object} [opts] { cwd, allowBang, deps:{ execSync } }
177
+ * @returns {{ prompt:string, warnings:string[] }}
178
+ */
179
+ export function expandCommand(command, args = [], opts = {}) {
180
+ const cwd = opts.cwd || process.cwd();
181
+ const execSync = opts.deps?.execSync || _deps.execSync;
182
+ const warnings = [];
183
+
184
+ let text = substituteArgs(command.body || "", args);
185
+
186
+ if (opts.allowBang !== false) {
187
+ text = runBangs(text, { cwd, execSync });
188
+ }
189
+
190
+ // @file expansion appends a <referenced-files> block when anything resolves.
191
+ const expanded = expandFileRefs(text, { cwd });
192
+ for (const w of expanded.warnings) warnings.push(w);
193
+
194
+ return { prompt: expanded.prompt, warnings };
195
+ }
196
+
197
+ export { _deps };
@@ -58,6 +58,7 @@ import {
58
58
  } from "../lib/session-hooks.js";
59
59
  import { HookEvents } from "../lib/hook-manager.js";
60
60
  import { IterationBudget } from "../lib/iteration-budget.js";
61
+ import { loadMcpConfig } from "../runtime/mcp-config.js";
61
62
  import {
62
63
  AGENT_TOOLS,
63
64
  buildSystemPrompt,
@@ -354,6 +355,10 @@ export async function startAgentRepl(options = {}) {
354
355
  // and applies bundle manifest metadata (model/provider override, agentId).
355
356
  let _bundleResolved = null;
356
357
  let _bundleMcpClient = null;
358
+ // --mcp-config (interactive parity with headless): ad-hoc MCP servers loaded
359
+ // for this session via the shared mcp-config engine. Holds {mcpClient,
360
+ // extraToolDefinitions, externalToolExecutors, externalToolDescriptors}.
361
+ let _adhocMcp = null;
357
362
  if (options.bundlePath) {
358
363
  try {
359
364
  const { loadBundle } =
@@ -446,6 +451,28 @@ export async function startAgentRepl(options = {}) {
446
451
  }
447
452
  }
448
453
 
454
+ // --mcp-config: connect ad-hoc MCP servers for this interactive session and
455
+ // expose their tools to the LLM (Claude-Code parity with headless). Reuses the
456
+ // shared engine, so tools surface as mcp__<server>__<tool>. Best-effort: a bad
457
+ // config is reported but never aborts the REPL.
458
+ if (options.mcpConfig) {
459
+ try {
460
+ _adhocMcp = await loadMcpConfig(options.mcpConfig, {
461
+ writeErr: (s) => process.stderr.write(s),
462
+ });
463
+ const toolCount = _adhocMcp.extraToolDefinitions.length;
464
+ logger.log(
465
+ chalk.gray(
466
+ `MCP: ${_adhocMcp.connected.length} server(s), ${toolCount} tool(s) ` +
467
+ `(mcp__<server>__<tool>)`,
468
+ ),
469
+ );
470
+ } catch (mcpErr) {
471
+ logger.log(chalk.yellow(`MCP: --mcp-config failed — ${mcpErr.message}`));
472
+ _adhocMcp = null;
473
+ }
474
+ }
475
+
449
476
  // Apply bundle approval policy to this session (after both gate and sessionId are ready)
450
477
  if (_bundleResolved?.approvalPolicy?.default && _approvalGate && sessionId) {
451
478
  try {
@@ -1567,7 +1594,12 @@ export async function startAgentRepl(options = {}) {
1567
1594
  checkpointSession: sessionId,
1568
1595
  prepareCall,
1569
1596
  approvalGate: _approvalGate,
1570
- mcpClient: _bundleMcpClient || undefined,
1597
+ // MCP: --mcp-config (ad-hoc) wins; bundle MCP is the fallback. The 3
1598
+ // tool channels expose --mcp-config servers' tools to the LLM directly.
1599
+ mcpClient: _adhocMcp?.mcpClient || _bundleMcpClient || undefined,
1600
+ extraToolDefinitions: _adhocMcp?.extraToolDefinitions,
1601
+ externalToolExecutors: _adhocMcp?.externalToolExecutors,
1602
+ externalToolDescriptors: _adhocMcp?.externalToolDescriptors,
1571
1603
  chatFn: _fallbackChatFn,
1572
1604
  });
1573
1605
 
@@ -1778,6 +1810,15 @@ export async function startAgentRepl(options = {}) {
1778
1810
  }
1779
1811
  }
1780
1812
 
1813
+ // Disconnect ad-hoc (--mcp-config) MCP servers
1814
+ if (_adhocMcp?.mcpClient) {
1815
+ try {
1816
+ await _adhocMcp.mcpClient.disconnectAll();
1817
+ } catch (_e) {
1818
+ // Non-critical
1819
+ }
1820
+ }
1821
+
1781
1822
  // Shutdown runtime
1782
1823
  try {
1783
1824
  await shutdown();