chainlesschain 0.162.47 → 0.162.49
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 +2 -2
- package/src/assets/web-panel/assets/{AIOps-BBjFOl4N.js → AIOps-DmdtNmWd.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-B7A6lHz3.js → ActionButton-DkuYVafg.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DZgNxdvw.js → Analytics-Ba_h8Tub.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CFbPWVnh.js → AppLayout-yb8Zm9MX.js} +4 -4
- package/src/assets/web-panel/assets/{Audit-Ao9RxevG.js → Audit-BGjex2fm.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-B1AGqahI.js → Backup-IFQ2hOF2.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-M01bA2JM.js → BaseInput-W8AkPkrV.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CC0Gmd9k.js → Chat-BgI7t-iW.js} +6 -6
- package/src/assets/web-panel/assets/ChatBubbleRenderer-CfpKEQUF.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-CpEqAg5P.js → Checkbox-DuWsZP4g.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-9c1H2P_L.js → Codegen-DyoTNmYg.js} +1 -1
- package/src/assets/web-panel/assets/{Col-sH2Obo8q.js → Col-DttmlDRk.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DyzjH37r.js → Community-D9nnIdKn.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DsNatLOG.js → Compact-C8KVQaHb.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-B3ws7qwL.js → Compliance-R2owqgjj.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-odGfUMsv.js → Cowork-DwGMMjRn.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-bxO6NLDR.js → Cron-BSTcN_lK.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-xHCPQpE9.js → Crosschain-CTNuIbFD.js} +1 -1
- package/src/assets/web-panel/assets/{DID-3l7HvzNC.js → DID-CgApGsFP.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BNzT5YBj.js → Dashboard-D_OJ3UN5.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DZynMYpG.js → Dropdown-B84Jwra_.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-6HZ2XA9g.js → EmailListRenderer-Bv-YO-6y.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DqzqNzRP.js → FamilyGuardDashboard-drgZ408Y.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CI7lf1MF.js → Federation-CtzFkdW2.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-D2m3YZ-k.js → FormItemContext-BFAvNhl9.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-Bwcc-9EB.js → GenericCardRenderer-DnuEyz_l.js} +1 -1
- package/src/assets/web-panel/assets/{Git-C7qN3bA1.js → Git-jlHajmRJ.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-JfuugS1c.js → Governance-DmJC7PGL.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BNsHm2IH.js → Inference-B-u7xD2n.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CTH_FiL1.js → KnowledgeGraph-BaYCA2Cd.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CxcfBcmS.js → Logs-DTNYQWfp.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-B9PU3qlx.js → Marketplace-CUu1xYvo.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BlvQpnvj.js → McpTools-BmoeTyrC.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-Diiig9gp.js → Memory-DxTU3QU7.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CGfUn4xi.js → MobileBridge-CpcOlKAD.js} +2 -2
- package/src/assets/web-panel/assets/MobileProjects-Bjh_z16l.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-CT_1x1Gr.js → Mtc-LfxwOm0x.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-DPAjU1Qn.js → MtcAudit-D6A9Gjkh.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-nks2HW0C.js → Multisig-Ch_jofPV.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BWd74a91.js → NLProgramming-Bkvogg0I.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-B342yG3u.js → Notes-C5t5Xihm.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-C9kjn20B.js → NotificationSettings-CTpDUNCb.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-ST2lr-Bi.js +1 -0
- package/src/assets/web-panel/assets/{Organization-Ch-qkiyP.js → Organization-Dr37BaXa.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DeV865TY.js → Overflow-ZGjsdP7N.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-9pzyAAE1.js → P2P-bWJU5Vxd.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-oGgQiWpR.js → PdhVaultBrowser-BRVoW-ye.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-MPDfvUva.js → Permissions-BOSnFZaC.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Es8GFY4_.js → PersonalDataHub-X4SgjP6P.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-R10lJHtN.js → Pipeline-DoJhB9bj.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-8YMg8xrV.js → Privacy-OM9lDj-R.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-B-L3DMBv.js → ProjectInit-BXQEOmLn.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-pR4l7GCe.js → ProjectSettings-DBXo3K5u.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-5vaXw-7d.js → Projects-CJ4DBJlS.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-C_E-G6Bh.js → Providers-Tk9SawmO.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CM-FSYbK.js → QuickAsk-DRI1-nTC.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DcwFptAP.js → Recommend-DtrIVTu9.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-DJKj97w-.js → Reputation-DkH8ImwZ.js} +1 -1
- package/src/assets/web-panel/assets/{Row-BfI2mGUm.js → Row-DpA9dlvi.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-D6tzthT0.js → RssFeed-DV3OhxWd.js} +2 -2
- package/src/assets/web-panel/assets/{Search-CkxIxA5y.js → Search-QxdntiQx.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BeTfFgoX.js → Security-CGuEnrD2.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BahX89X1.js → Services-BvwSSD8b.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-55na3k97.js → Skeleton-sx_8L3-5.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DZ-dxsiR.js → Skills-dWOwxRsu.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BUTClrI0.js → Sla-zxXnfKrT.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-C0c2AmSU.js → SpeechSettings-CmFlcNjr.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Vr5zzdPz.js → SyncSettings-BeXeqURL.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BrouZA03.js → Tasks-iImd8xSO.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-DnaU_7nb.js → Templates-DlgR3XFH.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-1dw4SFBA.js → Tenant-0P8HgQaM.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-B5VDEEHD.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-dDxMvAvM.js → TimelineRenderer-hbO7agZs.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CPJMLjkE.js → Tokens-CsmhgTBO.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-B1IWp6HK.js → Trigger-DnaF_2PP.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-iswzFvfA.js → Trust-C1oafGj1.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CuMmDUvU.js → UkeySign-eLL4DOmC.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BguHSBQ5.js → VideoEditing-CX45sVq7.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CW4PFXSw.js → Wallet-aWPqpHdQ.js} +3 -3
- package/src/assets/web-panel/assets/{WebAuthn-YOO0lqjJ.js → WebAuthn-DMYV1MAo.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-A_z3yLuG.js → WorkflowEditor-D9uRIJvH.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BSHj9uiJ.js → chat-BmWYfCxG.js} +1 -1
- package/src/assets/web-panel/assets/{colors-D7gA-kvN.js → colors-DqvTCkBe.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-ChDt0wUv.js → compact-item-Bh0L0ejI.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-5UAhDsNU.js → createContext-r2qgp1mn.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CusWDjWW.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-7ysmGHSf.js → hasIn-BcffXa-S.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dj3IHF24.js → index-585fuGAN.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6OX-6N1.js → index-B3mmDuOv.js} +1 -1
- package/src/assets/web-panel/assets/{index-WM_jYoK8.js → index-B6pAm1iJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-s_MPIulF.js → index-B7z0qK1W.js} +1 -1
- package/src/assets/web-panel/assets/{index-DslmMjVo.js → index-BCBqTRWH.js} +1 -1
- package/src/assets/web-panel/assets/{index-HUiKkQOf.js → index-BLN-neIf.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dq4fd0cB.js → index-BMn_luHQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-NtFFgW-x.js → index-BQlAPNSU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BoCy4bxa.js → index-BXXxkeij.js} +1 -1
- package/src/assets/web-panel/assets/{index-Db4hf2WV.js → index-BXae4ZyX.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9R6sJiP.js → index-BbMox24t.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_xBUmAE.js → index-BeV-KoQl.js} +1 -1
- package/src/assets/web-panel/assets/index-BhYltBvN.js +1 -0
- package/src/assets/web-panel/assets/{index-BGlolJwU.js → index-Bma_yHcC.js} +1 -1
- package/src/assets/web-panel/assets/{index-pk9WOpeN.js → index-BtyXyl3t.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGVsJplV.js → index-C2-02rrp.js} +1 -1
- package/src/assets/web-panel/assets/{index-BzPxVTI2.js → index-C9nh3ANl.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cl10KB1R.js → index-CAlxkpnv.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSRnnGez.js → index-CCyB-RK5.js} +3 -3
- package/src/assets/web-panel/assets/{index-DttrXREN.js → index-CDR3GmaO.js} +1 -1
- package/src/assets/web-panel/assets/{index-BhsGGFa4.js → index-CJt0iuep.js} +1 -1
- package/src/assets/web-panel/assets/{index--UCy6rsJ.js → index-CKnEtlZD.js} +1 -1
- package/src/assets/web-panel/assets/{index-C62SpQws.js → index-COrfHebA.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-pJNNc5.js → index-CY8RXaZR.js} +1 -1
- package/src/assets/web-panel/assets/index-CZiIHw4e.js +1 -0
- package/src/assets/web-panel/assets/{index-CylU_ot3.js → index-Cbj6C3pA.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4nXq9eB.js → index-Ct8qhPZe.js} +1 -1
- package/src/assets/web-panel/assets/{index-CHdEHlNY.js → index-DPFT7J7I.js} +1 -1
- package/src/assets/web-panel/assets/{index-COwKdUAS.js → index-DRXcGa5y.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcPEgfGF.js → index-DW1y18GR.js} +1 -1
- package/src/assets/web-panel/assets/{index-trVj-RG1.js → index-DWlDSE0F.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfjMbFZA.js → index-Dcjol7ot.js} +1 -1
- package/src/assets/web-panel/assets/{index-_8xZf-zU.js → index-De36_UgR.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZOmjAajS.js → index-DutDlDUF.js} +1 -1
- package/src/assets/web-panel/assets/{index-C93TfkpQ.js → index-DxajFkK2.js} +1 -1
- package/src/assets/web-panel/assets/{index-QPBJl4wk.js → index-S4E77Aer.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLoQ2FkI.js → index-U86pxDyR.js} +1 -1
- package/src/assets/web-panel/assets/{index-DcKVERvm.js → index-XwbSqOB2.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTzZlYgQ.js → index-h4O0AcBt.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BcWdcWH2.js → initDefaultProps-C1d8I-BX.js} +1 -1
- package/src/assets/web-panel/assets/{motion-D-GC86LC.js → motion-Dq7fiy4Y.js} +1 -1
- package/src/assets/web-panel/assets/{move-7N01T4vz.js → move-Bqb2dySM.js} +1 -1
- package/src/assets/web-panel/assets/{omit-B8_G8lAi.js → omit-BUYqb4My.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BeEwgLq8.js → pickAttrs-DeytiKlZ.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CiQwkkN_.js → placementArrow-xrXZWCqG.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CtfIP2YM.js → responsiveObserve-CcL2K-YY.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DnA-Z3fK.js → slide-DmCWaic7.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Ckaf7hg9.js → statusUtils-CqNrFif7.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CE7d6-LQ.js → styleChecker-C436m5Xy.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BBC2EqTi.js → useFlexGapSupport-CVhutCN8.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BWncdesy.js → useFs-DUd49Bui.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BVJgKdiT.js → usePersonalDataHub-fuS9raic.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CdZoGwk8.js → vnode-C3kmDmk-.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-8BsyiHWV.js → zoom-hX-F1dT-.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +62 -0
- package/src/lib/agent-core.js +2 -0
- package/src/lib/agent-worktree.js +78 -0
- package/src/lib/ide-context.js +168 -0
- package/src/lib/mcp-client.js +1 -0
- package/src/lib/memory-injection.js +2 -0
- package/src/lib/safe-mode.js +31 -0
- package/src/lib/settings-hooks.cjs +5 -0
- package/src/repl/agent-repl.js +251 -18
- package/src/repl/config-summary.js +59 -0
- package/src/repl/conversation-export.js +133 -0
- package/src/repl/doctor-status.js +114 -0
- package/src/repl/ide-status.js +76 -0
- package/src/repl/memory-status.js +45 -0
- package/src/repl/permissions-status.js +51 -0
- package/src/repl/recent-sessions.js +46 -0
- package/src/repl/session-cost.js +119 -0
- package/src/runtime/agent-core.js +21 -0
- package/src/runtime/headless-runner.js +11 -2
- package/src/runtime/headless-stream.js +15 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DKe4yjqq.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-CFvH6A9E.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-DKQ3FPUN.js +0 -1
- package/src/assets/web-panel/assets/Terminal-Pe1vBvOm.js +0 -3
- package/src/assets/web-panel/assets/devWarning-NW6VMT5L.js +0 -1
- package/src/assets/web-panel/assets/index-A6oY6RQQ.js +0 -1
- package/src/assets/web-panel/assets/index-Cn2g4vqt.js +0 -1
package/src/repl/agent-repl.js
CHANGED
|
@@ -76,6 +76,7 @@ import { composeSystemPrompt } from "../runtime/system-prompt.js";
|
|
|
76
76
|
import { makeFallbackChatFn } from "../runtime/fallback-model.js";
|
|
77
77
|
import { resolveSlashMacro } from "./slash-macro.js";
|
|
78
78
|
import { expandMcpPrompt, renderMcpSurface } from "./mcp-prompt.js";
|
|
79
|
+
import { newCostStore, addUsage } from "./session-cost.js";
|
|
79
80
|
|
|
80
81
|
/**
|
|
81
82
|
* Reference to the runtime DB for hook execution (set during startAgentRepl)
|
|
@@ -757,21 +758,30 @@ export async function startAgentRepl(options = {}) {
|
|
|
757
758
|
"/cd",
|
|
758
759
|
"/clear",
|
|
759
760
|
"/compact",
|
|
761
|
+
"/config",
|
|
760
762
|
"/context",
|
|
763
|
+
"/cost",
|
|
761
764
|
"/cowork",
|
|
765
|
+
"/doctor",
|
|
762
766
|
"/exit",
|
|
767
|
+
"/export",
|
|
763
768
|
"/help",
|
|
769
|
+
"/ide",
|
|
764
770
|
"/mcp",
|
|
771
|
+
"/memory",
|
|
765
772
|
"/model",
|
|
766
773
|
"/output-style",
|
|
774
|
+
"/permissions",
|
|
767
775
|
"/plan",
|
|
768
776
|
"/profile",
|
|
769
777
|
"/provider",
|
|
770
778
|
"/quit",
|
|
771
779
|
"/reindex",
|
|
780
|
+
"/reload-skills",
|
|
772
781
|
"/rewind",
|
|
773
782
|
"/search",
|
|
774
783
|
"/session",
|
|
784
|
+
"/sessions",
|
|
775
785
|
"/stats",
|
|
776
786
|
"/statusline",
|
|
777
787
|
"/sub-agents",
|
|
@@ -879,6 +889,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
879
889
|
let _curModel = model; // tracks the per-turn active model for the readout
|
|
880
890
|
let _ctxUsedTokens = 0;
|
|
881
891
|
let _turnCount = 0;
|
|
892
|
+
const _costStore = newCostStore(); // running token spend for `/cost`
|
|
882
893
|
let _renderStatus = null;
|
|
883
894
|
try {
|
|
884
895
|
const slm = await import("../lib/status-line.cjs");
|
|
@@ -953,8 +964,16 @@ export async function startAgentRepl(options = {}) {
|
|
|
953
964
|
const { runBangCommand } = await import("../lib/repl-bang-memorize.js");
|
|
954
965
|
const res = runBangCommand(trimmed, { cwd: process.cwd() });
|
|
955
966
|
logger.log(chalk.gray(`$ ${res.cmd}`));
|
|
956
|
-
if (res.stdout)
|
|
957
|
-
|
|
967
|
+
if (res.stdout)
|
|
968
|
+
process.stdout.write(
|
|
969
|
+
res.stdout.endsWith("\n") ? res.stdout : `${res.stdout}\n`,
|
|
970
|
+
);
|
|
971
|
+
if (res.stderr)
|
|
972
|
+
process.stderr.write(
|
|
973
|
+
chalk.red(
|
|
974
|
+
res.stderr.endsWith("\n") ? res.stderr : `${res.stderr}\n`,
|
|
975
|
+
),
|
|
976
|
+
);
|
|
958
977
|
if (res.error) logger.error(`shell error: ${res.error.message}`);
|
|
959
978
|
logger.log(chalk.gray(`(exit ${res.exitCode})`));
|
|
960
979
|
messages.push(res.contextMessage);
|
|
@@ -969,14 +988,17 @@ export async function startAgentRepl(options = {}) {
|
|
|
969
988
|
// cc.md (auto-loaded next session) and keep it active in this one.
|
|
970
989
|
if (trimmed.startsWith("#") && trimmed.slice(1).trim()) {
|
|
971
990
|
try {
|
|
972
|
-
const { appendMemoryNote } =
|
|
991
|
+
const { appendMemoryNote } =
|
|
992
|
+
await import("../lib/repl-bang-memorize.js");
|
|
973
993
|
const res = appendMemoryNote(trimmed, { cwd: process.cwd() });
|
|
974
994
|
messages.push({
|
|
975
995
|
role: "system",
|
|
976
996
|
content: `<memory-note source="${res.target}">${res.note}</memory-note>`,
|
|
977
997
|
});
|
|
978
998
|
logger.log(
|
|
979
|
-
chalk.green(
|
|
999
|
+
chalk.green(
|
|
1000
|
+
`✔ remembered in ${res.target}${res.created ? " (created)" : ""}`,
|
|
1001
|
+
),
|
|
980
1002
|
);
|
|
981
1003
|
} catch (err) {
|
|
982
1004
|
logger.error(`# memorize failed: ${err.message}`);
|
|
@@ -1009,15 +1031,36 @@ export async function startAgentRepl(options = {}) {
|
|
|
1009
1031
|
logger.log(
|
|
1010
1032
|
` ${chalk.cyan("/statusline")} Context-usage line on/off (/statusline [on|off])`,
|
|
1011
1033
|
);
|
|
1034
|
+
logger.log(
|
|
1035
|
+
` ${chalk.cyan("/config")} Effective config (provider/model, keys masked)`,
|
|
1036
|
+
);
|
|
1037
|
+
logger.log(
|
|
1038
|
+
` ${chalk.cyan("/doctor")} Session health check (provider/key/IDE/MCP/hooks)`,
|
|
1039
|
+
);
|
|
1040
|
+
logger.log(
|
|
1041
|
+
` ${chalk.cyan("/memory")} Project-memory files loaded (cc.md hierarchy + rules)`,
|
|
1042
|
+
);
|
|
1012
1043
|
logger.log(
|
|
1013
1044
|
` ${chalk.cyan("/context")} Live context-window usage by role`,
|
|
1014
1045
|
);
|
|
1046
|
+
logger.log(
|
|
1047
|
+
` ${chalk.cyan("/cost")} Session token spend + estimated $ (so far)`,
|
|
1048
|
+
);
|
|
1049
|
+
logger.log(
|
|
1050
|
+
` ${chalk.cyan("/permissions")} Allow/ask/deny rules in effect this session`,
|
|
1051
|
+
);
|
|
1052
|
+
logger.log(
|
|
1053
|
+
` ${chalk.cyan("/export")} Save this conversation to a Markdown file (/export [path])`,
|
|
1054
|
+
);
|
|
1015
1055
|
logger.log(
|
|
1016
1056
|
` ${chalk.cyan("/rewind")} Rewind conversation to an earlier turn (double-Esc lists)`,
|
|
1017
1057
|
);
|
|
1018
1058
|
logger.log(
|
|
1019
1059
|
` ${chalk.cyan("/cd <dir>")} Change working directory mid-session (completion/memory follow)`,
|
|
1020
1060
|
);
|
|
1061
|
+
logger.log(
|
|
1062
|
+
` ${chalk.cyan("/reload-skills")} Re-scan skill layers without restarting`,
|
|
1063
|
+
);
|
|
1021
1064
|
logger.log(
|
|
1022
1065
|
` ${chalk.cyan("/compact")} Smart compact (importance-based)`,
|
|
1023
1066
|
);
|
|
@@ -1026,6 +1069,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
1026
1069
|
);
|
|
1027
1070
|
logger.log(` ${chalk.cyan("/task clear")} Clear current task`);
|
|
1028
1071
|
logger.log(` ${chalk.cyan("/session")} Show current session info`);
|
|
1072
|
+
logger.log(
|
|
1073
|
+
` ${chalk.cyan("/sessions")} List recent resumable sessions (/session resume <id> to switch)`,
|
|
1074
|
+
);
|
|
1029
1075
|
logger.log(
|
|
1030
1076
|
` ${chalk.cyan("/reindex")} Reindex notes for BM25 search`,
|
|
1031
1077
|
);
|
|
@@ -1055,6 +1101,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
1055
1101
|
logger.log(
|
|
1056
1102
|
` ${chalk.cyan("/sub-agents")} Show active/completed sub-agents`,
|
|
1057
1103
|
);
|
|
1104
|
+
logger.log(
|
|
1105
|
+
` ${chalk.cyan("/ide")} IDE bridge status (connected editor, tools, or why not)`,
|
|
1106
|
+
);
|
|
1058
1107
|
logger.log(chalk.bold("\nCapabilities:"));
|
|
1059
1108
|
logger.log(" Read, write, and edit files");
|
|
1060
1109
|
logger.log(" Run shell commands (git, npm, etc.)");
|
|
@@ -1278,14 +1327,11 @@ export async function startAgentRepl(options = {}) {
|
|
|
1278
1327
|
|
|
1279
1328
|
if (trimmed === "/rewind" || trimmed.startsWith("/rewind ")) {
|
|
1280
1329
|
try {
|
|
1281
|
-
const { listUserTurns, rewindToTurn, renderTurnList } =
|
|
1282
|
-
"../lib/repl-rewind.js"
|
|
1283
|
-
);
|
|
1330
|
+
const { listUserTurns, rewindToTurn, renderTurnList } =
|
|
1331
|
+
await import("../lib/repl-rewind.js");
|
|
1284
1332
|
const arg = trimmed.slice("/rewind".length).trim();
|
|
1285
1333
|
if (!arg) {
|
|
1286
|
-
logger.log(
|
|
1287
|
-
chalk.bold("\nRewind — pick a user turn (newest first):"),
|
|
1288
|
-
);
|
|
1334
|
+
logger.log(chalk.bold("\nRewind — pick a user turn (newest first):"));
|
|
1289
1335
|
logger.log(renderTurnList(listUserTurns(messages)));
|
|
1290
1336
|
logger.log(
|
|
1291
1337
|
chalk.gray(
|
|
@@ -1320,9 +1366,8 @@ export async function startAgentRepl(options = {}) {
|
|
|
1320
1366
|
// window. Reuses the same categorizer + estimator as the archived view.
|
|
1321
1367
|
try {
|
|
1322
1368
|
const { categorizeContext } = await import("../commands/context.js");
|
|
1323
|
-
const { estimateTokens } =
|
|
1324
|
-
"../harness/prompt-compressor.js"
|
|
1325
|
-
);
|
|
1369
|
+
const { estimateTokens } =
|
|
1370
|
+
await import("../harness/prompt-compressor.js");
|
|
1326
1371
|
const { buckets, counts, total } = categorizeContext(
|
|
1327
1372
|
messages,
|
|
1328
1373
|
estimateTokens,
|
|
@@ -1417,7 +1462,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
1417
1462
|
}
|
|
1418
1463
|
|
|
1419
1464
|
// Session info
|
|
1420
|
-
if (trimmed.startsWith("/session")) {
|
|
1465
|
+
if (trimmed === "/session" || trimmed.startsWith("/session ")) {
|
|
1421
1466
|
const sessionArg = trimmed.slice(8).trim();
|
|
1422
1467
|
if (sessionArg.startsWith("resume ")) {
|
|
1423
1468
|
const resumeId = sessionArg.slice(7).trim();
|
|
@@ -1462,6 +1507,24 @@ export async function startAgentRepl(options = {}) {
|
|
|
1462
1507
|
}
|
|
1463
1508
|
|
|
1464
1509
|
// Reindex notes
|
|
1510
|
+
// `/reload-skills` (Claude-Code 2.1.152 parity): re-scan the 6 skill
|
|
1511
|
+
// layers (incl. .claude/skills) without restarting the session.
|
|
1512
|
+
if (trimmed === "/reload-skills") {
|
|
1513
|
+
try {
|
|
1514
|
+
const { reloadSkills } = await import("../runtime/agent-core.js");
|
|
1515
|
+
const n = reloadSkills();
|
|
1516
|
+
logger.log(
|
|
1517
|
+
chalk.green(
|
|
1518
|
+
`✔ skills reloaded — ${n} available (6 layers re-scanned)`,
|
|
1519
|
+
),
|
|
1520
|
+
);
|
|
1521
|
+
} catch (err) {
|
|
1522
|
+
logger.error(`/reload-skills failed: ${err.message}`);
|
|
1523
|
+
}
|
|
1524
|
+
prompt();
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1465
1528
|
if (trimmed === "/reindex") {
|
|
1466
1529
|
if (contextEngine) {
|
|
1467
1530
|
contextEngine.reindexNotes();
|
|
@@ -2077,6 +2140,42 @@ export async function startAgentRepl(options = {}) {
|
|
|
2077
2140
|
return;
|
|
2078
2141
|
}
|
|
2079
2142
|
|
|
2143
|
+
// `/sessions` — list recent RESUMABLE conversations (read-only; the ids
|
|
2144
|
+
// work with `cc agent --resume <id>`). `/session` shows the current one.
|
|
2145
|
+
if (trimmed === "/sessions" || trimmed === "/sessions ") {
|
|
2146
|
+
try {
|
|
2147
|
+
const { listRecentSessions } = await import("../lib/recent-session.js");
|
|
2148
|
+
const { renderRecentSessions } = await import("./recent-sessions.js");
|
|
2149
|
+
const sessions = listRecentSessions({ db: _hookDb }, { scan: 20 });
|
|
2150
|
+
logger.log(renderRecentSessions(sessions, { currentId: sessionId }));
|
|
2151
|
+
} catch (err) {
|
|
2152
|
+
logger.error(chalk.red(`/sessions failed: ${err.message}`));
|
|
2153
|
+
}
|
|
2154
|
+
prompt();
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
// `/memory` — project-memory files auto-loaded into the system prompt
|
|
2159
|
+
// (cc.md hierarchy + imports + path-scoped rules). Distinct from `#` (add
|
|
2160
|
+
// a note) and `cc memory recall` (scoped store).
|
|
2161
|
+
if (trimmed === "/memory" || trimmed === "/memory ") {
|
|
2162
|
+
try {
|
|
2163
|
+
const { loadProjectInstructions } =
|
|
2164
|
+
await import("../lib/project-instructions.js");
|
|
2165
|
+
const { renderMemoryFiles } = await import("./memory-status.js");
|
|
2166
|
+
const loaded = loadProjectInstructions({ cwd: process.cwd() });
|
|
2167
|
+
logger.log(
|
|
2168
|
+
renderMemoryFiles(loaded, {
|
|
2169
|
+
enabled: process.env.CC_PROJECT_MEMORY !== "0",
|
|
2170
|
+
}),
|
|
2171
|
+
);
|
|
2172
|
+
} catch (err) {
|
|
2173
|
+
logger.error(chalk.red(`/memory failed: ${err.message}`));
|
|
2174
|
+
}
|
|
2175
|
+
prompt();
|
|
2176
|
+
return;
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2080
2179
|
// `/mcp` — overview of connected MCP servers' resources + prompts.
|
|
2081
2180
|
if (trimmed === "/mcp" || trimmed === "/mcp ") {
|
|
2082
2181
|
const mcpClient = _adhocMcp?.mcpClient || _bundleMcpClient;
|
|
@@ -2085,6 +2184,131 @@ export async function startAgentRepl(options = {}) {
|
|
|
2085
2184
|
return;
|
|
2086
2185
|
}
|
|
2087
2186
|
|
|
2187
|
+
// `/config` — effective configuration (secret-safe): the LLM provider/model
|
|
2188
|
+
// in effect, whether keys are set, web-search backend, config path.
|
|
2189
|
+
if (trimmed === "/config" || trimmed === "/config ") {
|
|
2190
|
+
try {
|
|
2191
|
+
const { loadConfig } = await import("../lib/config-manager.js");
|
|
2192
|
+
const { getConfigPath } = await import("../lib/paths.js");
|
|
2193
|
+
const { renderConfigSummary } = await import("./config-summary.js");
|
|
2194
|
+
logger.log(
|
|
2195
|
+
renderConfigSummary(loadConfig(), {
|
|
2196
|
+
path: getConfigPath(),
|
|
2197
|
+
activeProvider: provider,
|
|
2198
|
+
activeModel: _curModel || model,
|
|
2199
|
+
}),
|
|
2200
|
+
);
|
|
2201
|
+
} catch (err) {
|
|
2202
|
+
logger.error(chalk.red(`/config failed: ${err.message}`));
|
|
2203
|
+
}
|
|
2204
|
+
prompt();
|
|
2205
|
+
return;
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
// `/doctor` — consolidated session-health readout (Claude-Code parity):
|
|
2209
|
+
// provider/key/IDE/MCP/permissions/hooks in one pass-or-warn view.
|
|
2210
|
+
if (trimmed === "/doctor" || trimmed === "/doctor ") {
|
|
2211
|
+
let config = {};
|
|
2212
|
+
try {
|
|
2213
|
+
config = (await import("../lib/config-manager.js")).loadConfig() || {};
|
|
2214
|
+
} catch (_err) {
|
|
2215
|
+
// config read is best-effort; checks degrade gracefully
|
|
2216
|
+
}
|
|
2217
|
+
const { buildDoctorChecks, renderDoctor } =
|
|
2218
|
+
await import("./doctor-status.js");
|
|
2219
|
+
const { ideToolNames } = await import("./ide-status.js");
|
|
2220
|
+
const checks = buildDoctorChecks({
|
|
2221
|
+
config,
|
|
2222
|
+
ideTools: ideToolNames(_adhocMcp),
|
|
2223
|
+
mcpServers: _adhocMcp?.connected,
|
|
2224
|
+
permissionRules: _permissionRules,
|
|
2225
|
+
settingsHooks: _settingsHooks,
|
|
2226
|
+
});
|
|
2227
|
+
logger.log(renderDoctor(checks));
|
|
2228
|
+
prompt();
|
|
2229
|
+
return;
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
// `/export [path]` — dump the live conversation to a Markdown transcript
|
|
2233
|
+
// (Claude-Code parity). Distinct from `cc export` (knowledge base). Captures
|
|
2234
|
+
// exactly what's in context now, persisted or not.
|
|
2235
|
+
if (trimmed === "/export" || trimmed.startsWith("/export ")) {
|
|
2236
|
+
const arg = trimmed.slice("/export".length).trim();
|
|
2237
|
+
try {
|
|
2238
|
+
const { renderConversationMarkdown, defaultExportFilename } =
|
|
2239
|
+
await import("./conversation-export.js");
|
|
2240
|
+
const fs = await import("fs");
|
|
2241
|
+
const path = await import("path");
|
|
2242
|
+
const md = renderConversationMarkdown(messages, {
|
|
2243
|
+
provider,
|
|
2244
|
+
model: _curModel || model,
|
|
2245
|
+
sessionId,
|
|
2246
|
+
exportedAt: new Date().toISOString(),
|
|
2247
|
+
});
|
|
2248
|
+
const file = arg
|
|
2249
|
+
? path.resolve(process.cwd(), arg)
|
|
2250
|
+
: path.resolve(process.cwd(), defaultExportFilename(new Date()));
|
|
2251
|
+
fs.writeFileSync(file, md, "utf-8");
|
|
2252
|
+
logger.log(
|
|
2253
|
+
chalk.green(`Exported ${messages.length} messages → ${file}`),
|
|
2254
|
+
);
|
|
2255
|
+
} catch (err) {
|
|
2256
|
+
logger.error(chalk.red(`/export failed: ${err.message}`));
|
|
2257
|
+
}
|
|
2258
|
+
prompt();
|
|
2259
|
+
return;
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
// `/permissions` — allow/ask/deny rules in effect this session (Claude-Code
|
|
2263
|
+
// parity): what the agent runs unprompted, asks about, or is blocked from.
|
|
2264
|
+
if (trimmed === "/permissions" || trimmed === "/permissions ") {
|
|
2265
|
+
let files = [];
|
|
2266
|
+
try {
|
|
2267
|
+
const { loadSettings } = await import("../lib/settings-loader.cjs");
|
|
2268
|
+
files = loadSettings({ cwd: process.cwd() }).files || [];
|
|
2269
|
+
} catch (_err) {
|
|
2270
|
+
// source listing is best-effort — still show the live rules
|
|
2271
|
+
}
|
|
2272
|
+
const { renderPermissions } = await import("./permissions-status.js");
|
|
2273
|
+
logger.log(renderPermissions(_permissionRules, { files }));
|
|
2274
|
+
prompt();
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
// `/cost` — running token spend + estimated $ for this session (Claude-Code
|
|
2279
|
+
// parity). In-memory accumulation, so it works without session persistence.
|
|
2280
|
+
if (trimmed === "/cost" || trimmed === "/cost ") {
|
|
2281
|
+
let overrides;
|
|
2282
|
+
try {
|
|
2283
|
+
const { loadConfig } = await import("../lib/config-manager.js");
|
|
2284
|
+
overrides = loadConfig()?.llm?.pricing;
|
|
2285
|
+
} catch (_err) {
|
|
2286
|
+
// pricing overrides are optional — fall back to the built-in table
|
|
2287
|
+
}
|
|
2288
|
+
const { renderSessionCost } = await import("./session-cost.js");
|
|
2289
|
+
logger.log(
|
|
2290
|
+
renderSessionCost(_costStore, { pricingOverrides: overrides }),
|
|
2291
|
+
);
|
|
2292
|
+
prompt();
|
|
2293
|
+
return;
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
// `/ide` — IDE bridge connection status (Claude-Code parity): which editor
|
|
2297
|
+
// is connected, its tools, or why discovery came up empty.
|
|
2298
|
+
if (trimmed === "/ide" || trimmed === "/ide ") {
|
|
2299
|
+
let diag = null;
|
|
2300
|
+
try {
|
|
2301
|
+
const { diagnoseIde } = await import("../lib/ide-bridge.js");
|
|
2302
|
+
diag = diagnoseIde({ cwd: process.cwd(), env: process.env });
|
|
2303
|
+
} catch (_err) {
|
|
2304
|
+
// discovery is best-effort — fall back to in-session tools only
|
|
2305
|
+
}
|
|
2306
|
+
const { renderIdeStatus } = await import("./ide-status.js");
|
|
2307
|
+
logger.log(renderIdeStatus(_adhocMcp, diag));
|
|
2308
|
+
prompt();
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2088
2312
|
// User-defined slash-command macros (.claude/commands/*.md), Claude-Code
|
|
2089
2313
|
// parity. resolveSlashMacro maps a leading /name to a command macro and
|
|
2090
2314
|
// expands its template; a non-match returns the line unchanged so a literal
|
|
@@ -2201,9 +2425,17 @@ export async function startAgentRepl(options = {}) {
|
|
|
2201
2425
|
// Ephemeral: persistence stores effectivePrompt, not this snapshot.
|
|
2202
2426
|
// Best-effort; CC_IDE_CONTEXT=0 disables.
|
|
2203
2427
|
try {
|
|
2204
|
-
const { buildIdePromptContext } =
|
|
2428
|
+
const { buildIdePromptContext, expandIdeMentions } =
|
|
2429
|
+
await import("../lib/ide-context.js");
|
|
2205
2430
|
const ideCtx = await buildIdePromptContext(_adhocMcp);
|
|
2206
2431
|
if (ideCtx) userContent += `\n\n${ideCtx}`;
|
|
2432
|
+
// Explicit @selection / @diagnostics mentions (Claude-Code parity);
|
|
2433
|
+
// scan the user's original prompt, append the expansion ephemerally.
|
|
2434
|
+
const mentioned = await expandIdeMentions(effectivePrompt, _adhocMcp);
|
|
2435
|
+
for (const w of mentioned.warnings) {
|
|
2436
|
+
logger.info(chalk.yellow(`[@ide] ${w}`));
|
|
2437
|
+
}
|
|
2438
|
+
if (mentioned.block) userContent += `\n\n${mentioned.block}`;
|
|
2207
2439
|
} catch (_err) {
|
|
2208
2440
|
// optional polish — never fail the turn over it
|
|
2209
2441
|
}
|
|
@@ -2305,6 +2537,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
2305
2537
|
});
|
|
2306
2538
|
_turnAbort = null;
|
|
2307
2539
|
|
|
2540
|
+
// Running spend for `/cost` (in-memory, works without persistence).
|
|
2541
|
+
if (usageEvents?.length) addUsage(_costStore, usageEvents);
|
|
2542
|
+
|
|
2308
2543
|
if (sessionId && usageEvents?.length) {
|
|
2309
2544
|
for (const ue of usageEvents) {
|
|
2310
2545
|
try {
|
|
@@ -2433,9 +2668,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
2433
2668
|
// Esc interrupt: an aborted turn is normal flow, not an error — the
|
|
2434
2669
|
// partial conversation stays usable and queued lines still drain.
|
|
2435
2670
|
if (err?.name === "AbortError" || /abort/i.test(err?.message || "")) {
|
|
2436
|
-
logger.log(
|
|
2437
|
-
chalk.yellow("⎋ turn interrupted — partial progress kept"),
|
|
2438
|
-
);
|
|
2671
|
+
logger.log(chalk.yellow("⎋ turn interrupted — partial progress kept"));
|
|
2439
2672
|
prompt();
|
|
2440
2673
|
return;
|
|
2441
2674
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/config` REPL command (Claude-Code parity) — show the effective
|
|
3
|
+
* configuration in a readable, SECRET-SAFE form: the LLM provider/model/baseURL
|
|
4
|
+
* actually in effect, whether an API key is set (never the key itself), the
|
|
5
|
+
* web-search backend, and the config-file path. Also surfaces the session's
|
|
6
|
+
* active provider/model, which can differ from the file (a --provider flag or
|
|
7
|
+
* .claude/settings.json overrides it).
|
|
8
|
+
*
|
|
9
|
+
* Pure: takes the loaded config + context, returns plain text. The REPL does
|
|
10
|
+
* the I/O. NEVER prints a secret — only "set (…1234)" / "not set".
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** Mask a secret: presence + last 4 chars only, or "not set". */
|
|
14
|
+
export function maskSecret(v) {
|
|
15
|
+
if (v == null || v === "") return "not set";
|
|
16
|
+
const s = String(v);
|
|
17
|
+
return s.length <= 4 ? "set (hidden)" : `set (…${s.slice(-4)})`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {object|null} config loaded config.json
|
|
22
|
+
* @param {object} [opts] { path, activeProvider, activeModel }
|
|
23
|
+
* @returns {string}
|
|
24
|
+
*/
|
|
25
|
+
export function renderConfigSummary(config, opts = {}) {
|
|
26
|
+
const cfg = config || {};
|
|
27
|
+
const llm = cfg.llm || {};
|
|
28
|
+
const lines = ["Effective configuration:"];
|
|
29
|
+
if (opts.path) lines.push(` config file: ${opts.path}`);
|
|
30
|
+
|
|
31
|
+
lines.push(" llm:");
|
|
32
|
+
lines.push(` provider: ${llm.provider || "(unset → defaults to ollama)"}`);
|
|
33
|
+
lines.push(` model: ${llm.model || "(unset)"}`);
|
|
34
|
+
if (llm.visionModel) lines.push(` vision: ${llm.visionModel}`);
|
|
35
|
+
if (llm.baseUrl) lines.push(` baseUrl: ${llm.baseUrl}`);
|
|
36
|
+
lines.push(` apiKey: ${maskSecret(llm.apiKey)}`);
|
|
37
|
+
|
|
38
|
+
const ws = cfg.webSearch || {};
|
|
39
|
+
if (ws.provider || ws.apiKey) {
|
|
40
|
+
lines.push(" webSearch:");
|
|
41
|
+
lines.push(` provider: ${ws.provider || "(unset → auto)"}`);
|
|
42
|
+
lines.push(` apiKey: ${maskSecret(ws.apiKey)}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (opts.activeProvider || opts.activeModel) {
|
|
46
|
+
const ap = opts.activeProvider || "?";
|
|
47
|
+
const am = opts.activeModel || "?";
|
|
48
|
+
const differs = !!llm.provider && (ap !== llm.provider || am !== llm.model);
|
|
49
|
+
lines.push(
|
|
50
|
+
` active this session: ${ap} · ${am}` +
|
|
51
|
+
(differs ? " (overrides config)" : ""),
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
lines.push(
|
|
56
|
+
" note: keys are hidden; env vars (e.g. *_API_KEY) can override config at runtime.",
|
|
57
|
+
);
|
|
58
|
+
return lines.join("\n");
|
|
59
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/export` REPL command — dump the LIVE in-memory conversation to a Markdown
|
|
3
|
+
* transcript (Claude-Code parity). Distinct from `cc export` (knowledge-base
|
|
4
|
+
* export) and from `cc session export` (which reads the persisted JSONL store):
|
|
5
|
+
* this renders the agent REPL's working `messages` array, so it captures
|
|
6
|
+
* exactly what's in context right now, persisted or not.
|
|
7
|
+
*
|
|
8
|
+
* Pure over the OpenAI-shaped message list the agent loop maintains:
|
|
9
|
+
* {role:"user"|"assistant"|"system", content} content: string | parts[]
|
|
10
|
+
* {role:"assistant", content, tool_calls:[{function:{name,arguments}}]}
|
|
11
|
+
* {role:"tool", content, tool_call_id}
|
|
12
|
+
* The REPL does the file I/O; this only produces text + a default filename.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const TOOL_BLOCK_CAP = 4000;
|
|
16
|
+
|
|
17
|
+
/** Render message content (string, or OpenAI multimodal parts[]) as text. */
|
|
18
|
+
function contentToText(content) {
|
|
19
|
+
if (content == null) return "";
|
|
20
|
+
if (typeof content === "string") return content;
|
|
21
|
+
if (Array.isArray(content)) {
|
|
22
|
+
return content
|
|
23
|
+
.map((part) => {
|
|
24
|
+
if (typeof part === "string") return part;
|
|
25
|
+
if (!part || typeof part !== "object") return "";
|
|
26
|
+
if (part.type === "text" || typeof part.text === "string") {
|
|
27
|
+
return part.text || "";
|
|
28
|
+
}
|
|
29
|
+
if (part.type === "image_url" || part.image_url) return "[image]";
|
|
30
|
+
return "";
|
|
31
|
+
})
|
|
32
|
+
.filter(Boolean)
|
|
33
|
+
.join("\n");
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
return JSON.stringify(content, null, 2);
|
|
37
|
+
} catch {
|
|
38
|
+
return String(content);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function fence(body, lang = "") {
|
|
43
|
+
const text =
|
|
44
|
+
typeof body === "string"
|
|
45
|
+
? body
|
|
46
|
+
: (() => {
|
|
47
|
+
try {
|
|
48
|
+
return JSON.stringify(body, null, 2);
|
|
49
|
+
} catch {
|
|
50
|
+
return String(body);
|
|
51
|
+
}
|
|
52
|
+
})();
|
|
53
|
+
const capped =
|
|
54
|
+
text.length > TOOL_BLOCK_CAP
|
|
55
|
+
? `${text.slice(0, TOOL_BLOCK_CAP)}\n… [truncated]`
|
|
56
|
+
: text;
|
|
57
|
+
const guard = capped.includes("```") ? "````" : "```";
|
|
58
|
+
return `${guard}${lang}\n${capped}\n${guard}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Pretty-print a tool_call's JSON-string arguments, falling back to raw. */
|
|
62
|
+
function prettyArgs(argStr) {
|
|
63
|
+
if (typeof argStr !== "string") return fence(argStr, "json");
|
|
64
|
+
try {
|
|
65
|
+
return fence(JSON.parse(argStr), "json");
|
|
66
|
+
} catch {
|
|
67
|
+
return fence(argStr);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const two = (n) => String(n).padStart(2, "0");
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* A timestamped default filename, e.g. chainlesschain-export-20260613-130600.md.
|
|
75
|
+
* Takes a Date so callers/tests stay deterministic.
|
|
76
|
+
*/
|
|
77
|
+
export function defaultExportFilename(date = new Date()) {
|
|
78
|
+
const d = date instanceof Date && !isNaN(date) ? date : new Date(0);
|
|
79
|
+
const stamp =
|
|
80
|
+
`${d.getFullYear()}${two(d.getMonth() + 1)}${two(d.getDate())}` +
|
|
81
|
+
`-${two(d.getHours())}${two(d.getMinutes())}${two(d.getSeconds())}`;
|
|
82
|
+
return `chainlesschain-export-${stamp}.md`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Render the conversation as Markdown.
|
|
87
|
+
* @param {Array} messages the REPL's working message list
|
|
88
|
+
* @param {object} [meta] { provider, model, sessionId, exportedAt }
|
|
89
|
+
* @returns {string}
|
|
90
|
+
*/
|
|
91
|
+
export function renderConversationMarkdown(messages, meta = {}) {
|
|
92
|
+
const msgs = Array.isArray(messages) ? messages : [];
|
|
93
|
+
const L = ["# ChainlessChain Conversation Export", ""];
|
|
94
|
+
const bits = [];
|
|
95
|
+
if (meta.sessionId) bits.push(`session: ${meta.sessionId}`);
|
|
96
|
+
if (meta.provider) bits.push(`provider: ${meta.provider}`);
|
|
97
|
+
if (meta.model) bits.push(`model: ${meta.model}`);
|
|
98
|
+
if (meta.exportedAt) bits.push(`exported: ${meta.exportedAt}`);
|
|
99
|
+
if (bits.length) {
|
|
100
|
+
L.push(`> ${bits.join(" · ")}`, "");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let users = 0;
|
|
104
|
+
let assistants = 0;
|
|
105
|
+
for (const m of msgs) {
|
|
106
|
+
if (!m || typeof m !== "object") continue;
|
|
107
|
+
const role = m.role;
|
|
108
|
+
if (role === "user") {
|
|
109
|
+
users += 1;
|
|
110
|
+
L.push("## 👤 User", "", contentToText(m.content), "");
|
|
111
|
+
} else if (role === "assistant") {
|
|
112
|
+
const text = contentToText(m.content);
|
|
113
|
+
if (text) {
|
|
114
|
+
assistants += 1;
|
|
115
|
+
L.push("## 🤖 Assistant", "", text, "");
|
|
116
|
+
}
|
|
117
|
+
if (Array.isArray(m.tool_calls)) {
|
|
118
|
+
for (const tc of m.tool_calls) {
|
|
119
|
+
const name = tc?.function?.name || tc?.name || "?";
|
|
120
|
+
L.push(`**🔧 tool_call — \`${name}\`**`, "");
|
|
121
|
+
L.push(prettyArgs(tc?.function?.arguments ?? tc?.arguments), "");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
} else if (role === "tool") {
|
|
125
|
+
L.push("**↩ tool_result**", "", fence(contentToText(m.content)), "");
|
|
126
|
+
} else if (role === "system") {
|
|
127
|
+
L.push("## ⚙ System", "", contentToText(m.content), "");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
L.push("---", `_${users} user / ${assistants} assistant turns_`, "");
|
|
132
|
+
return L.join("\n");
|
|
133
|
+
}
|