@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.
- package/dist/index.js +128 -8
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) =>
|
|
2129
|
-
const
|
|
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
|
-
|
|
2229
|
-
|
|
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