@kaddo/mcp 3.103.0 → 3.104.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 +199 -45
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10815,18 +10815,23 @@ 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
+ function exists2(p) {
10820
+ return fs3.existsSync(p);
10820
10821
  }
10821
- function readFile2(p2) {
10822
- return fs3.readFileSync(p2, "utf-8");
10822
+ function readFile2(p) {
10823
+ return fs3.readFileSync(p, "utf-8");
10824
+ }
10825
+ function writeFile2(filePath, content) {
10826
+ fs3.mkdirSync(path3.dirname(filePath), { recursive: true });
10827
+ fs3.writeFileSync(filePath, content, "utf-8");
10823
10828
  }
10824
10829
  function readDir2(dirPath) {
10825
10830
  if (!exists2(dirPath)) return [];
10826
10831
  return fs3.readdirSync(dirPath);
10827
10832
  }
10828
- function isFile2(p2) {
10829
- return exists2(p2) && fs3.statSync(p2).isFile();
10833
+ function isFile2(p) {
10834
+ return exists2(p) && fs3.statSync(p).isFile();
10830
10835
  }
10831
10836
  function join3(...parts) {
10832
10837
  return path3.join(...parts);
@@ -11051,8 +11056,8 @@ function isActiveState2(s) {
11051
11056
  return ACTIVE_STATES3.includes(s);
11052
11057
  }
11053
11058
  function lifecycleFolderOf2(filePath) {
11054
- const p2 = filePath.replace(/\\/g, "/");
11055
- const m2 = p2.match(/\/delivery\/work-items\/([^/]+)\//);
11059
+ const p = filePath.replace(/\\/g, "/");
11060
+ const m2 = p.match(/\/delivery\/work-items\/([^/]+)\//);
11056
11061
  if (m2 && isLifecycleState2(m2[1])) return m2[1];
11057
11062
  return null;
11058
11063
  }
@@ -11065,8 +11070,8 @@ function lifecycleStateOf2(item) {
11065
11070
  return "ready";
11066
11071
  }
11067
11072
  var ARCH_DIR3 = "knowledge";
11068
- function toPosix6(p2) {
11069
- return p2.replace(/\\/g, "/");
11073
+ function toPosix6(p) {
11074
+ return p.replace(/\\/g, "/");
11070
11075
  }
11071
11076
  function relativeTo2(dir, filePath) {
11072
11077
  const base = toPosix6(dir).replace(/\/+$/, "");
@@ -12311,10 +12316,10 @@ ${QUALITY2}
12311
12316
  `;
12312
12317
  var EXTERNAL_PATH2 = ".kaddo/external.yml";
12313
12318
  function loadExternalRegistry2(dir) {
12314
- const p2 = join3(dir, EXTERNAL_PATH2);
12315
- if (!exists2(p2)) return [];
12319
+ const p = join3(dir, EXTERNAL_PATH2);
12320
+ if (!exists2(p)) return [];
12316
12321
  try {
12317
- const parsed = parseYaml62(readFile2(p2));
12322
+ const parsed = parseYaml62(readFile2(p));
12318
12323
  return Array.isArray(parsed?.external) ? parsed.external : [];
12319
12324
  } catch {
12320
12325
  return [];
@@ -12326,8 +12331,8 @@ var ALL_STATUSES2 = ["draft", "ready", "in-progress", "blocked", "completed"];
12326
12331
  function scopeStatuses2(scope) {
12327
12332
  return scope === "all" ? { included: ALL_STATUSES2, excluded: ["archived"] } : { included: ACTIVE_STATUSES2, excluded: ["completed", "archived"] };
12328
12333
  }
12329
- function toPosix22(p2) {
12330
- return p2.replace(/\\/g, "/");
12334
+ function toPosix22(p) {
12335
+ return p.replace(/\\/g, "/");
12331
12336
  }
12332
12337
  function slug3(s) {
12333
12338
  return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
@@ -12465,8 +12470,8 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
12465
12470
  };
12466
12471
  }
12467
12472
  var KNOWLEDGE32 = "knowledge";
12468
- function toPosix32(p2) {
12469
- return p2.replace(/\\/g, "/");
12473
+ function toPosix32(p) {
12474
+ return p.replace(/\\/g, "/");
12470
12475
  }
12471
12476
  function slug22(s) {
12472
12477
  return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
@@ -12475,9 +12480,9 @@ function isAdr22(a2) {
12475
12480
  return toPosix32(a2.filePath).includes("/tech/decisions/") && Boolean(a2.type);
12476
12481
  }
12477
12482
  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));
12483
+ const p = join3(dir, KNOWLEDGE32, "product", "capabilities.md");
12484
+ if (!exists2(p)) return [];
12485
+ 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
12486
  }
12482
12487
  function humanMissing2(field) {
12483
12488
  switch (field) {
@@ -15783,7 +15788,7 @@ var AGENT_PROMPTS2 = [
15783
15788
  // Graph relationship quality (metadata hints → precise front matter — VS-056)
15784
15789
  { fileName: "graph-agent.md", content: GRAPH_AGENT2 }
15785
15790
  // Every official prompt ends with its responsibility boundaries + Agent Trace footer.
15786
- ].map((p2) => ({ fileName: p2.fileName, content: withResponsibilityTrace2(p2.fileName, p2.content) }));
15791
+ ].map((p) => ({ fileName: p.fileName, content: withResponsibilityTrace2(p.fileName, p.content) }));
15787
15792
  function withAgentFrontMatter2(fileName, content) {
15788
15793
  const name = fileName.replace(/\.md$/, "");
15789
15794
  const fm = [
@@ -16422,8 +16427,8 @@ var TECHNICAL_RELATIONSHIP_TYPES = /* @__PURE__ */ new Set([
16422
16427
  "implemented-by"
16423
16428
  ]);
16424
16429
  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("..");
16430
+ function isRelative(p) {
16431
+ return typeof p === "string" && p.trim() !== "" && !/^([a-zA-Z]:[\\/]|\/)/.test(p) && !p.includes("..");
16427
16432
  }
16428
16433
  function strList(v) {
16429
16434
  return Array.isArray(v) ? v.map((x2) => typeof x2 === "string" ? x2.trim() : "").filter(Boolean) : [];
@@ -16466,9 +16471,9 @@ function parseTopologyContent(raw, dir) {
16466
16471
  const moduleRaw = typeof e.module === "string" ? e.module.trim() : void 0;
16467
16472
  if (moduleRaw && !validModules.has(moduleRaw)) findings.push({ level: "warning", message: `Entity "${id}" references unregistered module "${moduleRaw}".` });
16468
16473
  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.` });
16474
+ const implementation = strList(rawImpl).filter((p) => {
16475
+ if (isRelative(p)) return true;
16476
+ findings.push({ level: "warning", message: `Entity "${id}" implementation ref "${p}" is not a safe relative path and was dropped.` });
16472
16477
  return false;
16473
16478
  });
16474
16479
  const prov = e.provenance;
@@ -16676,8 +16681,8 @@ function splitSections(body) {
16676
16681
  flush();
16677
16682
  return sections;
16678
16683
  }
16679
- function isPlaceholder(text3) {
16680
- const t = text3.trim();
16684
+ function isPlaceholder(text22) {
16685
+ const t = text22.trim();
16681
16686
  if (!t) return true;
16682
16687
  if (/^tbd$/i.test(t)) return true;
16683
16688
  if (/^_[^_]*_$/.test(t) && !t.includes("\n")) return true;
@@ -16704,15 +16709,15 @@ function extractBullets(body) {
16704
16709
  for (const line of body.split(/\r?\n/)) {
16705
16710
  const m2 = line.match(/^\s*[-*+]\s+(.*)$/);
16706
16711
  if (!m2) continue;
16707
- let text3 = m2[1].trim();
16712
+ let text22 = m2[1].trim();
16708
16713
  let checked = null;
16709
- const cb = text3.match(/^\[([ xX])\]\s*(.*)$/);
16714
+ const cb = text22.match(/^\[([ xX])\]\s*(.*)$/);
16710
16715
  if (cb) {
16711
16716
  checked = cb[1].toLowerCase() === "x";
16712
- text3 = cb[2].trim();
16717
+ text22 = cb[2].trim();
16713
16718
  }
16714
- if (!text3 || isPlaceholder(text3)) continue;
16715
- out.push({ text: text3, checked });
16719
+ if (!text22 || isPlaceholder(text22)) continue;
16720
+ out.push({ text: text22, checked });
16716
16721
  }
16717
16722
  return out;
16718
16723
  }
@@ -17457,7 +17462,7 @@ var SECRET_PATTERNS = [
17457
17462
  /\.pfx$/i
17458
17463
  ];
17459
17464
  function filterSecretPaths(paths) {
17460
- return paths.filter((p2) => !SECRET_PATTERNS.some((re) => re.test(p2)));
17465
+ return paths.filter((p) => !SECRET_PATTERNS.some((re) => re.test(p)));
17461
17466
  }
17462
17467
  function collectImplementationEvidence(dir, workItemId, input) {
17463
17468
  const artifacts = discoverWorkItems2(dir);
@@ -18488,6 +18493,92 @@ function createDefaultRegistry() {
18488
18493
  return registry22;
18489
18494
  }
18490
18495
  var registry = createDefaultRegistry();
18496
+ var CURRENT_CONSENT_VERSION = 1;
18497
+ var DEFERRAL_DURATION_MS = 24 * 60 * 60 * 1e3;
18498
+ function getConsentState(config) {
18499
+ if (!config) return "unset";
18500
+ const t = config.telemetry;
18501
+ if (!t) return "unset";
18502
+ if (t.consent === "enabled") return "enabled";
18503
+ if (t.consent === "disabled") return "disabled";
18504
+ if (t.consent === void 0) {
18505
+ if (t.enabled === true) return "enabled";
18506
+ if (t.enabled === false) return "disabled";
18507
+ }
18508
+ return "unset";
18509
+ }
18510
+ function persistConsent(dir, consent) {
18511
+ const configPath = join3(dir, ".kaddo", "config.yml");
18512
+ if (!exists2(configPath)) return;
18513
+ const raw = readFile2(configPath);
18514
+ const doc = parseYaml102(raw) ?? {};
18515
+ const prev = doc.telemetry ?? {};
18516
+ delete prev.enabled;
18517
+ doc.telemetry = { ...prev, consent, consentVersion: CURRENT_CONSENT_VERSION };
18518
+ writeFile2(configPath, stringifyYaml6(doc));
18519
+ }
18520
+ function isDeferralActive(dir) {
18521
+ const p = join3(dir, ".kaddo", "telemetry-deferred.json");
18522
+ if (!exists2(p)) return false;
18523
+ try {
18524
+ const data = JSON.parse(readFile2(p));
18525
+ return data.deferredUntil > Date.now();
18526
+ } catch {
18527
+ return false;
18528
+ }
18529
+ }
18530
+ function deferConsent(dir) {
18531
+ const p = join3(dir, ".kaddo", "telemetry-deferred.json");
18532
+ writeFile2(p, JSON.stringify({ deferredUntil: Date.now() + DEFERRAL_DURATION_MS }));
18533
+ }
18534
+ function consentNotice() {
18535
+ 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").';
18536
+ }
18537
+ var TELEMETRY_DIR = ".kaddo/telemetry";
18538
+ var IDENTITY_FILE = "identity.json";
18539
+ function identityPath(dir) {
18540
+ return join3(dir, TELEMETRY_DIR, IDENTITY_FILE);
18541
+ }
18542
+ function loadIdentity(dir) {
18543
+ const p = identityPath(dir);
18544
+ if (!exists2(p)) return null;
18545
+ try {
18546
+ return JSON.parse(readFile2(p));
18547
+ } catch {
18548
+ return null;
18549
+ }
18550
+ }
18551
+ var TELEMETRY_DIR2 = ".kaddo/telemetry";
18552
+ var BUFFER_FILE = "buffer.json";
18553
+ function bufferPath(dir) {
18554
+ return join3(dir, TELEMETRY_DIR2, BUFFER_FILE);
18555
+ }
18556
+ function readBuffer(dir) {
18557
+ const p = bufferPath(dir);
18558
+ if (!exists2(p)) return [];
18559
+ try {
18560
+ const data = JSON.parse(readFile2(p));
18561
+ return Array.isArray(data) ? data : [];
18562
+ } catch {
18563
+ return [];
18564
+ }
18565
+ }
18566
+ function bufferSize(dir) {
18567
+ return readBuffer(dir).length;
18568
+ }
18569
+ function getStatus(dir) {
18570
+ const config = loadConfig2(dir);
18571
+ const consent = getConsentState(config);
18572
+ const enabled = consent === "enabled";
18573
+ const identity = loadIdentity(dir);
18574
+ return {
18575
+ consent,
18576
+ enabled,
18577
+ registered: identity?.registered ?? false,
18578
+ pendingEvents: enabled ? bufferSize(dir) : 0,
18579
+ lastError: null
18580
+ };
18581
+ }
18491
18582
 
18492
18583
  // src/tools.ts
18493
18584
  var ok = (data) => ({ ok: true, data });
@@ -18719,12 +18810,45 @@ function verifyWorkItemTool(root, args) {
18719
18810
  return fail("Work Item verification failed.");
18720
18811
  }
18721
18812
  }
18813
+ function telemetryStatusTool(root) {
18814
+ const status = getStatus(root);
18815
+ const result = {
18816
+ consent: status.consent,
18817
+ registered: status.registered,
18818
+ pendingEvents: status.pendingEvents
18819
+ };
18820
+ if (status.consent === "unset" && !isDeferralActive(root)) {
18821
+ result.notice = consentNotice();
18822
+ }
18823
+ return ok(result);
18824
+ }
18825
+ function setTelemetryConsentTool(root, args) {
18826
+ const valid = ["enabled", "disabled", "not-now"];
18827
+ if (!valid.includes(args.consent)) {
18828
+ return fail(`consent must be one of: ${valid.join(", ")}`);
18829
+ }
18830
+ if (!args.confirm) {
18831
+ const preview = {
18832
+ action: args.consent,
18833
+ 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.",
18834
+ instruction: "Call again with confirm=true to apply."
18835
+ };
18836
+ return ok(preview);
18837
+ }
18838
+ if (args.consent === "not-now") {
18839
+ deferConsent(root);
18840
+ return ok({ applied: true, consent: "unset", deferred: true, message: "Consent deferred for 24 hours." });
18841
+ }
18842
+ const consent = args.consent;
18843
+ persistConsent(root, consent);
18844
+ return ok({ applied: true, consent, message: `Telemetry ${consent}.` });
18845
+ }
18722
18846
 
18723
18847
  // src/multirepo.ts
18724
18848
  import fs5 from "fs";
18725
18849
  import path4 from "path";
18726
18850
  import matter12 from "gray-matter";
18727
- import { parse as parseYaml11, stringify as stringifyYaml6 } from "yaml";
18851
+ import { parse as parseYaml11, stringify as stringifyYaml7 } from "yaml";
18728
18852
  var ok2 = (data) => ({ ok: true, data });
18729
18853
  var fail2 = (message) => ({ ok: false, message });
18730
18854
  function moduleRepoPath(m2) {
@@ -18733,7 +18857,7 @@ function moduleRepoPath(m2) {
18733
18857
  function loadModulesDescriptor(root) {
18734
18858
  return readYaml(root, ".kaddo/modules.yml") ?? { modules: [] };
18735
18859
  }
18736
- function loadConfig3(root) {
18860
+ function loadConfig4(root) {
18737
18861
  return readYaml(root, ".kaddo/config.yml");
18738
18862
  }
18739
18863
  function projectRole2(config) {
@@ -18799,7 +18923,7 @@ function slugify3(s) {
18799
18923
  return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
18800
18924
  }
18801
18925
  function modulesListTool(root, args) {
18802
- const config = loadConfig3(root);
18926
+ const config = loadConfig4(root);
18803
18927
  if (!config) return fail2("No Kaddo config found. Run `kaddo init` first.");
18804
18928
  if (!isCore2(config)) return fail2("kaddo_modules_list can only be called from a core repository (project.role: core).");
18805
18929
  const descriptor = loadModulesDescriptor(root);
@@ -18827,7 +18951,7 @@ function modulesListTool(root, args) {
18827
18951
  });
18828
18952
  }
18829
18953
  function getModuleContextTool(root, args) {
18830
- const config = loadConfig3(root);
18954
+ const config = loadConfig4(root);
18831
18955
  if (!config) return fail2("No Kaddo config found.");
18832
18956
  const descriptor = loadModulesDescriptor(root);
18833
18957
  const mod = (descriptor.modules ?? []).find((m2) => m2.id === args.module);
@@ -18921,7 +19045,7 @@ function validateWorkItemModulesTool(root, args) {
18921
19045
  });
18922
19046
  }
18923
19047
  function getWorkItemContextTool(root, args) {
18924
- const config = loadConfig3(root);
19048
+ const config = loadConfig4(root);
18925
19049
  if (!config) return fail2("No Kaddo config found.");
18926
19050
  const items = listWorkItems(root);
18927
19051
  const wi = items.find((w3) => w3.id === args.workItemId);
@@ -19027,7 +19151,7 @@ function suggestBranchStrategyTool(root, args) {
19027
19151
  });
19028
19152
  }
19029
19153
  function exportCapsuleTool(root, args) {
19030
- const config = loadConfig3(root);
19154
+ const config = loadConfig4(root);
19031
19155
  if (!config) return fail2("No Kaddo config found.");
19032
19156
  const role = projectRole2(config);
19033
19157
  const warnings = [];
@@ -19157,7 +19281,7 @@ function exportCapsuleTool(root, args) {
19157
19281
  return ok2({ exported: true, type: capsuleType, module: args.module ?? void 0, paths: { json: jsonPath, md: mdPath }, warnings });
19158
19282
  }
19159
19283
  function modulesDiscoverTool(root, args) {
19160
- const config = loadConfig3(root);
19284
+ const config = loadConfig4(root);
19161
19285
  if (!config) return fail2("No Kaddo config found.");
19162
19286
  if (!isCore2(config)) return fail2("Module discovery is only available from a core repository.");
19163
19287
  const sysName = config.system?.name ?? config.project?.name ?? "";
@@ -19214,7 +19338,7 @@ function modulesDiscoverTool(root, args) {
19214
19338
  descriptor.modules.push({ id: d3.id, name: d3.id, path: d3.path, parent_system: sysName, status: "configured" });
19215
19339
  }
19216
19340
  }
19217
- const ymlContent = stringifyYaml6({ version: descriptor.version ?? 1, system: sysName, workspace_roots: workspaceRoots2, modules: descriptor.modules ?? [] });
19341
+ const ymlContent = stringifyYaml7({ version: descriptor.version ?? 1, system: sysName, workspace_roots: workspaceRoots2, modules: descriptor.modules ?? [] });
19218
19342
  writeDerived(root, ".kaddo/modules.yml", ymlContent);
19219
19343
  return ok2({ discovered, applied: true });
19220
19344
  }
@@ -19260,7 +19384,7 @@ ${list3(data.owners)}
19260
19384
  }
19261
19385
 
19262
19386
  // ../cli/src/core/system-topology.ts
19263
- import { parse as parseYaml12, stringify as stringifyYaml7 } from "yaml";
19387
+ import { parse as parseYaml12, stringify as stringifyYaml8 } from "yaml";
19264
19388
  import fs6 from "fs";
19265
19389
  import path5 from "path";
19266
19390
  import crypto from "crypto";
@@ -19775,7 +19899,7 @@ function systemImpactCandidatesTool(root, args) {
19775
19899
 
19776
19900
  // ../cli/src/services/integrations.ts
19777
19901
  import matter14 from "gray-matter";
19778
- import { parse as parseYaml13, stringify as stringifyYaml8 } from "yaml";
19902
+ import { parse as parseYaml13, stringify as stringifyYaml9 } from "yaml";
19779
19903
 
19780
19904
  // ../cli/src/core/work-item-write.ts
19781
19905
  import fs7 from "fs";
@@ -22019,7 +22143,16 @@ function createServer(root) {
22019
22143
  server.registerTool(
22020
22144
  "kaddo_project_status",
22021
22145
  { title: "Kaddo project status", description: "Compact project status (state, phase, work items, ownership, graph quality, capsules).", inputSchema: {} },
22022
- async () => toolText(guarded(root, () => projectStatus(root)))
22146
+ async () => {
22147
+ const result = guarded(root, () => projectStatus(root));
22148
+ if (result.ok) {
22149
+ const config = loadConfig2(root);
22150
+ if (getConsentState(config) === "unset" && !isDeferralActive(root)) {
22151
+ result.data.telemetryNotice = consentNotice();
22152
+ }
22153
+ }
22154
+ return toolText(result);
22155
+ }
22023
22156
  );
22024
22157
  server.registerTool(
22025
22158
  "kaddo_list_work_items",
@@ -22344,6 +22477,27 @@ function createServer(root) {
22344
22477
  },
22345
22478
  async (args) => toolText(guarded(root, () => verifyWorkItemTool(root, args)))
22346
22479
  );
22480
+ server.registerTool(
22481
+ "kaddo_telemetry_status",
22482
+ {
22483
+ title: "Telemetry status",
22484
+ description: "Read-only telemetry consent state, registration status, and pending events. Includes a consent notice when telemetry has not been configured.",
22485
+ inputSchema: {}
22486
+ },
22487
+ async () => toolText(guarded(root, () => telemetryStatusTool(root)))
22488
+ );
22489
+ server.registerTool(
22490
+ "kaddo_set_telemetry_consent",
22491
+ {
22492
+ title: "Set telemetry consent",
22493
+ 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).",
22494
+ inputSchema: {
22495
+ consent: z4.enum(["enabled", "disabled", "not-now"]).describe("Consent decision: enable telemetry, disable it, or defer for 24 hours"),
22496
+ confirm: z4.boolean().optional().describe("Must be true to apply the change")
22497
+ }
22498
+ },
22499
+ async (args) => toolText(guarded(root, () => setTelemetryConsentTool(root, args)))
22500
+ );
22347
22501
  let installedSkills = [];
22348
22502
  try {
22349
22503
  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.104.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": {