chainlesschain 0.162.32 → 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.
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-Cg_uWAVl.js → AIOps-BYfi9NYS.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DSFtQ1c2.js → ActionButton-BiS_tAN7.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BMxpkw8y.js → Analytics-jiWl_p-B.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-tgVxlmsx.js → AppLayout-m4sIzDot.js} +3 -3
- package/src/assets/web-panel/assets/{Audit-DwzGllcp.js → Audit-CPla3Erm.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BG28Y2MV.js → Backup-BGeQzTaB.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-TXthbazl.js → BaseInput-DTf7Z1iU.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-D096SxaD.js → Chat-DPTlQlD-.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BgRXce4e.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-Czttw1JS.js → Checkbox-DY-XuQMu.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DZtMgv4q.js → Codegen-B6oxPiZI.js} +1 -1
- package/src/assets/web-panel/assets/{Col-D3DnfExY.js → Col-Dqxb4wSE.js} +1 -1
- package/src/assets/web-panel/assets/{Community-Bj5AdwqY.js → Community-DCIX514p.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BQ8Zszub.js → Compact-BGtCzDoJ.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DXacb34n.js → Compliance-zcOYd55o.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BgMUBTkw.js → Cowork-DVTtdIdM.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-fqBWOqlN.js → Cron-CPUaR69k.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-E4oa1MWy.js → Crosschain-DnjUS6QH.js} +1 -1
- package/src/assets/web-panel/assets/{DID-pwgfYZaV.js → DID-Dnz8VDmx.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-n8mdLFIR.js → Dashboard-CtWf27j7.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown--6DYqxk7.js → Dropdown-B4GC1ZV4.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CkjQluz3.js → EmailListRenderer-wjij3kzr.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-u-QTQ-OC.js → FamilyGuardDashboard-rS-2W4u5.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-D219M5Qc.js → Federation-90p5Tnoz.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BBU_aopC.js → FormItemContext-Cnrw7gzq.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-pTMCIHcM.js → GenericCardRenderer-C85NsWa3.js} +1 -1
- package/src/assets/web-panel/assets/{Git-ClcCARWt.js → Git-BFAVM9F8.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CvUi3I93.js → Governance-DBoRonpq.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DT-a4pVg.js → Inference-DHRyD66j.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DHMs2LY8.js → KnowledgeGraph-CTvUKecD.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-D2s4eV1N.js → Logs-CB0dv_Ts.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-YC5-fx-6.js → Marketplace-CN7Hm5Uw.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-7JHTEC4T.js → McpTools-q5H25_8L.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-BudotVLD.js → Memory-BCV3pZ1d.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CAiRyLVU.js → MobileBridge-C04Mngt4.js} +2 -2
- package/src/assets/web-panel/assets/MobileProjects-CUxONYre.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-d0iY0CeK.js → Mtc-ByAMz2DN.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-aI2cG1UP.js → MtcAudit-B7V7byJq.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-4bF70khG.js → Multisig-DtKmcVQV.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CwLib1S7.js → NLProgramming-CaMbT5SC.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Wt7AuFRU.js → Notes-DRjbSTCU.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-D081vV_7.js → NotificationSettings-B9YbJID5.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DCPei1L9.js → OrderTableRenderer-BcI_-vGS.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BNEsUNdP.js → Organization-oTask4BE.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-B_1iUXDD.js → Overflow-Bab06ey7.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Dbc-kNwJ.js → P2P--wlBeU0N.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-D8Xh289k.js → PdhVaultBrowser-D4t77Pwc.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-C77mM6-n.js → Permissions-B3sf6CJ3.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Dj0J3r_K.js → PersonalDataHub-BXOojk63.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-B6F0WQ2C.js → Pipeline-DReqtBFN.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-eDKOkyyq.js → Privacy-cT1GwKLx.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DAWwhr5_.js → ProjectInit-BhTAzVhH.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DwdK8k6I.js → ProjectSettings-CK-D8Fyj.js} +2 -2
- package/src/assets/web-panel/assets/Projects-CbHiwen6.js +1 -0
- package/src/assets/web-panel/assets/{Providers--DcYxQfN.js → Providers-B-ftiXa8.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DU268niT.js → QuickAsk-CT5XPwTF.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-ChnflhV1.js → Recommend-CohhlBZ_.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-DSsY3bQG.js → Reputation-CrgbixFz.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Zb-EjmgQ.js → Row-ClExmBn3.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CGLiixZB.js → RssFeed-VV0qizCJ.js} +3 -3
- package/src/assets/web-panel/assets/{Search-Dhr_po-U.js → Search-CqJapSiL.js} +1 -1
- package/src/assets/web-panel/assets/{Security-GMYNhGsR.js → Security-DY66Zie6.js} +2 -2
- package/src/assets/web-panel/assets/{Services-DiOpnVY0.js → Services-RQwxat7-.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DG3ez6ME.js → Skeleton-0v37UTU_.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DZGptytP.js → Skills-B4Vm4DxN.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CtGpE3xA.js → Sla-CggphTlo.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DQFw6Cf9.js → SpeechSettings-BAOU08C7.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-C8X78RpX.js → SyncSettings-DmtC4J1w.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-CExqxzL6.js +1 -0
- package/src/assets/web-panel/assets/{Templates-SF9_ZWsV.js → Templates-C1QK0YoU.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BbIQSVZz.js → Tenant-CieOfmqp.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DKr5zDwu.js → Terminal-DWdhrxRq.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BtLaNaWr.js → TimelineRenderer-CjFVUUDU.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CfYbk2NG.js → Tokens-Bwbk3id9.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BLX_XDP0.js → Trigger-uJle_yj4.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BWxUv9PR.js → Trust-BcOuxAA5.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DRwTyQD4.js → UkeySign-DUu7Ufg6.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BsC4VOSo.js → VideoEditing-Ck8JtQ2n.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CSsO1NJU.js → Wallet-B3jw43on.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-z1MxiFzS.js → WebAuthn-Baf9K0y7.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-B1vV7uuJ.js → WorkflowEditor-CTEDl_83.js} +1 -1
- package/src/assets/web-panel/assets/{chat-C0NJRaL2.js → chat-CKV51quV.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CHRiteWF.js → colors-BO_RP_yz.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-2XmBBKPD.js → compact-item-BZsxw_ZG.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DkedHC38.js → createContext-CAbvtzVL.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DQYatsRR.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-Bpn9Xrlw.js → hasIn-QmHT8zDz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CyJpmSHZ.js → index-5hlO2-JQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAFLFMXQ.js → index-8BMLlHCv.js} +1 -1
- package/src/assets/web-panel/assets/{index-De5vOO9V.js → index-9IqJODII.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtU4qZRF.js → index-B2aiE8jk.js} +1 -1
- package/src/assets/web-panel/assets/{index-NuBsCRaR.js → index-B3fwyCjJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYUd69vM.js → index-B5zhcul9.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdR7RfRP.js → index-B9Z83FTS.js} +1 -1
- package/src/assets/web-panel/assets/{index-De49R7TX.js → index-BCsZiq4i.js} +1 -1
- package/src/assets/web-panel/assets/{index-BveL_4n3.js → index-BEJa1FiF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYmwEaIk.js → index-BL7gQAuB.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMbF-Euw.js → index-BNvTNZ1V.js} +1 -1
- package/src/assets/web-panel/assets/{index-i4W_EAuh.js → index-BPZHeug4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BN068mCR.js → index-BRNYA0BV.js} +1 -1
- package/src/assets/web-panel/assets/{index-CJOoo72F.js → index-BnPBG3Tr.js} +1 -1
- package/src/assets/web-panel/assets/index-Bv_y1Ud7.js +1 -0
- package/src/assets/web-panel/assets/{index-DUBsq_1G.js → index-C3K1eHDd.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfY9U3X5.js → index-C6AA-xB2.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dk7P-q3n.js → index-C6i3reUS.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cljnfuxu.js → index-CEh2Ry_A.js} +1 -1
- package/src/assets/web-panel/assets/{index-cfSUlOfY.js → index-CSaI8R_7.js} +1 -1
- package/src/assets/web-panel/assets/{index-DM9JrnYi.js → index-CVoYeZ5Q.js} +1 -1
- package/src/assets/web-panel/assets/index-CZZnSJEX.js +1 -0
- package/src/assets/web-panel/assets/{index-alGjpoM1.js → index-CqiKnXtL.js} +1 -1
- package/src/assets/web-panel/assets/{index-Sk3-3tKa.js → index-CsBx0u5G.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZ1gOoiG.js → index-D8CHQnPl.js} +1 -1
- package/src/assets/web-panel/assets/{index-uHGxyZtQ.js → index-DBCYOypV.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9mNfpxi.js → index-DC1CFfQU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CCg6ZY4t.js → index-DKnngF_f.js} +1 -1
- package/src/assets/web-panel/assets/{index-BItcSqan.js → index-DKquNxL2.js} +3 -3
- package/src/assets/web-panel/assets/{index-D7U411hK.js → index-DRK0oAV5.js} +1 -1
- package/src/assets/web-panel/assets/{index-CToQxpWz.js → index-DeC7lehI.js} +1 -1
- package/src/assets/web-panel/assets/{index-DryKGM_t.js → index-DjrDGJP2.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5NGWgHp.js → index-Dln_vjSY.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOsIgPge.js → index-Dob6B6qS.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAeHmElB.js → index-GPY0LjCu.js} +1 -1
- package/src/assets/web-panel/assets/{index-DDy_RDjs.js → index-Ha2_56mf.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWgWrrWs.js → index-fnDgExTu.js} +1 -1
- package/src/assets/web-panel/assets/{index-CxvA72CP.js → index-jd2r-T4p.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE5Qm9UI.js → index-qPafbZmr.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DlDE-QgI.js → initDefaultProps-Bc2GWeWe.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CodUbIRF.js → motion-BI-Rxw6o.js} +1 -1
- package/src/assets/web-panel/assets/{move-DaLwsHeR.js → move-DRPdwDQB.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DdVg-3rL.js → omit-B4XTl3jW.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-KLR1EVCo.js → pickAttrs-Do5d86Wr.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-ChV7HvNw.js → placementArrow-B8VGZ0ZF.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BB_A8dBt.js → responsiveObserve-Cf0kI_vN.js} +1 -1
- package/src/assets/web-panel/assets/{slide-Bc1tQnIK.js → slide-Cb0psjSL.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CgrveSb0.js → statusUtils-Bjuo5Oal.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-vXAYhhjz.js → styleChecker-BLMhoHJ5.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BCIMPfq9.js → useFlexGapSupport-BdCwAfNU.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DMZGdr6G.js → useFs-9Jhaz5gG.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-118tWI_Z.js → usePersonalDataHub-xYFyXKwD.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-Z7O2Y7JP.js → vnode-CVhepE6Z.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BXym6zmD.js → zoom-IbbtJ4Zr.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +39 -0
- package/src/commands/command.js +187 -0
- package/src/commands/context.js +189 -0
- package/src/index.js +4 -0
- package/src/lib/goal-assess.js +228 -0
- package/src/lib/goal-store.js +33 -0
- package/src/lib/slash-commands.js +197 -0
- package/src/repl/agent-repl.js +42 -1
- package/src/runtime/agent-core.js +275 -0
- package/src/runtime/headless-runner.js +151 -0
- package/src/runtime/headless-stream.js +86 -0
- package/src/runtime/mcp-config.js +239 -0
- package/src/runtime/policies/agent-policy.js +1 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-PIx0Eu9I.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-CrJJOCFw.js +0 -1
- package/src/assets/web-panel/assets/Projects-Cb3p5QAP.js +0 -1
- package/src/assets/web-panel/assets/Tasks-DtVkhWCV.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DmNpkOdC.js +0 -1
- package/src/assets/web-panel/assets/index-7nAysteg.js +0 -1
- package/src/assets/web-panel/assets/index-BKWSQilQ.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,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* goal-assess — run-end LLM self-assessment of goal progress (cc goal Phase 2).
|
|
3
|
+
*
|
|
4
|
+
* After an agent run bound to a goal (--goal-assess), an LLM is asked to judge,
|
|
5
|
+
* from the run transcript, whether the goal advanced — and to propose key-result
|
|
6
|
+
* updates, a one-line progress note, and any concerns. The proposal is then
|
|
7
|
+
* persisted through goal-store (progress / key results / agent note / drift).
|
|
8
|
+
*
|
|
9
|
+
* This is OPT-IN because it spends extra tokens (one bonus completion per run).
|
|
10
|
+
* The LLM call is injected as `chat(promptString) => Promise<string>` so the
|
|
11
|
+
* orchestration is deterministically unit-testable without a provider.
|
|
12
|
+
*
|
|
13
|
+
* Parsing is deliberately tolerant: models wrap JSON in prose / code fences, so
|
|
14
|
+
* we extract the first balanced `{...}` block and validate the fields we use.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
recordProgress,
|
|
19
|
+
setKeyResult,
|
|
20
|
+
addDriftFlags,
|
|
21
|
+
getGoal,
|
|
22
|
+
} from "./goal-store.js";
|
|
23
|
+
|
|
24
|
+
/** Bound the transcript we feed the judge so the assessment stays cheap. */
|
|
25
|
+
const MAX_FINAL_TEXT = 2000;
|
|
26
|
+
const MAX_TOOLS = 40;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Build the assessment prompt. Asks for a strict JSON object describing whether
|
|
30
|
+
* the goal advanced + proposed updates.
|
|
31
|
+
* @param {object} goal
|
|
32
|
+
* @param {object} transcript { prompt, finalText, toolCalls:[{tool,args}] }
|
|
33
|
+
*/
|
|
34
|
+
export function buildAssessPrompt(goal, transcript = {}) {
|
|
35
|
+
const krLines = (goal.keyResults || [])
|
|
36
|
+
.map(
|
|
37
|
+
(k) =>
|
|
38
|
+
` - id=${k.id} | "${k.text}"` +
|
|
39
|
+
(k.target != null ? ` | ${k.current ?? 0}/${k.target}` : "") +
|
|
40
|
+
(k.done ? " | DONE" : ""),
|
|
41
|
+
)
|
|
42
|
+
.join("\n");
|
|
43
|
+
|
|
44
|
+
const tools = (transcript.toolCalls || [])
|
|
45
|
+
.slice(0, MAX_TOOLS)
|
|
46
|
+
.map((t) => t.tool)
|
|
47
|
+
.join(", ");
|
|
48
|
+
|
|
49
|
+
const finalText = String(transcript.finalText || "").slice(0, MAX_FINAL_TEXT);
|
|
50
|
+
|
|
51
|
+
return [
|
|
52
|
+
"You are assessing whether a single agent run advanced a long-running goal.",
|
|
53
|
+
"",
|
|
54
|
+
`GOAL: ${goal.objective}`,
|
|
55
|
+
`CURRENT PROGRESS: ${goal.progress ?? 0}%`,
|
|
56
|
+
goal.keyResults?.length
|
|
57
|
+
? `KEY RESULTS:\n${krLines}`
|
|
58
|
+
: "KEY RESULTS: (none)",
|
|
59
|
+
"",
|
|
60
|
+
`RUN TASK: ${String(transcript.prompt || "").slice(0, 500)}`,
|
|
61
|
+
tools ? `TOOLS USED: ${tools}` : "TOOLS USED: (none)",
|
|
62
|
+
`RUN RESULT:\n${finalText || "(no final text)"}`,
|
|
63
|
+
"",
|
|
64
|
+
"Reply with ONLY a JSON object (no prose, no code fence) of this shape:",
|
|
65
|
+
"{",
|
|
66
|
+
' "advanced": true|false, // did this run move the goal forward?',
|
|
67
|
+
' "progress": <0-100 or null>, // your estimate of new overall progress, or null to leave unchanged',
|
|
68
|
+
' "keyResults": [ // updates for specific key results (omit if none)',
|
|
69
|
+
' {"id": "<kr-id>", "current": <number or null>, "done": true|false}',
|
|
70
|
+
" ],",
|
|
71
|
+
' "note": "<one short sentence summarizing what changed>",',
|
|
72
|
+
' "concerns": ["<short concern>", ...] // anything blocking/at-risk (omit if none)',
|
|
73
|
+
"}",
|
|
74
|
+
].join("\n");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Tolerantly parse the judge's reply into a normalized assessment, or null.
|
|
79
|
+
* @param {string} text
|
|
80
|
+
* @returns {{advanced:boolean, progress:number|null, keyResults:object[], note:string, concerns:string[]}|null}
|
|
81
|
+
*/
|
|
82
|
+
export function parseAssessment(text) {
|
|
83
|
+
if (!text || typeof text !== "string") return null;
|
|
84
|
+
const json = extractFirstJsonObject(text);
|
|
85
|
+
if (!json) return null;
|
|
86
|
+
let obj;
|
|
87
|
+
try {
|
|
88
|
+
obj = JSON.parse(json);
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
if (!obj || typeof obj !== "object") return null;
|
|
93
|
+
|
|
94
|
+
const progress =
|
|
95
|
+
obj.progress == null || obj.progress === "" ? null : clampPct(obj.progress);
|
|
96
|
+
|
|
97
|
+
const keyResults = Array.isArray(obj.keyResults)
|
|
98
|
+
? obj.keyResults
|
|
99
|
+
.filter((k) => k && k.id)
|
|
100
|
+
.map((k) => ({
|
|
101
|
+
id: String(k.id),
|
|
102
|
+
current:
|
|
103
|
+
k.current == null || k.current === "" ? null : Number(k.current),
|
|
104
|
+
done: k.done === true,
|
|
105
|
+
}))
|
|
106
|
+
: [];
|
|
107
|
+
|
|
108
|
+
const concerns = Array.isArray(obj.concerns)
|
|
109
|
+
? obj.concerns.map((c) => String(c)).filter(Boolean)
|
|
110
|
+
: [];
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
advanced: obj.advanced === true,
|
|
114
|
+
progress,
|
|
115
|
+
keyResults,
|
|
116
|
+
note: obj.note ? String(obj.note) : "",
|
|
117
|
+
concerns,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function clampPct(v) {
|
|
122
|
+
const n = Math.round(Number(v));
|
|
123
|
+
if (!Number.isFinite(n)) return null;
|
|
124
|
+
return Math.max(0, Math.min(100, n));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Extract the first balanced top-level {...} block from text (fence-tolerant). */
|
|
128
|
+
function extractFirstJsonObject(text) {
|
|
129
|
+
const start = text.indexOf("{");
|
|
130
|
+
if (start < 0) return null;
|
|
131
|
+
let depth = 0;
|
|
132
|
+
let inStr = false;
|
|
133
|
+
let esc = false;
|
|
134
|
+
for (let i = start; i < text.length; i++) {
|
|
135
|
+
const ch = text[i];
|
|
136
|
+
if (inStr) {
|
|
137
|
+
if (esc) esc = false;
|
|
138
|
+
else if (ch === "\\") esc = true;
|
|
139
|
+
else if (ch === '"') inStr = false;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (ch === '"') inStr = true;
|
|
143
|
+
else if (ch === "{") depth++;
|
|
144
|
+
else if (ch === "}") {
|
|
145
|
+
depth--;
|
|
146
|
+
if (depth === 0) return text.slice(start, i + 1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Persist a parsed assessment to the goal via goal-store. Each sub-update is
|
|
154
|
+
* isolated (a bad key-result id can't void the progress note). Returns the
|
|
155
|
+
* final goal state.
|
|
156
|
+
* @param {string} goalId
|
|
157
|
+
* @param {object} a output of parseAssessment
|
|
158
|
+
* @param {object} [opts] { root }
|
|
159
|
+
*/
|
|
160
|
+
export function applyAssessment(goalId, a, opts = {}) {
|
|
161
|
+
if (!a) return getGoal(goalId, opts);
|
|
162
|
+
|
|
163
|
+
// Key-result updates first (they may drive derived progress).
|
|
164
|
+
for (const kr of a.keyResults || []) {
|
|
165
|
+
try {
|
|
166
|
+
setKeyResult(goalId, kr.id, { current: kr.current, done: kr.done }, opts);
|
|
167
|
+
} catch {
|
|
168
|
+
/* unknown / stale key-result id — skip, don't void the rest */
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Explicit progress and/or the agent note.
|
|
173
|
+
if (a.progress != null || a.note) {
|
|
174
|
+
try {
|
|
175
|
+
recordProgress(
|
|
176
|
+
goalId,
|
|
177
|
+
{ pct: a.progress, note: a.note, by: "agent" },
|
|
178
|
+
opts,
|
|
179
|
+
);
|
|
180
|
+
} catch {
|
|
181
|
+
/* best-effort */
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Drift: a no-advance run, plus any explicit concerns.
|
|
186
|
+
const flags = [];
|
|
187
|
+
if (!a.advanced) {
|
|
188
|
+
flags.push({ kind: "no-progress", detail: "run did not advance the goal" });
|
|
189
|
+
}
|
|
190
|
+
for (const c of a.concerns || []) flags.push({ kind: "concern", detail: c });
|
|
191
|
+
if (flags.length) {
|
|
192
|
+
try {
|
|
193
|
+
addDriftFlags(goalId, flags, opts);
|
|
194
|
+
} catch {
|
|
195
|
+
/* best-effort */
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return getGoal(goalId, opts);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Full orchestration: prompt → chat → parse → apply. Returns
|
|
204
|
+
* `{ assessment, goal }` (assessment is null when the judge gave no usable JSON).
|
|
205
|
+
* @param {object} params { goal, transcript, chat, opts }
|
|
206
|
+
* chat: (promptString) => Promise<string>
|
|
207
|
+
*/
|
|
208
|
+
export async function assessGoalProgress({
|
|
209
|
+
goal,
|
|
210
|
+
transcript,
|
|
211
|
+
chat,
|
|
212
|
+
opts = {},
|
|
213
|
+
}) {
|
|
214
|
+
if (!goal || typeof chat !== "function") {
|
|
215
|
+
return { assessment: null, goal: goal || null };
|
|
216
|
+
}
|
|
217
|
+
const prompt = buildAssessPrompt(goal, transcript);
|
|
218
|
+
let reply;
|
|
219
|
+
try {
|
|
220
|
+
reply = await chat(prompt);
|
|
221
|
+
} catch {
|
|
222
|
+
return { assessment: null, goal };
|
|
223
|
+
}
|
|
224
|
+
const assessment = parseAssessment(reply);
|
|
225
|
+
if (!assessment) return { assessment: null, goal };
|
|
226
|
+
const updated = applyAssessment(goal.id, assessment, opts);
|
|
227
|
+
return { assessment, goal: updated };
|
|
228
|
+
}
|
package/src/lib/goal-store.js
CHANGED
|
@@ -270,6 +270,39 @@ export function setStatus(id, status, opts = {}) {
|
|
|
270
270
|
);
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Append drift flags to a goal's `drift.flags` list. Each flag is normalized to
|
|
275
|
+
* `{ at, kind, detail }`. Used by the run-end assessment (cc goal Phase 2) to
|
|
276
|
+
* record "no progress this run" / concern signals. Capped at 20 (newest kept).
|
|
277
|
+
* @param {string} id
|
|
278
|
+
* @param {Array<string|object>} flags
|
|
279
|
+
*/
|
|
280
|
+
export function addDriftFlags(id, flags, opts = {}) {
|
|
281
|
+
const list = (Array.isArray(flags) ? flags : [flags]).filter(Boolean);
|
|
282
|
+
if (list.length === 0) return getGoal(id, opts);
|
|
283
|
+
return mutate(
|
|
284
|
+
id,
|
|
285
|
+
(g) => {
|
|
286
|
+
for (const f of list) {
|
|
287
|
+
const flag =
|
|
288
|
+
typeof f === "string"
|
|
289
|
+
? { at: nowIso(), kind: "concern", detail: f }
|
|
290
|
+
: {
|
|
291
|
+
at: nowIso(),
|
|
292
|
+
kind: f.kind || "concern",
|
|
293
|
+
detail: f.detail || "",
|
|
294
|
+
};
|
|
295
|
+
g.drift.flags.push(flag);
|
|
296
|
+
}
|
|
297
|
+
// Keep only the most recent 20 to bound the file size.
|
|
298
|
+
if (g.drift.flags.length > 20) {
|
|
299
|
+
g.drift.flags = g.drift.flags.slice(-20);
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
opts,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
273
306
|
/** Delete a goal. Returns true if it existed. */
|
|
274
307
|
export function deleteGoal(id, opts = {}) {
|
|
275
308
|
const root = opts.root || defaultRoot();
|