@dzhechkov/harness-cli 0.3.249 → 0.3.250
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/.dz-manifest.json +8 -8
- package/README.md +2 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +281 -1
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
- package/sbom.json +7 -7
- package/src/cli.ts +268 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dzhechkov/harness-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.250",
|
|
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",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"sbom.json"
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@dzhechkov/harness-core": "^0.3.
|
|
44
|
+
"@dzhechkov/harness-core": "^0.3.141",
|
|
45
45
|
"@dzhechkov/harness-presets": "^0.5.0",
|
|
46
46
|
"@dzhechkov/scout": "^0.8.0",
|
|
47
47
|
"@dzhechkov/skills-devops": "^0.3.0",
|
package/sbom.json
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"hashes": [
|
|
26
26
|
{
|
|
27
27
|
"alg": "SHA-256",
|
|
28
|
-
"content": "
|
|
28
|
+
"content": "1559995482381fd6ac146e6d229cb7d33476cdca8fad21fd361467dd698bd065"
|
|
29
29
|
}
|
|
30
30
|
]
|
|
31
31
|
},
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
"hashes": [
|
|
106
106
|
{
|
|
107
107
|
"alg": "SHA-256",
|
|
108
|
-
"content": "
|
|
108
|
+
"content": "e41e9538474ad80901474841e66f3be8b9f4fdc24f85b4168a13958f19f0762c"
|
|
109
109
|
}
|
|
110
110
|
]
|
|
111
111
|
},
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"hashes": [
|
|
116
116
|
{
|
|
117
117
|
"alg": "SHA-256",
|
|
118
|
-
"content": "
|
|
118
|
+
"content": "038b4bf4c9295202129662e1caf3b8b139c91f69b2e292373066154b644cc97c"
|
|
119
119
|
}
|
|
120
120
|
]
|
|
121
121
|
},
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
"hashes": [
|
|
126
126
|
{
|
|
127
127
|
"alg": "SHA-256",
|
|
128
|
-
"content": "
|
|
128
|
+
"content": "dafbac9c642da206ddfadb9081f01ef2a4cfba98dee719ef64200241d2b76726"
|
|
129
129
|
}
|
|
130
130
|
]
|
|
131
131
|
},
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
"hashes": [
|
|
186
186
|
{
|
|
187
187
|
"alg": "SHA-256",
|
|
188
|
-
"content": "
|
|
188
|
+
"content": "4e22f0553a19181eea750db59bd3eec84fd880473ac523705a1a26197665e87e"
|
|
189
189
|
}
|
|
190
190
|
]
|
|
191
191
|
},
|
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
"hashes": [
|
|
206
206
|
{
|
|
207
207
|
"alg": "SHA-256",
|
|
208
|
-
"content": "
|
|
208
|
+
"content": "9a32862f99d3d0211e08ea49c7091f5697acdd2789ac05137203abd6ed7a9b3e"
|
|
209
209
|
}
|
|
210
210
|
]
|
|
211
211
|
},
|
|
@@ -225,7 +225,7 @@
|
|
|
225
225
|
"hashes": [
|
|
226
226
|
{
|
|
227
227
|
"alg": "SHA-256",
|
|
228
|
-
"content": "
|
|
228
|
+
"content": "2e013ab73b4eb3dd248ed0fbbd5c050911ad667bb78054fde0bb021fbd1815db"
|
|
229
229
|
}
|
|
230
230
|
]
|
|
231
231
|
},
|
package/src/cli.ts
CHANGED
|
@@ -207,7 +207,28 @@ import {
|
|
|
207
207
|
readQuarantineState,
|
|
208
208
|
registrationExitCode,
|
|
209
209
|
renderRegistrationReport,
|
|
210
|
+
// Smart Backlog (feature smart-backlog) — goal-directed idea pipeline over the Brain vector engine.
|
|
211
|
+
readBacklogConfig,
|
|
212
|
+
readIdeas,
|
|
213
|
+
writeIdeas,
|
|
214
|
+
ideaId,
|
|
215
|
+
dedupIdea,
|
|
216
|
+
readGoalMap,
|
|
217
|
+
alignIdea,
|
|
218
|
+
mirrorIdeaVector,
|
|
219
|
+
snapshotIdeas,
|
|
220
|
+
spinRoulette,
|
|
221
|
+
rankRoulette,
|
|
222
|
+
seededRng,
|
|
223
|
+
eligibleIdeas,
|
|
224
|
+
stageEnrichment,
|
|
225
|
+
buildJiraDraft,
|
|
226
|
+
resolveJiraAdapter,
|
|
227
|
+
makeBacklogIO,
|
|
228
|
+
harmonizeBacklog,
|
|
229
|
+
BACKLOG_BACKENDS,
|
|
210
230
|
} from '@dzhechkov/harness-core';
|
|
231
|
+
import type { IdeaRecord, IdeaStatus } from '@dzhechkov/harness-core';
|
|
211
232
|
import type { Family, ModelRung, Candidate as BtoCandidate, DimScores } from '@dzhechkov/harness-core';
|
|
212
233
|
import type { SetupSpec } from '@dzhechkov/harness-core';
|
|
213
234
|
import type { ProvenanceMode, PackVerdict, ClaudeUsageModel, PatternRecord, TargetName, BookKU, HarmonizeReport, UsageCalibrationPlan, ClaimFinding, RecallUsagePatternRow, GateExecution, GateStep } from '@dzhechkov/harness-core';
|
|
@@ -240,6 +261,14 @@ Usage:
|
|
|
240
261
|
dz skills-verify [--dir <project>] [--expect a,b] [--static] [--strict] [--json] (does .claude/skills/ actually REGISTER? --static = instant layout scan for CI; default reads the authoritative system/init listing from a real session. exit 0 pass / 1 fail / 2 inconclusive — never a false pass)
|
|
241
262
|
dz compounding [--project <dir>] [--json] (honest learning-loop payoff report: pool write-only ratio, guard repeat-violation trajectory, cold-vs-warm replay readiness, instrumentation health — a gate without enough data says INSUFFICIENT_DATA, never a fake verdict)
|
|
242
263
|
dz score --slug <feature> [--project <dir>] [--json] (process scorecard for ONE feature-adr run, from its artifacts: ADR confirmation, discrimination, cross-model QE grade, live verification, README-first, learning loop, amendments — descriptive-only, a low score exits 0)
|
|
264
|
+
dz backlog add "<idea>" [--effort 1-5] [--proposal <text>] [--dry-run] [--project <dir>] [--json] (capture an idea: semantic dedup against existing ideas via the Brain vector engine (DUPLICATE>=0.92 merges, RELATED links, NEW creates) + GoalMap alignment; --dry-run classifies without writing)
|
|
265
|
+
dz backlog list [--status <s>] [--goal <id>] [--project <dir>] [--json] (list captured ideas, filterable by status/goal)
|
|
266
|
+
dz backlog show <id> [--project <dir>] [--json] (full record for one idea)
|
|
267
|
+
dz backlog goals [--validate] [--project <dir>] [--json] (list/validate the compass at .dz/backlog/goals.json)
|
|
268
|
+
dz backlog roulette [--pick <N>] [--seed <n>] [--commit] [--project <dir>] [--json] (WEIGHTED draw over eligible ideas: alignment^alpha * recencyDecay * 1/effort, seeded; --pick N = ranked shortlist; --commit flips the pick to in-progress (the only mutation))
|
|
269
|
+
dz backlog enrich <id> [--project <dir>] [--json] (stage the idea2prd input scaffold in features/<slug>/ and hand off to the idea2prd-manual skill — the CLI never fabricates a PRD)
|
|
270
|
+
dz backlog jira <id> [--project <dir>] [--json] (draft a Jira issue via the configurable adapter (backlog.jira.adapter: jira-mcp|copilot-mcp|none); none writes an auditable jira-outbox/<id>.json stub)
|
|
271
|
+
dz backlog harmonize [--apply] [--threshold <0-1>] [--project <dir>] [--json] (batch semantic dedup of the backlog ideas; --dry-run default, --apply snapshots first)
|
|
243
272
|
dz setup --target <name> [--preset <name>] [--select id,id,...] [--skills-dir <dir>] [--project <dir>] [--memory agentdb] [--no-memory] [--no-hooks] [--install-driver] [--force] [--enrich]
|
|
244
273
|
dz teach "<pattern>" [--reward <0-1>] [--domain <name>] [--type rule|success-pattern|lesson-learned] [--project <dir>] [--no-mirror] (--project pins the learned store to <dir>/.dz, not the cwd — pin to a canonical brain)
|
|
245
274
|
dz teach --from-json <file> [--project <dir>] [--no-mirror] (bulk-import a 'dz recall --all --json' export — share a learned store across machines)
|
|
@@ -2218,6 +2247,8 @@ async function cmdVector(options: Map<string, string>, flags: Set<string>, cwd:
|
|
|
2218
2247
|
return report.error !== undefined ? 1 : 0;
|
|
2219
2248
|
}
|
|
2220
2249
|
if (report.error !== undefined) {
|
|
2250
|
+
// Honest failure/skip. Under a non-agentdb engine backlog is reported `skipped` (it never touches the
|
|
2251
|
+
// shared manifest); a model bump needs a FULL reindex under the agentdb engine. Surface it, exit 1.
|
|
2221
2252
|
write(`dz vector reindex: ${report.error}`);
|
|
2222
2253
|
return 1;
|
|
2223
2254
|
}
|
|
@@ -6149,6 +6180,241 @@ function cmdDeliveryCheck(options: Map<string, string>, flags: Set<string>, cwd:
|
|
|
6149
6180
|
return 0;
|
|
6150
6181
|
}
|
|
6151
6182
|
|
|
6183
|
+
/* ------------------------------------------------------------------ */
|
|
6184
|
+
/* backlog — the Smart Backlog idea pipeline (feature smart-backlog) */
|
|
6185
|
+
/* ------------------------------------------------------------------ */
|
|
6186
|
+
|
|
6187
|
+
/** Thin dispatcher — ALL logic lives in harness-core/src/backlog.ts (05 architecture: handlers stay dumb). */
|
|
6188
|
+
async function cmdBacklog(options: Map<string, string>, flags: Set<string>, cwd: string, write: Write): Promise<number> {
|
|
6189
|
+
const projectRoot = resolve(cwd, options.get('project') ?? '.');
|
|
6190
|
+
const json = flags.has('json');
|
|
6191
|
+
const sub = options.get('_positional_0');
|
|
6192
|
+
const cfg = readBacklogConfig(projectRoot);
|
|
6193
|
+
|
|
6194
|
+
const emitErr = (msg: string): number => {
|
|
6195
|
+
write(json ? JSON.stringify({ error: msg, exitCode: 1 }) : `dz backlog: ${msg}`);
|
|
6196
|
+
return 1;
|
|
6197
|
+
};
|
|
6198
|
+
|
|
6199
|
+
if (sub === 'add') {
|
|
6200
|
+
const text = (options.get('_positional_1') ?? '').trim();
|
|
6201
|
+
if (text === '') return emitErr('an idea text is required: dz backlog add "<idea>"');
|
|
6202
|
+
const effort = Number(options.get('effort') ?? cfg.roulette.defaultEffort);
|
|
6203
|
+
const dryRun = flags.has('dry-run');
|
|
6204
|
+
const verdict = await dedupIdea(projectRoot, text, cfg);
|
|
6205
|
+
if (verdict.action === 'duplicate') {
|
|
6206
|
+
// DUPLICATE ⇒ snapshot + reinforce the existing root; NO new record (ADR-002 T-002b).
|
|
6207
|
+
const ideas = readIdeas(projectRoot);
|
|
6208
|
+
const match = ideas.find((i) => i.id === verdict.matchedId);
|
|
6209
|
+
if (!dryRun && match !== undefined) {
|
|
6210
|
+
const snap = snapshotIdeas(projectRoot, join(projectRoot, '.dz', 'backlog', `ideas.pre-merge-${Date.now()}.jsonl`));
|
|
6211
|
+
if (snap.error !== undefined) return emitErr(snap.error);
|
|
6212
|
+
match.uses += 1;
|
|
6213
|
+
writeIdeas(projectRoot, ideas);
|
|
6214
|
+
}
|
|
6215
|
+
if (json) write(JSON.stringify({ action: 'duplicate', matchedId: verdict.matchedId, cosine: verdict.cosine, exitCode: 0 }, null, 2));
|
|
6216
|
+
else write(`dz backlog: DUPLICATE of ${verdict.matchedId} (cosine ${verdict.cosine.toFixed(3)}${verdict.exactTextOnly ? ', exact-text' : ''}) — reinforced, no new record`);
|
|
6217
|
+
return 0;
|
|
6218
|
+
}
|
|
6219
|
+
const goalMap = readGoalMap(projectRoot);
|
|
6220
|
+
const align = await alignIdea(projectRoot, text, goalMap);
|
|
6221
|
+
const createdTs = new Date().toISOString();
|
|
6222
|
+
const rec: IdeaRecord = {
|
|
6223
|
+
id: ideaId(text, createdTs),
|
|
6224
|
+
text,
|
|
6225
|
+
status: 'new',
|
|
6226
|
+
createdTs,
|
|
6227
|
+
effort: Number.isFinite(effort) ? Math.max(1, Math.min(5, Math.floor(effort))) : cfg.roulette.defaultEffort,
|
|
6228
|
+
goalId: align.goalId,
|
|
6229
|
+
goalAlignment: align.goalAlignment,
|
|
6230
|
+
relatedIds: [...verdict.relatedIds],
|
|
6231
|
+
uses: 0,
|
|
6232
|
+
tags: [],
|
|
6233
|
+
};
|
|
6234
|
+
const proposal = options.get('proposal');
|
|
6235
|
+
if (proposal !== undefined) rec.proposal = proposal; // agent prose ONLY — the CLI never fabricates it
|
|
6236
|
+
if (dryRun) {
|
|
6237
|
+
if (json) write(JSON.stringify({ action: verdict.action, dryRun: true, idea: rec, exitCode: 0 }, null, 2));
|
|
6238
|
+
else write(`dz backlog (dry-run): ${verdict.action.toUpperCase()} — would create ${rec.id}; align ${rec.goalAlignment.toFixed(3)}${rec.goalId !== null ? ` → ${rec.goalId}` : ''}`);
|
|
6239
|
+
return 0;
|
|
6240
|
+
}
|
|
6241
|
+
const ideas = readIdeas(projectRoot);
|
|
6242
|
+
ideas.push(rec);
|
|
6243
|
+
writeIdeas(projectRoot, ideas);
|
|
6244
|
+
const mirror = await mirrorIdeaVector(projectRoot, rec); // best-effort — never blocks capture
|
|
6245
|
+
if (json) write(JSON.stringify({ action: verdict.action, idea: rec, related: verdict.relatedIds, exitCode: 0 }, null, 2));
|
|
6246
|
+
else {
|
|
6247
|
+
write(`dz backlog: ${verdict.action.toUpperCase()} — captured ${rec.id}`);
|
|
6248
|
+
if (rec.goalId !== null) write(` top goal: ${rec.goalId} (alignment ${rec.goalAlignment.toFixed(3)})`);
|
|
6249
|
+
if (verdict.relatedIds.length > 0) write(` related: ${verdict.relatedIds.join(', ')}`);
|
|
6250
|
+
write(' proposal: (write your own — dz backlog add --proposal "<text>"; the CLI never fabricates it)');
|
|
6251
|
+
if (mirror.error !== undefined) write(` ↳ vector mirror deferred: ${mirror.error}`);
|
|
6252
|
+
}
|
|
6253
|
+
return 0;
|
|
6254
|
+
}
|
|
6255
|
+
|
|
6256
|
+
if (sub === 'list') {
|
|
6257
|
+
let ideas = readIdeas(projectRoot);
|
|
6258
|
+
const status = options.get('status');
|
|
6259
|
+
const goal = options.get('goal');
|
|
6260
|
+
if (status !== undefined) ideas = ideas.filter((i) => i.status === (status as IdeaStatus));
|
|
6261
|
+
if (goal !== undefined) ideas = ideas.filter((i) => i.goalId === goal);
|
|
6262
|
+
if (json) {
|
|
6263
|
+
write(JSON.stringify({ ideas, exitCode: 0 }, null, 2));
|
|
6264
|
+
return 0;
|
|
6265
|
+
}
|
|
6266
|
+
if (ideas.length === 0) {
|
|
6267
|
+
write('dz backlog: no ideas' + (status !== undefined || goal !== undefined ? ' match the filter' : ' yet — dz backlog add "<idea>"'));
|
|
6268
|
+
return 0;
|
|
6269
|
+
}
|
|
6270
|
+
write(`dz backlog — ${ideas.length} idea(s)`);
|
|
6271
|
+
for (const i of ideas) write(` ${i.id} [${i.status}] e${i.effort} align ${i.goalAlignment.toFixed(2)}${i.goalId !== null ? ` (${i.goalId})` : ''} ${i.text.slice(0, 70)}`);
|
|
6272
|
+
return 0;
|
|
6273
|
+
}
|
|
6274
|
+
|
|
6275
|
+
if (sub === 'show') {
|
|
6276
|
+
const id = options.get('_positional_1');
|
|
6277
|
+
if (id === undefined) return emitErr('an idea id is required: dz backlog show <id>');
|
|
6278
|
+
const rec = readIdeas(projectRoot).find((i) => i.id === id);
|
|
6279
|
+
if (rec === undefined) return emitErr(`no idea ${id}`);
|
|
6280
|
+
write(json ? JSON.stringify({ idea: rec, exitCode: 0 }, null, 2) : JSON.stringify(rec, null, 2));
|
|
6281
|
+
return 0;
|
|
6282
|
+
}
|
|
6283
|
+
|
|
6284
|
+
if (sub === 'goals') {
|
|
6285
|
+
const goalMap = readGoalMap(projectRoot);
|
|
6286
|
+
if (flags.has('validate')) {
|
|
6287
|
+
const problems: string[] = [];
|
|
6288
|
+
const seen = new Set<string>();
|
|
6289
|
+
for (const g of goalMap.goals) {
|
|
6290
|
+
if (seen.has(g.id)) problems.push(`duplicate goal id: ${g.id}`);
|
|
6291
|
+
seen.add(g.id);
|
|
6292
|
+
if (g.statement.trim() === '') problems.push(`goal ${g.id} has an empty statement`);
|
|
6293
|
+
if (!(g.weight > 0 && g.weight <= 1)) problems.push(`goal ${g.id} weight ${g.weight} is out of (0,1]`);
|
|
6294
|
+
}
|
|
6295
|
+
if (json) write(JSON.stringify({ valid: problems.length === 0, problems, goals: goalMap.goals.length, exitCode: problems.length === 0 ? 0 : 1 }, null, 2));
|
|
6296
|
+
else if (problems.length === 0) write(`dz backlog goals: valid (${goalMap.goals.length} goal(s))`);
|
|
6297
|
+
else { write('dz backlog goals: INVALID'); for (const p of problems) write(` - ${p}`); }
|
|
6298
|
+
return problems.length === 0 ? 0 : 1;
|
|
6299
|
+
}
|
|
6300
|
+
if (json) {
|
|
6301
|
+
write(JSON.stringify({ goals: goalMap.goals, exitCode: 0 }, null, 2));
|
|
6302
|
+
return 0;
|
|
6303
|
+
}
|
|
6304
|
+
if (goalMap.goals.length === 0) {
|
|
6305
|
+
write('dz backlog goals: no compass — create .dz/backlog/goals.json ({ "version":1, "goals":[{ "id","statement","weight","keywords" }] })');
|
|
6306
|
+
return 0;
|
|
6307
|
+
}
|
|
6308
|
+
write(`dz backlog — ${goalMap.goals.length} goal(s)`);
|
|
6309
|
+
for (const g of goalMap.goals) write(` ${g.id} (w=${g.weight}): ${g.statement}`);
|
|
6310
|
+
return 0;
|
|
6311
|
+
}
|
|
6312
|
+
|
|
6313
|
+
if (sub === 'roulette') {
|
|
6314
|
+
const ideas = readIdeas(projectRoot);
|
|
6315
|
+
const pool = eligibleIdeas(ideas);
|
|
6316
|
+
if (pool.length === 0) {
|
|
6317
|
+
write(json ? JSON.stringify({ pick: null, eligible: 0, exitCode: 0 }) : 'dz backlog roulette: no eligible ideas (status new/enriched)');
|
|
6318
|
+
return 0;
|
|
6319
|
+
}
|
|
6320
|
+
const now = Date.now();
|
|
6321
|
+
const pickN = options.get('pick');
|
|
6322
|
+
if (pickN !== undefined) {
|
|
6323
|
+
const n = Math.max(1, Math.floor(Number(pickN)) || 1);
|
|
6324
|
+
const ranked = rankRoulette(ideas, cfg.roulette, now).slice(0, n);
|
|
6325
|
+
if (json) write(JSON.stringify({ ranked, exitCode: 0 }, null, 2));
|
|
6326
|
+
else { write(`dz backlog roulette — top ${ranked.length} by weight`); for (const i of ranked) write(` ${i.id} align ${i.goalAlignment.toFixed(2)} e${i.effort} ${i.text.slice(0, 60)}`); }
|
|
6327
|
+
return 0;
|
|
6328
|
+
}
|
|
6329
|
+
const seedOpt = options.get('seed');
|
|
6330
|
+
const seed = seedOpt !== undefined && Number.isFinite(Number(seedOpt)) ? Number(seedOpt) : Math.floor(Math.random() * 2 ** 31);
|
|
6331
|
+
const pick = spinRoulette(ideas, cfg.roulette, seededRng(seed), now)!;
|
|
6332
|
+
let committed = false;
|
|
6333
|
+
if (flags.has('commit')) {
|
|
6334
|
+
const idx = ideas.findIndex((i) => i.id === pick.id);
|
|
6335
|
+
ideas[idx]!.status = 'in-progress';
|
|
6336
|
+
writeIdeas(projectRoot, ideas);
|
|
6337
|
+
committed = true;
|
|
6338
|
+
}
|
|
6339
|
+
if (json) write(JSON.stringify({ pick, seed, committed, exitCode: 0 }, null, 2));
|
|
6340
|
+
else {
|
|
6341
|
+
write(`dz backlog roulette (seed ${seed}): ${pick.id} — ${pick.text.slice(0, 70)}`);
|
|
6342
|
+
write(` align ${pick.goalAlignment.toFixed(3)}${pick.goalId !== null ? ` (${pick.goalId})` : ''} · effort ${pick.effort}${committed ? ' · → in-progress' : ' · (add --commit to start it)'}`);
|
|
6343
|
+
}
|
|
6344
|
+
return 0;
|
|
6345
|
+
}
|
|
6346
|
+
|
|
6347
|
+
if (sub === 'enrich') {
|
|
6348
|
+
const id = options.get('_positional_1');
|
|
6349
|
+
if (id === undefined) return emitErr('an idea id is required: dz backlog enrich <id>');
|
|
6350
|
+
const ideas = readIdeas(projectRoot);
|
|
6351
|
+
const rec = ideas.find((i) => i.id === id);
|
|
6352
|
+
if (rec === undefined) return emitErr(`no idea ${id}`);
|
|
6353
|
+
const goalMap = readGoalMap(projectRoot);
|
|
6354
|
+
const related = ideas.filter((i) => rec.relatedIds.includes(i.id));
|
|
6355
|
+
const staging = stageEnrichment(projectRoot, rec, related, goalMap);
|
|
6356
|
+
rec.status = 'enriched';
|
|
6357
|
+
rec.enrichedPath = `features/${staging.slug}`;
|
|
6358
|
+
writeIdeas(projectRoot, ideas);
|
|
6359
|
+
if (json) write(JSON.stringify({ slug: staging.slug, scaffoldPath: staging.scaffoldPath, handoff: 'idea2prd-manual', exitCode: 0 }, null, 2));
|
|
6360
|
+
else {
|
|
6361
|
+
write(`dz backlog enrich: staged ${rec.id} → ${staging.scaffoldPath}`);
|
|
6362
|
+
write(' HAND OFF to the idea2prd-manual skill (agent phase) — the CLI stages the input; the agent writes the PRD.');
|
|
6363
|
+
write(' honesty/verification (claim-check, red-team, Brain recall) live at THIS layer, not assumed from idea2prd.');
|
|
6364
|
+
}
|
|
6365
|
+
return 0;
|
|
6366
|
+
}
|
|
6367
|
+
|
|
6368
|
+
if (sub === 'jira') {
|
|
6369
|
+
const id = options.get('_positional_1');
|
|
6370
|
+
if (id === undefined) return emitErr('an idea id is required: dz backlog jira <id>');
|
|
6371
|
+
const ideas = readIdeas(projectRoot);
|
|
6372
|
+
const rec = ideas.find((i) => i.id === id);
|
|
6373
|
+
if (rec === undefined) return emitErr(`no idea ${id}`);
|
|
6374
|
+
const adapter = resolveJiraAdapter(cfg);
|
|
6375
|
+
const draft = buildJiraDraft(rec, readGoalMap(projectRoot));
|
|
6376
|
+
const ref = await adapter.createIssue(draft, makeBacklogIO(projectRoot));
|
|
6377
|
+
rec.jiraKey = ref.key ?? `outbox:${rec.id}`;
|
|
6378
|
+
writeIdeas(projectRoot, ideas);
|
|
6379
|
+
const verify = await adapter.verify();
|
|
6380
|
+
if (json) write(JSON.stringify({ ref, verify, adapter: adapter.backend, exitCode: 0 }, null, 2));
|
|
6381
|
+
else {
|
|
6382
|
+
write(`dz backlog jira: ${adapter.backend} → ${ref.stub ? 'stub' : 'live'} ${ref.key ?? '(no key)'}`);
|
|
6383
|
+
write(` outbox: ${ref.outboxPath}`);
|
|
6384
|
+
if (!verify.ready) write(` ⚠ manual wiring: ${verify.instruction}`);
|
|
6385
|
+
}
|
|
6386
|
+
return 0;
|
|
6387
|
+
}
|
|
6388
|
+
|
|
6389
|
+
if (sub === 'harmonize') {
|
|
6390
|
+
const apply = flags.has('apply');
|
|
6391
|
+
const thr = options.get('threshold');
|
|
6392
|
+
const report = await harmonizeBacklog(projectRoot, { apply, ...(thr !== undefined ? { threshold: Number(thr) } : {}) });
|
|
6393
|
+
if (json) {
|
|
6394
|
+
write(JSON.stringify({ ...report, exitCode: 0 }, null, 2));
|
|
6395
|
+
return 0;
|
|
6396
|
+
}
|
|
6397
|
+
write(`dz backlog harmonize (${report.mode}${report.fellBackToExact ? ', exact-text' : ''}): ${report.kept} cluster(s), ${report.dropped} merge-able, ${report.unique} unique (θ ${report.threshold})`);
|
|
6398
|
+
for (const c of report.clusters) write(` keep ${c.keep} ← ${c.drops.join(', ')}`);
|
|
6399
|
+
if (report.snapshotPath !== undefined) write(` snapshot: ${report.snapshotPath}`);
|
|
6400
|
+
if (report.pruneError !== undefined) write(` ⚠ ${report.pruneError} — run dz vector reindex or re-run harmonize when the store is available`);
|
|
6401
|
+
if (!apply && report.dropped > 0) write(' (dry-run — re-run with --apply to merge, after the snapshot)');
|
|
6402
|
+
return 0;
|
|
6403
|
+
}
|
|
6404
|
+
|
|
6405
|
+
// bare / unknown sub — help.
|
|
6406
|
+
write('dz backlog — the Smart Backlog: a goal-directed idea pipeline over the Brain vector engine');
|
|
6407
|
+
write(' add "<idea>" [--effort N][--proposal <t>][--dry-run] capture (semantic dedup + GoalMap alignment)');
|
|
6408
|
+
write(' list [--status <s>][--goal <id>] list ideas');
|
|
6409
|
+
write(' show <id> full record');
|
|
6410
|
+
write(' goals [--validate] the compass (.dz/backlog/goals.json)');
|
|
6411
|
+
write(' roulette [--pick N][--seed n][--commit] weighted draw for what to work on next');
|
|
6412
|
+
write(' enrich <id> stage the idea2prd hand-off (agent expands)');
|
|
6413
|
+
write(` jira <id> draft a Jira issue (adapter: ${[...BACKLOG_BACKENDS].join('|')})`);
|
|
6414
|
+
write(' harmonize [--apply][--threshold 0-1] batch semantic dedup of the backlog');
|
|
6415
|
+
return sub === undefined ? 0 : 1;
|
|
6416
|
+
}
|
|
6417
|
+
|
|
6152
6418
|
/**
|
|
6153
6419
|
* `dz routing` — inspect the learned cost-optimal routing outcome store (feature learned-cost-routing). Shows
|
|
6154
6420
|
* what `args.models[stage]='auto-cost'` currently believes per (stage, complexity-tier, model): gated
|
|
@@ -6581,6 +6847,8 @@ export async function runCli(argv: string[], io: CliIo = {}): Promise<number> {
|
|
|
6581
6847
|
return cmdCompounding(options, flags, cwd, write);
|
|
6582
6848
|
case 'score':
|
|
6583
6849
|
return cmdScore(options, flags, cwd, write);
|
|
6850
|
+
case 'backlog':
|
|
6851
|
+
return await cmdBacklog(options, flags, cwd, write);
|
|
6584
6852
|
case 'routing':
|
|
6585
6853
|
return cmdRouting(options, flags, cwd, write);
|
|
6586
6854
|
case 'bto-optimize':
|