@kaddo/cli 3.42.0 → 3.43.0

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.
Files changed (3) hide show
  1. package/README.md +1 -0
  2. package/dist/index.js +173 -11
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -541,6 +541,7 @@ create --from roadmap → owners → guard → explain`.
541
541
  | v3.40.1 | ADR slug cleanup + MCP: suggested ADR filenames strip list/heading prefixes (no `ADR-001-1-…`) and normalize acronyms; new read-only MCP resource `kaddo://tech-decisions` sharing `buildTechDecisions` with `kaddo adr` |
542
542
  | v3.41 | Tech knowledge structure: `knowledge/tech/discovery/` for architecture-notes/decision-candidates (core vs decisions vs discovery); `kaddo adr` reads discovery-first with legacy fallback; `kaddo tech organize` migrates safely; `explain` shows `## Tech Knowledge` |
543
543
  | v3.42 | Agent & skill version metadata: installed agents/skills carry a `version:`; `kaddo agents status` / `kaddo skills status` classify up-to-date/outdated/unknown-version/modified/missing; `agents update` / `skills update` refresh outdated safely (never overwrite edits without `--force`); MCP `kaddo://installed-assets` |
544
+ | v3.43 | Capability-grounded roadmap: each `RM-xxx` candidate is graded on related domain / capability / source signals; `roadmap_quality` surfaced in `explain`/`context`/`understand`; `create --from roadmap` preserves `source_roadmap_candidate` + related metadata into the Work Item; roadmap-agent emits grounded fields (never materializes Work Items); MCP `kaddo://roadmap-quality` |
544
545
 
545
546
  **Optional modules (installed with `kaddo add`):**
546
547
 
package/dist/index.js CHANGED
@@ -2645,6 +2645,24 @@ Adapt priorities to the project state from the context pack:
2645
2645
  - **legacy** \u2014 prioritize risk reduction, unknowns and safe modernization before feature
2646
2646
  delivery.
2647
2647
 
2648
+ ## Grounding rules (VS-077)
2649
+
2650
+ Every candidate initiative must be **grounded** in the knowledge base \u2014 never a loose idea. For each
2651
+ \`### RM-xxx\` you must provide:
2652
+
2653
+ - **Related domain** \u2014 a domain from \`## Capability Domains\` in \`capabilities.md\` (or, if genuinely
2654
+ new, prefix it \`[new candidate domain] <name>\` \u2014 do not invent domains silently).
2655
+ - **Related capabilities** \u2014 one or more existing/partial capabilities.
2656
+ - **Source signals** \u2014 at least one traceable reason: Capability Gap, Roadmap Candidate Signal, Risk,
2657
+ Open Question, Assumption, Deferred Decision, Tech Decision Candidate, ADR, Business Goal,
2658
+ Operational Need or Legacy Modernization Signal.
2659
+ - **Expected value**, **Risks**, **Dependencies**, and **Suggested Work Items** (candidates only).
2660
+
2661
+ Do **not** emit a candidate with no source signal. Keep initiatives at initiative granularity (small
2662
+ tasks go under **Suggested Work Items**, not as their own RM). Every roadmap ends with a global
2663
+ **## Not Now** section. For pre-ai/legacy, prioritize stabilization, security, data, operations,
2664
+ architectural decisions and business-blocking gaps before expansive features.
2665
+
2648
2666
  ## Constraints
2649
2667
 
2650
2668
  - Do not invent business priorities or business facts \u2014 mark them as assumptions when inferred.
@@ -2653,6 +2671,8 @@ Adapt priorities to the project state from the context pack:
2653
2671
  implementation-agent, and only after Work Items are materialized. Your handoff is
2654
2672
  \`kaddo create --from roadmap\` \u2192 work-item-agent.
2655
2673
  - Do not create the work items themselves; only propose candidates.
2674
+ - **Never create files under \`knowledge/delivery/work-items/\`** \u2014 materialization is
2675
+ \`kaddo create --from roadmap\`, not the roadmap-agent.
2656
2676
  - Make clear that initiatives and work items are **candidates**, not final decisions.
2657
2677
  - Mark any uncertain information as an assumption or open question.
2658
2678
  - Keep sequencing justified by dependencies and risk.
@@ -2686,31 +2706,38 @@ human review \u2014 not final commitments.
2686
2706
 
2687
2707
  ### RM-001: <Initiative Name>
2688
2708
 
2689
- **Goal:**
2709
+ **Status:** candidate <!-- candidate | selected | deferred | rejected -->
2690
2710
 
2691
- **Related capabilities:**
2711
+ **Priority:** high / medium / low
2712
+
2713
+ **Suggested Knowledge Level:** K1 / K2 / K3 / K4
2692
2714
 
2693
- **Project area / domain:**
2715
+ **Related domain:** <one of the ## Capability Domains from capabilities.md>
2694
2716
 
2695
- **Impact:** Low / Medium / High
2717
+ **Related capabilities:**
2718
+ - <existing or partial capability>
2696
2719
 
2697
- **Risk:** Low / Medium / High
2720
+ **Source signals:** <!-- REQUIRED: why this candidate exists (at least one) -->
2721
+ - Capability Gap: <...>
2722
+ - Roadmap Candidate Signal: <...>
2723
+ - Risk / Open Question / Assumption / Deferred Decision / Tech Decision Candidate / ADR / Business Goal: <...>
2698
2724
 
2699
- **Suggested Knowledge Level:** K1 / K2 / K3 / K4
2725
+ **Problem / opportunity:**
2700
2726
 
2701
- **Dependencies:**
2727
+ **Expected value:**
2702
2728
 
2703
- **Why this comes now:**
2729
+ **Risks:**
2704
2730
 
2705
- **Candidate Work Items:**
2731
+ **Dependencies:**
2706
2732
 
2733
+ **Suggested Work Items:**
2707
2734
  - WI-CANDIDATE-001: <candidate work item>
2708
2735
  - type:
2709
2736
  - suggested knowledge level:
2710
2737
  - expected value:
2711
2738
  - notes:
2712
2739
 
2713
- **Open questions:**
2740
+ **Not now:**
2714
2741
 
2715
2742
  ---
2716
2743
 
@@ -3005,6 +3032,14 @@ that it should be materialized first (\`kaddo adr\` + the adr-writing skill) and
3005
3032
  without surfacing it. -->
3006
3033
  \`\`\`
3007
3034
 
3035
+ ### Preserve roadmap metadata (VS-077)
3036
+
3037
+ When a Work Item comes from \`kaddo create --from roadmap\`, the front matter already carries
3038
+ \`source_roadmap_candidate\`, \`related_domain\`, \`related_capability\` (+ \`related_capabilities\`),
3039
+ \`knowledge_level\`, \`expected_value\`, \`risks\` and \`dependencies\`. **Keep and refine** this metadata \u2014
3040
+ do not drop the trace back to the capability domain and source signals. Add \`related_decisions\` /
3041
+ \`decision_candidates\` when the work depends on a technical decision.
3042
+
3008
3043
  ## Where to Save the Result
3009
3044
 
3010
3045
  Save new output as a draft under \`knowledge/delivery/work-items/draft/\` unless a human
@@ -4979,6 +5014,16 @@ function buildRoadmapFrontMatter(id, type, level, title, candidate, answers) {
4979
5014
  `source: roadmap`,
4980
5015
  `source_id: ${candidate.id}`,
4981
5016
  `source_initiative: ${candidate.initiative?.id ?? "unknown"}`,
5017
+ // Capability-grounded traceability (VS-077): carry the roadmap metadata into the Work Item.
5018
+ `source_roadmap_candidate: ${candidate.initiative?.id ?? candidate.id}`,
5019
+ ...candidate.domain ? [`related_domain: "${candidate.domain.replace(/"/g, "'")}"`] : [],
5020
+ ...candidate.relatedCapabilities && candidate.relatedCapabilities.length > 0 ? [
5021
+ `related_capability: "${candidate.relatedCapabilities[0].replace(/"/g, "'")}"`,
5022
+ `related_capabilities: [${candidate.relatedCapabilities.map((c) => `"${c.replace(/"/g, "'")}"`).join(", ")}]`
5023
+ ] : [],
5024
+ ...candidate.expectedValue ? [`expected_value: "${candidate.expectedValue.replace(/"/g, "'")}"`] : [],
5025
+ ...candidate.risk ? [`risks: "${candidate.risk.replace(/"/g, "'")}"`] : [],
5026
+ ...candidate.dependencies && candidate.dependencies.length > 0 ? [`dependencies: [${candidate.dependencies.map((d) => `"${d.replace(/"/g, "'")}"`).join(", ")}]`] : [],
4982
5027
  `summary: "${summary.replace(/"/g, "'")}"`,
4983
5028
  "---"
4984
5029
  ];
@@ -8325,6 +8370,79 @@ function installedAssetsSummary(dir) {
8325
8370
  return { version: KADDO_VERSION, agents: assetStatus(dir, "agent"), skills: assetStatus(dir, "skill") };
8326
8371
  }
8327
8372
 
8373
+ // src/core/roadmap-quality.ts
8374
+ var ROADMAP_PATH2 = "knowledge/delivery/roadmap.md";
8375
+ var INITIATIVE_RE2 = /^#{2,4}\s+(RM-[\w.-]+)\s*[:\-–]?\s*(.*)$/;
8376
+ function fieldPresent(lines, label) {
8377
+ for (let i = 0; i < lines.length; i++) {
8378
+ const m = lines[i].match(new RegExp(`^\\s*[-*]?\\s*(?:\\*\\*)?\\s*(${label.source})\\s*(?:\\*\\*)?\\s*:\\s*(.*)$`, "i"));
8379
+ if (!m) continue;
8380
+ const inline = m[2].trim();
8381
+ if (inline) return true;
8382
+ for (let j = i + 1; j < lines.length; j++) {
8383
+ if (/^\s{2,}[-*]\s+\S/.test(lines[j])) return true;
8384
+ if (/^\S/.test(lines[j]) || /^#{2,4}\s/.test(lines[j])) break;
8385
+ if (/^\s*[-*]\s+\S/.test(lines[j]) && !/^\s{2,}/.test(lines[j])) break;
8386
+ }
8387
+ }
8388
+ return false;
8389
+ }
8390
+ function parseRoadmapCandidateQuality(md) {
8391
+ const lines = md.split(/\r?\n/);
8392
+ const items = [];
8393
+ let cur = null;
8394
+ const flush = () => {
8395
+ if (!cur) return;
8396
+ const hasRelatedDomain = fieldPresent(cur.lines, /related domain/);
8397
+ const hasRelatedCapability = fieldPresent(cur.lines, /related capabilit(?:y|ies)/);
8398
+ const hasSourceSignals = fieldPresent(cur.lines, /source signals?/);
8399
+ items.push({
8400
+ id: cur.id,
8401
+ title: cur.title,
8402
+ hasRelatedDomain,
8403
+ hasRelatedCapability,
8404
+ hasSourceSignals,
8405
+ grounded: hasRelatedDomain && hasRelatedCapability && hasSourceSignals
8406
+ });
8407
+ cur = null;
8408
+ };
8409
+ for (const line of lines) {
8410
+ const m = line.match(INITIATIVE_RE2);
8411
+ if (m) {
8412
+ flush();
8413
+ cur = { id: m[1], title: m[2].trim(), lines: [] };
8414
+ continue;
8415
+ }
8416
+ if (cur) cur.lines.push(line);
8417
+ }
8418
+ flush();
8419
+ return items;
8420
+ }
8421
+ function buildRoadmapQuality(dir) {
8422
+ let md = "";
8423
+ const p2 = join(dir, ROADMAP_PATH2);
8424
+ if (exists(p2)) {
8425
+ try {
8426
+ md = readFile(p2);
8427
+ } catch {
8428
+ md = "";
8429
+ }
8430
+ }
8431
+ const items = parseRoadmapCandidateQuality(md);
8432
+ const count = (pred) => items.filter(pred).length;
8433
+ const grounded = count((i) => i.grounded);
8434
+ return {
8435
+ candidates: items.length,
8436
+ grounded,
8437
+ with_related_domain: count((i) => i.hasRelatedDomain),
8438
+ with_related_capability: count((i) => i.hasRelatedCapability),
8439
+ with_source_signals: count((i) => i.hasSourceSignals),
8440
+ // Only "needs refinement" when there are candidates and at least one isn't grounded.
8441
+ needs_refinement: items.length > 0 && grounded < items.length,
8442
+ items
8443
+ };
8444
+ }
8445
+
8328
8446
  // src/core/project-explain.ts
8329
8447
  var ARCH_DIR4 = "knowledge";
8330
8448
  function normalizeTitle(t) {
@@ -8549,7 +8667,8 @@ function buildProjectExplanation(dir) {
8549
8667
  legacyLocation: exists(join(dir, "knowledge/tech/architecture-notes.md")) || exists(join(dir, "knowledge/tech/decision-candidates.md"))
8550
8668
  }
8551
8669
  },
8552
- installedAssets: installedAssetsSummary(dir)
8670
+ installedAssets: installedAssetsSummary(dir),
8671
+ roadmapQuality: buildRoadmapQuality(dir)
8553
8672
  };
8554
8673
  }
8555
8674
  function stateLabel(state) {
@@ -8784,6 +8903,20 @@ function renderExplanationHuman(exp) {
8784
8903
  lines.push("Tech discovery files are in the legacy `knowledge/tech/` root. Suggested cleanup: run `kaddo tech organize`.");
8785
8904
  lines.push("");
8786
8905
  }
8906
+ const rq = exp.roadmapQuality;
8907
+ if (rq.candidates > 0) {
8908
+ lines.push("## Roadmap Quality");
8909
+ lines.push(`- Candidates: ${rq.candidates}`);
8910
+ lines.push(`- Grounded: ${rq.grounded}/${rq.candidates}`);
8911
+ lines.push(`- With related domain: ${rq.with_related_domain}/${rq.candidates}`);
8912
+ lines.push(`- With related capability: ${rq.with_related_capability}/${rq.candidates}`);
8913
+ lines.push(`- With source signals: ${rq.with_source_signals}/${rq.candidates}`);
8914
+ if (rq.needs_refinement) {
8915
+ lines.push("");
8916
+ lines.push("Roadmap quality: needs refinement. Suggested: use roadmap-agent to add domain / capability / source signals.");
8917
+ }
8918
+ lines.push("");
8919
+ }
8787
8920
  const ia = exp.installedAssets;
8788
8921
  const agentsInstalled = ia.agents.total - ia.agents.missing;
8789
8922
  const skillsInstalled = ia.skills.total - ia.skills.missing;
@@ -9202,6 +9335,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
9202
9335
  nextStepRecommendation,
9203
9336
  techDecisions,
9204
9337
  techKnowledge,
9338
+ roadmapQuality: buildRoadmapQuality(dir),
9205
9339
  installedAssets: (() => {
9206
9340
  const s = installedAssetsSummary(dir);
9207
9341
  const compact = (a) => ({ total: a.total, installed: a.total - a.missing, outdated: a.outdated, unknown_version: a.unknown_version, modified: a.modified });
@@ -9316,6 +9450,24 @@ function renderContextPack(pack) {
9316
9450
  }
9317
9451
  }
9318
9452
  parts.push((knowledge.roadmapSummary || "No roadmap baseline found.") + "\n");
9453
+ const rq = pack.roadmapQuality;
9454
+ if (rq.candidates > 0) {
9455
+ parts.push("## Roadmap Quality\n");
9456
+ parts.push(
9457
+ [
9458
+ `- Candidates: ${rq.candidates}`,
9459
+ `- Grounded: ${rq.grounded}/${rq.candidates}`,
9460
+ `- With related domain: ${rq.with_related_domain}/${rq.candidates}`,
9461
+ `- With related capability: ${rq.with_related_capability}/${rq.candidates}`,
9462
+ `- With source signals: ${rq.with_source_signals}/${rq.candidates}`
9463
+ ].join("\n") + "\n"
9464
+ );
9465
+ if (rq.needs_refinement) {
9466
+ parts.push(
9467
+ "Roadmap quality: needs refinement. Use the roadmap-agent to add domain / capability / source signals.\n"
9468
+ );
9469
+ }
9470
+ }
9319
9471
  parts.push("## Active Work Items\n");
9320
9472
  if (knowledge.workItems.length > 0) {
9321
9473
  const lines = knowledge.workItems.map((wi) => {
@@ -9748,6 +9900,16 @@ function runUnderstand() {
9748
9900
  console.log(" \u2192 Use the adr-writing skill to create ADR drafts from `knowledge/tech/decision-candidates.md`");
9749
9901
  console.log(" into `knowledge/tech/decisions/` before implementing affected technical Work Items (`kaddo adr`).");
9750
9902
  }
9903
+ const rq = exp.roadmapQuality;
9904
+ if (rq.needs_refinement) {
9905
+ console.log("");
9906
+ console.log(`Roadmap quality: ${rq.grounded}/${rq.candidates} candidates grounded.`);
9907
+ console.log(" \u2192 Use roadmap-agent to ground roadmap candidates in capability domains, gaps and source signals");
9908
+ console.log(" before `kaddo create --from roadmap`.");
9909
+ } else if (rq.candidates > 0 && rq.grounded === rq.candidates) {
9910
+ console.log("");
9911
+ console.log("Roadmap candidates are grounded. \u2192 Run `kaddo create --from roadmap` to materialize the first Work Item.");
9912
+ }
9751
9913
  const ia = exp.installedAssets;
9752
9914
  const outdatedRecommended = ia.agents.items.filter(
9753
9915
  (a) => (a.state === "outdated" || a.state === "unknown-version") && assessment.recommendedAgents.includes(a.name)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/cli",
3
- "version": "3.42.0",
3
+ "version": "3.43.0",
4
4
  "description": "Knowledge Driven Development toolkit",
5
5
  "license": "MIT",
6
6
  "repository": {