@kaddo/mcp 3.103.0 → 3.105.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 +223 -56
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10783,7 +10783,7 @@ function getPrompt(root, name) {
10783
10783
  }
10784
10784
 
10785
10785
  // src/tools.ts
10786
- import matter11 from "gray-matter";
10786
+ import matter12 from "gray-matter";
10787
10787
 
10788
10788
  // ../cli/dist/core.js
10789
10789
  import fs3 from "fs";
@@ -10815,18 +10815,25 @@ import fs4 from "fs";
10815
10815
  import matter82 from "gray-matter";
10816
10816
  import matter92 from "gray-matter";
10817
10817
  import { parse as parseYaml92, stringify as stringifyYaml5 } from "yaml";
10818
- function exists2(p2) {
10819
- return fs3.existsSync(p2);
10818
+ import { parse as parseYaml102, stringify as stringifyYaml6 } from "yaml";
10819
+ import matter102 from "gray-matter";
10820
+ import matter11 from "gray-matter";
10821
+ function exists2(p) {
10822
+ return fs3.existsSync(p);
10820
10823
  }
10821
- function readFile2(p2) {
10822
- return fs3.readFileSync(p2, "utf-8");
10824
+ function readFile2(p) {
10825
+ return fs3.readFileSync(p, "utf-8");
10826
+ }
10827
+ function writeFile2(filePath, content) {
10828
+ fs3.mkdirSync(path3.dirname(filePath), { recursive: true });
10829
+ fs3.writeFileSync(filePath, content, "utf-8");
10823
10830
  }
10824
10831
  function readDir2(dirPath) {
10825
10832
  if (!exists2(dirPath)) return [];
10826
10833
  return fs3.readdirSync(dirPath);
10827
10834
  }
10828
- function isFile2(p2) {
10829
- return exists2(p2) && fs3.statSync(p2).isFile();
10835
+ function isFile2(p) {
10836
+ return exists2(p) && fs3.statSync(p).isFile();
10830
10837
  }
10831
10838
  function join3(...parts) {
10832
10839
  return path3.join(...parts);
@@ -11051,8 +11058,8 @@ function isActiveState2(s) {
11051
11058
  return ACTIVE_STATES3.includes(s);
11052
11059
  }
11053
11060
  function lifecycleFolderOf2(filePath) {
11054
- const p2 = filePath.replace(/\\/g, "/");
11055
- const m2 = p2.match(/\/delivery\/work-items\/([^/]+)\//);
11061
+ const p = filePath.replace(/\\/g, "/");
11062
+ const m2 = p.match(/\/delivery\/work-items\/([^/]+)\//);
11056
11063
  if (m2 && isLifecycleState2(m2[1])) return m2[1];
11057
11064
  return null;
11058
11065
  }
@@ -11065,8 +11072,8 @@ function lifecycleStateOf2(item) {
11065
11072
  return "ready";
11066
11073
  }
11067
11074
  var ARCH_DIR3 = "knowledge";
11068
- function toPosix6(p2) {
11069
- return p2.replace(/\\/g, "/");
11075
+ function toPosix6(p) {
11076
+ return p.replace(/\\/g, "/");
11070
11077
  }
11071
11078
  function relativeTo2(dir, filePath) {
11072
11079
  const base = toPosix6(dir).replace(/\/+$/, "");
@@ -12311,10 +12318,10 @@ ${QUALITY2}
12311
12318
  `;
12312
12319
  var EXTERNAL_PATH2 = ".kaddo/external.yml";
12313
12320
  function loadExternalRegistry2(dir) {
12314
- const p2 = join3(dir, EXTERNAL_PATH2);
12315
- if (!exists2(p2)) return [];
12321
+ const p = join3(dir, EXTERNAL_PATH2);
12322
+ if (!exists2(p)) return [];
12316
12323
  try {
12317
- const parsed = parseYaml62(readFile2(p2));
12324
+ const parsed = parseYaml62(readFile2(p));
12318
12325
  return Array.isArray(parsed?.external) ? parsed.external : [];
12319
12326
  } catch {
12320
12327
  return [];
@@ -12326,8 +12333,8 @@ var ALL_STATUSES2 = ["draft", "ready", "in-progress", "blocked", "completed"];
12326
12333
  function scopeStatuses2(scope) {
12327
12334
  return scope === "all" ? { included: ALL_STATUSES2, excluded: ["archived"] } : { included: ACTIVE_STATUSES2, excluded: ["completed", "archived"] };
12328
12335
  }
12329
- function toPosix22(p2) {
12330
- return p2.replace(/\\/g, "/");
12336
+ function toPosix22(p) {
12337
+ return p.replace(/\\/g, "/");
12331
12338
  }
12332
12339
  function slug3(s) {
12333
12340
  return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
@@ -12465,8 +12472,8 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
12465
12472
  };
12466
12473
  }
12467
12474
  var KNOWLEDGE32 = "knowledge";
12468
- function toPosix32(p2) {
12469
- return p2.replace(/\\/g, "/");
12475
+ function toPosix32(p) {
12476
+ return p.replace(/\\/g, "/");
12470
12477
  }
12471
12478
  function slug22(s) {
12472
12479
  return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
@@ -12475,9 +12482,9 @@ function isAdr22(a2) {
12475
12482
  return toPosix32(a2.filePath).includes("/tech/decisions/") && Boolean(a2.type);
12476
12483
  }
12477
12484
  function capabilityHeadings2(dir) {
12478
- const p2 = join3(dir, KNOWLEDGE32, "product", "capabilities.md");
12479
- if (!exists2(p2)) return [];
12480
- return readFile2(p2).split(/\r?\n/).map((l2) => l2.match(/^#{2,3}\s+(.+?)\s*$/)).filter((m2) => Boolean(m2)).map((m2) => m2[1].trim()).filter((h2) => !/^(summary|resumen|overview|capabilities|capacidades)$/i.test(h2));
12485
+ const p = join3(dir, KNOWLEDGE32, "product", "capabilities.md");
12486
+ if (!exists2(p)) return [];
12487
+ return readFile2(p).split(/\r?\n/).map((l2) => l2.match(/^#{2,3}\s+(.+?)\s*$/)).filter((m2) => Boolean(m2)).map((m2) => m2[1].trim()).filter((h2) => !/^(summary|resumen|overview|capabilities|capacidades)$/i.test(h2));
12481
12488
  }
12482
12489
  function humanMissing2(field) {
12483
12490
  switch (field) {
@@ -15783,7 +15790,7 @@ var AGENT_PROMPTS2 = [
15783
15790
  // Graph relationship quality (metadata hints → precise front matter — VS-056)
15784
15791
  { fileName: "graph-agent.md", content: GRAPH_AGENT2 }
15785
15792
  // Every official prompt ends with its responsibility boundaries + Agent Trace footer.
15786
- ].map((p2) => ({ fileName: p2.fileName, content: withResponsibilityTrace2(p2.fileName, p2.content) }));
15793
+ ].map((p) => ({ fileName: p.fileName, content: withResponsibilityTrace2(p.fileName, p.content) }));
15787
15794
  function withAgentFrontMatter2(fileName, content) {
15788
15795
  const name = fileName.replace(/\.md$/, "");
15789
15796
  const fm = [
@@ -16422,8 +16429,8 @@ var TECHNICAL_RELATIONSHIP_TYPES = /* @__PURE__ */ new Set([
16422
16429
  "implemented-by"
16423
16430
  ]);
16424
16431
  var PROVENANCE = /* @__PURE__ */ new Set(["declared", "derived", "agent-reviewed"]);
16425
- function isRelative(p2) {
16426
- return typeof p2 === "string" && p2.trim() !== "" && !/^([a-zA-Z]:[\\/]|\/)/.test(p2) && !p2.includes("..");
16432
+ function isRelative(p) {
16433
+ return typeof p === "string" && p.trim() !== "" && !/^([a-zA-Z]:[\\/]|\/)/.test(p) && !p.includes("..");
16427
16434
  }
16428
16435
  function strList(v) {
16429
16436
  return Array.isArray(v) ? v.map((x2) => typeof x2 === "string" ? x2.trim() : "").filter(Boolean) : [];
@@ -16466,9 +16473,9 @@ function parseTopologyContent(raw, dir) {
16466
16473
  const moduleRaw = typeof e.module === "string" ? e.module.trim() : void 0;
16467
16474
  if (moduleRaw && !validModules.has(moduleRaw)) findings.push({ level: "warning", message: `Entity "${id}" references unregistered module "${moduleRaw}".` });
16468
16475
  const rawImpl = e.implementation ?? e.implementation_refs;
16469
- const implementation = strList(rawImpl).filter((p2) => {
16470
- if (isRelative(p2)) return true;
16471
- findings.push({ level: "warning", message: `Entity "${id}" implementation ref "${p2}" is not a safe relative path and was dropped.` });
16476
+ const implementation = strList(rawImpl).filter((p) => {
16477
+ if (isRelative(p)) return true;
16478
+ findings.push({ level: "warning", message: `Entity "${id}" implementation ref "${p}" is not a safe relative path and was dropped.` });
16472
16479
  return false;
16473
16480
  });
16474
16481
  const prov = e.provenance;
@@ -16676,8 +16683,8 @@ function splitSections(body) {
16676
16683
  flush();
16677
16684
  return sections;
16678
16685
  }
16679
- function isPlaceholder(text3) {
16680
- const t = text3.trim();
16686
+ function isPlaceholder(text22) {
16687
+ const t = text22.trim();
16681
16688
  if (!t) return true;
16682
16689
  if (/^tbd$/i.test(t)) return true;
16683
16690
  if (/^_[^_]*_$/.test(t) && !t.includes("\n")) return true;
@@ -16704,15 +16711,15 @@ function extractBullets(body) {
16704
16711
  for (const line of body.split(/\r?\n/)) {
16705
16712
  const m2 = line.match(/^\s*[-*+]\s+(.*)$/);
16706
16713
  if (!m2) continue;
16707
- let text3 = m2[1].trim();
16714
+ let text22 = m2[1].trim();
16708
16715
  let checked = null;
16709
- const cb = text3.match(/^\[([ xX])\]\s*(.*)$/);
16716
+ const cb = text22.match(/^\[([ xX])\]\s*(.*)$/);
16710
16717
  if (cb) {
16711
16718
  checked = cb[1].toLowerCase() === "x";
16712
- text3 = cb[2].trim();
16719
+ text22 = cb[2].trim();
16713
16720
  }
16714
- if (!text3 || isPlaceholder(text3)) continue;
16715
- out.push({ text: text3, checked });
16721
+ if (!text22 || isPlaceholder(text22)) continue;
16722
+ out.push({ text: text22, checked });
16716
16723
  }
16717
16724
  return out;
16718
16725
  }
@@ -17457,7 +17464,7 @@ var SECRET_PATTERNS = [
17457
17464
  /\.pfx$/i
17458
17465
  ];
17459
17466
  function filterSecretPaths(paths) {
17460
- return paths.filter((p2) => !SECRET_PATTERNS.some((re) => re.test(p2)));
17467
+ return paths.filter((p) => !SECRET_PATTERNS.some((re) => re.test(p)));
17461
17468
  }
17462
17469
  function collectImplementationEvidence(dir, workItemId, input) {
17463
17470
  const artifacts = discoverWorkItems2(dir);
@@ -18488,6 +18495,103 @@ function createDefaultRegistry() {
18488
18495
  return registry22;
18489
18496
  }
18490
18497
  var registry = createDefaultRegistry();
18498
+ var CURRENT_CONSENT_VERSION = 1;
18499
+ var DEFERRAL_DURATION_MS = 24 * 60 * 60 * 1e3;
18500
+ function getConsentState(config) {
18501
+ if (!config) return "unset";
18502
+ const t = config.telemetry;
18503
+ if (!t) return "unset";
18504
+ if (t.consent === "enabled") return "enabled";
18505
+ if (t.consent === "disabled") return "disabled";
18506
+ if (t.consent === void 0) {
18507
+ if (t.enabled === true) return "enabled";
18508
+ if (t.enabled === false) return "disabled";
18509
+ }
18510
+ return "unset";
18511
+ }
18512
+ function persistConsent(dir, consent) {
18513
+ const configPath = join3(dir, ".kaddo", "config.yml");
18514
+ if (!exists2(configPath)) return;
18515
+ const raw = readFile2(configPath);
18516
+ const doc = parseYaml102(raw) ?? {};
18517
+ const prev = doc.telemetry ?? {};
18518
+ delete prev.enabled;
18519
+ doc.telemetry = { ...prev, consent, consentVersion: CURRENT_CONSENT_VERSION };
18520
+ writeFile2(configPath, stringifyYaml6(doc));
18521
+ }
18522
+ function isDeferralActive(dir) {
18523
+ const p = join3(dir, ".kaddo", "telemetry-deferred.json");
18524
+ if (!exists2(p)) return false;
18525
+ try {
18526
+ const data = JSON.parse(readFile2(p));
18527
+ return data.deferredUntil > Date.now();
18528
+ } catch {
18529
+ return false;
18530
+ }
18531
+ }
18532
+ function deferConsent(dir) {
18533
+ const p = join3(dir, ".kaddo", "telemetry-deferred.json");
18534
+ writeFile2(p, JSON.stringify({ deferredUntil: Date.now() + DEFERRAL_DURATION_MS }));
18535
+ }
18536
+ function consentNotice() {
18537
+ return '[Telemetry consent] Anonymous usage telemetry has not been configured.\n Collects: command usage, version, lifecycle events.\n Never collects: source code, knowledge, prompts or PII.\n Use kaddo_set_telemetry_consent to enable, disable, or defer ("not-now").';
18538
+ }
18539
+ var TELEMETRY_DIR = ".kaddo/telemetry";
18540
+ var IDENTITY_FILE = "identity.json";
18541
+ function identityPath(dir) {
18542
+ return join3(dir, TELEMETRY_DIR, IDENTITY_FILE);
18543
+ }
18544
+ function loadIdentity(dir) {
18545
+ const p = identityPath(dir);
18546
+ if (!exists2(p)) return null;
18547
+ try {
18548
+ return JSON.parse(readFile2(p));
18549
+ } catch {
18550
+ return null;
18551
+ }
18552
+ }
18553
+ var TELEMETRY_DIR2 = ".kaddo/telemetry";
18554
+ var BUFFER_FILE = "buffer.json";
18555
+ function bufferPath(dir) {
18556
+ return join3(dir, TELEMETRY_DIR2, BUFFER_FILE);
18557
+ }
18558
+ function readBuffer(dir) {
18559
+ const p = bufferPath(dir);
18560
+ if (!exists2(p)) return [];
18561
+ try {
18562
+ const data = JSON.parse(readFile2(p));
18563
+ return Array.isArray(data) ? data : [];
18564
+ } catch {
18565
+ return [];
18566
+ }
18567
+ }
18568
+ function bufferSize(dir) {
18569
+ return readBuffer(dir).length;
18570
+ }
18571
+ function getStatus(dir) {
18572
+ const config = loadConfig2(dir);
18573
+ const consent = getConsentState(config);
18574
+ const enabled = consent === "enabled";
18575
+ const identity = loadIdentity(dir);
18576
+ return {
18577
+ consent,
18578
+ enabled,
18579
+ registered: identity?.registered ?? false,
18580
+ pendingEvents: enabled ? bufferSize(dir) : 0,
18581
+ lastError: null
18582
+ };
18583
+ }
18584
+ var INITIATIVE_STATES = [
18585
+ "candidate",
18586
+ "planned",
18587
+ "in-progress",
18588
+ "completed",
18589
+ "deferred",
18590
+ "cancelled"
18591
+ ];
18592
+ var STATUS_SET = new Set(INITIATIVE_STATES);
18593
+ var WORK_ITEMS_DIR2 = "knowledge/delivery/work-items";
18594
+ var DRAFT_DIR = `${WORK_ITEMS_DIR2}/draft`;
18491
18595
 
18492
18596
  // src/tools.ts
18493
18597
  var ok = (data) => ({ ok: true, data });
@@ -18584,12 +18688,12 @@ function markWorkItemReady(root, id, confirm2) {
18584
18688
  }
18585
18689
  const raw = readText(root, item.path);
18586
18690
  if (!raw) return fail(`Could not read Work Item file: ${item.path}`);
18587
- const parsed = matter11(raw);
18691
+ const parsed = matter12(raw);
18588
18692
  const data = { ...parsed.data };
18589
18693
  const today2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
18590
18694
  data.status = "ready";
18591
18695
  data.ready_at = today2;
18592
- const newContent = matter11.stringify(parsed.content, data);
18696
+ const newContent = matter12.stringify(parsed.content, data);
18593
18697
  const posixPath = item.path.replace(/\\/g, "/");
18594
18698
  let newRelPath = item.path;
18595
18699
  if (posixPath.includes("/work-items/draft/")) {
@@ -18719,12 +18823,45 @@ function verifyWorkItemTool(root, args) {
18719
18823
  return fail("Work Item verification failed.");
18720
18824
  }
18721
18825
  }
18826
+ function telemetryStatusTool(root) {
18827
+ const status = getStatus(root);
18828
+ const result = {
18829
+ consent: status.consent,
18830
+ registered: status.registered,
18831
+ pendingEvents: status.pendingEvents
18832
+ };
18833
+ if (status.consent === "unset" && !isDeferralActive(root)) {
18834
+ result.notice = consentNotice();
18835
+ }
18836
+ return ok(result);
18837
+ }
18838
+ function setTelemetryConsentTool(root, args) {
18839
+ const valid = ["enabled", "disabled", "not-now"];
18840
+ if (!valid.includes(args.consent)) {
18841
+ return fail(`consent must be one of: ${valid.join(", ")}`);
18842
+ }
18843
+ if (!args.confirm) {
18844
+ const preview = {
18845
+ action: args.consent,
18846
+ effect: args.consent === "enabled" ? "Anonymous usage telemetry will be activated. Kaddo will send command usage, version, and lifecycle events to telemetry.kaddo.org. No source code, knowledge, prompts or PII is ever transmitted." : args.consent === "disabled" ? "Telemetry will be permanently disabled. No data will be sent. Kaddo will not ask again." : "Consent decision deferred for 24 hours. Telemetry remains inactive. Kaddo will remind again after the deferral expires.",
18847
+ instruction: "Call again with confirm=true to apply."
18848
+ };
18849
+ return ok(preview);
18850
+ }
18851
+ if (args.consent === "not-now") {
18852
+ deferConsent(root);
18853
+ return ok({ applied: true, consent: "unset", deferred: true, message: "Consent deferred for 24 hours." });
18854
+ }
18855
+ const consent = args.consent;
18856
+ persistConsent(root, consent);
18857
+ return ok({ applied: true, consent, message: `Telemetry ${consent}.` });
18858
+ }
18722
18859
 
18723
18860
  // src/multirepo.ts
18724
18861
  import fs5 from "fs";
18725
18862
  import path4 from "path";
18726
- import matter12 from "gray-matter";
18727
- import { parse as parseYaml11, stringify as stringifyYaml6 } from "yaml";
18863
+ import matter13 from "gray-matter";
18864
+ import { parse as parseYaml11, stringify as stringifyYaml7 } from "yaml";
18728
18865
  var ok2 = (data) => ({ ok: true, data });
18729
18866
  var fail2 = (message) => ({ ok: false, message });
18730
18867
  function moduleRepoPath(m2) {
@@ -18733,7 +18870,7 @@ function moduleRepoPath(m2) {
18733
18870
  function loadModulesDescriptor(root) {
18734
18871
  return readYaml(root, ".kaddo/modules.yml") ?? { modules: [] };
18735
18872
  }
18736
- function loadConfig3(root) {
18873
+ function loadConfig4(root) {
18737
18874
  return readYaml(root, ".kaddo/config.yml");
18738
18875
  }
18739
18876
  function projectRole2(config) {
@@ -18799,7 +18936,7 @@ function slugify3(s) {
18799
18936
  return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
18800
18937
  }
18801
18938
  function modulesListTool(root, args) {
18802
- const config = loadConfig3(root);
18939
+ const config = loadConfig4(root);
18803
18940
  if (!config) return fail2("No Kaddo config found. Run `kaddo init` first.");
18804
18941
  if (!isCore2(config)) return fail2("kaddo_modules_list can only be called from a core repository (project.role: core).");
18805
18942
  const descriptor = loadModulesDescriptor(root);
@@ -18827,7 +18964,7 @@ function modulesListTool(root, args) {
18827
18964
  });
18828
18965
  }
18829
18966
  function getModuleContextTool(root, args) {
18830
- const config = loadConfig3(root);
18967
+ const config = loadConfig4(root);
18831
18968
  if (!config) return fail2("No Kaddo config found.");
18832
18969
  const descriptor = loadModulesDescriptor(root);
18833
18970
  const mod = (descriptor.modules ?? []).find((m2) => m2.id === args.module);
@@ -18875,7 +19012,7 @@ function validateWorkItemModulesTool(root, args) {
18875
19012
  if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
18876
19013
  const raw = readText(root, wi.path);
18877
19014
  if (!raw) return fail2(`Could not read Work Item file.`);
18878
- const parsed = matter12(raw);
19015
+ const parsed = matter13(raw);
18879
19016
  const data = parsed.data;
18880
19017
  const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
18881
19018
  const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
@@ -18921,14 +19058,14 @@ function validateWorkItemModulesTool(root, args) {
18921
19058
  });
18922
19059
  }
18923
19060
  function getWorkItemContextTool(root, args) {
18924
- const config = loadConfig3(root);
19061
+ const config = loadConfig4(root);
18925
19062
  if (!config) return fail2("No Kaddo config found.");
18926
19063
  const items = listWorkItems(root);
18927
19064
  const wi = items.find((w3) => w3.id === args.workItemId);
18928
19065
  if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
18929
19066
  const raw = readText(root, wi.path);
18930
19067
  if (!raw) return fail2(`Could not read Work Item file.`);
18931
- const parsed = matter12(raw);
19068
+ const parsed = matter13(raw);
18932
19069
  const data = parsed.data;
18933
19070
  const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
18934
19071
  const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
@@ -19014,7 +19151,7 @@ function suggestBranchStrategyTool(root, args) {
19014
19151
  if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
19015
19152
  const raw = readText(root, wi.path);
19016
19153
  if (!raw) return fail2(`Could not read Work Item file.`);
19017
- const parsed = matter12(raw);
19154
+ const parsed = matter13(raw);
19018
19155
  const data = parsed.data;
19019
19156
  const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
19020
19157
  const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
@@ -19027,7 +19164,7 @@ function suggestBranchStrategyTool(root, args) {
19027
19164
  });
19028
19165
  }
19029
19166
  function exportCapsuleTool(root, args) {
19030
- const config = loadConfig3(root);
19167
+ const config = loadConfig4(root);
19031
19168
  if (!config) return fail2("No Kaddo config found.");
19032
19169
  const role = projectRole2(config);
19033
19170
  const warnings = [];
@@ -19157,7 +19294,7 @@ function exportCapsuleTool(root, args) {
19157
19294
  return ok2({ exported: true, type: capsuleType, module: args.module ?? void 0, paths: { json: jsonPath, md: mdPath }, warnings });
19158
19295
  }
19159
19296
  function modulesDiscoverTool(root, args) {
19160
- const config = loadConfig3(root);
19297
+ const config = loadConfig4(root);
19161
19298
  if (!config) return fail2("No Kaddo config found.");
19162
19299
  if (!isCore2(config)) return fail2("Module discovery is only available from a core repository.");
19163
19300
  const sysName = config.system?.name ?? config.project?.name ?? "";
@@ -19214,7 +19351,7 @@ function modulesDiscoverTool(root, args) {
19214
19351
  descriptor.modules.push({ id: d3.id, name: d3.id, path: d3.path, parent_system: sysName, status: "configured" });
19215
19352
  }
19216
19353
  }
19217
- const ymlContent = stringifyYaml6({ version: descriptor.version ?? 1, system: sysName, workspace_roots: workspaceRoots2, modules: descriptor.modules ?? [] });
19354
+ const ymlContent = stringifyYaml7({ version: descriptor.version ?? 1, system: sysName, workspace_roots: workspaceRoots2, modules: descriptor.modules ?? [] });
19218
19355
  writeDerived(root, ".kaddo/modules.yml", ymlContent);
19219
19356
  return ok2({ discovered, applied: true });
19220
19357
  }
@@ -19260,7 +19397,7 @@ ${list3(data.owners)}
19260
19397
  }
19261
19398
 
19262
19399
  // ../cli/src/core/system-topology.ts
19263
- import { parse as parseYaml12, stringify as stringifyYaml7 } from "yaml";
19400
+ import { parse as parseYaml12, stringify as stringifyYaml8 } from "yaml";
19264
19401
  import fs6 from "fs";
19265
19402
  import path5 from "path";
19266
19403
  import crypto from "crypto";
@@ -19774,14 +19911,14 @@ function systemImpactCandidatesTool(root, args) {
19774
19911
  }
19775
19912
 
19776
19913
  // ../cli/src/services/integrations.ts
19777
- import matter14 from "gray-matter";
19778
- import { parse as parseYaml13, stringify as stringifyYaml8 } from "yaml";
19914
+ import matter15 from "gray-matter";
19915
+ import { parse as parseYaml13, stringify as stringifyYaml9 } from "yaml";
19779
19916
 
19780
19917
  // ../cli/src/core/work-item-write.ts
19781
19918
  import fs7 from "fs";
19782
19919
  import path6 from "path";
19783
19920
  import crypto4 from "crypto";
19784
- import matter13 from "gray-matter";
19921
+ import matter14 from "gray-matter";
19785
19922
  var SECTION_ORDER2 = [
19786
19923
  "Actor",
19787
19924
  "Outcome",
@@ -20829,7 +20966,7 @@ async function integrationsWorkItemTool(root, args) {
20829
20966
  }
20830
20967
 
20831
20968
  // ../cli/src/core/context-pack.ts
20832
- import matter15 from "gray-matter";
20969
+ import matter16 from "gray-matter";
20833
20970
  var CONTEXT_PACK_VERSION = "1";
20834
20971
  var ARCH_DIR4 = "knowledge";
20835
20972
  function readScanJson(dir) {
@@ -20842,7 +20979,7 @@ function readScanJson(dir) {
20842
20979
  }
20843
20980
  }
20844
20981
  function firstParagraph4(markdown) {
20845
- const body = matter15(markdown).content.trim();
20982
+ const body = matter16(markdown).content.trim();
20846
20983
  const para = body.split("\n\n").map((p) => p.trim()).find((p) => p && !p.startsWith("#"));
20847
20984
  return para ?? "";
20848
20985
  }
@@ -22019,7 +22156,16 @@ function createServer(root) {
22019
22156
  server.registerTool(
22020
22157
  "kaddo_project_status",
22021
22158
  { title: "Kaddo project status", description: "Compact project status (state, phase, work items, ownership, graph quality, capsules).", inputSchema: {} },
22022
- async () => toolText(guarded(root, () => projectStatus(root)))
22159
+ async () => {
22160
+ const result = guarded(root, () => projectStatus(root));
22161
+ if (result.ok) {
22162
+ const config = loadConfig2(root);
22163
+ if (getConsentState(config) === "unset" && !isDeferralActive(root)) {
22164
+ result.data.telemetryNotice = consentNotice();
22165
+ }
22166
+ }
22167
+ return toolText(result);
22168
+ }
22023
22169
  );
22024
22170
  server.registerTool(
22025
22171
  "kaddo_list_work_items",
@@ -22344,6 +22490,27 @@ function createServer(root) {
22344
22490
  },
22345
22491
  async (args) => toolText(guarded(root, () => verifyWorkItemTool(root, args)))
22346
22492
  );
22493
+ server.registerTool(
22494
+ "kaddo_telemetry_status",
22495
+ {
22496
+ title: "Telemetry status",
22497
+ description: "Read-only telemetry consent state, registration status, and pending events. Includes a consent notice when telemetry has not been configured.",
22498
+ inputSchema: {}
22499
+ },
22500
+ async () => toolText(guarded(root, () => telemetryStatusTool(root)))
22501
+ );
22502
+ server.registerTool(
22503
+ "kaddo_set_telemetry_consent",
22504
+ {
22505
+ title: "Set telemetry consent",
22506
+ description: "Set telemetry consent to enabled, disabled, or not-now (defer 24h). Without confirm=true returns a preview of the action. With confirm=true applies the change. Writes only .kaddo/config.yml (consent field).",
22507
+ inputSchema: {
22508
+ consent: z4.enum(["enabled", "disabled", "not-now"]).describe("Consent decision: enable telemetry, disable it, or defer for 24 hours"),
22509
+ confirm: z4.boolean().optional().describe("Must be true to apply the change")
22510
+ }
22511
+ },
22512
+ async (args) => toolText(guarded(root, () => setTelemetryConsentTool(root, args)))
22513
+ );
22347
22514
  let installedSkills = [];
22348
22515
  try {
22349
22516
  assertKaddoProject(root);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/mcp",
3
- "version": "3.103.0",
3
+ "version": "3.105.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": {