@kaddo/mcp 3.42.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 (2) hide show
  1. package/dist/index.js +304 -26
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1629,7 +1629,12 @@ function splitInitiatives(markdown) {
1629
1629
  return blocks;
1630
1630
  }
1631
1631
  function parseBlock(block) {
1632
- const meta = { relatedCapabilities: [], dependencies: [], openQuestions: [] };
1632
+ const meta = {
1633
+ relatedCapabilities: [],
1634
+ dependencies: [],
1635
+ openQuestions: [],
1636
+ sourceSignals: []
1637
+ };
1633
1638
  const rawCandidates = [];
1634
1639
  let listField = null;
1635
1640
  let inCandidateSection = false;
@@ -1679,7 +1684,7 @@ function parseBlock(block) {
1679
1684
  meta.impact = value || void 0;
1680
1685
  } else if (key === "risk") {
1681
1686
  meta.risk = value || void 0;
1682
- } else if (key === "project area / domain" || key === "domain" || key === "project area") {
1687
+ } else if (key === "project area / domain" || key === "domain" || key === "project area" || key === "related domain") {
1683
1688
  meta.domain = value || void 0;
1684
1689
  } else if (key === "related capabilities") {
1685
1690
  listField = "relatedCapabilities";
@@ -1690,6 +1695,9 @@ function parseBlock(block) {
1690
1695
  } else if (key === "open questions") {
1691
1696
  listField = "openQuestions";
1692
1697
  if (value) meta.openQuestions.push(value);
1698
+ } else if (key === "source signals" || key === "source signal") {
1699
+ listField = "sourceSignals";
1700
+ if (value) meta.sourceSignals.push(value);
1693
1701
  }
1694
1702
  continue;
1695
1703
  }
@@ -1707,6 +1715,7 @@ function parseBlock(block) {
1707
1715
  }
1708
1716
  }
1709
1717
  flush();
1718
+ const decisionCandidates = decisionCandidatesFromSignals(meta.sourceSignals);
1710
1719
  return rawCandidates.map((c) => ({
1711
1720
  ...c,
1712
1721
  initiative: block.initiative.id || block.initiative.title ? { ...block.initiative } : void 0,
@@ -1715,9 +1724,23 @@ function parseBlock(block) {
1715
1724
  impact: meta.impact,
1716
1725
  risk: meta.risk,
1717
1726
  dependencies: meta.dependencies.length ? [...meta.dependencies] : void 0,
1718
- openQuestions: meta.openQuestions.length ? [...meta.openQuestions] : void 0
1727
+ openQuestions: meta.openQuestions.length ? [...meta.openQuestions] : void 0,
1728
+ sourceSignals: meta.sourceSignals.length ? [...meta.sourceSignals] : void 0,
1729
+ decisionCandidates: decisionCandidates.length ? decisionCandidates : void 0
1719
1730
  }));
1720
1731
  }
1732
+ function decisionCandidatesFromSignals(signals) {
1733
+ const out = [];
1734
+ for (const sig of signals) {
1735
+ const m = sig.match(/decision candidate\s*:?\s*(.+)$/i);
1736
+ if (!m) continue;
1737
+ const rest = m[1].trim();
1738
+ const token = rest.match(/\b[A-Z][A-Z0-9_]{2,}\b/);
1739
+ const value = token ? token[0] : rest.replace(/[.。]+$/, "").trim();
1740
+ if (value && !out.includes(value)) out.push(value);
1741
+ }
1742
+ return out;
1743
+ }
1721
1744
  var WI_ID_RE = /\bWI-[A-Za-z0-9-]*\d/;
1722
1745
  var HEADING_RE = /^#{2,4}\s+(.*)$/;
1723
1746
  var FLEX_BULLET_RE = /^\s*[-*]\s+(?:\[[ xX]?\]\s+)?(WI-[A-Za-z0-9-]*\d)\b[\s:.\-–)]*\s*(.*)$/;
@@ -1799,10 +1822,36 @@ function parseRoadmapCandidates(markdown) {
1799
1822
  if (strict.length > 0) return strict;
1800
1823
  return parseFlexible(markdown);
1801
1824
  }
1825
+ function countRoadmapInitiatives(markdown) {
1826
+ if (markdown == null) return 0;
1827
+ let n = 0;
1828
+ for (const line of markdown.split(/\r?\n/)) if (INITIATIVE_RE.test(line)) n++;
1829
+ return n;
1830
+ }
1802
1831
  function roadmapStats(markdown, materialized) {
1803
- if (markdown == null) return { present: false, candidates: 0, materialized, remaining: 0 };
1832
+ if (markdown == null)
1833
+ return {
1834
+ present: false,
1835
+ initiatives: 0,
1836
+ work_item_candidates: 0,
1837
+ materialized_work_items: materialized,
1838
+ remaining_work_item_candidates: 0,
1839
+ candidates: 0,
1840
+ materialized,
1841
+ remaining: 0
1842
+ };
1804
1843
  const candidates = parseRoadmapCandidates(markdown).length;
1805
- return { present: true, candidates, materialized, remaining: Math.max(0, candidates - materialized) };
1844
+ const remaining = Math.max(0, candidates - materialized);
1845
+ return {
1846
+ present: true,
1847
+ initiatives: countRoadmapInitiatives(markdown),
1848
+ work_item_candidates: candidates,
1849
+ materialized_work_items: materialized,
1850
+ remaining_work_item_candidates: remaining,
1851
+ candidates,
1852
+ materialized,
1853
+ remaining
1854
+ };
1806
1855
  }
1807
1856
 
1808
1857
  // ../cli/src/core/open-questions.ts
@@ -3628,6 +3677,24 @@ Adapt priorities to the project state from the context pack:
3628
3677
  - **legacy** \u2014 prioritize risk reduction, unknowns and safe modernization before feature
3629
3678
  delivery.
3630
3679
 
3680
+ ## Grounding rules (VS-077)
3681
+
3682
+ Every candidate initiative must be **grounded** in the knowledge base \u2014 never a loose idea. For each
3683
+ \`### RM-xxx\` you must provide:
3684
+
3685
+ - **Related domain** \u2014 a domain from \`## Capability Domains\` in \`capabilities.md\` (or, if genuinely
3686
+ new, prefix it \`[new candidate domain] <name>\` \u2014 do not invent domains silently).
3687
+ - **Related capabilities** \u2014 one or more existing/partial capabilities.
3688
+ - **Source signals** \u2014 at least one traceable reason: Capability Gap, Roadmap Candidate Signal, Risk,
3689
+ Open Question, Assumption, Deferred Decision, Tech Decision Candidate, ADR, Business Goal,
3690
+ Operational Need or Legacy Modernization Signal.
3691
+ - **Expected value**, **Risks**, **Dependencies**, and **Suggested Work Items** (candidates only).
3692
+
3693
+ Do **not** emit a candidate with no source signal. Keep initiatives at initiative granularity (small
3694
+ tasks go under **Suggested Work Items**, not as their own RM). Every roadmap ends with a global
3695
+ **## Not Now** section. For pre-ai/legacy, prioritize stabilization, security, data, operations,
3696
+ architectural decisions and business-blocking gaps before expansive features.
3697
+
3631
3698
  ## Constraints
3632
3699
 
3633
3700
  - Do not invent business priorities or business facts \u2014 mark them as assumptions when inferred.
@@ -3636,6 +3703,8 @@ Adapt priorities to the project state from the context pack:
3636
3703
  implementation-agent, and only after Work Items are materialized. Your handoff is
3637
3704
  \`kaddo create --from roadmap\` \u2192 work-item-agent.
3638
3705
  - Do not create the work items themselves; only propose candidates.
3706
+ - **Never create files under \`knowledge/delivery/work-items/\`** \u2014 materialization is
3707
+ \`kaddo create --from roadmap\`, not the roadmap-agent.
3639
3708
  - Make clear that initiatives and work items are **candidates**, not final decisions.
3640
3709
  - Mark any uncertain information as an assumption or open question.
3641
3710
  - Keep sequencing justified by dependencies and risk.
@@ -3669,31 +3738,38 @@ human review \u2014 not final commitments.
3669
3738
 
3670
3739
  ### RM-001: <Initiative Name>
3671
3740
 
3672
- **Goal:**
3741
+ **Status:** candidate <!-- candidate | selected | deferred | rejected -->
3673
3742
 
3674
- **Related capabilities:**
3743
+ **Priority:** high / medium / low
3675
3744
 
3676
- **Project area / domain:**
3745
+ **Suggested Knowledge Level:** K1 / K2 / K3 / K4
3677
3746
 
3678
- **Impact:** Low / Medium / High
3747
+ **Related domain:** <one of the ## Capability Domains from capabilities.md>
3679
3748
 
3680
- **Risk:** Low / Medium / High
3749
+ **Related capabilities:**
3750
+ - <existing or partial capability>
3681
3751
 
3682
- **Suggested Knowledge Level:** K1 / K2 / K3 / K4
3752
+ **Source signals:** <!-- REQUIRED: why this candidate exists (at least one) -->
3753
+ - Capability Gap: <...>
3754
+ - Roadmap Candidate Signal: <...>
3755
+ - Risk / Open Question / Assumption / Deferred Decision / Tech Decision Candidate / ADR / Business Goal: <...>
3683
3756
 
3684
- **Dependencies:**
3757
+ **Problem / opportunity:**
3685
3758
 
3686
- **Why this comes now:**
3759
+ **Expected value:**
3687
3760
 
3688
- **Candidate Work Items:**
3761
+ **Risks:**
3689
3762
 
3763
+ **Dependencies:**
3764
+
3765
+ **Suggested Work Items:**
3690
3766
  - WI-CANDIDATE-001: <candidate work item>
3691
3767
  - type:
3692
3768
  - suggested knowledge level:
3693
3769
  - expected value:
3694
3770
  - notes:
3695
3771
 
3696
- **Open questions:**
3772
+ **Not now:**
3697
3773
 
3698
3774
  ---
3699
3775
 
@@ -3988,6 +4064,23 @@ that it should be materialized first (\`kaddo adr\` + the adr-writing skill) and
3988
4064
  without surfacing it. -->
3989
4065
  \`\`\`
3990
4066
 
4067
+ ### Preserve roadmap metadata (VS-077 / VS-078)
4068
+
4069
+ When a Work Item comes from \`kaddo create --from roadmap\`, the front matter already carries the trace
4070
+ back to the roadmap. **Keep and refine \u2014 never delete** these fields:
4071
+
4072
+ - \`source_roadmap_initiative\` and \`source_work_item_candidate\` (the RM-xxx initiative and the
4073
+ WI-CANDIDATE-xxx it was materialized from)
4074
+ - \`related_domain\` and \`domains\` (keep them consistent \u2014 \`domains\` must not be empty when
4075
+ \`related_domain\` exists)
4076
+ - \`related_capabilities\` (a real list, one capability per item \u2014 never a single comma-joined string)
4077
+ - \`expected_value\`, \`risks\`, \`dependencies\`
4078
+ - \`source_signals\` (do **not** invent them \u2014 if absent, leave them absent)
4079
+ - \`decision_candidates\` and \`related_decisions\` (when the work depends on a technical decision)
4080
+
4081
+ Do not drop the trace back to the capability domain and source signals. If a Work Item depends on a
4082
+ tech decision candidate with no ADR yet, keep the warning surfaced in the body.
4083
+
3991
4084
  ## Where to Save the Result
3992
4085
 
3993
4086
  Save new output as a draft under \`knowledge/delivery/work-items/draft/\` unless a human
@@ -5208,6 +5301,88 @@ function installedAssetsSummary(dir) {
5208
5301
  return { version: KADDO_VERSION, agents: assetStatus(dir, "agent"), skills: assetStatus(dir, "skill") };
5209
5302
  }
5210
5303
 
5304
+ // ../cli/src/core/roadmap-quality.ts
5305
+ var ROADMAP_PATH = "knowledge/delivery/roadmap.md";
5306
+ var INITIATIVE_RE2 = /^#{2,4}\s+(RM-[\w.-]+)\s*[:\-–]?\s*(.*)$/;
5307
+ function fieldPresent(lines, label) {
5308
+ for (let i = 0; i < lines.length; i++) {
5309
+ const m = lines[i].match(new RegExp(`^\\s*[-*]?\\s*(?:\\*\\*)?\\s*(${label.source})\\s*(?:\\*\\*)?\\s*:\\s*(.*)$`, "i"));
5310
+ if (!m) continue;
5311
+ const inline = m[2].trim();
5312
+ if (inline) return true;
5313
+ for (let j = i + 1; j < lines.length; j++) {
5314
+ if (/^\s{2,}[-*]\s+\S/.test(lines[j])) return true;
5315
+ if (/^\S/.test(lines[j]) || /^#{2,4}\s/.test(lines[j])) break;
5316
+ if (/^\s*[-*]\s+\S/.test(lines[j]) && !/^\s{2,}/.test(lines[j])) break;
5317
+ }
5318
+ }
5319
+ return false;
5320
+ }
5321
+ function parseRoadmapCandidateQuality(md) {
5322
+ const lines = md.split(/\r?\n/);
5323
+ const items = [];
5324
+ let cur = null;
5325
+ const flush = () => {
5326
+ if (!cur) return;
5327
+ const hasRelatedDomain = fieldPresent(cur.lines, /related domain/);
5328
+ const hasRelatedCapability = fieldPresent(cur.lines, /related capabilit(?:y|ies)/);
5329
+ const hasSourceSignals = fieldPresent(cur.lines, /source signals?/);
5330
+ items.push({
5331
+ id: cur.id,
5332
+ title: cur.title,
5333
+ hasRelatedDomain,
5334
+ hasRelatedCapability,
5335
+ hasSourceSignals,
5336
+ grounded: hasRelatedDomain && hasRelatedCapability && hasSourceSignals
5337
+ });
5338
+ cur = null;
5339
+ };
5340
+ for (const line of lines) {
5341
+ const m = line.match(INITIATIVE_RE2);
5342
+ if (m) {
5343
+ flush();
5344
+ cur = { id: m[1], title: m[2].trim(), lines: [] };
5345
+ continue;
5346
+ }
5347
+ if (cur) cur.lines.push(line);
5348
+ }
5349
+ flush();
5350
+ return items;
5351
+ }
5352
+ function buildRoadmapQuality(dir) {
5353
+ let md = "";
5354
+ const p = join(dir, ROADMAP_PATH);
5355
+ if (exists(p)) {
5356
+ try {
5357
+ md = readFile(p);
5358
+ } catch {
5359
+ md = "";
5360
+ }
5361
+ }
5362
+ const items = parseRoadmapCandidateQuality(md);
5363
+ const count = (pred) => items.filter(pred).length;
5364
+ const grounded = count((i) => i.grounded);
5365
+ const initiatives = {
5366
+ total: items.length,
5367
+ grounded,
5368
+ with_related_domain: count((i) => i.hasRelatedDomain),
5369
+ with_related_capability: count((i) => i.hasRelatedCapability),
5370
+ with_source_signals: count((i) => i.hasSourceSignals),
5371
+ // Only "needs refinement" when there are initiatives and at least one isn't grounded.
5372
+ needs_refinement: items.length > 0 && grounded < items.length,
5373
+ items
5374
+ };
5375
+ const wiCandidates = md ? parseRoadmapCandidates(md) : [];
5376
+ const wcount = (pred) => wiCandidates.filter(pred).length;
5377
+ const work_item_candidates = {
5378
+ total: wiCandidates.length,
5379
+ with_source_initiative: wcount((c) => Boolean(c.initiative?.id || c.initiative?.title)),
5380
+ with_related_domain: wcount((c) => Boolean(c.domain)),
5381
+ with_related_capability: wcount((c) => Boolean(c.relatedCapabilities?.length))
5382
+ };
5383
+ return { initiatives, work_item_candidates };
5384
+ }
5385
+
5211
5386
  // ../cli/src/core/project-explain.ts
5212
5387
  var ARCH_DIR2 = "knowledge";
5213
5388
  function normalizeTitle(t) {
@@ -5432,7 +5607,8 @@ function buildProjectExplanation(dir) {
5432
5607
  legacyLocation: exists(join(dir, "knowledge/tech/architecture-notes.md")) || exists(join(dir, "knowledge/tech/decision-candidates.md"))
5433
5608
  }
5434
5609
  },
5435
- installedAssets: installedAssetsSummary(dir)
5610
+ installedAssets: installedAssetsSummary(dir),
5611
+ roadmapQuality: buildRoadmapQuality(dir)
5436
5612
  };
5437
5613
  }
5438
5614
  function stateLabel(state) {
@@ -5487,10 +5663,11 @@ function renderExplanationHuman(exp) {
5487
5663
  lines.push(`- Delivery: ${ls("Delivery")}`);
5488
5664
  lines.push(`- Agents: ${exp.knowledge.hasAgents ? "available" : "missing"}`);
5489
5665
  if (exp.roadmap.present) {
5490
- lines.push(`- Roadmap candidates: ${exp.roadmap.candidates}`);
5491
- lines.push(`- Materialized work items: ${exp.roadmap.materialized}`);
5492
- if (exp.roadmap.remaining > 0)
5493
- lines.push(`- Remaining candidates: ${exp.roadmap.remaining}`);
5666
+ lines.push(`- Roadmap initiatives: ${exp.roadmap.initiatives}`);
5667
+ lines.push(`- Work Item candidates: ${exp.roadmap.work_item_candidates}`);
5668
+ lines.push(`- Materialized Work Items: ${exp.roadmap.materialized_work_items}`);
5669
+ if (exp.roadmap.remaining_work_item_candidates > 0)
5670
+ lines.push(`- Remaining Work Item candidates: ${exp.roadmap.remaining_work_item_candidates}`);
5494
5671
  } else {
5495
5672
  lines.push(`- Work items: ${exp.workItems.total}`);
5496
5673
  }
@@ -5667,6 +5844,45 @@ function renderExplanationHuman(exp) {
5667
5844
  lines.push("Tech discovery files are in the legacy `knowledge/tech/` root. Suggested cleanup: run `kaddo tech organize`.");
5668
5845
  lines.push("");
5669
5846
  }
5847
+ if (exp.roadmap.present) {
5848
+ lines.push("## Roadmap Status");
5849
+ lines.push(`- Initiatives: ${exp.roadmap.initiatives}`);
5850
+ lines.push(`- Work Item candidates: ${exp.roadmap.work_item_candidates}`);
5851
+ lines.push(`- Materialized Work Items: ${exp.roadmap.materialized_work_items}`);
5852
+ lines.push(`- Remaining Work Item candidates: ${exp.roadmap.remaining_work_item_candidates}`);
5853
+ lines.push("");
5854
+ }
5855
+ const rq = exp.roadmapQuality;
5856
+ const rqi = rq.initiatives;
5857
+ const rqw = rq.work_item_candidates;
5858
+ if (rqi.total > 0 || rqw.total > 0) {
5859
+ lines.push("## Roadmap Quality");
5860
+ if (rqi.total > 0) {
5861
+ lines.push("Initiatives:");
5862
+ lines.push(`- Candidates evaluated: ${rqi.total}`);
5863
+ lines.push(`- Grounded: ${rqi.grounded}/${rqi.total}`);
5864
+ lines.push(`- With related domain: ${rqi.with_related_domain}/${rqi.total}`);
5865
+ lines.push(`- With related capability: ${rqi.with_related_capability}/${rqi.total}`);
5866
+ lines.push(`- With source signals: ${rqi.with_source_signals}/${rqi.total}`);
5867
+ }
5868
+ if (rqw.total > 0) {
5869
+ lines.push("");
5870
+ lines.push("Work Item Candidates:");
5871
+ lines.push(`- Candidates: ${rqw.total}`);
5872
+ lines.push(`- With source initiative: ${rqw.with_source_initiative}/${rqw.total}`);
5873
+ lines.push(`- With related domain: ${rqw.with_related_domain}/${rqw.total}`);
5874
+ lines.push(`- With related capability: ${rqw.with_related_capability}/${rqw.total}`);
5875
+ }
5876
+ if (rqi.needs_refinement) {
5877
+ lines.push("");
5878
+ lines.push("Roadmap quality: needs refinement.");
5879
+ lines.push("Suggested: use roadmap-agent to add domain / capability / source signals.");
5880
+ } else if (rqw.total > 0) {
5881
+ lines.push("");
5882
+ lines.push("Work Item candidate quality: good.");
5883
+ }
5884
+ lines.push("");
5885
+ }
5670
5886
  const ia = exp.installedAssets;
5671
5887
  const agentsInstalled = ia.agents.total - ia.agents.missing;
5672
5888
  const skillsInstalled = ia.skills.total - ia.skills.missing;
@@ -6892,6 +7108,32 @@ var RESOURCES = [
6892
7108
  }
6893
7109
  return [{ uri: "kaddo://installed-assets", text: JSON.stringify(installedAssetsSummary(root), null, 2), mimeType: "application/json" }];
6894
7110
  }
7111
+ },
7112
+ {
7113
+ uri: "kaddo://roadmap-quality",
7114
+ name: "Kaddo roadmap quality",
7115
+ description: "How well roadmap initiatives and Work Item candidates are grounded in capability domains, related capabilities and source signals (VS-077 / VS-077.1). Two levels: initiatives + work_item_candidates. Read-only.",
7116
+ mimeType: "application/json",
7117
+ read: (root) => {
7118
+ if (!hasKnowledge(root)) {
7119
+ return text("kaddo://roadmap-quality", "Knowledge repository not found. Run `kaddo bootstrap` first.", "text/plain");
7120
+ }
7121
+ return [{ uri: "kaddo://roadmap-quality", text: JSON.stringify(buildRoadmapQuality(root), null, 2), mimeType: "application/json" }];
7122
+ }
7123
+ },
7124
+ {
7125
+ uri: "kaddo://work-item-candidates",
7126
+ name: "Kaddo work item candidates",
7127
+ description: "Materializable Work Item candidates (WI-CANDIDATE-xxx) parsed from the roadmap, with their source initiative and inherited metadata (VS-078). Read-only.",
7128
+ mimeType: "application/json",
7129
+ read: (root) => {
7130
+ if (!hasKnowledge(root)) {
7131
+ return text("kaddo://work-item-candidates", "Knowledge repository not found. Run `kaddo bootstrap` first.", "text/plain");
7132
+ }
7133
+ const md = readText(root, "knowledge/delivery/roadmap.md");
7134
+ const candidates = md ? parseRoadmapCandidates(md) : [];
7135
+ return [{ uri: "kaddo://work-item-candidates", text: JSON.stringify({ candidates }, null, 2), mimeType: "application/json" }];
7136
+ }
6895
7137
  }
6896
7138
  ];
6897
7139
 
@@ -7220,6 +7462,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
7220
7462
  nextStepRecommendation,
7221
7463
  techDecisions,
7222
7464
  techKnowledge,
7465
+ roadmapQuality: buildRoadmapQuality(dir),
7223
7466
  installedAssets: (() => {
7224
7467
  const s = installedAssetsSummary(dir);
7225
7468
  const compact = (a) => ({ total: a.total, installed: a.total - a.missing, outdated: a.outdated, unknown_version: a.unknown_version, modified: a.modified });
@@ -7318,22 +7561,57 @@ function renderContextPack(pack) {
7318
7561
  }
7319
7562
  parts.push("## Current Knowledge\n");
7320
7563
  parts.push((knowledge.summary || "No project knowledge summary found yet.") + "\n");
7321
- parts.push("## Roadmap\n");
7564
+ parts.push("## Roadmap Status\n");
7322
7565
  if (pack.roadmap.present) {
7323
7566
  parts.push(
7324
7567
  [
7325
- `- Roadmap candidates: ${pack.roadmap.candidates}`,
7326
- `- Materialized work items: ${pack.roadmap.materialized}`,
7327
- `- Remaining candidates: ${pack.roadmap.remaining}`
7568
+ `- Initiatives: ${pack.roadmap.initiatives}`,
7569
+ `- Work Item candidates: ${pack.roadmap.work_item_candidates}`,
7570
+ `- Materialized Work Items: ${pack.roadmap.materialized_work_items}`,
7571
+ `- Remaining Work Item candidates: ${pack.roadmap.remaining_work_item_candidates}`
7328
7572
  ].join("\n") + "\n"
7329
7573
  );
7330
- if (pack.roadmap.remaining > 0) {
7574
+ if (pack.roadmap.remaining_work_item_candidates > 0) {
7331
7575
  parts.push(
7332
- "Candidates are not yet Work Items. Materialize them with `kaddo create --from roadmap`.\n"
7576
+ "Work Item candidates are not yet Work Items. Materialize them with `kaddo create --from roadmap`.\n"
7333
7577
  );
7334
7578
  }
7335
7579
  }
7336
7580
  parts.push((knowledge.roadmapSummary || "No roadmap baseline found.") + "\n");
7581
+ const rq = pack.roadmapQuality;
7582
+ const rqi = rq.initiatives;
7583
+ const rqw = rq.work_item_candidates;
7584
+ if (rqi.total > 0 || rqw.total > 0) {
7585
+ parts.push("## Roadmap Quality\n");
7586
+ if (rqi.total > 0) {
7587
+ parts.push(
7588
+ [
7589
+ "Initiatives:",
7590
+ `- Candidates evaluated: ${rqi.total}`,
7591
+ `- Grounded: ${rqi.grounded}/${rqi.total}`,
7592
+ `- With related domain: ${rqi.with_related_domain}/${rqi.total}`,
7593
+ `- With related capability: ${rqi.with_related_capability}/${rqi.total}`,
7594
+ `- With source signals: ${rqi.with_source_signals}/${rqi.total}`
7595
+ ].join("\n") + "\n"
7596
+ );
7597
+ }
7598
+ if (rqw.total > 0) {
7599
+ parts.push(
7600
+ [
7601
+ "Work Item Candidates:",
7602
+ `- Candidates: ${rqw.total}`,
7603
+ `- With source initiative: ${rqw.with_source_initiative}/${rqw.total}`,
7604
+ `- With related domain: ${rqw.with_related_domain}/${rqw.total}`,
7605
+ `- With related capability: ${rqw.with_related_capability}/${rqw.total}`
7606
+ ].join("\n") + "\n"
7607
+ );
7608
+ }
7609
+ if (rqi.needs_refinement) {
7610
+ parts.push(
7611
+ "Roadmap quality: needs refinement. Use the roadmap-agent to add domain / capability / source signals.\n"
7612
+ );
7613
+ }
7614
+ }
7337
7615
  parts.push("## Active Work Items\n");
7338
7616
  if (knowledge.workItems.length > 0) {
7339
7617
  const lines = knowledge.workItems.map((wi) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/mcp",
3
- "version": "3.42.0",
3
+ "version": "3.44.0",
4
4
  "description": "MCP server for Kaddo project knowledge: read-only resources/tools/prompts plus safe derived generation under .kaddo/.",
5
5
  "license": "MIT",
6
6
  "repository": {