@kaddo/mcp 3.23.0 → 3.23.1

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 +128 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2087,7 +2087,11 @@ function renderExplanationAgent(exp) {
2087
2087
  }
2088
2088
 
2089
2089
  // ../cli/src/core/impact-report.ts
2090
- var BROAD_GLOB = /^[^/*]+\/\*\*$/;
2090
+ function isBroadGlob(glob) {
2091
+ if (!glob.endsWith("/**")) return false;
2092
+ const prefix = glob.slice(0, -3);
2093
+ return prefix.length > 0 && prefix.split("/").length <= 2;
2094
+ }
2091
2095
  function levelFromRatio(r) {
2092
2096
  if (r >= 0.9) return "Very High";
2093
2097
  if (r >= 0.7) return "High";
@@ -2107,26 +2111,66 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
2107
2111
  let withLevel = 0;
2108
2112
  let withAcceptance = 0;
2109
2113
  let withDoD = 0;
2114
+ let withValidation = 0;
2110
2115
  let connectedRoadmap = 0;
2111
2116
  const globCounts = /* @__PURE__ */ new Map();
2117
+ const globOwners = /* @__PURE__ */ new Map();
2118
+ const gaps = {
2119
+ missing_source: [],
2120
+ missing_initiative: [],
2121
+ missing_code_ownership: [],
2122
+ missing_knowledge_level: [],
2123
+ missing_acceptance_criteria: [],
2124
+ missing_definition_of_done: [],
2125
+ missing_validation: [],
2126
+ broad_ownership_globs: [],
2127
+ ownership_overlaps: []
2128
+ };
2129
+ const gapItem = (wi, action) => ({
2130
+ id: wi.id || wi.title,
2131
+ title: wi.title,
2132
+ status: wi.lifecycle ?? wi.status,
2133
+ path: wi.relPath,
2134
+ suggested_action: action
2135
+ });
2112
2136
  for (const wi of wis) {
2137
+ const id = wi.id || wi.title;
2113
2138
  if (wi.codeGlobs.length > 0) withOwnership++;
2139
+ else gaps.missing_code_ownership.push(gapItem(wi, "Add `code:` globs to connect this Work Item to source paths (`kaddo owners suggest`)."));
2114
2140
  const hasSource = Boolean(wi.sourceId) || wi.source === "roadmap";
2115
2141
  if (hasSource) withSource++;
2142
+ else gaps.missing_source.push(gapItem(wi, "Add `source` or `source_id` if this Work Item came from the roadmap."));
2116
2143
  if (hasSource || wi.initiative) connectedRoadmap++;
2117
2144
  if (wi.initiative) withInitiative++;
2145
+ else gaps.missing_initiative.push(gapItem(wi, "Add `initiative` to connect this Work Item to a delivery initiative."));
2118
2146
  if (wi.knowledgeLevel) withLevel++;
2119
- for (const g2 of wi.codeGlobs) globCounts.set(g2, (globCounts.get(g2) ?? 0) + 1);
2147
+ else gaps.missing_knowledge_level.push(gapItem(wi, "Add `knowledge_level` (K0\u2013K4) to the front matter."));
2148
+ for (const g2 of wi.codeGlobs) {
2149
+ globCounts.set(g2, (globCounts.get(g2) ?? 0) + 1);
2150
+ globOwners.set(g2, [...globOwners.get(g2) ?? [], id]);
2151
+ if (isBroadGlob(g2)) {
2152
+ gaps.broad_ownership_globs.push({ id, title: wi.title, glob: g2, suggested_action: "Replace with specific files or narrower module paths." });
2153
+ }
2154
+ }
2120
2155
  try {
2121
2156
  const body = matter6(readFile(wi.filePath)).content;
2122
- if (hasSection(body, /acceptance/i)) withAcceptance++;
2123
- if (hasSection(body, /definition of done|^#{1,6}\s*done\b/i)) withDoD++;
2157
+ if (hasSection(body, /acceptance|criterios de aceptaci/i)) withAcceptance++;
2158
+ else gaps.missing_acceptance_criteria.push(gapItem(wi, "Add an `## Acceptance Criteria` section."));
2159
+ if (hasSection(body, /definition of done|^#{1,6}\s*dod\b|definici[oó]n de (terminado|hecho)/i)) withDoD++;
2160
+ else gaps.missing_definition_of_done.push(gapItem(wi, "Add a `## Definition of Done` section."));
2161
+ if (hasSection(body, /how to test|validation|validaci|c[oó]mo probarlo/i)) withValidation++;
2162
+ else gaps.missing_validation.push(gapItem(wi, "Add a `## How to test it` (validation) section."));
2124
2163
  } catch {
2125
2164
  }
2126
2165
  }
2127
2166
  const ownedCodePaths = [...globCounts.values()].reduce((a, b) => a + b, 0);
2128
- const broadGlobs = [...globCounts.keys()].filter((g2) => BROAD_GLOB.test(g2)).length;
2129
- const ownershipOverlaps = [...globCounts.values()].filter((n) => n > 1).length;
2167
+ const broadGlobs = [...globCounts.keys()].filter((g2) => isBroadGlob(g2)).length;
2168
+ for (const [glob, owners] of globOwners) {
2169
+ if (owners.length > 1) {
2170
+ gaps.ownership_overlaps.push({ code_path: glob, work_items: [...new Set(owners)], suggested_action: "Review whether the overlap is expected or should be narrowed." });
2171
+ }
2172
+ }
2173
+ const ownershipOverlaps = gaps.ownership_overlaps.length;
2130
2174
  let g;
2131
2175
  if (opts.scope) {
2132
2176
  const config = loadConfig(dir);
@@ -2209,6 +2253,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
2209
2253
  maintenance_readiness: levelFromRatio((coverageRatio(withOwnership) + (deliveryTraceable ? 1 : 0)) / 2)
2210
2254
  };
2211
2255
  let score = null;
2256
+ let score_breakdown = null;
2212
2257
  if (total > 0) {
2213
2258
  const healthPts = [layerStatus("Business"), layerStatus("Product"), layerStatus("Tech"), layerStatus("Delivery")].filter((s) => s !== "Missing").length / 4 * 20;
2214
2259
  const covAvg = knowledge_coverage.reduce((a, c) => a + coverageRatio(c.have), 0) / knowledge_coverage.length;
@@ -2220,13 +2265,34 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
2220
2265
  const graphPts = (g.available ? qualityMap[g.quality] ?? 0 : 0) * 15;
2221
2266
  const ctxPts = { Low: 0.25, Medium: 0.5, High: 0.8, "Very High": 1 }[ctxLevel] * 10;
2222
2267
  score = Math.round(healthPts + coveragePts + ownPts + tracePts + graphPts + ctxPts);
2268
+ score_breakdown = {
2269
+ knowledge_health: { points: Math.round(healthPts), max: 20 },
2270
+ knowledge_coverage: { points: Math.round(coveragePts), max: 20 },
2271
+ ownership_coverage: { points: Math.round(ownPts), max: 15 },
2272
+ traceability: { points: Math.round(tracePts), max: 20 },
2273
+ graph_quality: { points: Math.round(graphPts), max: 15 },
2274
+ context_readiness: { points: Math.round(ctxPts), max: 10 }
2275
+ };
2223
2276
  }
2277
+ const idsOf = (items) => items.map((i) => i.id);
2278
+ const groupedAction = (items, verb) => {
2279
+ if (items.length === 0) return null;
2280
+ const ids = idsOf(items);
2281
+ return ids.length <= 3 ? `${verb} ${ids.join(", ")}.` : `${verb} ${ids.length} Work Items: ${ids.slice(0, 3).join(", ")}, \u2026`;
2282
+ };
2224
2283
  const actions = [];
2225
2284
  if (!g.available) actions.push("Run `kaddo graph export --scope all` to inspect full traceability.");
2226
2285
  else if (g.scope === "active" && g.quality === "empty") actions.push("Run `kaddo graph export --scope all` to include completed Work Items.");
2227
2286
  if (exp.roadmap.remaining > 0) actions.push(`Materialize the ${exp.roadmap.remaining} remaining roadmap candidate(s) with \`kaddo create --from roadmap\`.`);
2228
- if (total > 0 && withOwnership < total) actions.push("Add code ownership to Work Items without `code:` globs (`kaddo owners suggest`).");
2229
- if (total > 0 && withInitiative < total) actions.push("Add an initiative to Work Items missing one.");
2287
+ for (const a of [
2288
+ groupedAction(gaps.missing_code_ownership, "Add `code:` ownership to"),
2289
+ groupedAction(gaps.missing_source, "Add `source`/`source_id` to"),
2290
+ groupedAction(gaps.missing_initiative, "Add an `initiative` to"),
2291
+ groupedAction(gaps.missing_acceptance_criteria, "Add Acceptance Criteria to"),
2292
+ groupedAction(gaps.missing_definition_of_done, "Add a Definition of Done to"),
2293
+ groupedAction(gaps.missing_validation, "Add a validation (How to test it) section to")
2294
+ ]) if (a) actions.push(a);
2295
+ if (gaps.broad_ownership_globs.length > 0) actions.push(`Narrow ${gaps.broad_ownership_globs.length} broad ownership glob(s) into specific paths.`);
2230
2296
  if (g.available && g.hints > 0) actions.push("Review graph hints (`kaddo://graph-hints` or `.kaddo/graph-hints.md`).");
2231
2297
  if (skills.length === 0) actions.push("Install reusable skills with `kaddo add skills`.");
2232
2298
  actions.push("Run `kaddo guard` before committing to catch knowledge drift.");
@@ -2278,7 +2344,9 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
2278
2344
  note: "Guard history is not persisted. Run `kaddo guard` to check drift; future versions may store runs for trend analysis."
2279
2345
  },
2280
2346
  impact_signals,
2347
+ actionable_gaps: gaps,
2281
2348
  score,
2349
+ score_breakdown,
2282
2350
  suggested_actions: actions
2283
2351
  };
2284
2352
  }
@@ -2367,6 +2435,58 @@ function renderImpactMarkdown(r) {
2367
2435
  L.push(`- AI context readiness: ${s.ai_context_readiness}`);
2368
2436
  L.push(`- Maintenance readiness: ${s.maintenance_readiness}`);
2369
2437
  L.push("");
2438
+ L.push("## Actionable Gaps", "");
2439
+ const gp = r.actionable_gaps;
2440
+ const gapSection = (title, items) => {
2441
+ if (items.length === 0) return;
2442
+ L.push(`### ${title}`, "");
2443
+ for (const it of items) {
2444
+ L.push(`- ${it.id} \u2014 ${it.title}`);
2445
+ L.push(` - Path: ${it.path}`);
2446
+ L.push(` - Suggested action: ${it.suggested_action}`);
2447
+ }
2448
+ L.push("");
2449
+ };
2450
+ gapSection("Work Items missing source", gp.missing_source);
2451
+ gapSection("Work Items missing initiative", gp.missing_initiative);
2452
+ gapSection("Work Items missing code ownership", gp.missing_code_ownership);
2453
+ gapSection("Work Items missing knowledge level", gp.missing_knowledge_level);
2454
+ gapSection("Work Items missing acceptance criteria", gp.missing_acceptance_criteria);
2455
+ gapSection("Work Items missing Definition of Done", gp.missing_definition_of_done);
2456
+ gapSection("Work Items missing validation", gp.missing_validation);
2457
+ if (gp.broad_ownership_globs.length > 0) {
2458
+ L.push("### Broad ownership globs", "");
2459
+ for (const b of gp.broad_ownership_globs) {
2460
+ L.push(`- ${b.id} \u2014 ${b.title}`);
2461
+ L.push(` - Glob: \`${b.glob}\``);
2462
+ L.push(` - Suggested action: ${b.suggested_action}`);
2463
+ }
2464
+ L.push("");
2465
+ }
2466
+ if (gp.ownership_overlaps.length > 0) {
2467
+ L.push("### Ownership overlaps", "");
2468
+ for (const o of gp.ownership_overlaps) {
2469
+ L.push(`- \`${o.code_path}\``);
2470
+ L.push(` - Owned by: ${o.work_items.join(", ")}`);
2471
+ L.push(` - Suggested action: ${o.suggested_action}`);
2472
+ }
2473
+ L.push("");
2474
+ }
2475
+ const anyGaps = gp.missing_source.length + gp.missing_initiative.length + gp.missing_code_ownership.length + gp.missing_knowledge_level.length + gp.missing_acceptance_criteria.length + gp.missing_definition_of_done.length + gp.missing_validation.length + gp.broad_ownership_globs.length + gp.ownership_overlaps.length;
2476
+ if (anyGaps === 0) {
2477
+ L.push("No actionable knowledge gaps detected. \u{1F389}", "");
2478
+ }
2479
+ if (r.score_breakdown) {
2480
+ L.push("## Score Breakdown", "");
2481
+ const b = r.score_breakdown;
2482
+ L.push(`- Knowledge Health: ${b.knowledge_health.points}/${b.knowledge_health.max}`);
2483
+ L.push(`- Knowledge Coverage: ${b.knowledge_coverage.points}/${b.knowledge_coverage.max}`);
2484
+ L.push(`- Ownership Coverage: ${b.ownership_coverage.points}/${b.ownership_coverage.max}`);
2485
+ L.push(`- Traceability: ${b.traceability.points}/${b.traceability.max}`);
2486
+ L.push(`- Graph Quality: ${b.graph_quality.points}/${b.graph_quality.max}`);
2487
+ L.push(`- Context Readiness: ${b.context_readiness.points}/${b.context_readiness.max}`);
2488
+ L.push("");
2489
+ }
2370
2490
  L.push("## Suggested Actions", "");
2371
2491
  r.suggested_actions.forEach((a, i) => L.push(`${i + 1}. ${a}`));
2372
2492
  L.push("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/mcp",
3
- "version": "3.23.0",
3
+ "version": "3.23.1",
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": {