@kaddo/mcp 3.39.1 → 3.40.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 +87 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2433,6 +2433,76 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
2433
2433
  };
2434
2434
  }
2435
2435
 
2436
+ // ../cli/src/core/decisions.ts
2437
+ var CANDIDATES_PATH = "knowledge/tech/decision-candidates.md";
2438
+ var DECISIONS_DIR = "knowledge/tech/decisions";
2439
+ function slugify(s) {
2440
+ return s.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
2441
+ }
2442
+ function parseDecisionCandidates(md) {
2443
+ const out = [];
2444
+ for (const line of md.split(/\r?\n/)) {
2445
+ const m = line.match(/^##\s+(.+?)\s*$/);
2446
+ if (m) {
2447
+ const t = m[1].trim();
2448
+ if (t && !/^_.*_$/.test(t)) out.push(t);
2449
+ }
2450
+ }
2451
+ return out;
2452
+ }
2453
+ function countAdrs(dir) {
2454
+ const base = join(dir, DECISIONS_DIR);
2455
+ if (!exists(base)) return { total: 0, draft: 0, accepted: 0 };
2456
+ let total = 0;
2457
+ let draft = 0;
2458
+ let accepted = 0;
2459
+ for (const entry of readDir(base)) {
2460
+ if (!entry.endsWith(".md") || entry === ".gitkeep") continue;
2461
+ const full = join(base, entry);
2462
+ if (!isFile(full)) continue;
2463
+ total += 1;
2464
+ let content = "";
2465
+ try {
2466
+ content = readFile(full);
2467
+ } catch {
2468
+ continue;
2469
+ }
2470
+ const status = content.match(/^\s*status:\s*([a-z-]+)/im)?.[1]?.toLowerCase();
2471
+ if (status === "accepted") accepted += 1;
2472
+ else draft += 1;
2473
+ }
2474
+ return { total, draft, accepted };
2475
+ }
2476
+ function buildTechDecisions(dir) {
2477
+ const candFile = join(dir, CANDIDATES_PATH);
2478
+ let titles = [];
2479
+ if (exists(candFile)) {
2480
+ try {
2481
+ titles = parseDecisionCandidates(readFile(candFile));
2482
+ } catch {
2483
+ titles = [];
2484
+ }
2485
+ }
2486
+ const { total, draft, accepted } = countAdrs(dir);
2487
+ const candidate_list = titles.map((title, i) => {
2488
+ const n = String(total + i + 1).padStart(3, "0");
2489
+ return { title, source: CANDIDATES_PATH, suggestedAdrFile: `${DECISIONS_DIR}/ADR-${n}-${slugify(title)}.md` };
2490
+ });
2491
+ let status;
2492
+ if (accepted > 0) status = "accepted-adrs";
2493
+ else if (total > 0) status = "draft-adrs";
2494
+ else if (titles.length > 0) status = "candidates";
2495
+ else status = "none";
2496
+ return {
2497
+ status,
2498
+ candidates: titles.length,
2499
+ adrs: total,
2500
+ draft_adrs: draft,
2501
+ accepted_adrs: accepted,
2502
+ candidate_list
2503
+ };
2504
+ }
2505
+
2436
2506
  // ../cli/src/core/project-explain.ts
2437
2507
  var ARCH_DIR2 = "knowledge";
2438
2508
  function normalizeTitle(t) {
@@ -2644,7 +2714,8 @@ function buildProjectExplanation(dir) {
2644
2714
  missingKnowledge,
2645
2715
  suggestedNextSteps,
2646
2716
  readiness,
2647
- nextStepRecommendation: readiness.nextStepRecommendation
2717
+ nextStepRecommendation: readiness.nextStepRecommendation,
2718
+ techDecisions: buildTechDecisions(dir)
2648
2719
  };
2649
2720
  }
2650
2721
  function stateLabel(state) {
@@ -2858,6 +2929,16 @@ function renderExplanationHuman(exp) {
2858
2929
  lines.push("### Recommended next step");
2859
2930
  lines.push(r.recommended_next_step.label);
2860
2931
  lines.push("");
2932
+ const td = exp.techDecisions;
2933
+ lines.push("## Tech Decisions");
2934
+ lines.push(`- Decision candidates: ${td.candidates}`);
2935
+ lines.push(`- ADRs: ${td.adrs} (draft: ${td.draft_adrs}, accepted: ${td.accepted_adrs})`);
2936
+ lines.push(`- Status: ${td.status}`);
2937
+ if (td.candidates > 0 && td.adrs === 0) {
2938
+ lines.push("");
2939
+ lines.push("Use the adr-writing skill to materialize decision candidates into ADRs (`kaddo adr`) before implementing related technical Work Items.");
2940
+ }
2941
+ lines.push("");
2861
2942
  return lines.join("\n").trimEnd() + "\n";
2862
2943
  }
2863
2944
  function renderExplanationAgent(exp) {
@@ -4313,6 +4394,10 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
4313
4394
  }
4314
4395
  });
4315
4396
  const nextStepRecommendation = resolveNextStep(dir, now);
4397
+ const techDecisions = buildTechDecisions(dir);
4398
+ if (techDecisions.candidates > 0 && techDecisions.adrs === 0) {
4399
+ missing.push(`${techDecisions.candidates} technical decision candidate(s) not yet materialized as ADRs (run \`kaddo adr\`).`);
4400
+ }
4316
4401
  const unifiedPhase = {
4317
4402
  ...phase,
4318
4403
  nextStep: nextStepRecommendation.label,
@@ -4350,6 +4435,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
4350
4435
  roadmap,
4351
4436
  phase: unifiedPhase,
4352
4437
  nextStepRecommendation,
4438
+ techDecisions,
4353
4439
  deliveryMix,
4354
4440
  external: loadExternalCapsules(dir),
4355
4441
  graph: loadGraphSummary(dir),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/mcp",
3
- "version": "3.39.1",
3
+ "version": "3.40.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": {