@kaddo/mcp 3.47.0 → 3.49.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 +233 -27
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -384,6 +384,7 @@ function parseArtifact(filePath, raw) {
384
384
  initiative: String(data.initiative ?? data.source_initiative ?? ""),
385
385
  source: data.source ? String(data.source) : "",
386
386
  sourceId: String(data.source_id ?? ""),
387
+ rawFrontmatter: data,
387
388
  decisions: Array.isArray(data.decisions) ? data.decisions.map(String).filter(Boolean) : [],
388
389
  capsules: Array.isArray(data.capsules) ? data.capsules.map(String).filter(Boolean) : []
389
390
  };
@@ -5516,6 +5517,121 @@ function buildRoadmapQuality(dir) {
5516
5517
  return { initiatives, work_item_candidates };
5517
5518
  }
5518
5519
 
5520
+ // ../cli/src/core/scan-signals.ts
5521
+ function signalCount(signals) {
5522
+ return Object.values(signals).reduce((sum, arr) => sum + arr.length, 0);
5523
+ }
5524
+ function hasWarnings(signals) {
5525
+ return signals.tests.some((s) => s.label.includes("No test directory"));
5526
+ }
5527
+ function renderSignalsCompact(signals) {
5528
+ const lines = [];
5529
+ const CATEGORIES = [
5530
+ ["auth", "Auth"],
5531
+ ["payments", "Payments"],
5532
+ ["webhooks", "Webhooks"],
5533
+ ["storage", "Storage"],
5534
+ ["background_jobs", "Background jobs"],
5535
+ ["email", "Email"],
5536
+ ["database", "Database"],
5537
+ ["migrations", "Migrations"],
5538
+ ["api_routes", "API routes"],
5539
+ ["tests", "Tests"],
5540
+ ["security", "Security"],
5541
+ ["infrastructure", "Infrastructure"],
5542
+ ["external_integrations", "External integrations"],
5543
+ ["environment", "Environment"]
5544
+ ];
5545
+ for (const [key, label] of CATEGORIES) {
5546
+ const arr = signals[key];
5547
+ if (arr.length === 0) continue;
5548
+ if (arr.length === 1) {
5549
+ lines.push(`- ${label}: ${arr[0].label}`);
5550
+ } else {
5551
+ lines.push(`- ${label}: ${arr.length}`);
5552
+ }
5553
+ }
5554
+ return lines.length > 0 ? lines.join("\n") + "\n" : "No signals detected.\n";
5555
+ }
5556
+
5557
+ // ../cli/src/core/work-item-source.ts
5558
+ var VALID_SOURCES = [
5559
+ "manual",
5560
+ "roadmap",
5561
+ "jira",
5562
+ "github",
5563
+ "notion",
5564
+ "xlsx",
5565
+ "csv",
5566
+ "api",
5567
+ "external",
5568
+ "unknown"
5569
+ ];
5570
+ function isValidSource(s) {
5571
+ return VALID_SOURCES.includes(s);
5572
+ }
5573
+ function parseWorkItemSource(frontmatter) {
5574
+ const raw = frontmatter.source ? String(frontmatter.source) : "";
5575
+ if (raw && isValidSource(raw)) {
5576
+ return {
5577
+ type: raw,
5578
+ id: optStr(frontmatter.source_id),
5579
+ title: optStr(frontmatter.source_title),
5580
+ context: optStr(frontmatter.source_context),
5581
+ provider: optStr(frontmatter.source_provider),
5582
+ url: optStr(frontmatter.source_url),
5583
+ imported_at: optStr(frontmatter.source_imported_at),
5584
+ synced_at: optStr(frontmatter.source_synced_at),
5585
+ inferred: false
5586
+ };
5587
+ }
5588
+ if (raw && !isValidSource(raw)) {
5589
+ return {
5590
+ type: "unknown",
5591
+ id: optStr(frontmatter.source_id),
5592
+ title: optStr(frontmatter.source_title),
5593
+ context: optStr(frontmatter.source_context),
5594
+ provider: optStr(frontmatter.source_provider),
5595
+ url: optStr(frontmatter.source_url),
5596
+ imported_at: optStr(frontmatter.source_imported_at),
5597
+ synced_at: optStr(frontmatter.source_synced_at),
5598
+ inferred: true,
5599
+ reason: `Invalid source value: "${raw}".`
5600
+ };
5601
+ }
5602
+ if (frontmatter.source_work_item_candidate || frontmatter.source_roadmap_initiative) {
5603
+ return {
5604
+ type: "roadmap",
5605
+ id: optStr(frontmatter.source_id) ?? optStr(frontmatter.source_work_item_candidate),
5606
+ title: optStr(frontmatter.source_initiative_title),
5607
+ inferred: true,
5608
+ reason: "Inferred from legacy roadmap fields."
5609
+ };
5610
+ }
5611
+ if (frontmatter.source_id) {
5612
+ return {
5613
+ type: "unknown",
5614
+ id: optStr(frontmatter.source_id),
5615
+ inferred: true,
5616
+ reason: "Has source_id but no source type."
5617
+ };
5618
+ }
5619
+ return {
5620
+ type: "unknown",
5621
+ inferred: true,
5622
+ reason: "No source metadata found."
5623
+ };
5624
+ }
5625
+ function renderSourceCompact(source) {
5626
+ const parts = [source.type];
5627
+ if (source.id) parts.push(source.id);
5628
+ return parts.join(" \xB7 ");
5629
+ }
5630
+ function optStr(v) {
5631
+ if (typeof v === "string" && v.trim()) return v.trim();
5632
+ return void 0;
5633
+ }
5634
+
5519
5635
  // ../cli/src/core/project-route.ts
5520
5636
  function isUseful(q) {
5521
5637
  return q === "useful";
@@ -5532,11 +5648,11 @@ var enableKaddo = {
5532
5648
  var scanRepository = {
5533
5649
  id: "scan-repository",
5534
5650
  label: "Scan repository",
5535
- evaluate: (ctx) => ({
5536
- status: ctx.hasScan ? "done" : ctx.hasConfig ? "pending" : "pending",
5537
- evidence: ctx.hasScan ? [".kaddo/scan.json"] : void 0,
5538
- command: ctx.hasScan ? void 0 : "kaddo scan"
5539
- })
5651
+ evaluate: (ctx) => {
5652
+ if (!ctx.hasScan) return { status: "pending", command: "kaddo scan" };
5653
+ if (ctx.hasScanWarnings) return { status: "warning", evidence: [".kaddo/scan.json"], reason: "Scan has warnings (e.g. no test directory detected).", command: "kaddo scan" };
5654
+ return { status: "done", evidence: [".kaddo/scan.json"] };
5655
+ }
5540
5656
  };
5541
5657
  var defineBusiness = {
5542
5658
  id: "define-business",
@@ -5602,12 +5718,12 @@ var captureTechDecisions = {
5602
5718
  var createWorkSource = {
5603
5719
  id: "create-work-source",
5604
5720
  label: "Create or connect work source",
5605
- evaluate: (ctx) => ({
5606
- status: ctx.hasRoadmap || ctx.totalWorkItems > 0 ? "done" : "pending",
5607
- evidence: ctx.hasRoadmap ? ["knowledge/delivery/roadmap.md"] : void 0,
5608
- command: ctx.hasRoadmap ? void 0 : "kaddo roadmap",
5609
- agent: ctx.hasRoadmap ? void 0 : "roadmap-agent"
5610
- })
5721
+ evaluate: (ctx) => {
5722
+ if (!ctx.hasRoadmap && ctx.totalWorkItems === 0) return { status: "pending", command: "kaddo roadmap", agent: "roadmap-agent" };
5723
+ if (ctx.hasUnknownSources) return { status: "warning", evidence: ["Work Items with unknown source"], reason: "Some Work Items have no source metadata." };
5724
+ const evidence = ctx.hasRoadmap ? ["knowledge/delivery/roadmap.md"] : void 0;
5725
+ return { status: "done", evidence };
5726
+ }
5611
5727
  };
5612
5728
  var materializeWorkItem = {
5613
5729
  id: "materialize-work-item",
@@ -5823,9 +5939,22 @@ function buildRouteContext(dir) {
5823
5939
  totalAdrs: td.adrs,
5824
5940
  adaptersInstalled: adapters.length,
5825
5941
  hasGuardHistory: exists(join(dir, ".kaddo", "history", "guard-runs.jsonl")),
5942
+ hasScanWarnings: hasScan ? loadScanWarnings(dir) : false,
5943
+ hasUnknownSources: wis.some((w) => {
5944
+ const src = parseWorkItemSource(w.rawFrontmatter);
5945
+ return src.type === "unknown" && src.inferred;
5946
+ }),
5826
5947
  nextStepId: mapNextStepId(nextStep.id)
5827
5948
  };
5828
5949
  }
5950
+ function loadScanWarnings(dir) {
5951
+ try {
5952
+ const parsed = JSON.parse(readFile(join(dir, ".kaddo", "scan.json")));
5953
+ return parsed.signals ? hasWarnings(parsed.signals) : false;
5954
+ } catch {
5955
+ return false;
5956
+ }
5957
+ }
5829
5958
  function mapNextStepId(id) {
5830
5959
  const MAP = {
5831
5960
  init: "enable-kaddo",
@@ -5972,6 +6101,16 @@ function loadScan(dir) {
5972
6101
  return null;
5973
6102
  }
5974
6103
  }
6104
+ function loadScanSignals(dir) {
6105
+ const scanPath = join(dir, ".kaddo", "scan.json");
6106
+ if (!exists(scanPath)) return null;
6107
+ try {
6108
+ const parsed = JSON.parse(readFile(scanPath));
6109
+ return parsed.signals ?? null;
6110
+ } catch {
6111
+ return null;
6112
+ }
6113
+ }
5975
6114
  function hasAgents(dir) {
5976
6115
  const agentsDir = join(dir, ARCH_DIR2, "agents");
5977
6116
  if (!exists(agentsDir)) return false;
@@ -6020,17 +6159,21 @@ function buildProjectExplanation(dir) {
6020
6159
  hasAgents: hasAgents(dir)
6021
6160
  };
6022
6161
  const workItemArtifacts = discoverWorkItems(dir);
6023
- const items = workItemArtifacts.map((a) => ({
6024
- id: a.id || a.title,
6025
- title: a.title,
6026
- type: a.type,
6027
- status: a.status,
6028
- lifecycle: lifecycleStateOf({ status: a.status, filePath: a.filePath }),
6029
- initiative: a.initiative,
6030
- knowledgeLevel: a.knowledgeLevel,
6031
- hasOwnership: a.codeGlobs.length > 0,
6032
- domains: a.domains
6033
- }));
6162
+ const items = workItemArtifacts.map((a) => {
6163
+ const src = parseWorkItemSource(a.rawFrontmatter);
6164
+ return {
6165
+ id: a.id || a.title,
6166
+ title: a.title,
6167
+ type: a.type,
6168
+ status: a.status,
6169
+ lifecycle: lifecycleStateOf({ status: a.status, filePath: a.filePath }),
6170
+ initiative: a.initiative,
6171
+ knowledgeLevel: a.knowledgeLevel,
6172
+ hasOwnership: a.codeGlobs.length > 0,
6173
+ domains: a.domains,
6174
+ source: { type: src.type, ...src.id ? { id: src.id } : {}, inferred: src.inferred }
6175
+ };
6176
+ });
6034
6177
  const byState = lifecycleCounts(items.map((i) => i.lifecycle));
6035
6178
  const byType = {};
6036
6179
  for (const i of items) {
@@ -6143,7 +6286,8 @@ function buildProjectExplanation(dir) {
6143
6286
  },
6144
6287
  installedAssets: installedAssetsSummary(dir),
6145
6288
  roadmapQuality: buildRoadmapQuality(dir),
6146
- projectRoute: buildProjectRoute(dir)
6289
+ projectRoute: buildProjectRoute(dir),
6290
+ scanSignals: loadScanSignals(dir)
6147
6291
  };
6148
6292
  }
6149
6293
  function stateLabel(state) {
@@ -6188,6 +6332,10 @@ function renderExplanationHuman(exp) {
6188
6332
  lines.push(`- Infrastructure: ${exp.stack.infrastructureFiles.join(", ")}`);
6189
6333
  lines.push("");
6190
6334
  }
6335
+ if (exp.scanSignals && signalCount(exp.scanSignals) > 0) {
6336
+ lines.push("## Scan Signals");
6337
+ lines.push(renderSignalsCompact(exp.scanSignals));
6338
+ }
6191
6339
  const ls = (name) => exp.layers.find((l) => l.layer === name)?.status ?? "Missing";
6192
6340
  lines.push("## Knowledge Status");
6193
6341
  lines.push(`- Inventory: ${exp.knowledge.hasInventory ? "available" : "missing"}`);
@@ -6222,6 +6370,17 @@ function renderExplanationHuman(exp) {
6222
6370
  for (const [t, n] of typeEntries) lines.push(`- ${typeLabel(t)}: ${n}`);
6223
6371
  lines.push("");
6224
6372
  }
6373
+ const sourceCounts = {};
6374
+ for (const i of exp.workItems.items) {
6375
+ const t = i.source?.type ?? "unknown";
6376
+ sourceCounts[t] = (sourceCounts[t] ?? 0) + 1;
6377
+ }
6378
+ const sourceEntries = Object.entries(sourceCounts).filter(([, n]) => n > 0);
6379
+ if (sourceEntries.length > 0) {
6380
+ lines.push("## Work Item Sources");
6381
+ for (const [t, n] of sourceEntries) lines.push(`- ${t.charAt(0).toUpperCase() + t.slice(1)}: ${n}`);
6382
+ lines.push("");
6383
+ }
6225
6384
  const grouped = exp.workItems.initiatives.filter(
6226
6385
  (g) => LIFECYCLE_STATES.some((s2) => g.states[s2] > 0)
6227
6386
  );
@@ -7350,6 +7509,20 @@ function serializeDriftJson(r) {
7350
7509
 
7351
7510
  // src/workitems.ts
7352
7511
  import matter8 from "gray-matter";
7512
+ var VALID_SOURCES2 = /* @__PURE__ */ new Set(["manual", "roadmap", "jira", "github", "notion", "xlsx", "csv", "api", "external", "unknown"]);
7513
+ function optStr2(v) {
7514
+ return typeof v === "string" && v.trim() ? v.trim() : void 0;
7515
+ }
7516
+ function parseSource(data) {
7517
+ const raw = data.source ? String(data.source) : "";
7518
+ if (raw && VALID_SOURCES2.has(raw)) {
7519
+ return { type: raw, id: optStr2(data.source_id), title: optStr2(data.source_title), url: optStr2(data.source_url), provider: optStr2(data.source_provider), inferred: false };
7520
+ }
7521
+ if (data.source_work_item_candidate || data.source_roadmap_initiative) {
7522
+ return { type: "roadmap", id: optStr2(data.source_id) ?? optStr2(data.source_work_item_candidate), title: optStr2(data.source_initiative_title), inferred: true };
7523
+ }
7524
+ return { type: "unknown", inferred: true };
7525
+ }
7353
7526
  var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["draft", "ready", "in-progress", "blocked"]);
7354
7527
  function strArray(v) {
7355
7528
  return Array.isArray(v) ? v.map((x) => String(x)).filter(Boolean) : [];
@@ -7393,7 +7566,8 @@ function listWorkItems(root) {
7393
7566
  code: strArray(data.code),
7394
7567
  capabilities: strArray(data.capabilities),
7395
7568
  decisions: strArray(data.decisions),
7396
- capsules: strArray(data.capsules)
7569
+ capsules: strArray(data.capsules),
7570
+ source: parseSource(data)
7397
7571
  });
7398
7572
  }
7399
7573
  return items.sort((a, b) => a.id.localeCompare(b.id));
@@ -7699,6 +7873,25 @@ var RESOURCES = [
7699
7873
  }
7700
7874
  return [{ uri: "kaddo://project-route", text: JSON.stringify(buildProjectRoute(root), null, 2), mimeType: "application/json" }];
7701
7875
  }
7876
+ },
7877
+ {
7878
+ uri: "kaddo://scan-signals",
7879
+ name: "Kaddo scan signals",
7880
+ description: "Actionable signals detected by `kaddo scan`: auth, payments, webhooks, storage, background jobs, email, database, migrations, API routes, tests, security, infrastructure, integrations, environment (VS-081). Read-only.",
7881
+ mimeType: "application/json",
7882
+ read: (root) => {
7883
+ const body = readText(root, ".kaddo/scan.json");
7884
+ if (body) {
7885
+ try {
7886
+ const parsed = JSON.parse(body);
7887
+ if (parsed.signals) {
7888
+ return [{ uri: "kaddo://scan-signals", text: JSON.stringify(parsed.signals, null, 2), mimeType: "application/json" }];
7889
+ }
7890
+ } catch {
7891
+ }
7892
+ }
7893
+ return text("kaddo://scan-signals", "Scan signals not found. Run `kaddo scan` first.", "text/plain");
7894
+ }
7702
7895
  }
7703
7896
  ];
7704
7897
 
@@ -7877,7 +8070,8 @@ function toContextWorkItem(a) {
7877
8070
  status: a.status,
7878
8071
  lifecycle: lifecycleStateOf({ status: a.status, filePath: a.filePath }),
7879
8072
  knowledgeLevel: a.knowledgeLevel,
7880
- domains: a.domains
8073
+ domains: a.domains,
8074
+ source: parseWorkItemSource(a.rawFrontmatter)
7881
8075
  };
7882
8076
  }
7883
8077
  function toContextArtifact(a) {
@@ -8040,6 +8234,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
8040
8234
  graph: loadGraphSummary(dir),
8041
8235
  graphHints: loadGraphHints(dir),
8042
8236
  skills: discoverInstalledSkills(dir).map((s) => s.id),
8237
+ scanSignals: scanJson?.signals ?? null,
8043
8238
  projectRoute: buildProjectRoute(dir),
8044
8239
  mappedModules,
8045
8240
  missing,
@@ -8154,6 +8349,10 @@ function renderContextPack(pack) {
8154
8349
  } else {
8155
8350
  parts.push("Scan baseline missing. Run `kaddo scan` for better context.\n");
8156
8351
  }
8352
+ if (pack.scanSignals && signalCount(pack.scanSignals) > 0) {
8353
+ parts.push("## Scan Signals\n");
8354
+ parts.push(renderSignalsCompact(pack.scanSignals));
8355
+ }
8157
8356
  parts.push("## Current Knowledge\n");
8158
8357
  parts.push((knowledge.summary || "No project knowledge summary found yet.") + "\n");
8159
8358
  parts.push("## Roadmap Status\n");
@@ -8213,7 +8412,10 @@ function renderContextPack(pack) {
8213
8412
  const level = wi.knowledgeLevel ? ` [${wi.knowledgeLevel}]` : "";
8214
8413
  const status = wi.lifecycle ? ` (${wi.lifecycle})` : wi.status ? ` (${wi.status})` : "";
8215
8414
  const domains = wi.domains.length > 0 ? ` \xB7 domains: ${wi.domains.join(", ")}` : "";
8216
- return `- ${wi.id || wi.title} [${wi.type}]${level}${status} \u2014 ${wi.title}${domains}`;
8415
+ const line = `- ${wi.id || wi.title} [${wi.type}]${level}${status} \u2014 ${wi.title}${domains}`;
8416
+ const src = wi.source && wi.source.type !== "unknown" ? `
8417
+ - Source: ${renderSourceCompact(wi.source)}` : "";
8418
+ return `${line}${src}`;
8217
8419
  });
8218
8420
  parts.push(lines.join("\n") + "\n");
8219
8421
  } else {
@@ -8471,7 +8673,11 @@ function renderUnderstand(plan) {
8471
8673
  if (plan.activeWorkItems && plan.activeWorkItems.length > 0) {
8472
8674
  parts.push("## Active Work Items\n");
8473
8675
  parts.push(
8474
- plan.activeWorkItems.map((w) => `- ${w.id} [${w.type}] ${w.lifecycle} \u2014 ${w.title}`).join("\n") + "\n"
8676
+ plan.activeWorkItems.map((w) => {
8677
+ const src = w.source && w.source.type !== "unknown" ? `
8678
+ - Source: ${w.source.type}${w.source.id ? ` \xB7 ${w.source.id}` : ""}` : "";
8679
+ return `- ${w.id} [${w.type}] ${w.lifecycle} \u2014 ${w.title}${src}`;
8680
+ }).join("\n") + "\n"
8475
8681
  );
8476
8682
  }
8477
8683
  parts.push("## Context Pack\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/mcp",
3
- "version": "3.47.0",
3
+ "version": "3.49.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": {