@kaddo/mcp 3.36.0 → 3.37.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 +263 -89
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -502,14 +502,23 @@ function discoverWorkItems(dir) {
502
502
  function layer(layers, name) {
503
503
  return layers.find((l) => l.layer === name)?.status ?? "Missing";
504
504
  }
505
+ var NOT_READY_LAYER = ["Missing", "Placeholder", "Weak"];
505
506
  function baseComplete(layers) {
506
507
  return layer(layers, "Business") !== "Missing" && layer(layers, "Product") !== "Missing" && layer(layers, "Tech") !== "Missing";
507
508
  }
509
+ function baseUseful(layers) {
510
+ return ["Business", "Product", "Tech"].every((n) => !NOT_READY_LAYER.includes(layer(layers, n)));
511
+ }
512
+ function roadmapHasCandidates(roadmap) {
513
+ return roadmap.candidates > 0 || roadmap.remaining > 0;
514
+ }
508
515
  function determinePhase(input) {
509
516
  const { roadmap, workItems } = input;
510
517
  const active = workItems.byState.draft + workItems.byState.ready + workItems.byState["in-progress"] + workItems.byState.blocked;
511
518
  if (!baseComplete(input.layers)) return "Discovery";
519
+ if (!baseUseful(input.layers)) return "Knowledge Refinement";
512
520
  if (!roadmap.present) return "Planning";
521
+ if (!roadmapHasCandidates(roadmap) && workItems.total === 0) return "Planning";
513
522
  if (workItems.total === 0) return "Delivery Preparation";
514
523
  if (active > 0) return "Active Delivery";
515
524
  return "Maintenance";
@@ -539,6 +548,14 @@ function firstMissingLayerAgent(layers) {
539
548
  return { agent: "capability-agent", step: "Use capability-agent to create knowledge/product/capabilities.md" };
540
549
  return { agent: "architecture-agent", step: "Use architecture-agent to create knowledge/tech/current-state.md" };
541
550
  }
551
+ function firstUnrefinedLayerAgent(layers) {
552
+ const isThin = (n) => NOT_READY_LAYER.includes(layer(layers, n));
553
+ if (isThin("Business"))
554
+ return { agent: "business-agent", step: "Use business-agent to complete knowledge/business/business.md" };
555
+ if (isThin("Product"))
556
+ return { agent: "capability-agent", step: "Use capability-agent to complete knowledge/product/capabilities.md" };
557
+ return { agent: "architecture-agent", step: "Use architecture-agent to complete knowledge/tech/current-state.md" };
558
+ }
542
559
  function assessPhase(input) {
543
560
  const phase = determinePhase(input);
544
561
  const reasons = buildReasons(input);
@@ -556,6 +573,17 @@ function assessPhase(input) {
556
573
  llmInstructions = [`Use the ${m.agent} to fill the missing base knowledge.`, "Do not write code."];
557
574
  break;
558
575
  }
576
+ case "Knowledge Refinement": {
577
+ const m = firstUnrefinedLayerAgent(input.layers);
578
+ recommendedAgents = [m.agent];
579
+ nextStep = m.step;
580
+ llmInstructions = [
581
+ "The baseline files exist but still look like bootstrap placeholders.",
582
+ `Use the ${m.agent} to replace the placeholders with real, project-specific knowledge.`,
583
+ "Do not write code."
584
+ ];
585
+ break;
586
+ }
559
587
  case "Planning": {
560
588
  recommendedAgents = ["roadmap-agent"];
561
589
  nextStep = "Use roadmap-agent to create knowledge/delivery/roadmap.md";
@@ -1467,9 +1495,97 @@ function statusFor(layer2, a) {
1467
1495
  return "Missing";
1468
1496
  }
1469
1497
 
1498
+ // ../cli/src/core/artifact-quality.ts
1499
+ function isPlaceholderLine(line) {
1500
+ const t = line.trim();
1501
+ if (/^_.+_$/.test(t)) return true;
1502
+ if (/^[-*]\s+(\[[^\]]+\]\s*)?_.+_$/.test(t)) return true;
1503
+ if (/^[-*]\s+\[[^\]]+\]\s*$/.test(t)) return true;
1504
+ if (/^_(?:Describe|List|Document|What|Which|Who|Use|To be defined|No production code)\b/i.test(t)) return true;
1505
+ return false;
1506
+ }
1507
+ function isStructuralLine(line, inFrontMatter) {
1508
+ const t = line.trim();
1509
+ if (inFrontMatter) return true;
1510
+ if (t === "") return true;
1511
+ if (/^#{1,6}\s/.test(t)) return true;
1512
+ if (/^<!--/.test(t)) return true;
1513
+ if (/^>/.test(t)) return true;
1514
+ return false;
1515
+ }
1516
+ function analyzeContent(md) {
1517
+ const lines = md.split(/\r?\n/);
1518
+ let inFrontMatter = false;
1519
+ let seenFmFence = 0;
1520
+ let currentSectionHasUseful = false;
1521
+ const sectionsWithUseful = /* @__PURE__ */ new Set();
1522
+ let sectionIndex = 0;
1523
+ const contentLines = [];
1524
+ const usefulLines = [];
1525
+ for (const raw of lines) {
1526
+ const t = raw.trim();
1527
+ if (t === "---" && seenFmFence < 2) {
1528
+ seenFmFence += 1;
1529
+ inFrontMatter = seenFmFence === 1;
1530
+ continue;
1531
+ }
1532
+ if (seenFmFence === 1) continue;
1533
+ if (/^#{1,6}\s/.test(t)) {
1534
+ sectionIndex += 1;
1535
+ currentSectionHasUseful = false;
1536
+ continue;
1537
+ }
1538
+ if (isStructuralLine(raw, false)) continue;
1539
+ contentLines.push(t);
1540
+ if (!isPlaceholderLine(raw)) {
1541
+ usefulLines.push(t);
1542
+ if (!currentSectionHasUseful) {
1543
+ currentSectionHasUseful = true;
1544
+ sectionsWithUseful.add(sectionIndex);
1545
+ }
1546
+ }
1547
+ }
1548
+ if (usefulLines.length === 0) return "placeholder";
1549
+ const usefulWordCount = usefulLines.join(" ").split(/\s+/).filter(Boolean).length;
1550
+ const placeholderRatio = contentLines.length > 0 ? (contentLines.length - usefulLines.length) / contentLines.length : 1;
1551
+ if (usefulWordCount >= 80 && placeholderRatio < 0.25 && sectionsWithUseful.size >= 2) return "useful";
1552
+ return "weak";
1553
+ }
1554
+ function analyzeKnowledgeArtifact(dir, rel) {
1555
+ const p = join(dir, rel);
1556
+ if (!exists(p)) return "missing";
1557
+ try {
1558
+ return analyzeContent(readFile(p));
1559
+ } catch {
1560
+ return "missing";
1561
+ }
1562
+ }
1563
+
1470
1564
  // ../cli/src/core/layers.ts
1565
+ var LAYER_BASELINE = {
1566
+ Business: ["knowledge/business/business.md"],
1567
+ Product: ["knowledge/product/product.md", "knowledge/product/capabilities.md"],
1568
+ Tech: ["knowledge/tech/codebase.md", "knowledge/tech/current-state.md"],
1569
+ Delivery: ["knowledge/delivery/roadmap.md"]
1570
+ };
1571
+ function layerQuality(dir, layer2) {
1572
+ const order = ["missing", "placeholder", "weak", "useful"];
1573
+ let worst = null;
1574
+ for (const rel of LAYER_BASELINE[layer2]) {
1575
+ const q = analyzeKnowledgeArtifact(dir, rel);
1576
+ if (q === "missing") continue;
1577
+ if (worst === null || order.indexOf(q) < order.indexOf(worst)) worst = q;
1578
+ }
1579
+ return worst;
1580
+ }
1471
1581
  function knowledgeLayers(dir) {
1472
- return discoverLayers(dir);
1582
+ return discoverLayers(dir).map((l) => {
1583
+ if (l.status !== "Consolidated" && l.status !== "Structured") return l;
1584
+ const q = layerQuality(dir, l.layer);
1585
+ if (q === "placeholder") return { ...l, status: "Placeholder" };
1586
+ if (q === "weak") return { ...l, status: "Weak" };
1587
+ return l;
1588
+ });
1473
1589
  }
1474
1590
  function renderLayersMarkdown(layers) {
1475
1591
  const lines = [];
@@ -2102,26 +2218,7 @@ function buildAdapterStatuses(dir) {
2102
2218
  });
2103
2219
  }
2104
2220
 
2105
- // ../cli/src/core/readiness.ts
2106
- var KNOWLEDGE_FILES = [
2107
- { key: "current_state", path: "knowledge/tech/current-state.md", label: "knowledge/tech/current-state.md" },
2108
- { key: "codebase", path: "knowledge/tech/codebase.md", label: "knowledge/tech/codebase.md" },
2109
- { key: "capabilities", path: "knowledge/product/capabilities.md", label: "knowledge/product/capabilities.md" },
2110
- { key: "product", path: "knowledge/product/product.md", label: "knowledge/product/product.md" },
2111
- { key: "business", path: "knowledge/business/business.md", label: "knowledge/business/business.md" }
2112
- ];
2113
- function knowledgePresence(dir, rel) {
2114
- const p = join(dir, rel);
2115
- if (!exists(p)) return "missing";
2116
- let md;
2117
- try {
2118
- md = readFile(p);
2119
- } catch {
2120
- return "missing";
2121
- }
2122
- const body = md.replace(/^---[\s\S]*?---/m, "").split(/\r?\n/).filter((l) => !/^\s*#{1,6}\s/.test(l) && !/^\s*$/.test(l) && !/^\s*<!--/.test(l)).join(" ").trim();
2123
- return body.length >= 40 ? "present" : "weak";
2124
- }
2221
+ // ../cli/src/core/next-step.ts
2125
2222
  function roadmapSignal(dir) {
2126
2223
  const p = join(dir, "knowledge/delivery/roadmap.md");
2127
2224
  if (!exists(p)) return "missing";
@@ -2152,8 +2249,91 @@ function installedAdapters(dir) {
2152
2249
  (s) => s.state === "injected" || s.state === "legacy-injected" || s.state === "full-generated" && (s.originAdapter === s.id || s.originAdapter === null)
2153
2250
  ).map((s) => s.id);
2154
2251
  }
2252
+ var B = "knowledge/business/business.md";
2253
+ var P = "knowledge/product/product.md";
2254
+ var CAP = "knowledge/product/capabilities.md";
2255
+ var CS = "knowledge/tech/current-state.md";
2256
+ var CB = "knowledge/tech/codebase.md";
2257
+ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
2258
+ const config = loadConfig(dir);
2259
+ if (!config) {
2260
+ return { id: "init", phase: "Setup", label: "Run `kaddo init` to initialize Kaddo.", command: "kaddo init", reason: "Kaddo is not initialized in this project." };
2261
+ }
2262
+ const state = config.project.state;
2263
+ const q = (rel) => analyzeKnowledgeArtifact(dir, rel);
2264
+ const qBusiness = q(B), qProduct = q(P), qCap = q(CAP), qCurrentState = q(CS), qCodebase = q(CB);
2265
+ if (qBusiness === "missing" || qProduct === "missing") {
2266
+ return { id: "bootstrap", phase: "Setup", label: "Run `kaddo bootstrap` to create the project knowledge baseline.", command: "kaddo bootstrap", reason: "The knowledge baseline is incomplete." };
2267
+ }
2268
+ const ctx = buildCodexAdapterContext(dir);
2269
+ if (!ctx.hasAgents) {
2270
+ return { id: "add-agents", phase: "Setup", label: "Run `kaddo add agents` to install the Kaddo agent prompt packs.", command: "kaddo add agents", reason: "No Kaddo agents are installed yet." };
2271
+ }
2272
+ if (!ctx.hasSkills) {
2273
+ return { id: "add-skills", phase: "Setup", label: "Run `kaddo add skills` to install reusable Kaddo skills.", command: "kaddo add skills", reason: "No Kaddo skills are installed yet." };
2274
+ }
2275
+ if ((state === "pre-ai" || state === "legacy") && !exists(join(dir, ".kaddo", "scan.json"))) {
2276
+ return { id: "scan", phase: "Discovery", label: "Run `kaddo scan` to capture deterministic signals from the existing code.", command: "kaddo scan", reason: "No scan baseline exists for this existing project yet." };
2277
+ }
2278
+ if (!exists(join(dir, ".kaddo", "context-pack.md"))) {
2279
+ return { id: "context", phase: "Discovery", label: "Run `kaddo context` to prepare the LLM context pack.", command: "kaddo context", reason: "No context pack has been generated yet." };
2280
+ }
2281
+ if (!exists(join(dir, ".kaddo", "understand.md"))) {
2282
+ return { id: "understand", phase: "Discovery", label: "Run `kaddo understand` to summarize the project context.", command: "kaddo understand", reason: "No understand handoff has been generated yet." };
2283
+ }
2284
+ const refine = (id, agent, target, quality) => ({
2285
+ id,
2286
+ phase: "Knowledge Refinement",
2287
+ label: `Use ${agent} to complete \`${target}\`.`,
2288
+ agent,
2289
+ target,
2290
+ reason: `${target} is ${quality === "missing" ? "missing" : "still a bootstrap placeholder or too thin"}.`,
2291
+ instructions: [
2292
+ "The baseline file exists but does not yet hold real, project-specific knowledge.",
2293
+ `Use the ${agent} to replace the placeholders with real knowledge.`,
2294
+ "Do not write code."
2295
+ ]
2296
+ });
2297
+ if (qBusiness !== "useful") return refine("refine-business", "business-agent", B, qBusiness);
2298
+ if (qProduct !== "useful" || qCap !== "useful") {
2299
+ const target = qCap !== "useful" ? CAP : P;
2300
+ return refine("refine-product", "capability-agent", target, qCap !== "useful" ? qCap : qProduct);
2301
+ }
2302
+ if (qCurrentState !== "useful") return refine("refine-current-state", "architecture-agent", CS, qCurrentState);
2303
+ if (qCodebase !== "useful") {
2304
+ const agent = ctx.agents.includes("codebase-agent") ? "codebase-agent" : "architecture-agent";
2305
+ return refine("refine-codebase", agent, CB, qCodebase);
2306
+ }
2307
+ const oq = buildOpenQuestionsReport(dir, now);
2308
+ if (oq.summary.blocking_open > 0) {
2309
+ return { id: "questions", phase: "Knowledge Refinement", label: "Resolve, assume or defer the blocking open questions (`kaddo questions`).", command: "kaddo questions", reason: `${oq.summary.blocking_open} blocking open question(s) remain.` };
2310
+ }
2311
+ const roadmap = roadmapSignal(dir);
2312
+ if (roadmap !== "has-candidates") {
2313
+ return { id: "roadmap", phase: "Planning", label: "Use roadmap-agent to define roadmap candidates (`kaddo roadmap`).", command: "kaddo roadmap", agent: "roadmap-agent", target: "knowledge/delivery/roadmap.md", reason: "The roadmap has no candidates yet." };
2314
+ }
2315
+ const wi = workItemsSignal(dir);
2316
+ if (wi === "none" || wi === "none-ready") {
2317
+ return { id: "create-work-item", phase: "Delivery Preparation", label: "Run `kaddo create --from roadmap` to materialize the first Work Item.", command: "kaddo create --from roadmap", reason: "The roadmap has candidates but no Work Item is ready." };
2318
+ }
2319
+ const adapters = installedAdapters(dir);
2320
+ if (adapters.length === 0) {
2321
+ return { id: "install-adapter", phase: "Active Delivery", label: "Run `kaddo adapters list` and install the adapter for your preferred agent.", command: "kaddo adapters list", reason: "A Work Item is ready but no adapter is installed." };
2322
+ }
2323
+ return { id: "implement", phase: "Active Delivery", label: "Implement the ready Work Item and run `kaddo guard`.", reason: "A Work Item is ready and an adapter is installed." };
2324
+ }
2325
+
2326
+ // ../cli/src/core/readiness.ts
2327
+ var KNOWLEDGE_FILES = [
2328
+ { key: "current_state", path: "knowledge/tech/current-state.md", label: "knowledge/tech/current-state.md", agent: "architecture-agent" },
2329
+ { key: "codebase", path: "knowledge/tech/codebase.md", label: "knowledge/tech/codebase.md", agent: "codebase-agent" },
2330
+ { key: "capabilities", path: "knowledge/product/capabilities.md", label: "knowledge/product/capabilities.md", agent: "capability-agent" },
2331
+ { key: "product", path: "knowledge/product/product.md", label: "knowledge/product/product.md", agent: "product-agent" },
2332
+ { key: "business", path: "knowledge/business/business.md", label: "knowledge/business/business.md", agent: "business-agent" }
2333
+ ];
2155
2334
  function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
2156
2335
  const config = loadConfig(dir);
2336
+ const rec = resolveNextStep(dir, now);
2157
2337
  const stub = (overall2, label, command) => ({
2158
2338
  project_name: config?.project.name ?? "unknown",
2159
2339
  project_type: config?.project.state ?? "unknown",
@@ -2177,14 +2357,15 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
2177
2357
  resolved_questions: 0,
2178
2358
  deferred_questions: 0
2179
2359
  },
2180
- recommended_next_step: { label, ...command ? { command } : {} }
2360
+ recommended_next_step: { label, ...command ? { command } : {} },
2361
+ nextStepRecommendation: rec
2181
2362
  });
2182
- if (!config) return stub("not-initialized", "Run `kaddo init` to initialize Kaddo.", "kaddo init");
2363
+ if (!config) return stub("not-initialized", rec.label, rec.command);
2183
2364
  if (config.project.state === "new") return stub("not-applicable", "This project uses the standard new-project Kaddo flow.");
2184
2365
  if (config.project.state === "legacy") return stub("legacy-project", "Use the legacy project flow.");
2185
2366
  const scan = exists(join(dir, ".kaddo", "scan.json")) ? "available" : "missing";
2186
2367
  const understand = exists(join(dir, ".kaddo", "understand.md")) ? "available" : "missing";
2187
- const presence = Object.fromEntries(KNOWLEDGE_FILES.map((f) => [f.key, knowledgePresence(dir, f.path)]));
2368
+ const presence = Object.fromEntries(KNOWLEDGE_FILES.map((f) => [f.key, analyzeKnowledgeArtifact(dir, f.path)]));
2188
2369
  const ctx = buildCodexAdapterContext(dir);
2189
2370
  const agents = ctx.hasAgents ? "present" : "missing";
2190
2371
  const skills = ctx.hasSkills ? "present" : "missing";
@@ -2212,41 +2393,26 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
2212
2393
  resolved_questions: oq.summary.resolution.resolved,
2213
2394
  deferred_questions: oq.summary.resolution.deferred
2214
2395
  };
2396
+ const firstWeak = KNOWLEDGE_FILES.find((f) => presence[f.key] !== "useful");
2215
2397
  let overall;
2216
- let next;
2217
- const firstWeak = KNOWLEDGE_FILES.find((f) => presence[f.key] !== "present");
2218
- if (scan === "missing") {
2219
- overall = "initialized";
2220
- next = { label: "Run `kaddo scan` to capture deterministic signals from the existing code.", command: "kaddo scan" };
2221
- } else if (bootstrapBaseline === "incomplete") {
2222
- overall = "bootstrap-incomplete";
2223
- next = { label: "Run `kaddo bootstrap` to create the project knowledge baseline.", command: "kaddo bootstrap" };
2224
- } else if (agents === "missing") {
2225
- overall = "agents-missing";
2226
- next = { label: "Run `kaddo add agents` to install the Kaddo agent prompt packs.", command: "kaddo add agents" };
2227
- } else if (skills === "missing") {
2228
- overall = "skills-missing";
2229
- next = { label: "Run `kaddo add skills` to install reusable Kaddo skills.", command: "kaddo add skills" };
2230
- } else if (understand === "missing") {
2231
- overall = "scanned";
2232
- next = { label: "Run `kaddo understand` to summarize the project context.", command: "kaddo understand" };
2233
- } else if (firstWeak) {
2234
- overall = "knowledge-incomplete";
2235
- next = { label: `Complete \`${firstWeak.label}\` (it is ${presence[firstWeak.key]}).` };
2236
- } else if (oq.summary.blocking_open > 0) {
2237
- overall = "needs-decisions";
2238
- next = { label: "Resolve, assume or defer the blocking open questions (`kaddo questions`).", command: "kaddo questions" };
2239
- } else if (roadmap !== "has-candidates") {
2240
- overall = "ready-for-roadmap";
2241
- next = { label: "Run `kaddo roadmap` to draft the roadmap from the current knowledge.", command: "kaddo roadmap" };
2242
- } else if (work_items === "none" || work_items === "none-ready") {
2243
- overall = "ready-for-work-item";
2244
- next = { label: "Run `kaddo create --from roadmap` to materialize the first Work Item.", command: "kaddo create --from roadmap" };
2245
- } else {
2246
- overall = "ready-for-implementation";
2247
- next = adapters.length > 0 ? { label: "Implement the ready Work Item and run `kaddo guard`." } : { label: "Run `kaddo adapters list` and install the adapter for your preferred agent.", command: "kaddo adapters list" };
2248
- }
2249
- return { project_name: config.project.name, project_type: config.project.state, overall, signals, recommended_next_step: next };
2398
+ if (scan === "missing") overall = "initialized";
2399
+ else if (bootstrapBaseline === "incomplete") overall = "bootstrap-incomplete";
2400
+ else if (agents === "missing") overall = "agents-missing";
2401
+ else if (skills === "missing") overall = "skills-missing";
2402
+ else if (understand === "missing") overall = "scanned";
2403
+ else if (firstWeak) overall = "knowledge-incomplete";
2404
+ else if (oq.summary.blocking_open > 0) overall = "needs-decisions";
2405
+ else if (roadmap !== "has-candidates") overall = "ready-for-roadmap";
2406
+ else if (work_items === "none" || work_items === "none-ready") overall = "ready-for-work-item";
2407
+ else overall = "ready-for-implementation";
2408
+ return {
2409
+ project_name: config.project.name,
2410
+ project_type: config.project.state,
2411
+ overall,
2412
+ signals,
2413
+ recommended_next_step: { label: rec.label, ...rec.command ? { command: rec.command } : {} },
2414
+ nextStepRecommendation: rec
2415
+ };
2250
2416
  }
2251
2417
 
2252
2418
  // ../cli/src/core/project-explain.ts
@@ -2438,6 +2604,7 @@ function buildProjectExplanation(dir) {
2438
2604
  "Run `kaddo owners suggest` for Work Items without code ownership."
2439
2605
  );
2440
2606
  }
2607
+ const readiness = buildReadinessReport(dir);
2441
2608
  return {
2442
2609
  project,
2443
2610
  stack,
@@ -2458,7 +2625,8 @@ function buildProjectExplanation(dir) {
2458
2625
  mappedModules,
2459
2626
  missingKnowledge,
2460
2627
  suggestedNextSteps,
2461
- readiness: buildReadinessReport(dir)
2628
+ readiness,
2629
+ nextStepRecommendation: readiness.nextStepRecommendation
2462
2630
  };
2463
2631
  }
2464
2632
  function stateLabel(state) {
@@ -2639,7 +2807,7 @@ function renderExplanationHuman(exp) {
2639
2807
  lines.push("- Reason:");
2640
2808
  for (const r2 of assessment.reasons) lines.push(` - ${r2}`);
2641
2809
  }
2642
- if (assessment.nextStep) lines.push(`- Next step: ${assessment.nextStep}`);
2810
+ lines.push(`- Next step: ${exp.readiness.recommended_next_step.label}`);
2643
2811
  lines.push("");
2644
2812
  if (exp.suggestedNextSteps.length > 0) {
2645
2813
  lines.push("## Suggested Next Steps");
@@ -2656,9 +2824,11 @@ function renderExplanationHuman(exp) {
2656
2824
  lines.push(`- understand: ${s.understand}`);
2657
2825
  lines.push(`- agents: ${s.agents}`);
2658
2826
  lines.push(`- skills: ${s.skills}`);
2827
+ lines.push(`- business: ${s.business}`);
2828
+ lines.push(`- product: ${s.product}`);
2829
+ lines.push(`- capabilities: ${s.capabilities}`);
2659
2830
  lines.push(`- current-state: ${s.current_state}`);
2660
2831
  lines.push(`- codebase: ${s.codebase}`);
2661
- lines.push(`- capabilities: ${s.capabilities}`);
2662
2832
  lines.push(`- roadmap: ${s.roadmap}`);
2663
2833
  lines.push(`- work-items: ${s.work_items}`);
2664
2834
  lines.push(`- adapters: ${s.adapters.length > 0 ? s.adapters.join(", ") + " installed" : "none installed"}`);
@@ -4013,28 +4183,6 @@ function recommendedAgentsForState(state) {
4013
4183
  return ["capability-agent", "architecture-agent", "roadmap-agent"];
4014
4184
  }
4015
4185
  }
4016
- function nextStepsForState(state) {
4017
- switch (state) {
4018
- case "new":
4019
- return [
4020
- "Use roadmap-agent to shape an initial roadmap.",
4021
- "Use architecture-agent to outline the intended architecture."
4022
- ];
4023
- case "legacy":
4024
- return [
4025
- "Use legacy-agent to surface risks and unknowns before changing code.",
4026
- "Use architecture-agent to reconstruct the current architecture.",
4027
- "Use capability-agent to map existing capabilities."
4028
- ];
4029
- case "pre-ai":
4030
- default:
4031
- return [
4032
- "Use capability-agent to extract system capabilities.",
4033
- "Use architecture-agent to reconstruct the current architecture.",
4034
- "Use roadmap-agent to propose roadmap candidates."
4035
- ];
4036
- }
4037
- }
4038
4186
  var LLM_INSTRUCTIONS = [
4039
4187
  "Use this context pack as the project baseline.",
4040
4188
  "Do not write code yet.",
@@ -4106,6 +4254,24 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
4106
4254
  }
4107
4255
  const mappedModules = loadMappedModules(dir);
4108
4256
  const layers = knowledgeLayers(dir);
4257
+ const qa = (rel) => analyzeKnowledgeArtifact(dir, rel);
4258
+ const qBusiness = qa("knowledge/business/business.md");
4259
+ const qProduct = qa("knowledge/product/product.md");
4260
+ const qCapabilities = qa("knowledge/product/capabilities.md");
4261
+ const qCodebase = qa("knowledge/tech/codebase.md");
4262
+ const qCurrentState = qa("knowledge/tech/current-state.md");
4263
+ const qRoadmap = qa("knowledge/delivery/roadmap.md");
4264
+ const layerStatusOf = (name) => layers.find((l) => l.layer === name)?.status ?? "Missing";
4265
+ const knowledgeQuality = {
4266
+ business: { status: layerStatusOf("Business"), artifacts: { "knowledge/business/business.md": qBusiness } },
4267
+ product: { status: layerStatusOf("Product"), artifacts: { "knowledge/product/product.md": qProduct, "knowledge/product/capabilities.md": qCapabilities } },
4268
+ tech: { status: layerStatusOf("Tech"), artifacts: { "knowledge/tech/codebase.md": qCodebase, "knowledge/tech/current-state.md": qCurrentState } },
4269
+ delivery: { status: layerStatusOf("Delivery"), artifacts: { "knowledge/delivery/roadmap.md": qRoadmap } }
4270
+ };
4271
+ if (qBusiness === "placeholder") missing.push("Business context exists but still looks like a bootstrap placeholder.");
4272
+ if (qProduct === "placeholder" || qCapabilities === "placeholder") missing.push("Product capabilities exist but still look like a bootstrap placeholder.");
4273
+ if (qCurrentState === "placeholder") missing.push("Current state exists but still looks like a bootstrap placeholder.");
4274
+ if (qCodebase === "placeholder") missing.push("Codebase map exists but still looks like a bootstrap placeholder.");
4109
4275
  const allWorkItems = allArtifacts.filter((a) => a.isWorkItem);
4110
4276
  const wiWithOwnership = allWorkItems.filter((a) => a.codeGlobs.length > 0).length;
4111
4277
  const phase = assessPhase({
@@ -4128,6 +4294,12 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
4128
4294
  workItemsMissingOwnership: allWorkItems.length - wiWithOwnership
4129
4295
  }
4130
4296
  });
4297
+ const nextStepRecommendation = resolveNextStep(dir, now);
4298
+ const unifiedPhase = {
4299
+ ...phase,
4300
+ nextStep: nextStepRecommendation.label,
4301
+ recommendedAgents: nextStepRecommendation.agent ? [nextStepRecommendation.agent] : phase.recommendedAgents
4302
+ };
4131
4303
  return {
4132
4304
  version: CONTEXT_PACK_VERSION,
4133
4305
  generatedAt: now.toISOString(),
@@ -4156,8 +4328,10 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
4156
4328
  artifacts: allArtifacts.filter((a) => a.codeGlobs.length > 0).map(toContextArtifact)
4157
4329
  },
4158
4330
  layers,
4331
+ knowledgeQuality,
4159
4332
  roadmap,
4160
- phase,
4333
+ phase: unifiedPhase,
4334
+ nextStepRecommendation,
4161
4335
  deliveryMix,
4162
4336
  external: loadExternalCapsules(dir),
4163
4337
  graph: loadGraphSummary(dir),
@@ -4165,12 +4339,12 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
4165
4339
  skills: discoverInstalledSkills(dir).map((s) => s.id),
4166
4340
  mappedModules,
4167
4341
  missing,
4168
- // VS-052: the handoff is driven by the REAL phase, not project.state, so the pack never
4169
- // contradicts the Current Phase block above it.
4342
+ // VS-052/VS-073.2: the handoff is driven by the unified next step, so the pack never contradicts
4343
+ // the Current Phase block above it.
4170
4344
  handoff: {
4171
- recommendedAgents: phase.recommendedAgents.length > 0 ? phase.recommendedAgents : recommendedAgentsForState(state),
4172
- nextSteps: phase.nextStep ? [phase.nextStep] : nextStepsForState(state),
4173
- instructions: phase.llmInstructions.length > 0 ? phase.llmInstructions : LLM_INSTRUCTIONS,
4345
+ recommendedAgents: unifiedPhase.recommendedAgents.length > 0 ? unifiedPhase.recommendedAgents : recommendedAgentsForState(state),
4346
+ nextSteps: [nextStepRecommendation.label],
4347
+ instructions: unifiedPhase.llmInstructions.length > 0 ? unifiedPhase.llmInstructions : LLM_INSTRUCTIONS,
4174
4348
  operatingRules: OPERATING_RULES
4175
4349
  }
4176
4350
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/mcp",
3
- "version": "3.36.0",
3
+ "version": "3.37.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": {