@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 CHANGED
@@ -9,7 +9,7 @@
9
9
  },
10
10
  {
11
11
  "path": "README.md",
12
- "sha256": "0a4369c88d48323b3b2ceb18b89ec3a0b6f6f7016778de3ac583f8397531b1b3"
12
+ "sha256": "1559995482381fd6ac146e6d229cb7d33476cdca8fad21fd361467dd698bd065"
13
13
  },
14
14
  {
15
15
  "path": "coverage/coverage-final.json",
@@ -41,15 +41,15 @@
41
41
  },
42
42
  {
43
43
  "path": "dist/cli.d.ts.map",
44
- "sha256": "e176f74bf8ff0fe938e1bf9b25db392c1adbbd4b7b5b2b63520cb8baa99df82d"
44
+ "sha256": "e41e9538474ad80901474841e66f3be8b9f4fdc24f85b4168a13958f19f0762c"
45
45
  },
46
46
  {
47
47
  "path": "dist/cli.js",
48
- "sha256": "8bfdc5551ea8d4897f8bb2ab50e374145965e73b4dca8d01325f5388412d804c"
48
+ "sha256": "038b4bf4c9295202129662e1caf3b8b139c91f69b2e292373066154b644cc97c"
49
49
  },
50
50
  {
51
51
  "path": "dist/cli.js.map",
52
- "sha256": "0ae60f97c9b1b9e7afabcc8aa540eae700950539f5adc511c6f02cbe634775bd"
52
+ "sha256": "dafbac9c642da206ddfadb9081f01ef2a4cfba98dee719ef64200241d2b76726"
53
53
  },
54
54
  {
55
55
  "path": "dist/index.d.ts",
@@ -73,7 +73,7 @@
73
73
  },
74
74
  {
75
75
  "path": "package.json",
76
- "sha256": "207f5a322bb9154eb34478746318abf1f67660f670a92d7bede2fd4a725d4962"
76
+ "sha256": "4e22f0553a19181eea750db59bd3eec84fd880473ac523705a1a26197665e87e"
77
77
  },
78
78
  {
79
79
  "path": "src/bin.ts",
@@ -81,7 +81,7 @@
81
81
  },
82
82
  {
83
83
  "path": "src/cli.ts",
84
- "sha256": "1d3e6fb9e19aa8fd589487335a8004cc6f7a6af43472b40e31f9139a482f7e85"
84
+ "sha256": "9a32862f99d3d0211e08ea49c7091f5697acdd2789ac05137203abd6ed7a9b3e"
85
85
  },
86
86
  {
87
87
  "path": "src/index.ts",
@@ -89,7 +89,7 @@
89
89
  },
90
90
  {
91
91
  "path": "test/cli.test.ts",
92
- "sha256": "a82b15dd9d166764479528e9f9ede45fc39d1b4068a96f19f158aa5a4c03369b"
92
+ "sha256": "2e013ab73b4eb3dd248ed0fbbd5c050911ad667bb78054fde0bb021fbd1815db"
93
93
  },
94
94
  {
95
95
  "path": "tsconfig.json",
@@ -101,5 +101,5 @@
101
101
  }
102
102
  ]
103
103
  },
104
- "signature": "lLIp3ijF41vy7CmrewYTDYOOQ/4Hns3C54KhZ9akSU0+lUiBgf63r16/xpfwZlRRmuxTKes2wVyTJGdASgTiDQ=="
104
+ "signature": "d+++ms/ySpdJ2w5VE7F3b8IaZLWhIYcrvTRT1nPQmi2OHDdrQZ4YBHHW2KNxrauUdTXuUf/7tD//NQiGCQgQDw=="
105
105
  }
package/README.md CHANGED
@@ -726,7 +726,7 @@ Get the whole set with `dz init --target claude-code --preset meta`, or pick one
726
726
 
727
727
  > **A skill and its npx toolkit are not duplicates — they're a graduation.** Several skills (e.g. `feature-adr`, `design-thinking`) exist BOTH as a skill inside a `dz` preset AND as a standalone `npx` package. The preset's SKILL.md is **fully functional on its own** (the whole methodology — modules + references — travels with it, and it auto-activates by description), and it's the only way to compile that capability to the **non-Claude platforms** (Codex/OpenCode/Hermes/OpenClaude) via `dz`. The npx package adds **project-level runtime governance** around the same skill: a slash command, governance rules, a context shard, and (for feature-adr) reward-learning + `/harvest`. So: pick the **skill/preset** for a working capability across platforms; pick the **npx toolkit** when you want it as a governed, command-driven fixture of one project.
728
728
 
729
- ## All Commands (60)
729
+ ## All Commands (61)
730
730
 
731
731
  ```
732
732
  dz setup --target <name> [--preset <name>] [--select id,id,...] [--skills-dir <dir>] [--memory agentdb] [--no-memory] [--no-hooks] [--install-driver] [--force]
@@ -785,6 +785,7 @@ dz delivery-check --slug <slug> [--context-only] [--findings <f.json>] [--strict
785
785
  dz skills-verify [--dir <project>] [--expect a,b] [--static] [--strict] [--timeout <s>] [--json] # does .claude/skills/ actually REGISTER? --static = instant layout scan (CI-safe, no session): flags dirs that can never register; default also starts a real session and reads the authoritative system/init listing. exit 0 pass / 1 fail / 2 inconclusive — an unobservable registration is NEVER a pass
786
786
  dz compounding [--project <dir>] [--json] # honest learning-loop payoff report: pool write-only ratio, guard repeat-violation trajectory (rate per audit-half), cold-vs-warm replay readiness over unique untruncated prompt events, apply-leg staleness — a gate without enough data says INSUFFICIENT_DATA, never a verdict
787
787
  dz score --slug <feature> [--project <dir>] [--json] # process scorecard for ONE feature-adr run, from its artifacts: ADR confirmation, discrimination proof, cross-model QE grade, live verification, README-first, learning loop, amendments — DESCRIPTIVE-ONLY (a low score exits 0); evidence lines are shown so the reader judges the heuristics
788
+ dz backlog <sub> add "<idea>" | list | show <id> | goals | roulette [--seed n] | enrich <id> | jira <id> | harmonize [--apply] # brain-backed idea backlog: capture an idea → semantic dedup against past ideas/features via the REUSED agentdb vector engine (DUPLICATE≥0.92 / RELATED / NEW) + GoalMap alignment ("map+compass") → weighted seeded roulette picks one to work on → enrich STAGES an idea2prd hand-off → jira writes an auditable outbox via a configurable MCP adapter seam (jira-mcp|copilot-mcp|none). No 2nd vector store; without agentdb it degrades to exact-text dedup (honest)
788
789
  dz sign --init --out <path> | --pack <dir> --key <path> # --init: generate the Ed25519 keypair (private OUTSIDE the repo, prints the public key for keys/dz.pub); else sign a pack's manifest + CycloneDX SBOM
789
790
  dz sbom --pack <dir> [--out <file>] # emit the CycloneDX 1.5 SBOM for a pack standalone (file-level bill of materials); print to stdout or write to a file
790
791
  dz guard check --op <publish|teach|consolidate|reindex> [--text <s>] [--json] [--force <reason>] # declarative constraint layer before self-mutating ops: HARD violation → block (exit 1), SOFT → warn; zero-config defaults, .dz/guard.json to customise; dz guard --init | dz guard log (append-only audit). dz publish runs it automatically (--no-guard "<reason>" = logged escape hatch)
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAgSH,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,iBAAiB,CAAC;CAC5C;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,KACvD;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAu/L9E,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAwJ5E"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA6TH,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,iBAAiB,CAAC;CAC5C;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,KACvD;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAouM9E,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA0J5E"}
package/dist/cli.js CHANGED
@@ -9,7 +9,9 @@ import { fileURLToPath } from 'node:url';
9
9
  import { execSync, spawn } from 'node:child_process';
10
10
  import { homedir, tmpdir } from 'node:os';
11
11
  import { createRequire } from 'node:module';
12
- import { createSkill, getSkillInfo, getWorkflow, isTargetName, listSkills, runDoctor, runInit, benchmarkSkill, benchmarkSkills, scanMcp, reconcileCapabilities, RECONCILE_BANNER, buildRegistry, discoverSkillPackDirs, checkUpstream, compareSkills, checkAllUpstream, sweepSkillDrift, syncCanonicalSkill, checkUpgrades, discoverPackages, discoverSourcePackages, fetchAllDownloads, filterByCategory, pretrain, recommend, generatePlugin, publishPackages, runSetup, runMigrate, searchRegistry, runSync, runVerify, runInitAgentsMd, runInitGeminiMd, TARGET_NAMES, buildParityMatrix, TARGET_CAPABILITIES, TARGET_SHORT_LABELS, WORKFLOW_NAMES, importEcc, recordPattern, resolveLearningBackend, storeStats, consolidateSessions, pruneNoisePatterns, lessonDeltaReport, removePatternsByIds, snapshotStore, recallHybrid, teachGuard, mirrorPatternsToVector, mirrorEntriesToVector, patternVectorEntry, readMemoryLearningConfig, promotePatterns, quarantineExpiryCandidates, pruneQuarantinePatterns, clearAgentdbQuarantine, vectorMirrorEnabled, vectorTierStatus, resolveVectorEngine, reindexVectorStore, harmonizeVectorStore, importRvfCheckpoint, statuslineData, writeFeatureAdrState, computeUsage, deriveUsageCalibration, normalizeClaudeUsageModelKey, readUsageLimits, claimCheck, summarize, queryBookKnowledge, loadStorePatternsSync, patternRecordId, loadStoreRecords, recordToPattern, bundleSkills, brainHome, listBrain, promoteProjectToBrain, updateBrainSource, queryBrain, groundPrompt, expandKu, reindexBrainVectors, buildPrimer, exportBrainSlice, importBrainSlice, registerKusToBrain, RECALL_USAGE_LOG_RELATIVE, RECALL_USAGE_LOG_MAX_BYTES, parseRecallUsageLog, buildRecallUsageReport, buildManifest, buildSbom, resolveTrustRoot, decideVerifyPolicy, generateSigningKeypair, evaluateGuard, resolveRules, auditRecord, guardExitCode, DEFAULT_RULES, decideProvenance, isInsideTree, signManifest, verifyManifest, listSignablePackFiles, assertKeyOutsideTree, decidePublishGate, collectPackageFacts, planReleaseGates, selectAffectedPackages, classifyGateExecutions, buildFailureIssue, buildReleaseNotes, releaseTagName, firstOutputLine, formatPublishError, MANIFEST_NAME, SBOM_NAME, buildArchitectureMap, renderMapHuman, findArchitectureDrift, renderDriftReport, scanWorkspacePackages, loadSubsystemManifest, loadProductVision, checkFeatureAgainstArchitecture, renderArchCheck, planProjectSkills, guidanceForStage, renderInjectionReport, analyzeCorpus, renderRakeReport, renderCriticSection, rakeAsLesson, rakeReward, DEFAULT_RAKE_THRESHOLDS, streamSessionEvents, findLatestTranscript, detectProcessRakes, buildRetro, renderRetro, retroLessonText, PROCESS_SIGNATURES, RETRO_DOMAIN, scanForSetup, buildSetupPlan, scaffoldFromSpec, renderScaffoldPreview, readExistingForScaffold, assembleChallengeContext, buildChallengeBrief, planDiscriminationCheck, classifyDiscrimination, pickAdversaryModel, CHALLENGE_QUESTIONS, loadOutcomes, renderOutcomes, statsForKey, selectAutoCost, recordProvisional, finalizeOutcome, COST_LADDER, splitScenarios, budgetPlan, selectWinner, proseScopeOk, renderProseDiff, readScenarioIds, DEFAULT_MAX_JUDGE_RUNS, collectDeliveryFacts, planDeliveryCheck, renderDeliveryBrief, classifyDelivery, isUsablePlaneResult, renderDeliveryReview, scanSkillsLayout, parseInitFacts, verifyRegistration, buildContentProbePrompt, classifyContentProbe, renderContentProbe, findNonRegistrableSkillDirs, assembleCompoundingReport, scoreRun, renderScorecard, renderCompoundingReport, readReinforcementState, readQuarantineState, registrationExitCode, renderRegistrationReport, } from '@dzhechkov/harness-core';
12
+ import { createSkill, getSkillInfo, getWorkflow, isTargetName, listSkills, runDoctor, runInit, benchmarkSkill, benchmarkSkills, scanMcp, reconcileCapabilities, RECONCILE_BANNER, buildRegistry, discoverSkillPackDirs, checkUpstream, compareSkills, checkAllUpstream, sweepSkillDrift, syncCanonicalSkill, checkUpgrades, discoverPackages, discoverSourcePackages, fetchAllDownloads, filterByCategory, pretrain, recommend, generatePlugin, publishPackages, runSetup, runMigrate, searchRegistry, runSync, runVerify, runInitAgentsMd, runInitGeminiMd, TARGET_NAMES, buildParityMatrix, TARGET_CAPABILITIES, TARGET_SHORT_LABELS, WORKFLOW_NAMES, importEcc, recordPattern, resolveLearningBackend, storeStats, consolidateSessions, pruneNoisePatterns, lessonDeltaReport, removePatternsByIds, snapshotStore, recallHybrid, teachGuard, mirrorPatternsToVector, mirrorEntriesToVector, patternVectorEntry, readMemoryLearningConfig, promotePatterns, quarantineExpiryCandidates, pruneQuarantinePatterns, clearAgentdbQuarantine, vectorMirrorEnabled, vectorTierStatus, resolveVectorEngine, reindexVectorStore, harmonizeVectorStore, importRvfCheckpoint, statuslineData, writeFeatureAdrState, computeUsage, deriveUsageCalibration, normalizeClaudeUsageModelKey, readUsageLimits, claimCheck, summarize, queryBookKnowledge, loadStorePatternsSync, patternRecordId, loadStoreRecords, recordToPattern, bundleSkills, brainHome, listBrain, promoteProjectToBrain, updateBrainSource, queryBrain, groundPrompt, expandKu, reindexBrainVectors, buildPrimer, exportBrainSlice, importBrainSlice, registerKusToBrain, RECALL_USAGE_LOG_RELATIVE, RECALL_USAGE_LOG_MAX_BYTES, parseRecallUsageLog, buildRecallUsageReport, buildManifest, buildSbom, resolveTrustRoot, decideVerifyPolicy, generateSigningKeypair, evaluateGuard, resolveRules, auditRecord, guardExitCode, DEFAULT_RULES, decideProvenance, isInsideTree, signManifest, verifyManifest, listSignablePackFiles, assertKeyOutsideTree, decidePublishGate, collectPackageFacts, planReleaseGates, selectAffectedPackages, classifyGateExecutions, buildFailureIssue, buildReleaseNotes, releaseTagName, firstOutputLine, formatPublishError, MANIFEST_NAME, SBOM_NAME, buildArchitectureMap, renderMapHuman, findArchitectureDrift, renderDriftReport, scanWorkspacePackages, loadSubsystemManifest, loadProductVision, checkFeatureAgainstArchitecture, renderArchCheck, planProjectSkills, guidanceForStage, renderInjectionReport, analyzeCorpus, renderRakeReport, renderCriticSection, rakeAsLesson, rakeReward, DEFAULT_RAKE_THRESHOLDS, streamSessionEvents, findLatestTranscript, detectProcessRakes, buildRetro, renderRetro, retroLessonText, PROCESS_SIGNATURES, RETRO_DOMAIN, scanForSetup, buildSetupPlan, scaffoldFromSpec, renderScaffoldPreview, readExistingForScaffold, assembleChallengeContext, buildChallengeBrief, planDiscriminationCheck, classifyDiscrimination, pickAdversaryModel, CHALLENGE_QUESTIONS, loadOutcomes, renderOutcomes, statsForKey, selectAutoCost, recordProvisional, finalizeOutcome, COST_LADDER, splitScenarios, budgetPlan, selectWinner, proseScopeOk, renderProseDiff, readScenarioIds, DEFAULT_MAX_JUDGE_RUNS, collectDeliveryFacts, planDeliveryCheck, renderDeliveryBrief, classifyDelivery, isUsablePlaneResult, renderDeliveryReview, scanSkillsLayout, parseInitFacts, verifyRegistration, buildContentProbePrompt, classifyContentProbe, renderContentProbe, findNonRegistrableSkillDirs, assembleCompoundingReport, scoreRun, renderScorecard, renderCompoundingReport, readReinforcementState, readQuarantineState, registrationExitCode, renderRegistrationReport,
13
+ // Smart Backlog (feature smart-backlog) — goal-directed idea pipeline over the Brain vector engine.
14
+ readBacklogConfig, readIdeas, writeIdeas, ideaId, dedupIdea, readGoalMap, alignIdea, mirrorIdeaVector, snapshotIdeas, spinRoulette, rankRoulette, seededRng, eligibleIdeas, stageEnrichment, buildJiraDraft, resolveJiraAdapter, makeBacklogIO, harmonizeBacklog, BACKLOG_BACKENDS, } from '@dzhechkov/harness-core';
13
15
  import { getPreset, PRESET_NAMES } from '@dzhechkov/harness-presets';
14
16
  import { scanGitHub, analyzeRepo, generateReport, deepAnalyze, scanAllSources, ScoutMemory } from '@dzhechkov/scout';
15
17
  const USAGE = `dz - DZ cross-platform harness CLI
@@ -38,6 +40,14 @@ Usage:
38
40
  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)
39
41
  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)
40
42
  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)
43
+ 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)
44
+ dz backlog list [--status <s>] [--goal <id>] [--project <dir>] [--json] (list captured ideas, filterable by status/goal)
45
+ dz backlog show <id> [--project <dir>] [--json] (full record for one idea)
46
+ dz backlog goals [--validate] [--project <dir>] [--json] (list/validate the compass at .dz/backlog/goals.json)
47
+ 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))
48
+ 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)
49
+ 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)
50
+ dz backlog harmonize [--apply] [--threshold <0-1>] [--project <dir>] [--json] (batch semantic dedup of the backlog ideas; --dry-run default, --apply snapshots first)
41
51
  dz setup --target <name> [--preset <name>] [--select id,id,...] [--skills-dir <dir>] [--project <dir>] [--memory agentdb] [--no-memory] [--no-hooks] [--install-driver] [--force] [--enrich]
42
52
  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)
43
53
  dz teach --from-json <file> [--project <dir>] [--no-mirror] (bulk-import a 'dz recall --all --json' export — share a learned store across machines)
@@ -1885,6 +1895,8 @@ async function cmdVector(options, flags, cwd, write) {
1885
1895
  return report.error !== undefined ? 1 : 0;
1886
1896
  }
1887
1897
  if (report.error !== undefined) {
1898
+ // Honest failure/skip. Under a non-agentdb engine backlog is reported `skipped` (it never touches the
1899
+ // shared manifest); a model bump needs a FULL reindex under the agentdb engine. Surface it, exit 1.
1888
1900
  write(`dz vector reindex: ${report.error}`);
1889
1901
  return 1;
1890
1902
  }
@@ -5903,6 +5915,272 @@ function cmdDeliveryCheck(options, flags, cwd, write) {
5903
5915
  return 1;
5904
5916
  return 0;
5905
5917
  }
5918
+ /* ------------------------------------------------------------------ */
5919
+ /* backlog — the Smart Backlog idea pipeline (feature smart-backlog) */
5920
+ /* ------------------------------------------------------------------ */
5921
+ /** Thin dispatcher — ALL logic lives in harness-core/src/backlog.ts (05 architecture: handlers stay dumb). */
5922
+ async function cmdBacklog(options, flags, cwd, write) {
5923
+ const projectRoot = resolve(cwd, options.get('project') ?? '.');
5924
+ const json = flags.has('json');
5925
+ const sub = options.get('_positional_0');
5926
+ const cfg = readBacklogConfig(projectRoot);
5927
+ const emitErr = (msg) => {
5928
+ write(json ? JSON.stringify({ error: msg, exitCode: 1 }) : `dz backlog: ${msg}`);
5929
+ return 1;
5930
+ };
5931
+ if (sub === 'add') {
5932
+ const text = (options.get('_positional_1') ?? '').trim();
5933
+ if (text === '')
5934
+ return emitErr('an idea text is required: dz backlog add "<idea>"');
5935
+ const effort = Number(options.get('effort') ?? cfg.roulette.defaultEffort);
5936
+ const dryRun = flags.has('dry-run');
5937
+ const verdict = await dedupIdea(projectRoot, text, cfg);
5938
+ if (verdict.action === 'duplicate') {
5939
+ // DUPLICATE ⇒ snapshot + reinforce the existing root; NO new record (ADR-002 T-002b).
5940
+ const ideas = readIdeas(projectRoot);
5941
+ const match = ideas.find((i) => i.id === verdict.matchedId);
5942
+ if (!dryRun && match !== undefined) {
5943
+ const snap = snapshotIdeas(projectRoot, join(projectRoot, '.dz', 'backlog', `ideas.pre-merge-${Date.now()}.jsonl`));
5944
+ if (snap.error !== undefined)
5945
+ return emitErr(snap.error);
5946
+ match.uses += 1;
5947
+ writeIdeas(projectRoot, ideas);
5948
+ }
5949
+ if (json)
5950
+ write(JSON.stringify({ action: 'duplicate', matchedId: verdict.matchedId, cosine: verdict.cosine, exitCode: 0 }, null, 2));
5951
+ else
5952
+ write(`dz backlog: DUPLICATE of ${verdict.matchedId} (cosine ${verdict.cosine.toFixed(3)}${verdict.exactTextOnly ? ', exact-text' : ''}) — reinforced, no new record`);
5953
+ return 0;
5954
+ }
5955
+ const goalMap = readGoalMap(projectRoot);
5956
+ const align = await alignIdea(projectRoot, text, goalMap);
5957
+ const createdTs = new Date().toISOString();
5958
+ const rec = {
5959
+ id: ideaId(text, createdTs),
5960
+ text,
5961
+ status: 'new',
5962
+ createdTs,
5963
+ effort: Number.isFinite(effort) ? Math.max(1, Math.min(5, Math.floor(effort))) : cfg.roulette.defaultEffort,
5964
+ goalId: align.goalId,
5965
+ goalAlignment: align.goalAlignment,
5966
+ relatedIds: [...verdict.relatedIds],
5967
+ uses: 0,
5968
+ tags: [],
5969
+ };
5970
+ const proposal = options.get('proposal');
5971
+ if (proposal !== undefined)
5972
+ rec.proposal = proposal; // agent prose ONLY — the CLI never fabricates it
5973
+ if (dryRun) {
5974
+ if (json)
5975
+ write(JSON.stringify({ action: verdict.action, dryRun: true, idea: rec, exitCode: 0 }, null, 2));
5976
+ else
5977
+ write(`dz backlog (dry-run): ${verdict.action.toUpperCase()} — would create ${rec.id}; align ${rec.goalAlignment.toFixed(3)}${rec.goalId !== null ? ` → ${rec.goalId}` : ''}`);
5978
+ return 0;
5979
+ }
5980
+ const ideas = readIdeas(projectRoot);
5981
+ ideas.push(rec);
5982
+ writeIdeas(projectRoot, ideas);
5983
+ const mirror = await mirrorIdeaVector(projectRoot, rec); // best-effort — never blocks capture
5984
+ if (json)
5985
+ write(JSON.stringify({ action: verdict.action, idea: rec, related: verdict.relatedIds, exitCode: 0 }, null, 2));
5986
+ else {
5987
+ write(`dz backlog: ${verdict.action.toUpperCase()} — captured ${rec.id}`);
5988
+ if (rec.goalId !== null)
5989
+ write(` top goal: ${rec.goalId} (alignment ${rec.goalAlignment.toFixed(3)})`);
5990
+ if (verdict.relatedIds.length > 0)
5991
+ write(` related: ${verdict.relatedIds.join(', ')}`);
5992
+ write(' proposal: (write your own — dz backlog add --proposal "<text>"; the CLI never fabricates it)');
5993
+ if (mirror.error !== undefined)
5994
+ write(` ↳ vector mirror deferred: ${mirror.error}`);
5995
+ }
5996
+ return 0;
5997
+ }
5998
+ if (sub === 'list') {
5999
+ let ideas = readIdeas(projectRoot);
6000
+ const status = options.get('status');
6001
+ const goal = options.get('goal');
6002
+ if (status !== undefined)
6003
+ ideas = ideas.filter((i) => i.status === status);
6004
+ if (goal !== undefined)
6005
+ ideas = ideas.filter((i) => i.goalId === goal);
6006
+ if (json) {
6007
+ write(JSON.stringify({ ideas, exitCode: 0 }, null, 2));
6008
+ return 0;
6009
+ }
6010
+ if (ideas.length === 0) {
6011
+ write('dz backlog: no ideas' + (status !== undefined || goal !== undefined ? ' match the filter' : ' yet — dz backlog add "<idea>"'));
6012
+ return 0;
6013
+ }
6014
+ write(`dz backlog — ${ideas.length} idea(s)`);
6015
+ for (const i of ideas)
6016
+ write(` ${i.id} [${i.status}] e${i.effort} align ${i.goalAlignment.toFixed(2)}${i.goalId !== null ? ` (${i.goalId})` : ''} ${i.text.slice(0, 70)}`);
6017
+ return 0;
6018
+ }
6019
+ if (sub === 'show') {
6020
+ const id = options.get('_positional_1');
6021
+ if (id === undefined)
6022
+ return emitErr('an idea id is required: dz backlog show <id>');
6023
+ const rec = readIdeas(projectRoot).find((i) => i.id === id);
6024
+ if (rec === undefined)
6025
+ return emitErr(`no idea ${id}`);
6026
+ write(json ? JSON.stringify({ idea: rec, exitCode: 0 }, null, 2) : JSON.stringify(rec, null, 2));
6027
+ return 0;
6028
+ }
6029
+ if (sub === 'goals') {
6030
+ const goalMap = readGoalMap(projectRoot);
6031
+ if (flags.has('validate')) {
6032
+ const problems = [];
6033
+ const seen = new Set();
6034
+ for (const g of goalMap.goals) {
6035
+ if (seen.has(g.id))
6036
+ problems.push(`duplicate goal id: ${g.id}`);
6037
+ seen.add(g.id);
6038
+ if (g.statement.trim() === '')
6039
+ problems.push(`goal ${g.id} has an empty statement`);
6040
+ if (!(g.weight > 0 && g.weight <= 1))
6041
+ problems.push(`goal ${g.id} weight ${g.weight} is out of (0,1]`);
6042
+ }
6043
+ if (json)
6044
+ write(JSON.stringify({ valid: problems.length === 0, problems, goals: goalMap.goals.length, exitCode: problems.length === 0 ? 0 : 1 }, null, 2));
6045
+ else if (problems.length === 0)
6046
+ write(`dz backlog goals: valid (${goalMap.goals.length} goal(s))`);
6047
+ else {
6048
+ write('dz backlog goals: INVALID');
6049
+ for (const p of problems)
6050
+ write(` - ${p}`);
6051
+ }
6052
+ return problems.length === 0 ? 0 : 1;
6053
+ }
6054
+ if (json) {
6055
+ write(JSON.stringify({ goals: goalMap.goals, exitCode: 0 }, null, 2));
6056
+ return 0;
6057
+ }
6058
+ if (goalMap.goals.length === 0) {
6059
+ write('dz backlog goals: no compass — create .dz/backlog/goals.json ({ "version":1, "goals":[{ "id","statement","weight","keywords" }] })');
6060
+ return 0;
6061
+ }
6062
+ write(`dz backlog — ${goalMap.goals.length} goal(s)`);
6063
+ for (const g of goalMap.goals)
6064
+ write(` ${g.id} (w=${g.weight}): ${g.statement}`);
6065
+ return 0;
6066
+ }
6067
+ if (sub === 'roulette') {
6068
+ const ideas = readIdeas(projectRoot);
6069
+ const pool = eligibleIdeas(ideas);
6070
+ if (pool.length === 0) {
6071
+ write(json ? JSON.stringify({ pick: null, eligible: 0, exitCode: 0 }) : 'dz backlog roulette: no eligible ideas (status new/enriched)');
6072
+ return 0;
6073
+ }
6074
+ const now = Date.now();
6075
+ const pickN = options.get('pick');
6076
+ if (pickN !== undefined) {
6077
+ const n = Math.max(1, Math.floor(Number(pickN)) || 1);
6078
+ const ranked = rankRoulette(ideas, cfg.roulette, now).slice(0, n);
6079
+ if (json)
6080
+ write(JSON.stringify({ ranked, exitCode: 0 }, null, 2));
6081
+ else {
6082
+ write(`dz backlog roulette — top ${ranked.length} by weight`);
6083
+ for (const i of ranked)
6084
+ write(` ${i.id} align ${i.goalAlignment.toFixed(2)} e${i.effort} ${i.text.slice(0, 60)}`);
6085
+ }
6086
+ return 0;
6087
+ }
6088
+ const seedOpt = options.get('seed');
6089
+ const seed = seedOpt !== undefined && Number.isFinite(Number(seedOpt)) ? Number(seedOpt) : Math.floor(Math.random() * 2 ** 31);
6090
+ const pick = spinRoulette(ideas, cfg.roulette, seededRng(seed), now);
6091
+ let committed = false;
6092
+ if (flags.has('commit')) {
6093
+ const idx = ideas.findIndex((i) => i.id === pick.id);
6094
+ ideas[idx].status = 'in-progress';
6095
+ writeIdeas(projectRoot, ideas);
6096
+ committed = true;
6097
+ }
6098
+ if (json)
6099
+ write(JSON.stringify({ pick, seed, committed, exitCode: 0 }, null, 2));
6100
+ else {
6101
+ write(`dz backlog roulette (seed ${seed}): ${pick.id} — ${pick.text.slice(0, 70)}`);
6102
+ write(` align ${pick.goalAlignment.toFixed(3)}${pick.goalId !== null ? ` (${pick.goalId})` : ''} · effort ${pick.effort}${committed ? ' · → in-progress' : ' · (add --commit to start it)'}`);
6103
+ }
6104
+ return 0;
6105
+ }
6106
+ if (sub === 'enrich') {
6107
+ const id = options.get('_positional_1');
6108
+ if (id === undefined)
6109
+ return emitErr('an idea id is required: dz backlog enrich <id>');
6110
+ const ideas = readIdeas(projectRoot);
6111
+ const rec = ideas.find((i) => i.id === id);
6112
+ if (rec === undefined)
6113
+ return emitErr(`no idea ${id}`);
6114
+ const goalMap = readGoalMap(projectRoot);
6115
+ const related = ideas.filter((i) => rec.relatedIds.includes(i.id));
6116
+ const staging = stageEnrichment(projectRoot, rec, related, goalMap);
6117
+ rec.status = 'enriched';
6118
+ rec.enrichedPath = `features/${staging.slug}`;
6119
+ writeIdeas(projectRoot, ideas);
6120
+ if (json)
6121
+ write(JSON.stringify({ slug: staging.slug, scaffoldPath: staging.scaffoldPath, handoff: 'idea2prd-manual', exitCode: 0 }, null, 2));
6122
+ else {
6123
+ write(`dz backlog enrich: staged ${rec.id} → ${staging.scaffoldPath}`);
6124
+ write(' HAND OFF to the idea2prd-manual skill (agent phase) — the CLI stages the input; the agent writes the PRD.');
6125
+ write(' honesty/verification (claim-check, red-team, Brain recall) live at THIS layer, not assumed from idea2prd.');
6126
+ }
6127
+ return 0;
6128
+ }
6129
+ if (sub === 'jira') {
6130
+ const id = options.get('_positional_1');
6131
+ if (id === undefined)
6132
+ return emitErr('an idea id is required: dz backlog jira <id>');
6133
+ const ideas = readIdeas(projectRoot);
6134
+ const rec = ideas.find((i) => i.id === id);
6135
+ if (rec === undefined)
6136
+ return emitErr(`no idea ${id}`);
6137
+ const adapter = resolveJiraAdapter(cfg);
6138
+ const draft = buildJiraDraft(rec, readGoalMap(projectRoot));
6139
+ const ref = await adapter.createIssue(draft, makeBacklogIO(projectRoot));
6140
+ rec.jiraKey = ref.key ?? `outbox:${rec.id}`;
6141
+ writeIdeas(projectRoot, ideas);
6142
+ const verify = await adapter.verify();
6143
+ if (json)
6144
+ write(JSON.stringify({ ref, verify, adapter: adapter.backend, exitCode: 0 }, null, 2));
6145
+ else {
6146
+ write(`dz backlog jira: ${adapter.backend} → ${ref.stub ? 'stub' : 'live'} ${ref.key ?? '(no key)'}`);
6147
+ write(` outbox: ${ref.outboxPath}`);
6148
+ if (!verify.ready)
6149
+ write(` ⚠ manual wiring: ${verify.instruction}`);
6150
+ }
6151
+ return 0;
6152
+ }
6153
+ if (sub === 'harmonize') {
6154
+ const apply = flags.has('apply');
6155
+ const thr = options.get('threshold');
6156
+ const report = await harmonizeBacklog(projectRoot, { apply, ...(thr !== undefined ? { threshold: Number(thr) } : {}) });
6157
+ if (json) {
6158
+ write(JSON.stringify({ ...report, exitCode: 0 }, null, 2));
6159
+ return 0;
6160
+ }
6161
+ write(`dz backlog harmonize (${report.mode}${report.fellBackToExact ? ', exact-text' : ''}): ${report.kept} cluster(s), ${report.dropped} merge-able, ${report.unique} unique (θ ${report.threshold})`);
6162
+ for (const c of report.clusters)
6163
+ write(` keep ${c.keep} ← ${c.drops.join(', ')}`);
6164
+ if (report.snapshotPath !== undefined)
6165
+ write(` snapshot: ${report.snapshotPath}`);
6166
+ if (report.pruneError !== undefined)
6167
+ write(` ⚠ ${report.pruneError} — run dz vector reindex or re-run harmonize when the store is available`);
6168
+ if (!apply && report.dropped > 0)
6169
+ write(' (dry-run — re-run with --apply to merge, after the snapshot)');
6170
+ return 0;
6171
+ }
6172
+ // bare / unknown sub — help.
6173
+ write('dz backlog — the Smart Backlog: a goal-directed idea pipeline over the Brain vector engine');
6174
+ write(' add "<idea>" [--effort N][--proposal <t>][--dry-run] capture (semantic dedup + GoalMap alignment)');
6175
+ write(' list [--status <s>][--goal <id>] list ideas');
6176
+ write(' show <id> full record');
6177
+ write(' goals [--validate] the compass (.dz/backlog/goals.json)');
6178
+ write(' roulette [--pick N][--seed n][--commit] weighted draw for what to work on next');
6179
+ write(' enrich <id> stage the idea2prd hand-off (agent expands)');
6180
+ write(` jira <id> draft a Jira issue (adapter: ${[...BACKLOG_BACKENDS].join('|')})`);
6181
+ write(' harmonize [--apply][--threshold 0-1] batch semantic dedup of the backlog');
6182
+ return sub === undefined ? 0 : 1;
6183
+ }
5906
6184
  /**
5907
6185
  * `dz routing` — inspect the learned cost-optimal routing outcome store (feature learned-cost-routing). Shows
5908
6186
  * what `args.models[stage]='auto-cost'` currently believes per (stage, complexity-tier, model): gated
@@ -6348,6 +6626,8 @@ export async function runCli(argv, io = {}) {
6348
6626
  return cmdCompounding(options, flags, cwd, write);
6349
6627
  case 'score':
6350
6628
  return cmdScore(options, flags, cwd, write);
6629
+ case 'backlog':
6630
+ return await cmdBacklog(options, flags, cwd, write);
6351
6631
  case 'routing':
6352
6632
  return cmdRouting(options, flags, cwd, write);
6353
6633
  case 'bto-optimize':