@kaddo/mcp 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 (2) hide show
  1. package/dist/index.js +168 -44
  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
@@ -4015,13 +4064,22 @@ that it should be materialized first (\`kaddo adr\` + the adr-writing skill) and
4015
4064
  without surfacing it. -->
4016
4065
  \`\`\`
4017
4066
 
4018
- ### Preserve roadmap metadata (VS-077)
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)
4019
4080
 
4020
- When a Work Item comes from \`kaddo create --from roadmap\`, the front matter already carries
4021
- \`source_roadmap_candidate\`, \`related_domain\`, \`related_capability\` (+ \`related_capabilities\`),
4022
- \`knowledge_level\`, \`expected_value\`, \`risks\` and \`dependencies\`. **Keep and refine** this metadata \u2014
4023
- do not drop the trace back to the capability domain and source signals. Add \`related_decisions\` /
4024
- \`decision_candidates\` when the work depends on a technical decision.
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.
4025
4083
 
4026
4084
  ## Where to Save the Result
4027
4085
 
@@ -5304,16 +5362,25 @@ function buildRoadmapQuality(dir) {
5304
5362
  const items = parseRoadmapCandidateQuality(md);
5305
5363
  const count = (pred) => items.filter(pred).length;
5306
5364
  const grounded = count((i) => i.grounded);
5307
- return {
5308
- candidates: items.length,
5365
+ const initiatives = {
5366
+ total: items.length,
5309
5367
  grounded,
5310
5368
  with_related_domain: count((i) => i.hasRelatedDomain),
5311
5369
  with_related_capability: count((i) => i.hasRelatedCapability),
5312
5370
  with_source_signals: count((i) => i.hasSourceSignals),
5313
- // Only "needs refinement" when there are candidates and at least one isn't grounded.
5371
+ // Only "needs refinement" when there are initiatives and at least one isn't grounded.
5314
5372
  needs_refinement: items.length > 0 && grounded < items.length,
5315
5373
  items
5316
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 };
5317
5384
  }
5318
5385
 
5319
5386
  // ../cli/src/core/project-explain.ts
@@ -5596,10 +5663,11 @@ function renderExplanationHuman(exp) {
5596
5663
  lines.push(`- Delivery: ${ls("Delivery")}`);
5597
5664
  lines.push(`- Agents: ${exp.knowledge.hasAgents ? "available" : "missing"}`);
5598
5665
  if (exp.roadmap.present) {
5599
- lines.push(`- Roadmap candidates: ${exp.roadmap.candidates}`);
5600
- lines.push(`- Materialized work items: ${exp.roadmap.materialized}`);
5601
- if (exp.roadmap.remaining > 0)
5602
- 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}`);
5603
5671
  } else {
5604
5672
  lines.push(`- Work items: ${exp.workItems.total}`);
5605
5673
  }
@@ -5776,17 +5844,42 @@ function renderExplanationHuman(exp) {
5776
5844
  lines.push("Tech discovery files are in the legacy `knowledge/tech/` root. Suggested cleanup: run `kaddo tech organize`.");
5777
5845
  lines.push("");
5778
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
+ }
5779
5855
  const rq = exp.roadmapQuality;
5780
- if (rq.candidates > 0) {
5856
+ const rqi = rq.initiatives;
5857
+ const rqw = rq.work_item_candidates;
5858
+ if (rqi.total > 0 || rqw.total > 0) {
5781
5859
  lines.push("## Roadmap Quality");
5782
- lines.push(`- Candidates: ${rq.candidates}`);
5783
- lines.push(`- Grounded: ${rq.grounded}/${rq.candidates}`);
5784
- lines.push(`- With related domain: ${rq.with_related_domain}/${rq.candidates}`);
5785
- lines.push(`- With related capability: ${rq.with_related_capability}/${rq.candidates}`);
5786
- lines.push(`- With source signals: ${rq.with_source_signals}/${rq.candidates}`);
5787
- if (rq.needs_refinement) {
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) {
5788
5869
  lines.push("");
5789
- lines.push("Roadmap quality: needs refinement. Suggested: use roadmap-agent to add domain / capability / source signals.");
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.");
5790
5883
  }
5791
5884
  lines.push("");
5792
5885
  }
@@ -7019,7 +7112,7 @@ var RESOURCES = [
7019
7112
  {
7020
7113
  uri: "kaddo://roadmap-quality",
7021
7114
  name: "Kaddo roadmap quality",
7022
- description: "How well roadmap candidates are grounded in capability domains, related capabilities and source signals (VS-077). Read-only.",
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.",
7023
7116
  mimeType: "application/json",
7024
7117
  read: (root) => {
7025
7118
  if (!hasKnowledge(root)) {
@@ -7027,6 +7120,20 @@ var RESOURCES = [
7027
7120
  }
7028
7121
  return [{ uri: "kaddo://roadmap-quality", text: JSON.stringify(buildRoadmapQuality(root), null, 2), mimeType: "application/json" }];
7029
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
+ }
7030
7137
  }
7031
7138
  ];
7032
7139
 
@@ -7454,35 +7561,52 @@ function renderContextPack(pack) {
7454
7561
  }
7455
7562
  parts.push("## Current Knowledge\n");
7456
7563
  parts.push((knowledge.summary || "No project knowledge summary found yet.") + "\n");
7457
- parts.push("## Roadmap\n");
7564
+ parts.push("## Roadmap Status\n");
7458
7565
  if (pack.roadmap.present) {
7459
7566
  parts.push(
7460
7567
  [
7461
- `- Roadmap candidates: ${pack.roadmap.candidates}`,
7462
- `- Materialized work items: ${pack.roadmap.materialized}`,
7463
- `- 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}`
7464
7572
  ].join("\n") + "\n"
7465
7573
  );
7466
- if (pack.roadmap.remaining > 0) {
7574
+ if (pack.roadmap.remaining_work_item_candidates > 0) {
7467
7575
  parts.push(
7468
- "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"
7469
7577
  );
7470
7578
  }
7471
7579
  }
7472
7580
  parts.push((knowledge.roadmapSummary || "No roadmap baseline found.") + "\n");
7473
7581
  const rq = pack.roadmapQuality;
7474
- if (rq.candidates > 0) {
7582
+ const rqi = rq.initiatives;
7583
+ const rqw = rq.work_item_candidates;
7584
+ if (rqi.total > 0 || rqw.total > 0) {
7475
7585
  parts.push("## Roadmap Quality\n");
7476
- parts.push(
7477
- [
7478
- `- Candidates: ${rq.candidates}`,
7479
- `- Grounded: ${rq.grounded}/${rq.candidates}`,
7480
- `- With related domain: ${rq.with_related_domain}/${rq.candidates}`,
7481
- `- With related capability: ${rq.with_related_capability}/${rq.candidates}`,
7482
- `- With source signals: ${rq.with_source_signals}/${rq.candidates}`
7483
- ].join("\n") + "\n"
7484
- );
7485
- if (rq.needs_refinement) {
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) {
7486
7610
  parts.push(
7487
7611
  "Roadmap quality: needs refinement. Use the roadmap-agent to add domain / capability / source signals.\n"
7488
7612
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/mcp",
3
- "version": "3.43.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": {