@kaddo/cli 3.43.0 → 3.44.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 +232 -85
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -542,6 +542,7 @@ create --from roadmap → owners → guard → explain`.
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
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` |
545
+ | v3.44 | Roadmap counting alignment + materialization quality: `explain`/`context` separate **Roadmap initiatives** from **Work Item candidates** (`## Roadmap Status`); two-level `roadmapQuality` (initiatives + work_item_candidates); `create --from roadmap` normalizes metadata — fills `domains` from `related_domain`, splits comma-joined `related_capabilities` into a real list, carries `source_roadmap_initiative`/`source_work_item_candidate`/`source_signals`/`decision_candidates`, improved Source + Context-From-Roadmap body + ADR warning; MCP `kaddo://work-item-candidates` |
545
546
 
546
547
  **Optional modules (installed with `kaddo add`):**
547
548
 
package/dist/index.js CHANGED
@@ -3032,13 +3032,22 @@ that it should be materialized first (\`kaddo adr\` + the adr-writing skill) and
3032
3032
  without surfacing it. -->
3033
3033
  \`\`\`
3034
3034
 
3035
- ### Preserve roadmap metadata (VS-077)
3035
+ ### Preserve roadmap metadata (VS-077 / VS-078)
3036
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.
3037
+ When a Work Item comes from \`kaddo create --from roadmap\`, the front matter already carries the trace
3038
+ back to the roadmap. **Keep and refine \u2014 never delete** these fields:
3039
+
3040
+ - \`source_roadmap_initiative\` and \`source_work_item_candidate\` (the RM-xxx initiative and the
3041
+ WI-CANDIDATE-xxx it was materialized from)
3042
+ - \`related_domain\` and \`domains\` (keep them consistent \u2014 \`domains\` must not be empty when
3043
+ \`related_domain\` exists)
3044
+ - \`related_capabilities\` (a real list, one capability per item \u2014 never a single comma-joined string)
3045
+ - \`expected_value\`, \`risks\`, \`dependencies\`
3046
+ - \`source_signals\` (do **not** invent them \u2014 if absent, leave them absent)
3047
+ - \`decision_candidates\` and \`related_decisions\` (when the work depends on a technical decision)
3048
+
3049
+ Do not drop the trace back to the capability domain and source signals. If a Work Item depends on a
3050
+ tech decision candidate with no ADR yet, keep the warning surfaced in the body.
3042
3051
 
3043
3052
  ## Where to Save the Result
3044
3053
 
@@ -4530,7 +4539,12 @@ function splitInitiatives(markdown) {
4530
4539
  return blocks;
4531
4540
  }
4532
4541
  function parseBlock(block) {
4533
- const meta = { relatedCapabilities: [], dependencies: [], openQuestions: [] };
4542
+ const meta = {
4543
+ relatedCapabilities: [],
4544
+ dependencies: [],
4545
+ openQuestions: [],
4546
+ sourceSignals: []
4547
+ };
4534
4548
  const rawCandidates = [];
4535
4549
  let listField = null;
4536
4550
  let inCandidateSection = false;
@@ -4580,7 +4594,7 @@ function parseBlock(block) {
4580
4594
  meta.impact = value || void 0;
4581
4595
  } else if (key === "risk") {
4582
4596
  meta.risk = value || void 0;
4583
- } else if (key === "project area / domain" || key === "domain" || key === "project area") {
4597
+ } else if (key === "project area / domain" || key === "domain" || key === "project area" || key === "related domain") {
4584
4598
  meta.domain = value || void 0;
4585
4599
  } else if (key === "related capabilities") {
4586
4600
  listField = "relatedCapabilities";
@@ -4591,6 +4605,9 @@ function parseBlock(block) {
4591
4605
  } else if (key === "open questions") {
4592
4606
  listField = "openQuestions";
4593
4607
  if (value) meta.openQuestions.push(value);
4608
+ } else if (key === "source signals" || key === "source signal") {
4609
+ listField = "sourceSignals";
4610
+ if (value) meta.sourceSignals.push(value);
4594
4611
  }
4595
4612
  continue;
4596
4613
  }
@@ -4608,6 +4625,7 @@ function parseBlock(block) {
4608
4625
  }
4609
4626
  }
4610
4627
  flush();
4628
+ const decisionCandidates = decisionCandidatesFromSignals(meta.sourceSignals);
4611
4629
  return rawCandidates.map((c) => ({
4612
4630
  ...c,
4613
4631
  initiative: block.initiative.id || block.initiative.title ? { ...block.initiative } : void 0,
@@ -4616,9 +4634,23 @@ function parseBlock(block) {
4616
4634
  impact: meta.impact,
4617
4635
  risk: meta.risk,
4618
4636
  dependencies: meta.dependencies.length ? [...meta.dependencies] : void 0,
4619
- openQuestions: meta.openQuestions.length ? [...meta.openQuestions] : void 0
4637
+ openQuestions: meta.openQuestions.length ? [...meta.openQuestions] : void 0,
4638
+ sourceSignals: meta.sourceSignals.length ? [...meta.sourceSignals] : void 0,
4639
+ decisionCandidates: decisionCandidates.length ? decisionCandidates : void 0
4620
4640
  }));
4621
4641
  }
4642
+ function decisionCandidatesFromSignals(signals) {
4643
+ const out = [];
4644
+ for (const sig of signals) {
4645
+ const m = sig.match(/decision candidate\s*:?\s*(.+)$/i);
4646
+ if (!m) continue;
4647
+ const rest = m[1].trim();
4648
+ const token = rest.match(/\b[A-Z][A-Z0-9_]{2,}\b/);
4649
+ const value = token ? token[0] : rest.replace(/[.。]+$/, "").trim();
4650
+ if (value && !out.includes(value)) out.push(value);
4651
+ }
4652
+ return out;
4653
+ }
4622
4654
  var WI_ID_RE = /\bWI-[A-Za-z0-9-]*\d/;
4623
4655
  var HEADING_RE = /^#{2,4}\s+(.*)$/;
4624
4656
  var FLEX_BULLET_RE = /^\s*[-*]\s+(?:\[[ xX]?\]\s+)?(WI-[A-Za-z0-9-]*\d)\b[\s:.\-–)]*\s*(.*)$/;
@@ -4700,10 +4732,51 @@ function parseRoadmapCandidates(markdown) {
4700
4732
  if (strict.length > 0) return strict;
4701
4733
  return parseFlexible(markdown);
4702
4734
  }
4735
+ function countRoadmapInitiatives(markdown) {
4736
+ if (markdown == null) return 0;
4737
+ let n = 0;
4738
+ for (const line of markdown.split(/\r?\n/)) if (INITIATIVE_RE.test(line)) n++;
4739
+ return n;
4740
+ }
4703
4741
  function roadmapStats(markdown, materialized) {
4704
- if (markdown == null) return { present: false, candidates: 0, materialized, remaining: 0 };
4742
+ if (markdown == null)
4743
+ return {
4744
+ present: false,
4745
+ initiatives: 0,
4746
+ work_item_candidates: 0,
4747
+ materialized_work_items: materialized,
4748
+ remaining_work_item_candidates: 0,
4749
+ candidates: 0,
4750
+ materialized,
4751
+ remaining: 0
4752
+ };
4705
4753
  const candidates = parseRoadmapCandidates(markdown).length;
4706
- return { present: true, candidates, materialized, remaining: Math.max(0, candidates - materialized) };
4754
+ const remaining = Math.max(0, candidates - materialized);
4755
+ return {
4756
+ present: true,
4757
+ initiatives: countRoadmapInitiatives(markdown),
4758
+ work_item_candidates: candidates,
4759
+ materialized_work_items: materialized,
4760
+ remaining_work_item_candidates: remaining,
4761
+ candidates,
4762
+ materialized,
4763
+ remaining
4764
+ };
4765
+ }
4766
+ function normalizeCapabilityList(raw) {
4767
+ if (!raw || raw.length === 0) return [];
4768
+ const out = [];
4769
+ for (const entry of raw) {
4770
+ for (const part of entry.split(/[,;]/)) {
4771
+ const cap = part.replace(/[.。]+\s*$/, "").trim();
4772
+ if (cap && !out.includes(cap)) out.push(cap);
4773
+ }
4774
+ }
4775
+ return out;
4776
+ }
4777
+ function domainsFromRelated(domain) {
4778
+ const d = domain?.trim();
4779
+ return d ? [d] : [];
4707
4780
  }
4708
4781
 
4709
4782
  // src/commands/create.ts
@@ -4999,32 +5072,44 @@ function buildRoadmapFrontMatter(id, type, level, title, candidate, answers) {
4999
5072
  const today2 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5000
5073
  const summary = (answers.problem?.split(".")[0] ?? candidate.expectedValue ?? title).trim();
5001
5074
  const initiative = candidate.initiative?.title ?? candidate.initiative?.id ?? "";
5075
+ const q = (s) => s.replace(/"/g, "'");
5076
+ const domains = domainsFromRelated(candidate.domain);
5077
+ const relatedCapabilities = normalizeCapabilityList(candidate.relatedCapabilities);
5078
+ const risks = candidate.risk ? [candidate.risk] : [];
5079
+ const dependencies = candidate.dependencies ?? [];
5080
+ const sourceSignals = candidate.sourceSignals ?? [];
5081
+ const decisionCandidates = candidate.decisionCandidates ?? [];
5082
+ const yamlList2 = (key, items) => items.length === 0 ? [] : [`${key}:`, ...items.map((i) => ` - "${q(i)}"`)];
5002
5083
  const lines = [
5003
5084
  "---",
5004
5085
  `type: ${type}`,
5005
5086
  `id: ${id}`,
5006
- `title: "${title}"`,
5087
+ `title: "${q(title)}"`,
5007
5088
  `knowledge_level: ${level}`,
5008
5089
  `status: draft`,
5009
5090
  `phase: now`,
5010
- `initiative: "${initiative.replace(/"/g, "'")}"`,
5011
- `domains: []`,
5091
+ `initiative: "${q(initiative)}"`,
5092
+ ...yamlList2("domains", domains),
5093
+ ...domains.length === 0 ? ["domains: []"] : [],
5012
5094
  `code: []`,
5013
5095
  `created_at: ${today2}`,
5014
5096
  `source: roadmap`,
5015
5097
  `source_id: ${candidate.id}`,
5016
5098
  `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(", ")}]`] : [],
5027
- `summary: "${summary.replace(/"/g, "'")}"`,
5099
+ // Capability-grounded traceability (VS-077 / VS-078): distinguish the parent roadmap initiative
5100
+ // from the specific Work Item candidate it was materialized from.
5101
+ `source_roadmap_initiative: ${candidate.initiative?.id ?? "unknown"}`,
5102
+ `source_work_item_candidate: ${candidate.id}`,
5103
+ ...candidate.initiative?.title ? [`source_initiative_title: "${q(candidate.initiative.title)}"`] : [],
5104
+ ...candidate.domain ? [`related_domain: "${q(candidate.domain)}"`] : [],
5105
+ ...yamlList2("related_capabilities", relatedCapabilities),
5106
+ ...candidate.expectedValue ? [`expected_value: "${q(candidate.expectedValue)}"`] : [],
5107
+ ...yamlList2("risks", risks),
5108
+ ...yamlList2("dependencies", dependencies),
5109
+ ...yamlList2("source_signals", sourceSignals),
5110
+ ...yamlList2("decision_candidates", decisionCandidates),
5111
+ ...decisionCandidates.length > 0 ? ["related_decisions: []"] : [],
5112
+ `summary: "${q(summary)}"`,
5028
5113
  "---"
5029
5114
  ];
5030
5115
  return lines.join("\n");
@@ -5036,14 +5121,24 @@ function buildRoadmapBody(type, level, title, candidate, answers, qualityGate) {
5036
5121
  sections.push(`> Type: ${type} \xB7 Level: ${level}
5037
5122
  `);
5038
5123
  const initiativeLabel = candidate.initiative ? `${candidate.initiative.id ?? ""}${candidate.initiative.title ? ` \u2014 ${candidate.initiative.title}` : ""}`.trim() : "unknown";
5039
- sections.push(
5040
- [
5041
- "## Source\n",
5042
- `- Source: roadmap`,
5043
- `- Candidate: ${candidate.id}`,
5044
- `- Initiative: ${initiativeLabel || "unknown"}`
5045
- ].join("\n") + "\n"
5046
- );
5124
+ const relatedCapabilities = normalizeCapabilityList(candidate.relatedCapabilities);
5125
+ const sourceLines = [
5126
+ "## Source\n",
5127
+ `- Source: roadmap`,
5128
+ `- Roadmap Initiative: ${initiativeLabel || "unknown"}`,
5129
+ `- Work Item Candidate: ${candidate.id}`
5130
+ ];
5131
+ if (candidate.domain) sourceLines.push(`- Related domain: ${candidate.domain}`);
5132
+ if (relatedCapabilities.length) {
5133
+ sourceLines.push("- Related capabilities:");
5134
+ relatedCapabilities.forEach((c) => sourceLines.push(` - ${c}`));
5135
+ }
5136
+ sections.push(sourceLines.join("\n") + "\n");
5137
+ if (candidate.decisionCandidates?.length) {
5138
+ sections.push(
5139
+ "> Warning: This Work Item is related to technical decision candidates that have not been materialized as ADRs.\n"
5140
+ );
5141
+ }
5047
5142
  if (answers.problem) sections.push(`## Problem
5048
5143
 
5049
5144
  ${answers.problem}
@@ -5053,22 +5148,22 @@ ${answers.problem}
5053
5148
 
5054
5149
  ${expectedValue}
5055
5150
  `);
5056
- const ctx = [];
5057
- if (candidate.relatedCapabilities?.length)
5058
- ctx.push(`**Related capabilities:** ${candidate.relatedCapabilities.join(", ")}`);
5059
- if (candidate.domain) ctx.push(`**Domain:** ${candidate.domain}`);
5060
- if (candidate.impact) ctx.push(`**Impact:** ${candidate.impact}`);
5061
- if (candidate.risk) ctx.push(`**Risk:** ${candidate.risk}`);
5151
+ const initiativeRef = candidate.initiative?.id ? `roadmap initiative ${candidate.initiative.id}` : "a roadmap candidate";
5152
+ const ctx = [`This Work Item was materialized from ${initiativeRef}.`, ""];
5153
+ if (candidate.expectedValue) ctx.push(`**Expected value:** ${candidate.expectedValue}`);
5154
+ if (candidate.risk) ctx.push(`**Risks:** ${candidate.risk}`);
5062
5155
  if (candidate.dependencies?.length)
5063
- ctx.push(`**Dependencies:**
5064
- ${formatList(candidate.dependencies)}`);
5065
- const initiativeNote = candidate.initiative?.id ? `This candidate was created from the roadmap initiative ${candidate.initiative.id}.` : "This work item was created from a roadmap candidate.";
5066
- sections.push(
5067
- `## Context From Roadmap
5156
+ ctx.push(`**Dependencies:** ${candidate.dependencies.join("; ")}`);
5157
+ if (candidate.sourceSignals?.length) {
5158
+ ctx.push(`**Source signals:**
5159
+ ${formatList(candidate.sourceSignals)}`);
5160
+ } else {
5161
+ ctx.push("**Source signals:** _Not provided in roadmap._");
5162
+ }
5163
+ sections.push(`## Context From Roadmap
5068
5164
 
5069
- ${initiativeNote}${ctx.length ? "\n\n" + ctx.join("\n\n") : ""}
5070
- `
5071
- );
5165
+ ${ctx.join("\n\n")}
5166
+ `);
5072
5167
  if (answers.acceptance_criteria) {
5073
5168
  const items = answers.acceptance_criteria.split("\n").map((l) => l.trim()).filter(Boolean);
5074
5169
  sections.push(`## Acceptance Criteria
@@ -8431,16 +8526,25 @@ function buildRoadmapQuality(dir) {
8431
8526
  const items = parseRoadmapCandidateQuality(md);
8432
8527
  const count = (pred) => items.filter(pred).length;
8433
8528
  const grounded = count((i) => i.grounded);
8434
- return {
8435
- candidates: items.length,
8529
+ const initiatives = {
8530
+ total: items.length,
8436
8531
  grounded,
8437
8532
  with_related_domain: count((i) => i.hasRelatedDomain),
8438
8533
  with_related_capability: count((i) => i.hasRelatedCapability),
8439
8534
  with_source_signals: count((i) => i.hasSourceSignals),
8440
- // Only "needs refinement" when there are candidates and at least one isn't grounded.
8535
+ // Only "needs refinement" when there are initiatives and at least one isn't grounded.
8441
8536
  needs_refinement: items.length > 0 && grounded < items.length,
8442
8537
  items
8443
8538
  };
8539
+ const wiCandidates = md ? parseRoadmapCandidates(md) : [];
8540
+ const wcount = (pred) => wiCandidates.filter(pred).length;
8541
+ const work_item_candidates = {
8542
+ total: wiCandidates.length,
8543
+ with_source_initiative: wcount((c) => Boolean(c.initiative?.id || c.initiative?.title)),
8544
+ with_related_domain: wcount((c) => Boolean(c.domain)),
8545
+ with_related_capability: wcount((c) => Boolean(c.relatedCapabilities?.length))
8546
+ };
8547
+ return { initiatives, work_item_candidates };
8444
8548
  }
8445
8549
 
8446
8550
  // src/core/project-explain.ts
@@ -8723,10 +8827,11 @@ function renderExplanationHuman(exp) {
8723
8827
  lines.push(`- Delivery: ${ls("Delivery")}`);
8724
8828
  lines.push(`- Agents: ${exp.knowledge.hasAgents ? "available" : "missing"}`);
8725
8829
  if (exp.roadmap.present) {
8726
- lines.push(`- Roadmap candidates: ${exp.roadmap.candidates}`);
8727
- lines.push(`- Materialized work items: ${exp.roadmap.materialized}`);
8728
- if (exp.roadmap.remaining > 0)
8729
- lines.push(`- Remaining candidates: ${exp.roadmap.remaining}`);
8830
+ lines.push(`- Roadmap initiatives: ${exp.roadmap.initiatives}`);
8831
+ lines.push(`- Work Item candidates: ${exp.roadmap.work_item_candidates}`);
8832
+ lines.push(`- Materialized Work Items: ${exp.roadmap.materialized_work_items}`);
8833
+ if (exp.roadmap.remaining_work_item_candidates > 0)
8834
+ lines.push(`- Remaining Work Item candidates: ${exp.roadmap.remaining_work_item_candidates}`);
8730
8835
  } else {
8731
8836
  lines.push(`- Work items: ${exp.workItems.total}`);
8732
8837
  }
@@ -8903,17 +9008,42 @@ function renderExplanationHuman(exp) {
8903
9008
  lines.push("Tech discovery files are in the legacy `knowledge/tech/` root. Suggested cleanup: run `kaddo tech organize`.");
8904
9009
  lines.push("");
8905
9010
  }
9011
+ if (exp.roadmap.present) {
9012
+ lines.push("## Roadmap Status");
9013
+ lines.push(`- Initiatives: ${exp.roadmap.initiatives}`);
9014
+ lines.push(`- Work Item candidates: ${exp.roadmap.work_item_candidates}`);
9015
+ lines.push(`- Materialized Work Items: ${exp.roadmap.materialized_work_items}`);
9016
+ lines.push(`- Remaining Work Item candidates: ${exp.roadmap.remaining_work_item_candidates}`);
9017
+ lines.push("");
9018
+ }
8906
9019
  const rq = exp.roadmapQuality;
8907
- if (rq.candidates > 0) {
9020
+ const rqi = rq.initiatives;
9021
+ const rqw = rq.work_item_candidates;
9022
+ if (rqi.total > 0 || rqw.total > 0) {
8908
9023
  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) {
9024
+ if (rqi.total > 0) {
9025
+ lines.push("Initiatives:");
9026
+ lines.push(`- Candidates evaluated: ${rqi.total}`);
9027
+ lines.push(`- Grounded: ${rqi.grounded}/${rqi.total}`);
9028
+ lines.push(`- With related domain: ${rqi.with_related_domain}/${rqi.total}`);
9029
+ lines.push(`- With related capability: ${rqi.with_related_capability}/${rqi.total}`);
9030
+ lines.push(`- With source signals: ${rqi.with_source_signals}/${rqi.total}`);
9031
+ }
9032
+ if (rqw.total > 0) {
8915
9033
  lines.push("");
8916
- lines.push("Roadmap quality: needs refinement. Suggested: use roadmap-agent to add domain / capability / source signals.");
9034
+ lines.push("Work Item Candidates:");
9035
+ lines.push(`- Candidates: ${rqw.total}`);
9036
+ lines.push(`- With source initiative: ${rqw.with_source_initiative}/${rqw.total}`);
9037
+ lines.push(`- With related domain: ${rqw.with_related_domain}/${rqw.total}`);
9038
+ lines.push(`- With related capability: ${rqw.with_related_capability}/${rqw.total}`);
9039
+ }
9040
+ if (rqi.needs_refinement) {
9041
+ lines.push("");
9042
+ lines.push("Roadmap quality: needs refinement.");
9043
+ lines.push("Suggested: use roadmap-agent to add domain / capability / source signals.");
9044
+ } else if (rqw.total > 0) {
9045
+ lines.push("");
9046
+ lines.push("Work Item candidate quality: good.");
8917
9047
  }
8918
9048
  lines.push("");
8919
9049
  }
@@ -9434,35 +9564,52 @@ function renderContextPack(pack) {
9434
9564
  }
9435
9565
  parts.push("## Current Knowledge\n");
9436
9566
  parts.push((knowledge.summary || "No project knowledge summary found yet.") + "\n");
9437
- parts.push("## Roadmap\n");
9567
+ parts.push("## Roadmap Status\n");
9438
9568
  if (pack.roadmap.present) {
9439
9569
  parts.push(
9440
9570
  [
9441
- `- Roadmap candidates: ${pack.roadmap.candidates}`,
9442
- `- Materialized work items: ${pack.roadmap.materialized}`,
9443
- `- Remaining candidates: ${pack.roadmap.remaining}`
9571
+ `- Initiatives: ${pack.roadmap.initiatives}`,
9572
+ `- Work Item candidates: ${pack.roadmap.work_item_candidates}`,
9573
+ `- Materialized Work Items: ${pack.roadmap.materialized_work_items}`,
9574
+ `- Remaining Work Item candidates: ${pack.roadmap.remaining_work_item_candidates}`
9444
9575
  ].join("\n") + "\n"
9445
9576
  );
9446
- if (pack.roadmap.remaining > 0) {
9577
+ if (pack.roadmap.remaining_work_item_candidates > 0) {
9447
9578
  parts.push(
9448
- "Candidates are not yet Work Items. Materialize them with `kaddo create --from roadmap`.\n"
9579
+ "Work Item candidates are not yet Work Items. Materialize them with `kaddo create --from roadmap`.\n"
9449
9580
  );
9450
9581
  }
9451
9582
  }
9452
9583
  parts.push((knowledge.roadmapSummary || "No roadmap baseline found.") + "\n");
9453
9584
  const rq = pack.roadmapQuality;
9454
- if (rq.candidates > 0) {
9585
+ const rqi = rq.initiatives;
9586
+ const rqw = rq.work_item_candidates;
9587
+ if (rqi.total > 0 || rqw.total > 0) {
9455
9588
  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) {
9589
+ if (rqi.total > 0) {
9590
+ parts.push(
9591
+ [
9592
+ "Initiatives:",
9593
+ `- Candidates evaluated: ${rqi.total}`,
9594
+ `- Grounded: ${rqi.grounded}/${rqi.total}`,
9595
+ `- With related domain: ${rqi.with_related_domain}/${rqi.total}`,
9596
+ `- With related capability: ${rqi.with_related_capability}/${rqi.total}`,
9597
+ `- With source signals: ${rqi.with_source_signals}/${rqi.total}`
9598
+ ].join("\n") + "\n"
9599
+ );
9600
+ }
9601
+ if (rqw.total > 0) {
9602
+ parts.push(
9603
+ [
9604
+ "Work Item Candidates:",
9605
+ `- Candidates: ${rqw.total}`,
9606
+ `- With source initiative: ${rqw.with_source_initiative}/${rqw.total}`,
9607
+ `- With related domain: ${rqw.with_related_domain}/${rqw.total}`,
9608
+ `- With related capability: ${rqw.with_related_capability}/${rqw.total}`
9609
+ ].join("\n") + "\n"
9610
+ );
9611
+ }
9612
+ if (rqi.needs_refinement) {
9466
9613
  parts.push(
9467
9614
  "Roadmap quality: needs refinement. Use the roadmap-agent to add domain / capability / source signals.\n"
9468
9615
  );
@@ -9900,15 +10047,15 @@ function runUnderstand() {
9900
10047
  console.log(" \u2192 Use the adr-writing skill to create ADR drafts from `knowledge/tech/decision-candidates.md`");
9901
10048
  console.log(" into `knowledge/tech/decisions/` before implementing affected technical Work Items (`kaddo adr`).");
9902
10049
  }
9903
- const rq = exp.roadmapQuality;
9904
- if (rq.needs_refinement) {
10050
+ const rqi = exp.roadmapQuality.initiatives;
10051
+ if (rqi.needs_refinement) {
9905
10052
  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");
10053
+ console.log(`Roadmap quality: ${rqi.grounded}/${rqi.total} initiatives grounded.`);
10054
+ console.log(" \u2192 Use roadmap-agent to ground roadmap initiatives in capability domains, gaps and source signals");
9908
10055
  console.log(" before `kaddo create --from roadmap`.");
9909
- } else if (rq.candidates > 0 && rq.grounded === rq.candidates) {
10056
+ } else if (rqi.total > 0 && rqi.grounded === rqi.total) {
9910
10057
  console.log("");
9911
- console.log("Roadmap candidates are grounded. \u2192 Run `kaddo create --from roadmap` to materialize the first Work Item.");
10058
+ console.log("Roadmap initiatives are grounded. \u2192 Run `kaddo create --from roadmap` to materialize the first Work Item.");
9912
10059
  }
9913
10060
  const ia = exp.installedAssets;
9914
10061
  const outdatedRecommended = ia.agents.items.filter(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/cli",
3
- "version": "3.43.0",
3
+ "version": "3.44.0",
4
4
  "description": "Knowledge Driven Development toolkit",
5
5
  "license": "MIT",
6
6
  "repository": {