@dzhechkov/harness-cli 0.3.193 → 0.3.197
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/README.md +64 -7
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +214 -5
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
- package/src/cli.ts +220 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dzhechkov/harness-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.197",
|
|
4
4
|
"description": "The dz CLI — install AI skills for Claude Code, Codex, OpenCode, Hermes, OpenClaude, GitHub Copilot. 35 commands, 13 presets, 6 platform targets.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@dzhechkov/skills-reverse-engineering": "^0.1.0",
|
|
55
55
|
"@dzhechkov/skills-presentation-storyteller": "^0.1.0",
|
|
56
56
|
"@dzhechkov/skills-website-cloner": "^0.1.0",
|
|
57
|
-
"@dzhechkov/harness-core": "0.3.
|
|
57
|
+
"@dzhechkov/harness-core": "0.3.94"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@types/node": "^25.6.0",
|
package/src/cli.ts
CHANGED
|
@@ -51,9 +51,12 @@ import {
|
|
|
51
51
|
WORKFLOW_NAMES,
|
|
52
52
|
importEcc,
|
|
53
53
|
recordPattern,
|
|
54
|
+
resolveLearningBackend,
|
|
55
|
+
storeStats,
|
|
54
56
|
consolidateSessions,
|
|
55
57
|
pruneNoisePatterns,
|
|
56
58
|
recallHybrid,
|
|
59
|
+
teachGuard,
|
|
57
60
|
mirrorPatternsToVector,
|
|
58
61
|
vectorMirrorEnabled,
|
|
59
62
|
vectorTierStatus,
|
|
@@ -63,6 +66,8 @@ import {
|
|
|
63
66
|
importRvfCheckpoint,
|
|
64
67
|
statuslineData,
|
|
65
68
|
writeFeatureAdrState,
|
|
69
|
+
computeUsage,
|
|
70
|
+
readUsageLimits,
|
|
66
71
|
queryBookKnowledge,
|
|
67
72
|
loadStorePatternsSync,
|
|
68
73
|
bundleSkills,
|
|
@@ -72,6 +77,7 @@ import {
|
|
|
72
77
|
updateBrainSource,
|
|
73
78
|
queryBrain,
|
|
74
79
|
groundPrompt,
|
|
80
|
+
expandKu,
|
|
75
81
|
reindexBrainVectors,
|
|
76
82
|
buildPrimer,
|
|
77
83
|
exportBrainSlice,
|
|
@@ -116,10 +122,12 @@ Usage:
|
|
|
116
122
|
dz brain reindex [--json] (snapshot, re-embed book-KU brain vectors, stamp current model)
|
|
117
123
|
dz brain primer <slug> [--json] (print a source's capability card — KU-type histogram + top decision moments)
|
|
118
124
|
dz brain export --source <slug> --out <file> (export ONE source as a portable, lexical-only books.sqlite slice)
|
|
119
|
-
dz brain ground [<prompt>] [--k <N>] [--source <slug>] [--text]
|
|
125
|
+
dz brain ground [<prompt>] [--k <N>] [--source <slug>] [--text] [--budget <N>] [--full] (UserPromptSubmit hook; --budget inlines top-K KUs within ~N tokens; --full = ~8000)
|
|
126
|
+
dz brain expand <kuId> [--source <slug>] [--json] (full-content lookup for a citation kuId; --json emits the full KU object)
|
|
120
127
|
dz brain init [--project <dir>] [--k <N>] (wire the grounding hook into .claude/settings.json — opt-in)
|
|
121
128
|
dz statusline [--json] [--install] [--project <dir>] (live self-learning panel for Claude Code's status bar; reads the CC JSON payload from STDIN)
|
|
122
129
|
dz statusline --fa-record --slug <s> --step "<label>" [--recalled <n>] [--stored <n>] [--mode <m>] (feature-adr: record live per-run learning state → 📐 panel segment)
|
|
130
|
+
dz usage [--json] [--project <dir>] (ESTIMATE Claude session (5h-block) + weekly (7d) token usage from local transcripts; exit 0 ALWAYS; pct=null when memory.usage.{sessionTokenLimit,weeklyTokenLimit} unconfigured in .dz/config.json)
|
|
123
131
|
dz pretrain [--project <dir>]
|
|
124
132
|
dz recommend "<task description>"
|
|
125
133
|
dz compose <preset1+preset2+...> [--target <name>]
|
|
@@ -1035,11 +1043,14 @@ function cmdStatuslineFaRecord(options: Map<string, string>, cwd: string, write:
|
|
|
1035
1043
|
if (recalled === undefined) return 1;
|
|
1036
1044
|
const stored = parseCount('stored');
|
|
1037
1045
|
if (stored === undefined) return 1;
|
|
1046
|
+
const reinforced = parseCount('reinforced');
|
|
1047
|
+
if (reinforced === undefined) return 1;
|
|
1038
1048
|
|
|
1039
1049
|
const mode = options.get('mode');
|
|
1040
1050
|
const projectRoot = resolve(cwd, options.get('project') ?? '.');
|
|
1041
1051
|
const state = writeFeatureAdrState(projectRoot, {
|
|
1042
1052
|
slug, step, recalled, stored,
|
|
1053
|
+
...(reinforced > 0 ? { reinforced } : {}),
|
|
1043
1054
|
...(mode !== undefined && mode.trim() !== '' ? { mode: mode.trim() } : {}),
|
|
1044
1055
|
});
|
|
1045
1056
|
|
|
@@ -1047,7 +1058,7 @@ function cmdStatuslineFaRecord(options: Map<string, string>, cwd: string, write:
|
|
|
1047
1058
|
write(`dz statusline --fa-record: could not write learning state under ${projectRoot}/.dz/feature-adr/`);
|
|
1048
1059
|
return 1;
|
|
1049
1060
|
}
|
|
1050
|
-
write(`dz statusline: recorded /feature-adr learning state for "${slug}" (${step}) — 🎓 ${state.pool} pool · ↑${state.recalled} used · +${state.stored} new`);
|
|
1061
|
+
write(`dz statusline: recorded /feature-adr learning state for "${slug}" (${step}) — 🎓 ${state.pool} pool · ↑${state.recalled} used · +${state.stored} new · ↻${state.reinforced ?? 0} reinforced`);
|
|
1051
1062
|
return 0;
|
|
1052
1063
|
}
|
|
1053
1064
|
|
|
@@ -1089,7 +1100,7 @@ function cmdStatusline(
|
|
|
1089
1100
|
// Live /feature-adr run in flight → PREPEND the pipeline learning segment to the base dz line.
|
|
1090
1101
|
const fa = data.featureAdr;
|
|
1091
1102
|
if (fa !== undefined) {
|
|
1092
|
-
line = `📐 feature-adr ${fa.step} · 🎓 ${fa.pool} pool · ↑${fa.recalled} used · +${fa.stored} new · ${line}`;
|
|
1103
|
+
line = `📐 feature-adr ${fa.step} · 🎓 ${fa.pool} pool · ↑${fa.recalled} used · +${fa.stored} new · ↻${fa.reinforced ?? 0} reinforced · ${line}`;
|
|
1093
1104
|
}
|
|
1094
1105
|
|
|
1095
1106
|
write(line);
|
|
@@ -1101,6 +1112,73 @@ function cmdStatusline(
|
|
|
1101
1112
|
}
|
|
1102
1113
|
}
|
|
1103
1114
|
|
|
1115
|
+
/**
|
|
1116
|
+
* `dz usage` — print an ESTIMATE of Claude session (active 5h-block) + weekly (rolling 7d) token
|
|
1117
|
+
* usage, aggregated READONLY from the local transcript store (see {@link computeUsage}). `--json`
|
|
1118
|
+
* emits the single-line contract the feature-adr usage-probe agent parses; the human path prints a
|
|
1119
|
+
* compact `session ~74% (resets 19:00) · week ~52% (resets Sat)` or an unconfigured hint.
|
|
1120
|
+
*
|
|
1121
|
+
* **Exit code is 0 ALWAYS** — including on internal error the whole body is guarded and prints the
|
|
1122
|
+
* all-null JSON, so a probe can NEVER distinguish "usage unknown" from "command failed" via a
|
|
1123
|
+
* non-zero exit (unknown is a first-class value, INV-3). `--project <dir>` scopes ONLY the
|
|
1124
|
+
* `.dz/config.json` limits read; the measurement is account-wide (all projects, FR-1.6).
|
|
1125
|
+
*/
|
|
1126
|
+
function cmdUsage(options: Map<string, string>, flags: Set<string>, cwd: string, write: Write): number {
|
|
1127
|
+
const projectRoot = resolve(cwd, options.get('project') ?? '.');
|
|
1128
|
+
const nullContract = () =>
|
|
1129
|
+
JSON.stringify({
|
|
1130
|
+
sessionPct: null,
|
|
1131
|
+
weeklyPct: null,
|
|
1132
|
+
sessionTokens: 0,
|
|
1133
|
+
weeklyTokens: 0,
|
|
1134
|
+
resetsAt: { session: null, weekly: null },
|
|
1135
|
+
limits: { session: null, weekly: null },
|
|
1136
|
+
estimated: true,
|
|
1137
|
+
});
|
|
1138
|
+
try {
|
|
1139
|
+
const u = computeUsage(projectRoot);
|
|
1140
|
+
const lim = readUsageLimits(projectRoot);
|
|
1141
|
+
if (flags.has('json')) {
|
|
1142
|
+
write(
|
|
1143
|
+
JSON.stringify({
|
|
1144
|
+
sessionPct: u.sessionPct,
|
|
1145
|
+
weeklyPct: u.weeklyPct,
|
|
1146
|
+
sessionTokens: u.sessionTokens,
|
|
1147
|
+
weeklyTokens: u.weeklyTokens,
|
|
1148
|
+
resetsAt: { session: u.sessionResetsAt, weekly: u.weeklyResetsAt },
|
|
1149
|
+
limits: { session: lim.sessionTokenLimit ?? null, weekly: lim.weeklyTokenLimit ?? null },
|
|
1150
|
+
estimated: true,
|
|
1151
|
+
}),
|
|
1152
|
+
);
|
|
1153
|
+
return 0;
|
|
1154
|
+
}
|
|
1155
|
+
if (u.sessionPct === null && u.weeklyPct === null) {
|
|
1156
|
+
write(
|
|
1157
|
+
'usage: unconfigured — set memory.usage.sessionTokenLimit / weeklyTokenLimit in .dz/config.json (percentages are ESTIMATES calibrated from observed exhaustion)',
|
|
1158
|
+
);
|
|
1159
|
+
return 0;
|
|
1160
|
+
}
|
|
1161
|
+
// Compact human line — a short HH:MM / weekday hint on the resets, best-effort.
|
|
1162
|
+
const clock = (iso: string | null): string => {
|
|
1163
|
+
if (!iso) return '?';
|
|
1164
|
+
try {
|
|
1165
|
+
return new Date(iso).toISOString().slice(11, 16);
|
|
1166
|
+
} catch {
|
|
1167
|
+
return '?';
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
const s = u.sessionPct === null ? 'n/a' : '~' + u.sessionPct + '%';
|
|
1171
|
+
const w = u.weeklyPct === null ? 'n/a' : '~' + u.weeklyPct + '%';
|
|
1172
|
+
write('usage: session ' + s + ' (resets ' + clock(u.sessionResetsAt) + ') · week ' + w + ' (resets ' + clock(u.weeklyResetsAt) + ') · estimated');
|
|
1173
|
+
return 0;
|
|
1174
|
+
} catch {
|
|
1175
|
+
// never let a probe see a non-zero exit — print the all-null contract and exit 0.
|
|
1176
|
+
if (flags.has('json')) write(nullContract());
|
|
1177
|
+
else write('usage: unconfigured — set memory.usage.sessionTokenLimit / weeklyTokenLimit in .dz/config.json');
|
|
1178
|
+
return 0;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1104
1182
|
async function cmdTeach(options: Map<string, string>, flags: Set<string>, cwd: string, write: Write): Promise<number> {
|
|
1105
1183
|
const projectRoot = options.get('project') ?? cwd;
|
|
1106
1184
|
|
|
@@ -1174,9 +1252,37 @@ async function cmdTeach(options: Map<string, string>, flags: Set<string>, cwd: s
|
|
|
1174
1252
|
write(` Backend: memory (@dzhechkov/memory) Total now: ${loadStorePatternsSync(projectRoot).length}`);
|
|
1175
1253
|
// ONE batched mirror call through the same seam as single-teach (QR-6 — no bespoke path).
|
|
1176
1254
|
await emitMirror(projectRoot, importedRecs, 'dz-teach-import');
|
|
1255
|
+
if (imported > 0) {
|
|
1256
|
+
const report = await harmonizeVectorStore(projectRoot, {});
|
|
1257
|
+
write(` ℹ ${imported} imported — ${report.clusters.length} near-duplicate cluster(s): review with dz vector harmonize (dry-run); merge with dz vector harmonize --apply after backup`);
|
|
1258
|
+
}
|
|
1177
1259
|
return 0;
|
|
1178
1260
|
}
|
|
1179
1261
|
|
|
1262
|
+
const reinforce = options.get('reinforce');
|
|
1263
|
+
if (reinforce !== undefined && reinforce.trim() !== '') {
|
|
1264
|
+
const backend = resolveLearningBackend(projectRoot);
|
|
1265
|
+
const sampleReward = options.has('reward') ? parseFloat(options.get('reward') ?? '0.8') : undefined;
|
|
1266
|
+
backend.addSample({
|
|
1267
|
+
dzId: reinforce,
|
|
1268
|
+
kind: 'reinforce',
|
|
1269
|
+
ts: new Date().toISOString(),
|
|
1270
|
+
...(sampleReward !== undefined ? { reward: sampleReward } : {}),
|
|
1271
|
+
});
|
|
1272
|
+
const trained = await backend.train();
|
|
1273
|
+
if (trained.flushed > 0) {
|
|
1274
|
+
write(`↳ reinforced ${reinforce}`);
|
|
1275
|
+
return 0;
|
|
1276
|
+
}
|
|
1277
|
+
// HIGH-fix: a no-match must NOT auto-teach the raw argument — callers pass dzIds or truncated
|
|
1278
|
+
// text, so auto-teach minted garbage lessons (observed live). Fail with an honest advisory:
|
|
1279
|
+
// if the lesson is genuinely new, the caller teaches it EXPLICITLY with the full text.
|
|
1280
|
+
write(`dz teach --reinforce: no existing pattern matched ${JSON.stringify(reinforce)} — nothing reinforced`);
|
|
1281
|
+
write(' If this is a genuinely NEW lesson, teach it explicitly: dz teach "<full lesson text>" --reward <0-1> --domain <area>');
|
|
1282
|
+
write(' To find the exact pattern to reinforce: dz recall "<terms>" (match by its full text)');
|
|
1283
|
+
return 1;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1180
1286
|
const pattern = options.get('_positional_0');
|
|
1181
1287
|
if (!pattern) {
|
|
1182
1288
|
write('dz teach: pattern description required');
|
|
@@ -1189,6 +1295,23 @@ async function cmdTeach(options: Map<string, string>, flags: Set<string>, cwd: s
|
|
|
1189
1295
|
const reward = parseFloat(options.get('reward') ?? '0.8');
|
|
1190
1296
|
const domain = options.get('domain') ?? 'general';
|
|
1191
1297
|
|
|
1298
|
+
if (flags.has('guard')) {
|
|
1299
|
+
const verdict = await teachGuard(projectRoot, pattern, { reward: Math.max(0, Math.min(1, reward)) });
|
|
1300
|
+
if (verdict.action === 'reinforce') {
|
|
1301
|
+
const backend = resolveLearningBackend(projectRoot);
|
|
1302
|
+
backend.addSample({ dzId: verdict.dzId, kind: 'reinforce', reward: Math.max(0, Math.min(1, reward)), ts: new Date().toISOString() });
|
|
1303
|
+
const trained = await backend.train();
|
|
1304
|
+
// HIGH-fix: only claim success when the reinforce actually FLUSHED. With backend 'off'
|
|
1305
|
+
// (NoopLearningBackend) or a flush failure, flushed === 0 — falling through to the plain
|
|
1306
|
+
// teach below so the lesson is NEVER silently discarded (the exact silent-drop the ADR forbids).
|
|
1307
|
+
if (trained.flushed > 0) {
|
|
1308
|
+
write(`↳ reinforced existing pattern ${verdict.dzId} (cos=${verdict.cosine.toFixed(2)}) — not re-added`);
|
|
1309
|
+
return 0;
|
|
1310
|
+
}
|
|
1311
|
+
write(`dz teach --guard: reinforce of ${verdict.dzId} did not flush (backend off or write failure) — teaching the lesson normally instead`);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1192
1315
|
// Distill pattern into actionable rule (claude-smart inspired)
|
|
1193
1316
|
// Convert "what happened" into "what to do next time"
|
|
1194
1317
|
const isRule = pattern.toLowerCase().startsWith('always') || pattern.toLowerCase().startsWith('never') ||
|
|
@@ -1291,6 +1414,23 @@ async function cmdRecall(options: Map<string, string>, flags: Set<string>, cwd:
|
|
|
1291
1414
|
// With --json this is the portable export the agentdb-memory MCP bridge consumes.
|
|
1292
1415
|
if (all) {
|
|
1293
1416
|
const patterns = loadStorePatternsSync(projectRoot);
|
|
1417
|
+
if (flags.has('stats')) {
|
|
1418
|
+
const stats = storeStats(projectRoot);
|
|
1419
|
+
const backendStats = resolveLearningBackend(projectRoot).getStats();
|
|
1420
|
+
if (asJson) {
|
|
1421
|
+
write(JSON.stringify({ patterns, stats, learning: backendStats }));
|
|
1422
|
+
return 0;
|
|
1423
|
+
}
|
|
1424
|
+
write(`dz recall --all --stats — ${patterns.length} learned pattern(s)`);
|
|
1425
|
+
write(` backend: ${backendStats.backend}${backendStats.advisory !== undefined ? ` (${backendStats.advisory})` : ''}`);
|
|
1426
|
+
write(` domains: ${Object.entries(stats.perDomain).map(([k, v]) => `${k}=${v}`).join(', ') || 'none'}`);
|
|
1427
|
+
write(` exact-dup groups: ${stats.exactDupGroups}`);
|
|
1428
|
+
write(` re-teach trend: ${stats.teachEvents} teach event(s), ${stats.reinforceEvents} reinforce event(s)`);
|
|
1429
|
+
write(' top uses:');
|
|
1430
|
+
for (const row of stats.topUses) write(` ${row.uses}× [${row.reward.toFixed(2)}] (${row.domain}) ${row.pattern.slice(0, 80)}`);
|
|
1431
|
+
write(' near-dup density: run dz vector harmonize (dry-run)');
|
|
1432
|
+
return 0;
|
|
1433
|
+
}
|
|
1294
1434
|
if (asJson) {
|
|
1295
1435
|
write(JSON.stringify(patterns));
|
|
1296
1436
|
} else {
|
|
@@ -1556,6 +1696,13 @@ async function cmdVector(options: Map<string, string>, flags: Set<string>, cwd:
|
|
|
1556
1696
|
/* brain — the durable, cross-project knowledge brain (ADR §5.2 P0) */
|
|
1557
1697
|
/* ------------------------------------------------------------------ */
|
|
1558
1698
|
|
|
1699
|
+
/**
|
|
1700
|
+
* Token budget used when `dz brain ground --full` is specified (chars/4 heuristic, approximate). A
|
|
1701
|
+
* typical DDIA-scale KU is ~500–750 tokens; at k=5 the sum is well under 8000, so `--full` inlines
|
|
1702
|
+
* everything the top-K recall returns in practice. Documented in the brain usage + wiki.
|
|
1703
|
+
*/
|
|
1704
|
+
const GROUND_FULL_BUDGET = 8_000;
|
|
1705
|
+
|
|
1559
1706
|
const BRAIN_USAGE = `dz brain — the durable, cross-project knowledge brain
|
|
1560
1707
|
|
|
1561
1708
|
Usage:
|
|
@@ -1569,7 +1716,8 @@ Usage:
|
|
|
1569
1716
|
dz brain reindex [--json]
|
|
1570
1717
|
dz brain primer <slug> [--json]
|
|
1571
1718
|
dz brain export --source <slug> --out <file>
|
|
1572
|
-
dz brain ground [<prompt>] [--k <N>] [--source <slug>] [--text]
|
|
1719
|
+
dz brain ground [<prompt>] [--k <N>] [--source <slug>] [--text] [--budget <N>] [--full]
|
|
1720
|
+
dz brain expand <kuId> [--source <slug>] [--json]
|
|
1573
1721
|
dz brain init [--project <dir>] [--k <N>]
|
|
1574
1722
|
|
|
1575
1723
|
add: default promotes THIS project's digitized-book KUs into the brain. The three --from-* modes
|
|
@@ -1591,6 +1739,15 @@ Usage:
|
|
|
1591
1739
|
Always exits 0 — grounding is advisory and must never fail a prompt. Reranking is ON by
|
|
1592
1740
|
default here (grounding wants the most on-point citation first). --text prints the raw
|
|
1593
1741
|
citation block instead of the JSON wrapper (for manual inspection).
|
|
1742
|
+
Three grounding tiers: (1) pointer (default, no flags) — one compact citation line per KU,
|
|
1743
|
+
cheap every-turn; (2) model-driven expand (--budget N>0 / --full) — each citation carries
|
|
1744
|
+
its kuId and the directive tells the model to pull full content via \`dz brain expand\`;
|
|
1745
|
+
(3) budgeted eager (--budget N) — eager-inlines full content of the top-K KUs within ~N
|
|
1746
|
+
tokens (chars/4 approx), worth-ranked; a KU that would overflow stays a pointer. --full =
|
|
1747
|
+
--budget 8000. --budget 0 / absent = pointers-only.
|
|
1748
|
+
expand: full-content lookup by kuId — the command the grounding directive names when --budget/--full
|
|
1749
|
+
is used. Prints name, problem, pages, book, and the FULL content (untruncated); --json emits
|
|
1750
|
+
the whole KU object. Exit 1 if the kuId is not found.
|
|
1594
1751
|
init: wires \`brain ground\` into .claude/settings.json as an opt-in UserPromptSubmit hook.`;
|
|
1595
1752
|
|
|
1596
1753
|
/**
|
|
@@ -2009,12 +2166,28 @@ async function cmdBrain(
|
|
|
2009
2166
|
const kRaw = options.get('k');
|
|
2010
2167
|
const k = kRaw !== undefined ? Math.max(1, parseInt(kRaw, 10) || 5) : undefined;
|
|
2011
2168
|
|
|
2012
|
-
|
|
2169
|
+
// brain-ground-expand: --budget N eager-inlines top-K KU content within ~N tokens (chars/4);
|
|
2170
|
+
// --full = --budget GROUND_FULL_BUDGET; --budget 0 / absent ⇒ pointers-only (byte-identical).
|
|
2171
|
+
const budgetRaw = options.get('budget');
|
|
2172
|
+
let contentBudget: number | undefined;
|
|
2173
|
+
if (budgetRaw !== undefined) {
|
|
2174
|
+
const parsed = parseInt(budgetRaw, 10);
|
|
2175
|
+
if (isNaN(parsed) || parsed < 0) {
|
|
2176
|
+
write('dz brain ground: --budget must be a non-negative integer');
|
|
2177
|
+
return 1;
|
|
2178
|
+
}
|
|
2179
|
+
contentBudget = parsed === 0 ? undefined : parsed; // 0 → pointer-only (FR-03.6)
|
|
2180
|
+
} else if (flags.has('full')) {
|
|
2181
|
+
contentBudget = GROUND_FULL_BUDGET;
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
const gopts: { prompt: string; depsRoot: string; k?: number; source?: string; contentBudget?: number } = {
|
|
2013
2185
|
prompt,
|
|
2014
2186
|
depsRoot: resolveDepsRoot(cwd),
|
|
2015
2187
|
};
|
|
2016
2188
|
if (k !== undefined) gopts.k = k;
|
|
2017
2189
|
if (source !== undefined) gopts.source = source;
|
|
2190
|
+
if (contentBudget !== undefined) gopts.contentBudget = contentBudget;
|
|
2018
2191
|
|
|
2019
2192
|
const res = await groundPrompt(gopts);
|
|
2020
2193
|
if (!res.emitted) return 0; // no relevant citations → inject nothing
|
|
@@ -2033,6 +2206,46 @@ async function cmdBrain(
|
|
|
2033
2206
|
return 0;
|
|
2034
2207
|
}
|
|
2035
2208
|
|
|
2209
|
+
// ── dz brain expand <kuId> ───────────────────────────────────────────────────────────────────
|
|
2210
|
+
// Full-content lookup by kuId — the command named in GROUNDING_DIRECTIVE_EXPAND (brain-ground-expand
|
|
2211
|
+
// Tier 1). Prints the FULL `content` (+ name/problem/pages/book) for one citation the model wants
|
|
2212
|
+
// to read; --json emits the whole KU object. Exit 1 on missing kuId / not-found.
|
|
2213
|
+
if (sub === 'expand') {
|
|
2214
|
+
const kuId = options.get('_positional_1');
|
|
2215
|
+
if (!kuId) {
|
|
2216
|
+
write('dz brain expand: a kuId is required');
|
|
2217
|
+
write(' Example: dz brain expand ddia-ch05-ku01');
|
|
2218
|
+
return 1;
|
|
2219
|
+
}
|
|
2220
|
+
const source = options.get('source');
|
|
2221
|
+
const res = expandKu({
|
|
2222
|
+
kuId,
|
|
2223
|
+
depsRoot: resolveDepsRoot(cwd),
|
|
2224
|
+
...(source !== undefined ? { source } : {}),
|
|
2225
|
+
});
|
|
2226
|
+
if (res.error !== undefined) {
|
|
2227
|
+
write(`dz brain expand: ${res.error}`);
|
|
2228
|
+
return 1;
|
|
2229
|
+
}
|
|
2230
|
+
const ku = res.ku!;
|
|
2231
|
+
if (asJson) {
|
|
2232
|
+
write(JSON.stringify(ku, null, 2));
|
|
2233
|
+
return 0;
|
|
2234
|
+
}
|
|
2235
|
+
const ch = ku.chapter !== undefined && ku.chapter !== '' ? `гл.${ku.chapter}` : '';
|
|
2236
|
+
const pg = ku.pages !== undefined && ku.pages.length > 0 ? `с.${ku.pages.join('–')}` : '';
|
|
2237
|
+
write(`kuId: ${ku.kuId}`);
|
|
2238
|
+
write(`book: ${ku.book}`);
|
|
2239
|
+
if (ch) write(`chapter: ${ch}`);
|
|
2240
|
+
if (pg) write(`pages: ${pg}`);
|
|
2241
|
+
write(`name: ${ku.name}`);
|
|
2242
|
+
write(`problem: ${ku.problem}`);
|
|
2243
|
+
write('');
|
|
2244
|
+
write('content:');
|
|
2245
|
+
write(ku.content);
|
|
2246
|
+
return 0;
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2036
2249
|
// ── dz brain init ────────────────────────────────────────────────────────────────────────────
|
|
2037
2250
|
// Opt-in: wire `dz brain ground` into .claude/settings.json as a UserPromptSubmit hook.
|
|
2038
2251
|
// Idempotent read-merge-write — preserve every existing hook/key (e.g. the agentic-qe route hook).
|
|
@@ -3290,6 +3503,8 @@ export async function runCli(argv: string[], io: CliIo = {}): Promise<number> {
|
|
|
3290
3503
|
return await cmdBrain(options, flags, cwd, write, readStdin);
|
|
3291
3504
|
case 'statusline':
|
|
3292
3505
|
return cmdStatusline(options, flags, cwd, write, readStdin);
|
|
3506
|
+
case 'usage':
|
|
3507
|
+
return cmdUsage(options, flags, cwd, write);
|
|
3293
3508
|
case 'setup':
|
|
3294
3509
|
return await cmdSetup(options, flags, cwd, write);
|
|
3295
3510
|
case 'pretrain':
|