@kaddo/mcp 3.121.0 → 3.121.2

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 +1266 -187
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -389,7 +389,7 @@ function join(...parts) {
389
389
  }
390
390
 
391
391
  // ../cli/src/core/impact-report.ts
392
- import matter11 from "gray-matter";
392
+ import matter12 from "gray-matter";
393
393
 
394
394
  // ../cli/src/core/config.ts
395
395
  import { z } from "zod";
@@ -499,8 +499,8 @@ function loadConfig(dir) {
499
499
  const parsed = configSchema.safeParse(raw ?? {});
500
500
  if (!parsed.success) {
501
501
  const issues = parsed.error.issues.map((i) => {
502
- const path9 = i.path.join(".");
503
- return path9 ? ` - ${path9}: ${i.message}` : ` - ${i.message}`;
502
+ const path10 = i.path.join(".");
503
+ return path10 ? ` - ${path10}: ${i.message}` : ` - ${i.message}`;
504
504
  }).join("\n");
505
505
  throw new ConfigError(`Invalid .kaddo/config.yml:
506
506
  ${issues}`);
@@ -941,11 +941,11 @@ function moduleArtifactCoverage(dir, id) {
941
941
  };
942
942
  }
943
943
  function loadMappedModules(dir) {
944
- const path9 = join(dir, DESCRIPTOR_PATH);
945
- if (!exists(path9)) return [];
944
+ const path10 = join(dir, DESCRIPTOR_PATH);
945
+ if (!exists(path10)) return [];
946
946
  let parsed;
947
947
  try {
948
- parsed = parseYaml4(readFile(path9));
948
+ parsed = parseYaml4(readFile(path10));
949
949
  } catch {
950
950
  return [];
951
951
  }
@@ -2452,7 +2452,7 @@ function sectionParagraph2(md, title) {
2452
2452
  }
2453
2453
  return "";
2454
2454
  }
2455
- function parseCapsule(id, path9, md) {
2455
+ function parseCapsule(id, path10, md) {
2456
2456
  const { data } = matter4(md);
2457
2457
  const updatedAt = data.updated_at ? String(data.updated_at) : void 0;
2458
2458
  let ageDays = null;
@@ -2462,7 +2462,7 @@ function parseCapsule(id, path9, md) {
2462
2462
  }
2463
2463
  return {
2464
2464
  id,
2465
- path: path9,
2465
+ path: path10,
2466
2466
  system: data.system ? String(data.system) : id,
2467
2467
  owner: data.owner ? String(data.owner) : void 0,
2468
2468
  updatedAt,
@@ -2735,6 +2735,252 @@ function parseWorkItemResources(fm) {
2735
2735
  return out;
2736
2736
  }
2737
2737
 
2738
+ // ../cli/src/core/deprecation.ts
2739
+ import matter7 from "gray-matter";
2740
+ var DEPRECATION_ARTIFACT_TYPE = "deprecation-plan";
2741
+ var DEPRECATION_PLANS_DIR = "knowledge/legacy/deprecations";
2742
+ var TARGET_TYPES = [
2743
+ "module",
2744
+ "route",
2745
+ "function",
2746
+ "ui",
2747
+ "job",
2748
+ "database-object",
2749
+ "integration",
2750
+ "config",
2751
+ "other"
2752
+ ];
2753
+ var LIFECYCLE_STATES2 = [
2754
+ "candidate",
2755
+ "usage-validated",
2756
+ "soft-deprecated",
2757
+ "disabled",
2758
+ "removed-devtest",
2759
+ "production-ready",
2760
+ "production-changed",
2761
+ "observing",
2762
+ "completed",
2763
+ "deprecated-retained"
2764
+ ];
2765
+ var ENVIRONMENTS = ["development", "test", "production"];
2766
+ var ENVIRONMENT_STATES = ["active", "soft-deprecated", "disabled", "removed"];
2767
+ var EVIDENCE_SIGNALS = [
2768
+ "static",
2769
+ "runtime",
2770
+ "database",
2771
+ "external_consumers",
2772
+ "human_review"
2773
+ ];
2774
+ function str2(v) {
2775
+ return typeof v === "string" && v.trim() ? v.trim() : null;
2776
+ }
2777
+ function parseWorkItemDeprecationTargets(fm) {
2778
+ const v = fm.deprecation_targets;
2779
+ if (!Array.isArray(v)) return [];
2780
+ const out = [];
2781
+ for (const raw of v) {
2782
+ if (typeof raw === "string") {
2783
+ const s = raw.trim();
2784
+ if (!s) continue;
2785
+ const slash = s.indexOf("/");
2786
+ if (slash > 0) out.push({ plan: s.slice(0, slash).trim(), target: s.slice(slash + 1).trim() });
2787
+ else out.push({ plan: null, target: s });
2788
+ continue;
2789
+ }
2790
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
2791
+ const o2 = raw;
2792
+ const target = str2(o2.target) ?? str2(o2.id);
2793
+ if (!target) continue;
2794
+ out.push({ plan: str2(o2.plan), target });
2795
+ }
2796
+ }
2797
+ return out;
2798
+ }
2799
+ function strArray2(v) {
2800
+ if (Array.isArray(v)) return v.map((x2) => String(x2).trim()).filter(Boolean);
2801
+ if (typeof v === "string" && v.trim()) return [v.trim()];
2802
+ return [];
2803
+ }
2804
+ function isValidTargetType(t) {
2805
+ return TARGET_TYPES.includes(t);
2806
+ }
2807
+ function isValidLifecycleState(s) {
2808
+ return LIFECYCLE_STATES2.includes(s);
2809
+ }
2810
+ function isValidEnvironmentState(s) {
2811
+ return ENVIRONMENT_STATES.includes(s);
2812
+ }
2813
+ function parseAssessment(v) {
2814
+ if (!v || typeof v !== "object" || Array.isArray(v)) {
2815
+ return { unusedConfidence: null, blastRadius: null, reversibility: null };
2816
+ }
2817
+ const o2 = v;
2818
+ return {
2819
+ unusedConfidence: str2(o2.unused_confidence),
2820
+ blastRadius: str2(o2.blast_radius),
2821
+ reversibility: str2(o2.reversibility)
2822
+ };
2823
+ }
2824
+ function parseUsageEvidence(v) {
2825
+ if (!v || typeof v !== "object" || Array.isArray(v)) return {};
2826
+ const out = {};
2827
+ for (const signal of EVIDENCE_SIGNALS) {
2828
+ const raw = v[signal];
2829
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
2830
+ const o2 = raw;
2831
+ out[signal] = {
2832
+ status: str2(o2.status) ?? "unknown",
2833
+ quality: str2(o2.quality) ?? "unknown",
2834
+ detail: str2(o2.detail)
2835
+ };
2836
+ }
2837
+ return out;
2838
+ }
2839
+ function parseSoftDeprecation(v) {
2840
+ if (!v || typeof v !== "object" || Array.isArray(v)) return null;
2841
+ const o2 = v;
2842
+ const windowRaw = o2.observation_window_days;
2843
+ const window = typeof windowRaw === "number" && Number.isFinite(windowRaw) && windowRaw >= 0 ? Math.floor(windowRaw) : null;
2844
+ const exitCriteria = Array.isArray(o2.exit_criteria) ? o2.exit_criteria.map((c) => {
2845
+ if (typeof c === "string") return c.trim() ? { description: c.trim(), met: false } : null;
2846
+ if (c && typeof c === "object" && !Array.isArray(c)) {
2847
+ const co = c;
2848
+ const description = str2(co.description) ?? str2(co.criterion);
2849
+ if (!description) return null;
2850
+ return { description, met: co.met === true };
2851
+ }
2852
+ return null;
2853
+ }).filter((c) => Boolean(c)) : [];
2854
+ return {
2855
+ strategy: str2(o2.strategy),
2856
+ mechanism: str2(o2.mechanism),
2857
+ startedAt: str2(o2.started_at),
2858
+ observationWindowDays: window,
2859
+ exitCriteria
2860
+ };
2861
+ }
2862
+ function parseEnvironments(v) {
2863
+ if (!v || typeof v !== "object" || Array.isArray(v)) return {};
2864
+ const out = {};
2865
+ for (const env of ENVIRONMENTS) {
2866
+ const s = str2(v[env]);
2867
+ if (s) out[env] = s;
2868
+ }
2869
+ return out;
2870
+ }
2871
+ function parseTarget(raw) {
2872
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
2873
+ const o2 = raw;
2874
+ const id = str2(o2.id);
2875
+ if (!id) return null;
2876
+ return {
2877
+ id,
2878
+ type: str2(o2.type) ?? "other",
2879
+ locator: str2(o2.locator),
2880
+ title: str2(o2.title),
2881
+ lifecycle: str2(o2.lifecycle) ?? "candidate",
2882
+ environments: parseEnvironments(o2.environments),
2883
+ assessment: parseAssessment(o2.assessment),
2884
+ usageEvidence: parseUsageEvidence(o2.usage_evidence),
2885
+ dependsOnRemoval: strArray2(o2.depends_on_removal),
2886
+ resource: str2(o2.resource),
2887
+ module: str2(o2.module),
2888
+ softDeprecation: parseSoftDeprecation(o2.soft_deprecation)
2889
+ };
2890
+ }
2891
+ function parseTargets(v) {
2892
+ if (!Array.isArray(v)) return [];
2893
+ const out = [];
2894
+ for (const raw of v) {
2895
+ const t = parseTarget(raw);
2896
+ if (t) out.push(t);
2897
+ }
2898
+ return out;
2899
+ }
2900
+ function lifecycleCountsOf(targets) {
2901
+ const counts = {};
2902
+ for (const t of targets) counts[t.lifecycle] = (counts[t.lifecycle] ?? 0) + 1;
2903
+ return counts;
2904
+ }
2905
+ function purposeFromBody2(body) {
2906
+ const m2 = body.match(/^#{1,2}\s+Purpose\s*\n+([\s\S]*?)(?=\n#{1,2}\s|\s*$)/m);
2907
+ const text3 = m2 ? m2[1].trim() : "";
2908
+ return text3 || null;
2909
+ }
2910
+ function getDeprecationPlans(dir) {
2911
+ return discoverKnowledge(dir).filter((a2) => a2.type === DEPRECATION_ARTIFACT_TYPE).map((a2) => {
2912
+ const fm = a2.rawFrontmatter;
2913
+ const targets = parseTargets(fm.targets);
2914
+ return {
2915
+ id: a2.id || str2(fm.id) || a2.title,
2916
+ title: a2.title || str2(fm.title) || a2.id,
2917
+ path: a2.relPath,
2918
+ status: str2(fm.status),
2919
+ targetCount: targets.length,
2920
+ lifecycleCounts: lifecycleCountsOf(targets)
2921
+ };
2922
+ }).sort((x2, y) => x2.id.localeCompare(y.id));
2923
+ }
2924
+ function getDeprecationPlan(dir, id) {
2925
+ const match = discoverKnowledge(dir).find(
2926
+ (a2) => a2.type === DEPRECATION_ARTIFACT_TYPE && (a2.id === id || a2.title === id)
2927
+ );
2928
+ if (!match) return null;
2929
+ const raw = readFile(match.filePath);
2930
+ const parsed = matter7(raw);
2931
+ const fm = parsed.data ?? {};
2932
+ const body = parsed.content.trim();
2933
+ const targets = parseTargets(fm.targets);
2934
+ return {
2935
+ id: str2(fm.id) ?? match.id,
2936
+ title: str2(fm.title) ?? match.title,
2937
+ path: match.relPath,
2938
+ status: str2(fm.status),
2939
+ targetCount: targets.length,
2940
+ lifecycleCounts: lifecycleCountsOf(targets),
2941
+ purpose: str2(fm.purpose) ?? purposeFromBody2(body),
2942
+ targets,
2943
+ markdownBody: body
2944
+ };
2945
+ }
2946
+ function validateTarget(target) {
2947
+ const findings = [];
2948
+ const at = target.id;
2949
+ if (!isValidTargetType(String(target.type))) {
2950
+ findings.push({ severity: "error", target: at, message: `Unknown target type: ${target.type}` });
2951
+ }
2952
+ if (!isValidLifecycleState(String(target.lifecycle))) {
2953
+ findings.push({ severity: "error", target: at, message: `Unknown lifecycle state: ${target.lifecycle}` });
2954
+ }
2955
+ if (!target.locator) {
2956
+ findings.push({ severity: "warning", target: at, message: "Target has no locator \u2014 it may be ambiguous." });
2957
+ }
2958
+ for (const [env, state] of Object.entries(target.environments)) {
2959
+ if (!isValidEnvironmentState(String(state))) {
2960
+ findings.push({ severity: "error", target: at, message: `Unknown environment state for ${env}: ${state}` });
2961
+ }
2962
+ }
2963
+ const prodActive = target.environments.production === "active";
2964
+ if (target.lifecycle === "completed") {
2965
+ const stillActive = ENVIRONMENTS.filter((e) => target.environments[e] === "active");
2966
+ if (stillActive.length > 0) {
2967
+ findings.push({
2968
+ severity: "error",
2969
+ target: at,
2970
+ message: `Target is 'completed' but still active in: ${stillActive.join(", ")}.`
2971
+ });
2972
+ }
2973
+ }
2974
+ if ((target.lifecycle === "production-changed" || target.lifecycle === "observing") && prodActive) {
2975
+ findings.push({
2976
+ severity: "error",
2977
+ target: at,
2978
+ message: `Target is '${target.lifecycle}' but production is still 'active'.`
2979
+ });
2980
+ }
2981
+ return findings;
2982
+ }
2983
+
2738
2984
  // ../cli/src/core/graph.ts
2739
2985
  var KNOWLEDGE3 = "knowledge";
2740
2986
  var RESOURCE_ROLE_EDGE = {
@@ -2779,9 +3025,9 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
2779
3025
  ];
2780
3026
  const presentLayers = [];
2781
3027
  for (const layer2 of layerDocs) {
2782
- const path9 = layer2.files.map((f3) => `${KNOWLEDGE3}/${f3}`).find((rel) => exists(join(dir, rel)));
2783
- if (path9) {
2784
- addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path9 });
3028
+ const path10 = layer2.files.map((f3) => `${KNOWLEDGE3}/${f3}`).find((rel) => exists(join(dir, rel)));
3029
+ if (path10) {
3030
+ addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path10 });
2785
3031
  presentLayers.push(layer2.id);
2786
3032
  }
2787
3033
  }
@@ -2839,6 +3085,33 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
2839
3085
  addEdge(modId, resId, "depends_on");
2840
3086
  }
2841
3087
  }
3088
+ for (const summary of getDeprecationPlans(dir)) {
3089
+ const plan = getDeprecationPlan(dir, summary.id);
3090
+ if (!plan) continue;
3091
+ const planId = `deprecation-plan:${plan.id}`;
3092
+ addNode({ id: planId, type: "deprecation-plan", label: `${plan.id} ${plan.title}`.trim(), path: plan.path, status: plan.status ?? void 0 });
3093
+ for (const target of plan.targets) {
3094
+ const targetId = `deprecation-target:${target.id}`;
3095
+ addNode({ id: targetId, type: "deprecation-target", label: target.title ? `${target.id} ${target.title}` : target.id, path: plan.path, status: target.lifecycle });
3096
+ addEdge(planId, targetId, "targets");
3097
+ if (target.module) {
3098
+ const moduleId = `module:${target.module}`;
3099
+ addNode({ id: moduleId, type: "module", label: target.module });
3100
+ addEdge(targetId, moduleId, "targets");
3101
+ }
3102
+ if (target.resource) {
3103
+ const resourceId = `resource:${target.resource}`;
3104
+ const title = resourceTitleById.get(target.resource);
3105
+ addNode({ id: resourceId, type: "project-resource", label: title ? `${target.resource} ${title}` : target.resource });
3106
+ addEdge(targetId, resourceId, "targets");
3107
+ }
3108
+ for (const dependency of target.dependsOnRemoval) {
3109
+ const dependencyId = `deprecation-target:${dependency}`;
3110
+ addNode({ id: dependencyId, type: "deprecation-target", label: dependency });
3111
+ addEdge(targetId, dependencyId, "depends_on_removal");
3112
+ }
3113
+ }
3114
+ }
2842
3115
  for (const wi of selectedWIs) {
2843
3116
  const id = wi.id || wi.title;
2844
3117
  if (!id || !id.trim()) continue;
@@ -2869,6 +3142,11 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
2869
3142
  addNode({ id: resId, type: "project-resource", label: title ? `${rel.id} ${title}`.trim() : rel.id });
2870
3143
  addEdge(wiNodeId, resId, RESOURCE_ROLE_EDGE[rel.role] ?? "uses");
2871
3144
  }
3145
+ for (const ref of parseWorkItemDeprecationTargets(wi.rawFrontmatter)) {
3146
+ const targetId = `deprecation-target:${ref.target}`;
3147
+ addNode({ id: targetId, type: "deprecation-target", label: ref.target });
3148
+ addEdge(wiNodeId, targetId, "targets");
3149
+ }
2872
3150
  for (const dec of wi.decisions) {
2873
3151
  if (!dec || !dec.trim()) continue;
2874
3152
  const adrId = `adr:${dec}`;
@@ -3247,7 +3525,7 @@ function loadGraphHints(dir) {
3247
3525
  }
3248
3526
 
3249
3527
  // ../cli/src/services/installed-skills.ts
3250
- import matter7 from "gray-matter";
3528
+ import matter8 from "gray-matter";
3251
3529
  var SKILLS_DIR = join("knowledge", "skills");
3252
3530
  function discoverInstalledSkills(dir) {
3253
3531
  const base = join(dir, SKILLS_DIR);
@@ -3257,7 +3535,7 @@ function discoverInstalledSkills(dir) {
3257
3535
  const skillFile = join(base, entry, "skill.md");
3258
3536
  if (!isFile(skillFile)) continue;
3259
3537
  try {
3260
- const { data } = matter7(readFile(skillFile));
3538
+ const { data } = matter8(readFile(skillFile));
3261
3539
  if (data.type && String(data.type) !== "skill") continue;
3262
3540
  const id = String(data.id ?? entry);
3263
3541
  out.push({
@@ -4337,13 +4615,13 @@ function meaningful(text3) {
4337
4615
  });
4338
4616
  }
4339
4617
  function readPocSummary(dir) {
4340
- const path9 = join(dir, POC_ARTIFACT_PATH);
4341
- if (!exists(path9)) {
4618
+ const path10 = join(dir, POC_ARTIFACT_PATH);
4619
+ if (!exists(path10)) {
4342
4620
  return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
4343
4621
  }
4344
4622
  let markdown = "";
4345
4623
  try {
4346
- markdown = readFile(path9);
4624
+ markdown = readFile(path10);
4347
4625
  } catch {
4348
4626
  return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
4349
4627
  }
@@ -4362,7 +4640,7 @@ function readPocSummary(dir) {
4362
4640
 
4363
4641
  // ../cli/src/core/poc-report.ts
4364
4642
  import { createHash } from "crypto";
4365
- import matter8 from "gray-matter";
4643
+ import matter9 from "gray-matter";
4366
4644
  var POC_REPORT_PREFIX = "poc-report-v";
4367
4645
  var SECRET_LINE = /(^|\s)(?:[A-Z][A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|API_KEY|PRIVATE_KEY)|(?:token|secret|password|api[_-]?key))\s*[:=]\s*\S+/i;
4368
4646
  var reportName = /^poc-report-v(\d{3,})\.md$/;
@@ -4378,11 +4656,11 @@ function redactSecrets(value) {
4378
4656
  return line.replace(/([:=]\s*)\S+.*/, "$1[redacted]");
4379
4657
  }).join("\n");
4380
4658
  }
4381
- function source(dir, path9, kind) {
4382
- const absolute = join(dir, path9);
4659
+ function source(dir, path10, kind) {
4660
+ const absolute = join(dir, path10);
4383
4661
  if (!exists(absolute)) return null;
4384
4662
  const raw = readFile(absolute);
4385
- return { path: posix(path9), kind, digest: sha(raw), content: redactSecrets(raw) };
4663
+ return { path: posix(path10), kind, digest: sha(raw), content: redactSecrets(raw) };
4386
4664
  }
4387
4665
  function reportFiles(dir) {
4388
4666
  const delivery = join(dir, "knowledge", "delivery");
@@ -4390,7 +4668,7 @@ function reportFiles(dir) {
4390
4668
  const match = name.match(reportName);
4391
4669
  if (!match) return [];
4392
4670
  try {
4393
- const parsed = matter8(readFile(join(delivery, name)));
4671
+ const parsed = matter9(readFile(join(delivery, name)));
4394
4672
  const fingerprint = typeof parsed.data.source_fingerprint === "string" ? parsed.data.source_fingerprint : "";
4395
4673
  const manifest = parsed.data.source_manifest;
4396
4674
  const sourceManifest = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? Object.fromEntries(Object.entries(manifest).filter(([, digest]) => typeof digest === "string")) : {};
@@ -4422,9 +4700,9 @@ function buildPocReportContext(dir) {
4422
4700
  };
4423
4701
  }
4424
4702
  const sources = [];
4425
- const add = (path9, kind) => {
4426
- if (sources.some((item2) => item2.path === posix(path9))) return;
4427
- const item = source(dir, path9, kind);
4703
+ const add = (path10, kind) => {
4704
+ if (sources.some((item2) => item2.path === posix(path10))) return;
4705
+ const item = source(dir, path10, kind);
4428
4706
  if (item) sources.push(item);
4429
4707
  };
4430
4708
  add(POC_ARTIFACT_PATH, "poc");
@@ -4452,7 +4730,7 @@ function buildPocReportContext(dir) {
4452
4730
  const sourceFingerprint = sha(sources.map((item) => `${item.path}\0${item.digest}`).join("\n"));
4453
4731
  const status = !latestReport ? "missing" : latestReport.sourceFingerprint === sourceFingerprint ? "current" : "stale";
4454
4732
  const currentManifest = Object.fromEntries(sources.map((item) => [item.path, item.digest]));
4455
- const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((path9) => currentManifest[path9] !== latestReport?.sourceManifest[path9]).sort() : [];
4733
+ const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((path10) => currentManifest[path10] !== latestReport?.sourceManifest[path10]).sort() : [];
4456
4734
  const sourceList = sources.map((item) => `- ${item.path} (${item.kind})`).join("\n") || "- No sources selected.";
4457
4735
  return {
4458
4736
  eligible: true,
@@ -4603,10 +4881,10 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
4603
4881
  const q3 = (rel) => analyzeKnowledgeArtifact(dir, rel);
4604
4882
  const resolveAgent = (agent) => {
4605
4883
  const file = agent.endsWith(".md") ? agent : `${agent}.md`;
4606
- const path9 = agentInstallPath(file);
4607
- const installed = isFile(join(dir, path9));
4884
+ const path10 = agentInstallPath(file);
4885
+ const installed = isFile(join(dir, path10));
4608
4886
  const group = agentGroupOf(file);
4609
- return { agentPath: path9, agentInstalled: installed, installCommand: installed ? void 0 : `kaddo add agents --group ${group}` };
4887
+ return { agentPath: path10, agentInstalled: installed, installCommand: installed ? void 0 : `kaddo add agents --group ${group}` };
4610
4888
  };
4611
4889
  const moduleRepo = isModule(config);
4612
4890
  const coreRepo = isCore(config);
@@ -5036,7 +5314,7 @@ function deriveDeliveryCompletedStatus(dir) {
5036
5314
  }
5037
5315
 
5038
5316
  // ../cli/src/core/assets.ts
5039
- import matter9 from "gray-matter";
5317
+ import matter10 from "gray-matter";
5040
5318
 
5041
5319
  // ../cli/src/core/version.ts
5042
5320
  import { fileURLToPath } from "url";
@@ -8347,7 +8625,7 @@ function canonicalSkills() {
8347
8625
  }
8348
8626
  function versionOf(content) {
8349
8627
  try {
8350
- const v = matter9(content).data.version;
8628
+ const v = matter10(content).data.version;
8351
8629
  return v === void 0 || v === null ? null : String(v);
8352
8630
  } catch {
8353
8631
  return null;
@@ -9187,7 +9465,7 @@ function renderRouteCompact(route) {
9187
9465
  }
9188
9466
 
9189
9467
  // ../cli/src/core/metadata-health.ts
9190
- import matter10 from "gray-matter";
9468
+ import matter11 from "gray-matter";
9191
9469
  var KNOWLEDGE_FILES2 = [
9192
9470
  "knowledge/business/business.md",
9193
9471
  "knowledge/product/product.md",
@@ -9207,7 +9485,7 @@ function analyzeMetadataHealth(dir) {
9207
9485
  let data;
9208
9486
  try {
9209
9487
  const raw = readFile(p);
9210
- data = matter10(raw).data;
9488
+ data = matter11(raw).data;
9211
9489
  } catch {
9212
9490
  continue;
9213
9491
  }
@@ -9239,7 +9517,7 @@ function analyzeMetadataHealth(dir) {
9239
9517
  if (isFile(p) && entry.endsWith(".md")) {
9240
9518
  try {
9241
9519
  const raw = readFile(p);
9242
- const fm = matter10(raw).data;
9520
+ const fm = matter11(raw).data;
9243
9521
  if (fm.status === "done") {
9244
9522
  const rel = p.replace(dir + "/", "").replace(dir + "\\", "").replace(/\\/g, "/");
9245
9523
  findings.push({
@@ -10107,7 +10385,7 @@ function buildGuardHistory(dir) {
10107
10385
  const d3 = hotspotDir(t.code_path);
10108
10386
  hotspotMap.set(d3, (hotspotMap.get(d3) ?? 0) + 1);
10109
10387
  }
10110
- const hotspots = [...hotspotMap.entries()].map(([path9, warnings]) => ({ path: path9, warnings })).sort((a2, b2) => b2.warnings - a2.warnings);
10388
+ const hotspots = [...hotspotMap.entries()].map(([path10, warnings]) => ({ path: path10, warnings })).sort((a2, b2) => b2.warnings - a2.warnings);
10111
10389
  return {
10112
10390
  available: true,
10113
10391
  total_runs: sorted.length,
@@ -10194,7 +10472,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
10194
10472
  }
10195
10473
  }
10196
10474
  try {
10197
- const body = matter11(readFile(wi.filePath)).content;
10475
+ const body = matter12(readFile(wi.filePath)).content;
10198
10476
  if (hasSection(body, /acceptance|criterios de aceptaci/i)) withAcceptance++;
10199
10477
  else gaps.missing_acceptance_criteria.push(gapItem(wi, "Add an `## Acceptance Criteria` section."));
10200
10478
  if (hasSection(body, /definition of done|^#{1,6}\s*dod\b|definici[oó]n de (terminado|hecho)/i)) withDoD++;
@@ -10907,7 +11185,7 @@ function serializeDriftJson(r2) {
10907
11185
  }
10908
11186
 
10909
11187
  // src/workitems.ts
10910
- import matter12 from "gray-matter";
11188
+ import matter13 from "gray-matter";
10911
11189
  var VALID_SOURCES2 = /* @__PURE__ */ new Set(["manual", "roadmap", "jira", "github", "notion", "xlsx", "csv", "api", "external", "unknown"]);
10912
11190
  function optStr3(v) {
10913
11191
  return typeof v === "string" && v.trim() ? v.trim() : void 0;
@@ -10930,7 +11208,7 @@ function parseSource(data) {
10930
11208
  return { type: "unknown", inferred: true };
10931
11209
  }
10932
11210
  var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["draft", "ready", "in-progress", "blocked"]);
10933
- function strArray2(v) {
11211
+ function strArray3(v) {
10934
11212
  return Array.isArray(v) ? v.map((x2) => String(x2)).filter(Boolean) : [];
10935
11213
  }
10936
11214
  function lifecycleOf(data, relPath) {
@@ -10998,15 +11276,15 @@ function listWorkItems(root) {
10998
11276
  if (raw === null) continue;
10999
11277
  let parsed;
11000
11278
  try {
11001
- parsed = matter12(raw);
11279
+ parsed = matter13(raw);
11002
11280
  } catch {
11003
11281
  continue;
11004
11282
  }
11005
11283
  const data = parsed.data;
11006
11284
  if (!data.type) continue;
11007
11285
  const status = lifecycleOf(data, rel);
11008
- const code = strArray2(data.code);
11009
- const domains = strArray2(data.domains);
11286
+ const code = strArray3(data.code);
11287
+ const domains = strArray3(data.domains);
11010
11288
  const source3 = parseSource(data);
11011
11289
  const readiness = status === "draft" ? assessReadiness(data, parsed.content, code, domains, source3) : void 0;
11012
11290
  items.push({
@@ -11018,9 +11296,9 @@ function listWorkItems(root) {
11018
11296
  path: rel,
11019
11297
  summary: String(data.summary ?? "") || firstParagraph2(parsed.content),
11020
11298
  code,
11021
- capabilities: strArray2(data.capabilities),
11022
- decisions: strArray2(data.decisions),
11023
- capsules: strArray2(data.capsules),
11299
+ capabilities: strArray3(data.capabilities),
11300
+ decisions: strArray3(data.decisions),
11301
+ capsules: strArray3(data.capsules),
11024
11302
  source: source3,
11025
11303
  domains,
11026
11304
  readiness,
@@ -11497,14 +11775,14 @@ function getPrompt(root, name) {
11497
11775
  }
11498
11776
 
11499
11777
  // src/tools.ts
11500
- import matter16 from "gray-matter";
11778
+ import matter20 from "gray-matter";
11501
11779
 
11502
11780
  // ../cli/dist/core.js
11503
11781
  import fs3 from "fs";
11504
11782
  import path3 from "path";
11505
11783
  import { z as z3 } from "zod";
11506
11784
  import { parse as parseYaml10, stringify as stringifyYaml5 } from "yaml";
11507
- import matter13 from "gray-matter";
11785
+ import matter14 from "gray-matter";
11508
11786
  import matter22 from "gray-matter";
11509
11787
  import { parse as parseYaml62, stringify as stringifyYaml42 } from "yaml";
11510
11788
  import { parse as parseYaml22 } from "yaml";
@@ -11514,28 +11792,31 @@ import { parse as parseYaml42, stringify as stringifyYaml22 } from "yaml";
11514
11792
  import matter32 from "gray-matter";
11515
11793
  import matter42 from "gray-matter";
11516
11794
  import matter52 from "gray-matter";
11795
+ import matter62 from "gray-matter";
11517
11796
  import { parse as parseYaml72 } from "yaml";
11518
11797
  import { createHash as createHash2 } from "crypto";
11519
- import matter62 from "gray-matter";
11520
11798
  import matter72 from "gray-matter";
11799
+ import matter82 from "gray-matter";
11521
11800
  import { fileURLToPath as fileURLToPath2 } from "url";
11522
11801
  import { dirname as dirname2, join as join22, parse as parse2 } from "path";
11523
11802
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
11524
- import matter82 from "gray-matter";
11525
11803
  import matter92 from "gray-matter";
11526
11804
  import matter102 from "gray-matter";
11805
+ import matter112 from "gray-matter";
11527
11806
  import { parse as parseYaml82, stringify as stringifyYaml52 } from "yaml";
11528
11807
  import fs4 from "fs";
11529
11808
  import path4 from "path";
11530
11809
  import crypto2 from "crypto";
11531
- import matter112 from "gray-matter";
11810
+ import matter122 from "gray-matter";
11532
11811
  import crypto3 from "crypto";
11533
11812
  import fs5 from "fs";
11534
- import matter122 from "gray-matter";
11535
11813
  import matter132 from "gray-matter";
11814
+ import matter142 from "gray-matter";
11536
11815
  import { parse as parseYaml92, stringify as stringifyYaml6 } from "yaml";
11537
11816
  import { parse as parseYaml102, stringify as stringifyYaml7 } from "yaml";
11538
- import matter14 from "gray-matter";
11817
+ import matter15 from "gray-matter";
11818
+ import matter16 from "gray-matter";
11819
+ import matter17 from "gray-matter";
11539
11820
  function exists2(p) {
11540
11821
  return fs3.existsSync(p);
11541
11822
  }
@@ -11654,8 +11935,8 @@ function loadConfig2(dir) {
11654
11935
  const parsed = configSchema2.safeParse(raw ?? {});
11655
11936
  if (!parsed.success) {
11656
11937
  const issues = parsed.error.issues.map((i) => {
11657
- const path52 = i.path.join(".");
11658
- return path52 ? ` - ${path52}: ${i.message}` : ` - ${i.message}`;
11938
+ const path62 = i.path.join(".");
11939
+ return path62 ? ` - ${path62}: ${i.message}` : ` - ${i.message}`;
11659
11940
  }).join("\n");
11660
11941
  throw new ConfigError2(`Invalid .kaddo/config.yml:
11661
11942
  ${issues}`);
@@ -11711,7 +11992,7 @@ function parseImpactAnalysis2(data) {
11711
11992
  }
11712
11993
  function parseArtifact2(filePath, raw) {
11713
11994
  try {
11714
- const { data } = matter13(raw);
11995
+ const { data } = matter14(raw);
11715
11996
  const globs = Array.isArray(data.code) ? data.code.filter(Boolean) : [];
11716
11997
  return {
11717
11998
  filePath,
@@ -11764,7 +12045,7 @@ function readArtifacts2(archDir) {
11764
12045
  walkDir(archDir);
11765
12046
  return artifacts;
11766
12047
  }
11767
- var LIFECYCLE_STATES2 = [
12048
+ var LIFECYCLE_STATES3 = [
11768
12049
  "draft",
11769
12050
  "ready",
11770
12051
  "in-progress",
@@ -11779,7 +12060,7 @@ var LEGACY_STATUS_MAP2 = {
11779
12060
  canceled: "archived",
11780
12061
  "in-progress": "in-progress"
11781
12062
  };
11782
- var STATE_SET2 = new Set(LIFECYCLE_STATES2);
12063
+ var STATE_SET2 = new Set(LIFECYCLE_STATES3);
11783
12064
  function isLifecycleState2(s) {
11784
12065
  return STATE_SET2.has(s);
11785
12066
  }
@@ -11801,7 +12082,7 @@ function lifecycleStateOf2(item) {
11801
12082
  return "ready";
11802
12083
  }
11803
12084
  function emptyLifecycleCounts2() {
11804
- return Object.fromEntries(LIFECYCLE_STATES2.map((s) => [s, 0]));
12085
+ return Object.fromEntries(LIFECYCLE_STATES3.map((s) => [s, 0]));
11805
12086
  }
11806
12087
  var ARCH_DIR3 = "knowledge";
11807
12088
  function toPosix7(p) {
@@ -11859,11 +12140,11 @@ function moduleArtifactCoverage2(dir, id) {
11859
12140
  };
11860
12141
  }
11861
12142
  function loadMappedModules2(dir) {
11862
- const path52 = join3(dir, DESCRIPTOR_PATH2);
11863
- if (!exists2(path52)) return [];
12143
+ const path62 = join3(dir, DESCRIPTOR_PATH2);
12144
+ if (!exists2(path62)) return [];
11864
12145
  let parsed;
11865
12146
  try {
11866
- parsed = parseYaml32(readFile2(path52));
12147
+ parsed = parseYaml32(readFile2(path62));
11867
12148
  } catch {
11868
12149
  return [];
11869
12150
  }
@@ -13266,10 +13547,10 @@ function suggestInitiativesForExternalItem(dir, integration, externalId) {
13266
13547
  );
13267
13548
  }
13268
13549
  var RESOURCE_ARTIFACT_TYPE2 = "project-resource";
13269
- function str2(v) {
13550
+ function str3(v) {
13270
13551
  return typeof v === "string" && v.trim() ? v.trim() : null;
13271
13552
  }
13272
- function strArray3(v) {
13553
+ function strArray4(v) {
13273
13554
  if (Array.isArray(v)) return v.map((x2) => String(x2).trim()).filter(Boolean);
13274
13555
  if (typeof v === "string" && v.trim()) return [v.trim()];
13275
13556
  return [];
@@ -13280,16 +13561,16 @@ function parseInterfaces2(v) {
13280
13561
  for (const raw of v) {
13281
13562
  if (!raw || typeof raw !== "object") continue;
13282
13563
  const o2 = raw;
13283
- const type = str2(o2.type);
13564
+ const type = str3(o2.type);
13284
13565
  if (!type) continue;
13285
13566
  out.push({
13286
13567
  type,
13287
- tool: str2(o2.tool),
13288
- provider: str2(o2.provider),
13289
- purpose: str2(o2.purpose),
13290
- operations: strArray3(o2.operations),
13291
- environments: strArray3(o2.environments),
13292
- constraints: str2(o2.constraints)
13568
+ tool: str3(o2.tool),
13569
+ provider: str3(o2.provider),
13570
+ purpose: str3(o2.purpose),
13571
+ operations: strArray4(o2.operations),
13572
+ environments: strArray4(o2.environments),
13573
+ constraints: str3(o2.constraints)
13293
13574
  });
13294
13575
  }
13295
13576
  return out;
@@ -13298,7 +13579,7 @@ function parseBoundaries2(v) {
13298
13579
  if (!v || typeof v !== "object" || Array.isArray(v)) return {};
13299
13580
  const out = {};
13300
13581
  for (const [env, val] of Object.entries(v)) {
13301
- const list22 = strArray3(val);
13582
+ const list22 = strArray4(val);
13302
13583
  if (list22.length > 0) out[env] = list22;
13303
13584
  }
13304
13585
  return out;
@@ -13306,17 +13587,17 @@ function parseBoundaries2(v) {
13306
13587
  function parseAuth2(v) {
13307
13588
  if (!v || typeof v !== "object" || Array.isArray(v)) return { mode: null, refs: [] };
13308
13589
  const o2 = v;
13309
- const refs = strArray3(o2.refs).filter((r2) => !r2.includes("=") && /^[A-Za-z0-9_.-]+$/.test(r2));
13310
- return { mode: str2(o2.mode), refs };
13590
+ const refs = strArray4(o2.refs).filter((r2) => !r2.includes("=") && /^[A-Za-z0-9_.-]+$/.test(r2));
13591
+ return { mode: str3(o2.mode), refs };
13311
13592
  }
13312
13593
  function parseScope2(v) {
13313
13594
  if (!v || typeof v !== "object" || Array.isArray(v)) return null;
13314
13595
  const o2 = v;
13315
- const type = str2(o2.type);
13596
+ const type = str3(o2.type);
13316
13597
  if (!type) return null;
13317
- return { type, module: str2(o2.module) };
13598
+ return { type, module: str3(o2.module) };
13318
13599
  }
13319
- function purposeFromBody2(body) {
13600
+ function purposeFromBody3(body) {
13320
13601
  const m2 = body.match(/^#{1,2}\s+Purpose\s*\n+([\s\S]*?)(?=\n#{1,2}\s|\s*$)/m);
13321
13602
  const text22 = m2 ? m2[1].trim() : "";
13322
13603
  return text22 || null;
@@ -13325,17 +13606,17 @@ function toDetail2(filePath, relPath, raw) {
13325
13606
  const parsed = matter42(raw);
13326
13607
  const fm = parsed.data ?? {};
13327
13608
  const body = parsed.content.trim();
13328
- const id = str2(fm.id) ?? "";
13609
+ const id = str3(fm.id) ?? "";
13329
13610
  return {
13330
13611
  id,
13331
- title: str2(fm.title) ?? id,
13332
- resourceType: str2(fm.resource_type),
13333
- provider: str2(fm.provider),
13334
- environments: strArray3(fm.environments),
13612
+ title: str3(fm.title) ?? id,
13613
+ resourceType: str3(fm.resource_type),
13614
+ provider: str3(fm.provider),
13615
+ environments: strArray4(fm.environments),
13335
13616
  scope: parseScope2(fm.scope),
13336
- modules: strArray3(fm.modules),
13617
+ modules: strArray4(fm.modules),
13337
13618
  path: relPath,
13338
- purpose: str2(fm.purpose) ?? purposeFromBody2(body),
13619
+ purpose: str3(fm.purpose) ?? purposeFromBody3(body),
13339
13620
  interfaces: parseInterfaces2(fm.access_interfaces),
13340
13621
  boundaries: parseBoundaries2(fm.access_boundaries),
13341
13622
  ...(() => {
@@ -13349,13 +13630,13 @@ function getResources2(dir) {
13349
13630
  return discoverKnowledge2(dir).filter((a2) => a2.type === RESOURCE_ARTIFACT_TYPE2).map((a2) => {
13350
13631
  const fm = a2.rawFrontmatter;
13351
13632
  return {
13352
- id: a2.id || str2(fm.id) || a2.title,
13353
- title: a2.title || str2(fm.title) || a2.id,
13354
- resourceType: str2(fm.resource_type),
13355
- provider: str2(fm.provider),
13356
- environments: strArray3(fm.environments),
13633
+ id: a2.id || str3(fm.id) || a2.title,
13634
+ title: a2.title || str3(fm.title) || a2.id,
13635
+ resourceType: str3(fm.resource_type),
13636
+ provider: str3(fm.provider),
13637
+ environments: strArray4(fm.environments),
13357
13638
  scope: parseScope2(fm.scope),
13358
- modules: strArray3(fm.modules),
13639
+ modules: strArray4(fm.modules),
13359
13640
  path: a2.relPath
13360
13641
  };
13361
13642
  }).sort((x2, y) => x2.id.localeCompare(y.id));
@@ -13375,12 +13656,184 @@ function parseWorkItemResources2(fm) {
13375
13656
  for (const raw of v) {
13376
13657
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
13377
13658
  const o2 = raw;
13378
- const id = str2(o2.id);
13659
+ const id = str3(o2.id);
13379
13660
  if (!id) continue;
13380
- out.push({ id, role: str2(o2.role) ?? "affected" });
13661
+ out.push({ id, role: str3(o2.role) ?? "affected" });
13662
+ }
13663
+ return out;
13664
+ }
13665
+ var DEPRECATION_ARTIFACT_TYPE2 = "deprecation-plan";
13666
+ var ENVIRONMENTS2 = ["development", "test", "production"];
13667
+ var EVIDENCE_SIGNALS2 = [
13668
+ "static",
13669
+ "runtime",
13670
+ "database",
13671
+ "external_consumers",
13672
+ "human_review"
13673
+ ];
13674
+ function str22(v) {
13675
+ return typeof v === "string" && v.trim() ? v.trim() : null;
13676
+ }
13677
+ function parseWorkItemDeprecationTargets2(fm) {
13678
+ const v = fm.deprecation_targets;
13679
+ if (!Array.isArray(v)) return [];
13680
+ const out = [];
13681
+ for (const raw of v) {
13682
+ if (typeof raw === "string") {
13683
+ const s = raw.trim();
13684
+ if (!s) continue;
13685
+ const slash = s.indexOf("/");
13686
+ if (slash > 0) out.push({ plan: s.slice(0, slash).trim(), target: s.slice(slash + 1).trim() });
13687
+ else out.push({ plan: null, target: s });
13688
+ continue;
13689
+ }
13690
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
13691
+ const o2 = raw;
13692
+ const target = str22(o2.target) ?? str22(o2.id);
13693
+ if (!target) continue;
13694
+ out.push({ plan: str22(o2.plan), target });
13695
+ }
13381
13696
  }
13382
13697
  return out;
13383
13698
  }
13699
+ function strArray22(v) {
13700
+ if (Array.isArray(v)) return v.map((x2) => String(x2).trim()).filter(Boolean);
13701
+ if (typeof v === "string" && v.trim()) return [v.trim()];
13702
+ return [];
13703
+ }
13704
+ function parseAssessment2(v) {
13705
+ if (!v || typeof v !== "object" || Array.isArray(v)) {
13706
+ return { unusedConfidence: null, blastRadius: null, reversibility: null };
13707
+ }
13708
+ const o2 = v;
13709
+ return {
13710
+ unusedConfidence: str22(o2.unused_confidence),
13711
+ blastRadius: str22(o2.blast_radius),
13712
+ reversibility: str22(o2.reversibility)
13713
+ };
13714
+ }
13715
+ function parseUsageEvidence2(v) {
13716
+ if (!v || typeof v !== "object" || Array.isArray(v)) return {};
13717
+ const out = {};
13718
+ for (const signal of EVIDENCE_SIGNALS2) {
13719
+ const raw = v[signal];
13720
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
13721
+ const o2 = raw;
13722
+ out[signal] = {
13723
+ status: str22(o2.status) ?? "unknown",
13724
+ quality: str22(o2.quality) ?? "unknown",
13725
+ detail: str22(o2.detail)
13726
+ };
13727
+ }
13728
+ return out;
13729
+ }
13730
+ function parseSoftDeprecation2(v) {
13731
+ if (!v || typeof v !== "object" || Array.isArray(v)) return null;
13732
+ const o2 = v;
13733
+ const windowRaw = o2.observation_window_days;
13734
+ const window = typeof windowRaw === "number" && Number.isFinite(windowRaw) && windowRaw >= 0 ? Math.floor(windowRaw) : null;
13735
+ const exitCriteria = Array.isArray(o2.exit_criteria) ? o2.exit_criteria.map((c) => {
13736
+ if (typeof c === "string") return c.trim() ? { description: c.trim(), met: false } : null;
13737
+ if (c && typeof c === "object" && !Array.isArray(c)) {
13738
+ const co = c;
13739
+ const description = str22(co.description) ?? str22(co.criterion);
13740
+ if (!description) return null;
13741
+ return { description, met: co.met === true };
13742
+ }
13743
+ return null;
13744
+ }).filter((c) => Boolean(c)) : [];
13745
+ return {
13746
+ strategy: str22(o2.strategy),
13747
+ mechanism: str22(o2.mechanism),
13748
+ startedAt: str22(o2.started_at),
13749
+ observationWindowDays: window,
13750
+ exitCriteria
13751
+ };
13752
+ }
13753
+ function parseEnvironments2(v) {
13754
+ if (!v || typeof v !== "object" || Array.isArray(v)) return {};
13755
+ const out = {};
13756
+ for (const env of ENVIRONMENTS2) {
13757
+ const s = str22(v[env]);
13758
+ if (s) out[env] = s;
13759
+ }
13760
+ return out;
13761
+ }
13762
+ function parseTarget2(raw) {
13763
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
13764
+ const o2 = raw;
13765
+ const id = str22(o2.id);
13766
+ if (!id) return null;
13767
+ return {
13768
+ id,
13769
+ type: str22(o2.type) ?? "other",
13770
+ locator: str22(o2.locator),
13771
+ title: str22(o2.title),
13772
+ lifecycle: str22(o2.lifecycle) ?? "candidate",
13773
+ environments: parseEnvironments2(o2.environments),
13774
+ assessment: parseAssessment2(o2.assessment),
13775
+ usageEvidence: parseUsageEvidence2(o2.usage_evidence),
13776
+ dependsOnRemoval: strArray22(o2.depends_on_removal),
13777
+ resource: str22(o2.resource),
13778
+ module: str22(o2.module),
13779
+ softDeprecation: parseSoftDeprecation2(o2.soft_deprecation)
13780
+ };
13781
+ }
13782
+ function parseTargets2(v) {
13783
+ if (!Array.isArray(v)) return [];
13784
+ const out = [];
13785
+ for (const raw of v) {
13786
+ const t = parseTarget2(raw);
13787
+ if (t) out.push(t);
13788
+ }
13789
+ return out;
13790
+ }
13791
+ function lifecycleCountsOf2(targets) {
13792
+ const counts = {};
13793
+ for (const t of targets) counts[t.lifecycle] = (counts[t.lifecycle] ?? 0) + 1;
13794
+ return counts;
13795
+ }
13796
+ function purposeFromBody22(body) {
13797
+ const m2 = body.match(/^#{1,2}\s+Purpose\s*\n+([\s\S]*?)(?=\n#{1,2}\s|\s*$)/m);
13798
+ const text22 = m2 ? m2[1].trim() : "";
13799
+ return text22 || null;
13800
+ }
13801
+ function getDeprecationPlans2(dir) {
13802
+ return discoverKnowledge2(dir).filter((a2) => a2.type === DEPRECATION_ARTIFACT_TYPE2).map((a2) => {
13803
+ const fm = a2.rawFrontmatter;
13804
+ const targets = parseTargets2(fm.targets);
13805
+ return {
13806
+ id: a2.id || str22(fm.id) || a2.title,
13807
+ title: a2.title || str22(fm.title) || a2.id,
13808
+ path: a2.relPath,
13809
+ status: str22(fm.status),
13810
+ targetCount: targets.length,
13811
+ lifecycleCounts: lifecycleCountsOf2(targets)
13812
+ };
13813
+ }).sort((x2, y) => x2.id.localeCompare(y.id));
13814
+ }
13815
+ function getDeprecationPlan2(dir, id) {
13816
+ const match = discoverKnowledge2(dir).find(
13817
+ (a2) => a2.type === DEPRECATION_ARTIFACT_TYPE2 && (a2.id === id || a2.title === id)
13818
+ );
13819
+ if (!match) return null;
13820
+ const raw = readFile2(match.filePath);
13821
+ const parsed = matter52(raw);
13822
+ const fm = parsed.data ?? {};
13823
+ const body = parsed.content.trim();
13824
+ const targets = parseTargets2(fm.targets);
13825
+ return {
13826
+ id: str22(fm.id) ?? match.id,
13827
+ title: str22(fm.title) ?? match.title,
13828
+ path: match.relPath,
13829
+ status: str22(fm.status),
13830
+ targetCount: targets.length,
13831
+ lifecycleCounts: lifecycleCountsOf2(targets),
13832
+ purpose: str22(fm.purpose) ?? purposeFromBody22(body),
13833
+ targets,
13834
+ markdownBody: body
13835
+ };
13836
+ }
13384
13837
  var KNOWLEDGE22 = "knowledge";
13385
13838
  var RESOURCE_ROLE_EDGE2 = {
13386
13839
  affected: "affects",
@@ -13424,9 +13877,9 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
13424
13877
  ];
13425
13878
  const presentLayers = [];
13426
13879
  for (const layer2 of layerDocs) {
13427
- const path52 = layer2.files.map((f3) => `${KNOWLEDGE22}/${f3}`).find((rel) => exists2(join3(dir, rel)));
13428
- if (path52) {
13429
- addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path52 });
13880
+ const path62 = layer2.files.map((f3) => `${KNOWLEDGE22}/${f3}`).find((rel) => exists2(join3(dir, rel)));
13881
+ if (path62) {
13882
+ addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path62 });
13430
13883
  presentLayers.push(layer2.id);
13431
13884
  }
13432
13885
  }
@@ -13484,6 +13937,33 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
13484
13937
  addEdge(modId, resId, "depends_on");
13485
13938
  }
13486
13939
  }
13940
+ for (const summary of getDeprecationPlans2(dir)) {
13941
+ const plan = getDeprecationPlan2(dir, summary.id);
13942
+ if (!plan) continue;
13943
+ const planId = `deprecation-plan:${plan.id}`;
13944
+ addNode({ id: planId, type: "deprecation-plan", label: `${plan.id} ${plan.title}`.trim(), path: plan.path, status: plan.status ?? void 0 });
13945
+ for (const target of plan.targets) {
13946
+ const targetId = `deprecation-target:${target.id}`;
13947
+ addNode({ id: targetId, type: "deprecation-target", label: target.title ? `${target.id} ${target.title}` : target.id, path: plan.path, status: target.lifecycle });
13948
+ addEdge(planId, targetId, "targets");
13949
+ if (target.module) {
13950
+ const moduleId = `module:${target.module}`;
13951
+ addNode({ id: moduleId, type: "module", label: target.module });
13952
+ addEdge(targetId, moduleId, "targets");
13953
+ }
13954
+ if (target.resource) {
13955
+ const resourceId = `resource:${target.resource}`;
13956
+ const title = resourceTitleById.get(target.resource);
13957
+ addNode({ id: resourceId, type: "project-resource", label: title ? `${target.resource} ${title}` : target.resource });
13958
+ addEdge(targetId, resourceId, "targets");
13959
+ }
13960
+ for (const dependency of target.dependsOnRemoval) {
13961
+ const dependencyId = `deprecation-target:${dependency}`;
13962
+ addNode({ id: dependencyId, type: "deprecation-target", label: dependency });
13963
+ addEdge(targetId, dependencyId, "depends_on_removal");
13964
+ }
13965
+ }
13966
+ }
13487
13967
  for (const wi of selectedWIs) {
13488
13968
  const id = wi.id || wi.title;
13489
13969
  if (!id || !id.trim()) continue;
@@ -13514,6 +13994,11 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
13514
13994
  addNode({ id: resId, type: "project-resource", label: title ? `${rel.id} ${title}`.trim() : rel.id });
13515
13995
  addEdge(wiNodeId, resId, RESOURCE_ROLE_EDGE2[rel.role] ?? "uses");
13516
13996
  }
13997
+ for (const ref of parseWorkItemDeprecationTargets2(wi.rawFrontmatter)) {
13998
+ const targetId = `deprecation-target:${ref.target}`;
13999
+ addNode({ id: targetId, type: "deprecation-target", label: ref.target });
14000
+ addEdge(wiNodeId, targetId, "targets");
14001
+ }
13517
14002
  for (const dec of wi.decisions) {
13518
14003
  if (!dec || !dec.trim()) continue;
13519
14004
  const adrId = `adr:${dec}`;
@@ -13873,13 +14358,13 @@ function meaningful2(text22) {
13873
14358
  });
13874
14359
  }
13875
14360
  function readPocSummary2(dir) {
13876
- const path52 = join3(dir, POC_ARTIFACT_PATH2);
13877
- if (!exists2(path52)) {
14361
+ const path62 = join3(dir, POC_ARTIFACT_PATH2);
14362
+ if (!exists2(path62)) {
13878
14363
  return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
13879
14364
  }
13880
14365
  let markdown = "";
13881
14366
  try {
13882
- markdown = readFile2(path52);
14367
+ markdown = readFile2(path62);
13883
14368
  } catch {
13884
14369
  return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
13885
14370
  }
@@ -13910,11 +14395,11 @@ function redactSecrets2(value) {
13910
14395
  return line.replace(/([:=]\s*)\S+.*/, "$1[redacted]");
13911
14396
  }).join("\n");
13912
14397
  }
13913
- function source2(dir, path52, kind) {
13914
- const absolute = join3(dir, path52);
14398
+ function source2(dir, path62, kind) {
14399
+ const absolute = join3(dir, path62);
13915
14400
  if (!exists2(absolute)) return null;
13916
14401
  const raw = readFile2(absolute);
13917
- return { path: posix2(path52), kind, digest: sha2(raw), content: redactSecrets2(raw) };
14402
+ return { path: posix2(path62), kind, digest: sha2(raw), content: redactSecrets2(raw) };
13918
14403
  }
13919
14404
  function reportFiles2(dir) {
13920
14405
  const delivery = join3(dir, "knowledge", "delivery");
@@ -13922,7 +14407,7 @@ function reportFiles2(dir) {
13922
14407
  const match = name.match(reportName2);
13923
14408
  if (!match) return [];
13924
14409
  try {
13925
- const parsed = matter62(readFile2(join3(delivery, name)));
14410
+ const parsed = matter72(readFile2(join3(delivery, name)));
13926
14411
  const fingerprint = typeof parsed.data.source_fingerprint === "string" ? parsed.data.source_fingerprint : "";
13927
14412
  const manifest = parsed.data.source_manifest;
13928
14413
  const sourceManifest = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? Object.fromEntries(Object.entries(manifest).filter(([, digest]) => typeof digest === "string")) : {};
@@ -13954,9 +14439,9 @@ function buildPocReportContext2(dir) {
13954
14439
  };
13955
14440
  }
13956
14441
  const sources = [];
13957
- const add = (path52, kind) => {
13958
- if (sources.some((item2) => item2.path === posix2(path52))) return;
13959
- const item = source2(dir, path52, kind);
14442
+ const add = (path62, kind) => {
14443
+ if (sources.some((item2) => item2.path === posix2(path62))) return;
14444
+ const item = source2(dir, path62, kind);
13960
14445
  if (item) sources.push(item);
13961
14446
  };
13962
14447
  add(POC_ARTIFACT_PATH2, "poc");
@@ -13984,7 +14469,7 @@ function buildPocReportContext2(dir) {
13984
14469
  const sourceFingerprint = sha2(sources.map((item) => `${item.path}\0${item.digest}`).join("\n"));
13985
14470
  const status = !latestReport ? "missing" : latestReport.sourceFingerprint === sourceFingerprint ? "current" : "stale";
13986
14471
  const currentManifest = Object.fromEntries(sources.map((item) => [item.path, item.digest]));
13987
- const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((path52) => currentManifest[path52] !== latestReport?.sourceManifest[path52]).sort() : [];
14472
+ const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((path62) => currentManifest[path62] !== latestReport?.sourceManifest[path62]).sort() : [];
13988
14473
  const sourceList = sources.map((item) => `- ${item.path} (${item.kind})`).join("\n") || "- No sources selected.";
13989
14474
  return {
13990
14475
  eligible: true,
@@ -14021,7 +14506,7 @@ function canonicalReport(context, proposal) {
14021
14506
  source_manifest: Object.fromEntries(context.sources.map((item) => [item.path, item.digest]))
14022
14507
  };
14023
14508
  if (supersedes) data.supersedes = supersedes;
14024
- return matter62.stringify(redactSecrets2(proposal).trimEnd() + "\n", data);
14509
+ return matter72.stringify(redactSecrets2(proposal).trimEnd() + "\n", data);
14025
14510
  }
14026
14511
  function persistPocReport(dir, proposal, opts = {}) {
14027
14512
  const context = buildPocReportContext2(dir);
@@ -17987,6 +18472,8 @@ function getWorkItem(dir, workItemId) {
17987
18472
  markdownBody: body.trim(),
17988
18473
  // External systems this WI relates to (WI-030), separate from affectedModules (code).
17989
18474
  resources: parseWorkItemResources2(fm),
18475
+ // Deprecation Targets this WI retires (WI-050), referenced not copied.
18476
+ deprecationTargets: parseWorkItemDeprecationTargets2(fm),
17990
18477
  ...parseSystemImpact(dir, fm)
17991
18478
  };
17992
18479
  return { ...detail, refinement: computeRefinementStatus(detail) };
@@ -17995,8 +18482,8 @@ function parseGraphReason(v) {
17995
18482
  if (!v || typeof v !== "object") return null;
17996
18483
  const o2 = v;
17997
18484
  const relationship = typeof o2.relationship === "string" && o2.relationship.trim() ? o2.relationship.trim() : null;
17998
- const path52 = Array.isArray(o2.path) ? o2.path.map(String).filter(Boolean) : [];
17999
- return relationship || path52.length ? { relationship, path: path52 } : null;
18485
+ const path62 = Array.isArray(o2.path) ? o2.path.map(String).filter(Boolean) : [];
18486
+ return relationship || path62.length ? { relationship, path: path62 } : null;
18000
18487
  }
18001
18488
  function parseExplain(o2) {
18002
18489
  const refs = Array.isArray(o2.evidence) ? o2.evidence : Array.isArray(o2.evidence_refs) ? o2.evidence_refs : [];
@@ -18051,7 +18538,7 @@ function parseOriginalSnapshot(fm) {
18051
18538
  function readBody(filePath) {
18052
18539
  try {
18053
18540
  const raw = readFile2(filePath);
18054
- return matter102(raw).content;
18541
+ return matter112(raw).content;
18055
18542
  } catch {
18056
18543
  return "";
18057
18544
  }
@@ -18336,7 +18823,7 @@ function ensureH1(body, title) {
18336
18823
  ${trimmed}`;
18337
18824
  }
18338
18825
  function serialize2(data, body) {
18339
- return matter112.stringify(`
18826
+ return matter122.stringify(`
18340
18827
  ${body.trim()}
18341
18828
  `, data);
18342
18829
  }
@@ -18701,6 +19188,61 @@ function buildRefinementHandoff(dir, workItemId) {
18701
19188
  text: lines.join("\n")
18702
19189
  };
18703
19190
  }
19191
+ var DAY_MS = 24 * 60 * 60 * 1e3;
19192
+ function isStrongNoUsage(sig) {
19193
+ return sig.status === "no-usage" && (sig.quality === "observed-fact" || sig.quality === "human-confirmed");
19194
+ }
19195
+ function deriveUnusedConfidence(evidence) {
19196
+ const present = EVIDENCE_SIGNALS2.map((name) => ({ name, sig: evidence[name] })).filter(
19197
+ (e) => Boolean(e.sig)
19198
+ );
19199
+ const usageFound = present.filter((e) => e.sig.status === "usage-found");
19200
+ if (usageFound.length > 0) {
19201
+ return {
19202
+ confidence: "in-use",
19203
+ requiresHumanConfirmation: false,
19204
+ rationale: [`Usage found via: ${usageFound.map((e) => e.name).join(", ")}.`]
19205
+ };
19206
+ }
19207
+ const strong = present.filter((e) => isStrongNoUsage(e.sig));
19208
+ const unknown = present.filter((e) => e.sig.status === "unknown");
19209
+ const notEvaluated = EVIDENCE_SIGNALS2.filter(
19210
+ (name) => !evidence[name] || evidence[name]?.status === "not-evaluated"
19211
+ );
19212
+ const rationale = [];
19213
+ if (strong.length > 0) rationale.push(`No usage observed via: ${strong.map((e) => e.name).join(", ")}.`);
19214
+ if (unknown.length > 0) {
19215
+ rationale.push(`Unknown (looked but could not determine): ${unknown.map((e) => e.name).join(", ")} \u2014 NOT treated as no-usage.`);
19216
+ }
19217
+ if (notEvaluated.length > 0) {
19218
+ rationale.push(`Not evaluated: ${notEvaluated.join(", ")} \u2014 absence of a reference is not evidence of no usage.`);
19219
+ }
19220
+ let confidence;
19221
+ if (strong.length === 0) {
19222
+ confidence = "insufficient-evidence";
19223
+ } else if (unknown.length > 0) {
19224
+ confidence = "medium";
19225
+ } else if (strong.length >= 2) {
19226
+ confidence = "high";
19227
+ } else {
19228
+ confidence = "medium";
19229
+ }
19230
+ return { confidence, requiresHumanConfirmation: true, rationale };
19231
+ }
19232
+ var CANDIDATE_DISCOVERY_GUIDANCE = [
19233
+ "Propose deprecation candidates from multiple sources: static references (code/routes/UI/jobs),",
19234
+ "the System Graph (dependencies and blast radius), Legacy Knowledge (risks/unknowns/modernization",
19235
+ "candidates), Project Resources (databases/external systems and their boundaries), runtime evidence",
19236
+ "when telemetry exists, and database metadata when reachable.",
19237
+ "",
19238
+ "Rules:",
19239
+ "- Discovery is a PROPOSAL, not a materialization: never create or retire anything automatically.",
19240
+ "- Always explain WHY something is a candidate, citing the signal(s) observed.",
19241
+ "- Absence of a reference is NOT evidence of no usage. If a source cannot be evaluated, record it",
19242
+ " as not-evaluated/unknown and require human investigation \u2014 never collapse it to no-usage.",
19243
+ "- A modernization candidate is not automatically a deprecation target.",
19244
+ "- Soft deprecation precedes physical removal; order retirements by dependency, dependents first."
19245
+ ].join("\n");
18704
19246
  var WorkItemNotReadyError = class extends Error {
18705
19247
  constructor(workItemId, actualState) {
18706
19248
  super(`Work Item "${workItemId}" must be in "ready" state to produce an Implementation Handoff, but is "${actualState}".`);
@@ -18731,6 +19273,16 @@ function buildImplementationHandoff(dir, workItemId) {
18731
19273
  const domains = match.domains;
18732
19274
  const scopeConfidence = match.scopeConfidence;
18733
19275
  const wiResources = parseWorkItemResources2(match.rawFrontmatter);
19276
+ const wiDeprecationRefs = parseWorkItemDeprecationTargets2(match.rawFrontmatter);
19277
+ const resolveTarget = (ref) => {
19278
+ const plans = ref.plan ? [getDeprecationPlan2(dir, ref.plan)].filter(Boolean) : getDeprecationPlans2(dir).map((p) => getDeprecationPlan2(dir, p.id));
19279
+ for (const p of plans) {
19280
+ if (!p) continue;
19281
+ const t = p.targets.find((x2) => x2.id.toUpperCase() === ref.target.toUpperCase());
19282
+ if (t) return { planId: p.id, target: t };
19283
+ }
19284
+ return null;
19285
+ };
18734
19286
  const lines = [
18735
19287
  `Implement Work Item ${wiId} \u2014 "${wiTitle}" \u2014 in project "${projectName}" using Kaddo.`,
18736
19288
  "",
@@ -18855,6 +19407,48 @@ function buildImplementationHandoff(dir, workItemId) {
18855
19407
  "installed or connected. Do not execute any interface or migration without explicit human confirmation."
18856
19408
  );
18857
19409
  }
19410
+ if (wiDeprecationRefs.length > 0) {
19411
+ lines.push(
19412
+ "",
19413
+ "--- Relevant Deprecation Targets ---",
19414
+ "",
19415
+ "This Work Item is part of a Controlled Deprecation plan. It RETIRES the targets below. Soft",
19416
+ "deprecation precedes physical removal; in Dev/Test you may remove code/endpoints/dev-test data",
19417
+ "when the resource boundary allows and there is confirmation. A production change needs a Runbook",
19418
+ "and explicit human approval \u2014 never execute a destructive production operation autonomously."
19419
+ );
19420
+ for (const ref of wiDeprecationRefs) {
19421
+ const resolved = resolveTarget(ref);
19422
+ if (!resolved) {
19423
+ lines.push("", `- ${ref.target}${ref.plan ? ` (plan ${ref.plan})` : ""} \u2014 target not found in any Deprecation Plan.`);
19424
+ continue;
19425
+ }
19426
+ const { planId, target: t } = resolved;
19427
+ lines.push("", `- ${t.id} [${t.type}] \u2014 plan ${planId}, lifecycle: ${t.lifecycle}`);
19428
+ if (t.locator) lines.push(` locator: ${t.locator}`);
19429
+ const envs = Object.entries(t.environments).map(([e, s]) => `${e}=${s}`).join(" ");
19430
+ if (envs) lines.push(` environments: ${envs}`);
19431
+ const unused = deriveUnusedConfidence(t.usageEvidence);
19432
+ lines.push(` unused confidence: ${unused.confidence}${unused.requiresHumanConfirmation ? " (human confirmation required)" : ""}`);
19433
+ if (t.assessment.blastRadius) lines.push(` blast radius: ${t.assessment.blastRadius}`);
19434
+ if (t.assessment.reversibility) lines.push(` reversibility: ${t.assessment.reversibility}`);
19435
+ if (t.dependsOnRemoval.length > 0) lines.push(` depends_on_removal: ${t.dependsOnRemoval.join(", ")} (retire these first)`);
19436
+ if (t.resource) {
19437
+ const res = getResource2(dir, t.resource);
19438
+ if (res) {
19439
+ const bounds = Object.entries(res.boundaries).map(([env, ops]) => `${env}: ${ops.join("/")}`).join(" \xB7 ");
19440
+ lines.push(` resource: ${res.id} \u2014 ${res.title}${bounds ? ` (boundaries \u2014 ${bounds})` : ""}`);
19441
+ } else {
19442
+ lines.push(` resource: ${t.resource} \u2014 definition not found under knowledge/tech/resources/.`);
19443
+ }
19444
+ }
19445
+ }
19446
+ lines.push(
19447
+ "",
19448
+ "Record what you actually retired as Evidence. Prepare a rollback for every change; for production,",
19449
+ "the rollback/abort path lives in the Runbook, not here."
19450
+ );
19451
+ }
18858
19452
  lines.push(
18859
19453
  "",
18860
19454
  "--- Implementation Plan ---",
@@ -18876,6 +19470,7 @@ function buildImplementationHandoff(dir, workItemId) {
18876
19470
  lifecycle,
18877
19471
  affectedModules,
18878
19472
  resources: wiResources,
19473
+ deprecationTargets: wiDeprecationRefs,
18879
19474
  domains,
18880
19475
  scopeConfidence,
18881
19476
  hasUnmaterializedDecisions: unmaterialized,
@@ -19127,7 +19722,7 @@ function detectFormat(raw) {
19127
19722
  if (!trimmed) throw new ImportError("EMPTY_INPUT", "Import content is empty.");
19128
19723
  if (trimmed.startsWith("---")) {
19129
19724
  try {
19130
- const { data } = matter122(trimmed);
19725
+ const { data } = matter132(trimmed);
19131
19726
  if (data && typeof data === "object" && Object.keys(data).length > 0) {
19132
19727
  const d3 = data;
19133
19728
  if (d3.type === "work-item" || d3.id && typeof d3.id === "string" && /^WI-\d+/.test(d3.id)) {
@@ -19149,7 +19744,7 @@ function parseContent(raw) {
19149
19744
  let bodyText = trimmed;
19150
19745
  if (format === "kaddo-frontmatter" || format === "markdown-frontmatter") {
19151
19746
  try {
19152
- const parsed = matter122(trimmed);
19747
+ const parsed = matter132(trimmed);
19153
19748
  frontmatterFields = parsed.data ?? {};
19154
19749
  bodyText = parsed.content.trim();
19155
19750
  } catch {
@@ -19246,7 +19841,7 @@ function findDuplicateByHash(dir, hash) {
19246
19841
  const raw = readFile2(item.filePath);
19247
19842
  if (!raw) continue;
19248
19843
  try {
19249
- const { data } = matter122(raw);
19844
+ const { data } = matter132(raw);
19250
19845
  const source22 = data?.source;
19251
19846
  if (source22 && typeof source22 === "object" && !Array.isArray(source22)) {
19252
19847
  const s = source22;
@@ -19755,8 +20350,8 @@ async function handleJiraResponse(res) {
19755
20350
  }
19756
20351
  return await res.json();
19757
20352
  }
19758
- async function jiraFetch(baseUrl, path52, auth, timeoutMs, params) {
19759
- const url = new URL(path52, baseUrl + "/");
20353
+ async function jiraFetch(baseUrl, path62, auth, timeoutMs, params) {
20354
+ const url = new URL(path62, baseUrl + "/");
19760
20355
  if (params) for (const [k2, v] of Object.entries(params)) url.searchParams.set(k2, v);
19761
20356
  const controller = new AbortController();
19762
20357
  const timer = setTimeout(() => controller.abort(), timeoutMs);
@@ -19774,8 +20369,8 @@ async function jiraFetch(baseUrl, path52, auth, timeoutMs, params) {
19774
20369
  clearTimeout(timer);
19775
20370
  }
19776
20371
  }
19777
- async function jiraPost(baseUrl, path52, auth, timeoutMs, body) {
19778
- const url = new URL(path52, baseUrl + "/");
20372
+ async function jiraPost(baseUrl, path62, auth, timeoutMs, body) {
20373
+ const url = new URL(path62, baseUrl + "/");
19779
20374
  const controller = new AbortController();
19780
20375
  const timer = setTimeout(() => controller.abort(), timeoutMs);
19781
20376
  try {
@@ -20141,7 +20736,7 @@ function buildInitiativeContent(id, input) {
20141
20736
  if (input.priority) data.priority = input.priority;
20142
20737
  if (input.source) data.source = input.source;
20143
20738
  if (input.sourceId) data.source_id = input.sourceId;
20144
- return matter14.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
20739
+ return matter15.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
20145
20740
  }
20146
20741
  function createInitiative(dir, input) {
20147
20742
  if (!input.title?.trim()) throw new InitiativeWriteError("Initiative title is required.");
@@ -20165,10 +20760,10 @@ function transitionInitiative(dir, id, to) {
20165
20760
  throw new InitiativeWriteError(`Invalid transition ${ini.status} \u2192 ${to}.`);
20166
20761
  }
20167
20762
  const raw = readFile2(ini.filePath);
20168
- const { data, content } = matter14(raw, {});
20763
+ const { data, content } = matter15(raw, {});
20169
20764
  data.status = to;
20170
20765
  if (to === "completed") data.completed_at = today2();
20171
- const newContent = matter14.stringify(content, data);
20766
+ const newContent = matter15.stringify(content, data);
20172
20767
  writeFile2(ini.filePath, newContent);
20173
20768
  const updated = parseInitiative2(dir, ini.filePath, newContent);
20174
20769
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after transition.");
@@ -20178,7 +20773,7 @@ function addCandidate(dir, id, candidate) {
20178
20773
  const ini = getInitiative(dir, id);
20179
20774
  if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
20180
20775
  const raw = readFile2(ini.filePath);
20181
- const { data, content } = matter14(raw, {});
20776
+ const { data, content } = matter15(raw, {});
20182
20777
  const existing = Array.isArray(data.candidates) ? data.candidates : [];
20183
20778
  const fallbackId = `WI-CANDIDATE-${String(existing.length + 1).padStart(3, "0")}`;
20184
20779
  const normalized = normalizeCandidate(candidate, fallbackId);
@@ -20186,7 +20781,7 @@ function addCandidate(dir, id, candidate) {
20186
20781
  throw new InitiativeWriteError(`Candidate ${normalized.id} already exists in ${id}.`);
20187
20782
  }
20188
20783
  data.candidates = [...existing, candidateToData(normalized)];
20189
- const newContent = matter14.stringify(content, data);
20784
+ const newContent = matter15.stringify(content, data);
20190
20785
  writeFile2(ini.filePath, newContent);
20191
20786
  const updated = parseInitiative2(dir, ini.filePath, newContent);
20192
20787
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding a candidate.");
@@ -20196,7 +20791,7 @@ function updateInitiative(dir, id, patch) {
20196
20791
  const ini = getInitiative(dir, id);
20197
20792
  if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
20198
20793
  const raw = readFile2(ini.filePath);
20199
- const { data, content } = matter14(raw, {});
20794
+ const { data, content } = matter15(raw, {});
20200
20795
  if (patch.title !== void 0) {
20201
20796
  if (!patch.title.trim()) throw new InitiativeWriteError("Initiative title cannot be empty.");
20202
20797
  data.title = patch.title.trim();
@@ -20206,7 +20801,7 @@ function updateInitiative(dir, id, patch) {
20206
20801
  if (patch.knowledgeLevel !== void 0) data.knowledge_level = patch.knowledgeLevel;
20207
20802
  if (patch.domains !== void 0) data.domains = patch.domains;
20208
20803
  if (patch.relatedCapabilities !== void 0) data.related_capabilities = patch.relatedCapabilities;
20209
- const newContent = matter14.stringify(content, data);
20804
+ const newContent = matter15.stringify(content, data);
20210
20805
  writeFile2(ini.filePath, newContent);
20211
20806
  const updated = parseInitiative2(dir, ini.filePath, newContent);
20212
20807
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after update.");
@@ -20224,10 +20819,10 @@ function addExternalLink(dir, id, link) {
20224
20819
  throw new InitiativeWriteError(`External link ${link.integration}:${link.externalId} already exists on ${id}.`);
20225
20820
  }
20226
20821
  const raw = readFile2(ini.filePath);
20227
- const { data, content } = matter14(raw, {});
20822
+ const { data, content } = matter15(raw, {});
20228
20823
  const existing = Array.isArray(data.external_links) ? data.external_links : [];
20229
20824
  data.external_links = [...existing, externalLinkToData(link)];
20230
- const newContent = matter14.stringify(content, data);
20825
+ const newContent = matter15.stringify(content, data);
20231
20826
  writeFile2(ini.filePath, newContent);
20232
20827
  const updated = parseInitiative2(dir, ini.filePath, newContent);
20233
20828
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding an external link.");
@@ -20296,7 +20891,7 @@ function buildWorkItemContent(wiId, iniId, candidate) {
20296
20891
  "_What did we learn? Update after completion._",
20297
20892
  ""
20298
20893
  ].join("\n");
20299
- return matter14.stringify("\n" + body, data);
20894
+ return matter15.stringify("\n" + body, data);
20300
20895
  }
20301
20896
  function materializeCandidate(dir, id, candidateId) {
20302
20897
  const ini = getInitiative(dir, id);
@@ -20313,13 +20908,13 @@ function materializeCandidate(dir, id, candidateId) {
20313
20908
  ensureDir2(join3(dir, DRAFT_DIR));
20314
20909
  writeFile2(join3(dir, DRAFT_DIR, fileName), buildWorkItemContent(wiId, ini.id, candidate));
20315
20910
  const raw = readFile2(ini.filePath);
20316
- const { data, content } = matter14(raw, {});
20911
+ const { data, content } = matter15(raw, {});
20317
20912
  const cands = Array.isArray(data.candidates) ? data.candidates : [];
20318
20913
  for (const c of cands) {
20319
20914
  if (c && typeof c === "object" && String(c.id) === candidateId) c.materialized_as = wiId;
20320
20915
  }
20321
20916
  data.candidates = cands;
20322
- const newContent = matter14.stringify(content, data);
20917
+ const newContent = matter15.stringify(content, data);
20323
20918
  writeFile2(ini.filePath, newContent);
20324
20919
  const updated = parseInitiative2(dir, ini.filePath, newContent);
20325
20920
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after materialization.");
@@ -20433,7 +21028,7 @@ function evaluateInitiativeCompletion(dir, ini) {
20433
21028
  // ../cli/src/core/resource-write.ts
20434
21029
  import fs6 from "fs";
20435
21030
  import path5 from "path";
20436
- import matter15 from "gray-matter";
21031
+ import matter18 from "gray-matter";
20437
21032
  var RESOURCES_DIR = "knowledge/tech/resources";
20438
21033
  var ResourceWriteError = class extends Error {
20439
21034
  constructor(code, message) {
@@ -20513,7 +21108,7 @@ function buildFrontmatter(id, input, base = {}) {
20513
21108
  return data;
20514
21109
  }
20515
21110
  function serialize(data, body) {
20516
- return matter15.stringify(`
21111
+ return matter18.stringify(`
20517
21112
  ${body.trim()}
20518
21113
  `, data);
20519
21114
  }
@@ -20553,7 +21148,7 @@ function updateResource(dir, id, input) {
20553
21148
  throw new ResourceWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
20554
21149
  }
20555
21150
  const raw = readFile(match.filePath);
20556
- const parsed = matter15(raw);
21151
+ const parsed = matter18(raw);
20557
21152
  const base = parsed.data ?? {};
20558
21153
  const data = buildFrontmatter(match.id || id, input, base);
20559
21154
  let body = parsed.content.trim();
@@ -20588,6 +21183,355 @@ function deleteResource(dir, id, opts = {}) {
20588
21183
  return { deleted: true, path: match.relPath };
20589
21184
  }
20590
21185
 
21186
+ // ../cli/src/core/deprecation-observation.ts
21187
+ var DAY_MS2 = 24 * 60 * 60 * 1e3;
21188
+ function addDaysIso(startIso, days) {
21189
+ const start = Date.parse(startIso);
21190
+ if (Number.isNaN(start)) return null;
21191
+ return new Date(start + days * DAY_MS2).toISOString().slice(0, 10);
21192
+ }
21193
+ function observationStatus(target, now = /* @__PURE__ */ new Date()) {
21194
+ const sd = target.softDeprecation;
21195
+ const reasons = [];
21196
+ if (!sd) {
21197
+ return {
21198
+ declared: false,
21199
+ windowElapsed: false,
21200
+ windowEndsAt: null,
21201
+ criteriaTotal: 0,
21202
+ criteriaMet: 0,
21203
+ allCriteriaMet: false,
21204
+ eligibleForRemovalReview: false,
21205
+ reasons: ["No soft-deprecation/observation window declared \u2014 not eligible for removal review."]
21206
+ };
21207
+ }
21208
+ const criteriaTotal = sd.exitCriteria.length;
21209
+ const criteriaMet = sd.exitCriteria.filter((c) => c.met).length;
21210
+ const allCriteriaMet = criteriaTotal > 0 && criteriaMet === criteriaTotal;
21211
+ let windowElapsed = false;
21212
+ let windowEndsAt = null;
21213
+ if (sd.startedAt && sd.observationWindowDays != null) {
21214
+ windowEndsAt = addDaysIso(sd.startedAt, sd.observationWindowDays);
21215
+ if (windowEndsAt) {
21216
+ windowElapsed = now.getTime() >= Date.parse(windowEndsAt);
21217
+ }
21218
+ }
21219
+ if (!sd.startedAt) reasons.push("Observation has not started (no started_at).");
21220
+ else if (sd.observationWindowDays == null) reasons.push("No observation_window_days declared.");
21221
+ else if (!windowElapsed) reasons.push(`Observation window not elapsed (ends ${windowEndsAt ?? "unknown"}).`);
21222
+ if (criteriaTotal === 0) reasons.push("No exit criteria declared.");
21223
+ else if (!allCriteriaMet) reasons.push(`Exit criteria not all met (${criteriaMet}/${criteriaTotal}).`);
21224
+ const eligibleForRemovalReview = windowElapsed && allCriteriaMet;
21225
+ if (eligibleForRemovalReview) {
21226
+ reasons.length = 0;
21227
+ reasons.push("Observation window elapsed and all exit criteria met \u2014 eligible for removal REVIEW (human decision).");
21228
+ }
21229
+ return {
21230
+ declared: true,
21231
+ windowElapsed,
21232
+ windowEndsAt,
21233
+ criteriaTotal,
21234
+ criteriaMet,
21235
+ allCriteriaMet,
21236
+ eligibleForRemovalReview,
21237
+ reasons
21238
+ };
21239
+ }
21240
+
21241
+ // ../cli/src/core/deprecation-intelligence.ts
21242
+ function isStrongNoUsage2(sig) {
21243
+ return sig.status === "no-usage" && (sig.quality === "observed-fact" || sig.quality === "human-confirmed");
21244
+ }
21245
+ function deriveUnusedConfidence2(evidence) {
21246
+ const present = EVIDENCE_SIGNALS.map((name) => ({ name, sig: evidence[name] })).filter(
21247
+ (e) => Boolean(e.sig)
21248
+ );
21249
+ const usageFound = present.filter((e) => e.sig.status === "usage-found");
21250
+ if (usageFound.length > 0) {
21251
+ return {
21252
+ confidence: "in-use",
21253
+ requiresHumanConfirmation: false,
21254
+ rationale: [`Usage found via: ${usageFound.map((e) => e.name).join(", ")}.`]
21255
+ };
21256
+ }
21257
+ const strong = present.filter((e) => isStrongNoUsage2(e.sig));
21258
+ const unknown = present.filter((e) => e.sig.status === "unknown");
21259
+ const notEvaluated = EVIDENCE_SIGNALS.filter(
21260
+ (name) => !evidence[name] || evidence[name]?.status === "not-evaluated"
21261
+ );
21262
+ const rationale = [];
21263
+ if (strong.length > 0) rationale.push(`No usage observed via: ${strong.map((e) => e.name).join(", ")}.`);
21264
+ if (unknown.length > 0) {
21265
+ rationale.push(`Unknown (looked but could not determine): ${unknown.map((e) => e.name).join(", ")} \u2014 NOT treated as no-usage.`);
21266
+ }
21267
+ if (notEvaluated.length > 0) {
21268
+ rationale.push(`Not evaluated: ${notEvaluated.join(", ")} \u2014 absence of a reference is not evidence of no usage.`);
21269
+ }
21270
+ let confidence;
21271
+ if (strong.length === 0) {
21272
+ confidence = "insufficient-evidence";
21273
+ } else if (unknown.length > 0) {
21274
+ confidence = "medium";
21275
+ } else if (strong.length >= 2) {
21276
+ confidence = "high";
21277
+ } else {
21278
+ confidence = "medium";
21279
+ }
21280
+ return { confidence, requiresHumanConfirmation: true, rationale };
21281
+ }
21282
+ function deriveRemovalWaves(plan) {
21283
+ const ids = new Set(plan.targets.map((t) => t.id));
21284
+ const deps = /* @__PURE__ */ new Map();
21285
+ const blocked = [];
21286
+ for (const t of plan.targets) {
21287
+ const resolved = [];
21288
+ for (const d3 of t.dependsOnRemoval) {
21289
+ if (d3 === t.id) {
21290
+ blocked.push({ target: t.id, reason: "Target depends on its own removal." });
21291
+ continue;
21292
+ }
21293
+ if (!ids.has(d3)) {
21294
+ blocked.push({ target: t.id, reason: `depends_on_removal references unknown target: ${d3}.` });
21295
+ continue;
21296
+ }
21297
+ resolved.push(d3);
21298
+ }
21299
+ deps.set(t.id, resolved);
21300
+ }
21301
+ const placed = /* @__PURE__ */ new Set();
21302
+ const waves = [];
21303
+ let remaining = plan.targets.map((t) => t.id);
21304
+ while (remaining.length > 0) {
21305
+ const wave = remaining.filter((id) => (deps.get(id) ?? []).every((d3) => placed.has(d3)));
21306
+ if (wave.length === 0) break;
21307
+ for (const id of wave) placed.add(id);
21308
+ waves.push(wave);
21309
+ remaining = remaining.filter((id) => !placed.has(id));
21310
+ }
21311
+ for (const id of remaining) {
21312
+ blocked.push({ target: id, reason: "Involved in a removal dependency cycle \u2014 cannot be ordered." });
21313
+ }
21314
+ return { waves, blocked };
21315
+ }
21316
+ var RETIRED_STATES = /* @__PURE__ */ new Set([
21317
+ "removed-devtest",
21318
+ "production-changed",
21319
+ "observing",
21320
+ "completed",
21321
+ "deprecated-retained"
21322
+ ]);
21323
+ function isRetiredLifecycle(state) {
21324
+ return RETIRED_STATES.has(state);
21325
+ }
21326
+ function targetsEligibleForRemoval(plan) {
21327
+ const byId = new Map(plan.targets.map((t) => [t.id, t]));
21328
+ return plan.targets.filter((t) => !isRetiredLifecycle(String(t.lifecycle))).filter(
21329
+ (t) => t.dependsOnRemoval.every((d3) => {
21330
+ const dep = byId.get(d3);
21331
+ return dep ? isRetiredLifecycle(String(dep.lifecycle)) : false;
21332
+ })
21333
+ ).map((t) => t.id);
21334
+ }
21335
+ function planIntelligence(plan) {
21336
+ const waves = deriveRemovalWaves(plan);
21337
+ const eligible = new Set(targetsEligibleForRemoval(plan));
21338
+ const byId = new Map(plan.targets.map((t) => [t.id, t]));
21339
+ const targets = plan.targets.map((t) => ({
21340
+ id: t.id,
21341
+ unused: deriveUnusedConfidence2(t.usageEvidence),
21342
+ eligibleForRemovalReview: eligible.has(t.id),
21343
+ blockedBy: t.dependsOnRemoval.filter((d3) => {
21344
+ const dep = byId.get(d3);
21345
+ return !dep || !isRetiredLifecycle(String(dep.lifecycle));
21346
+ }),
21347
+ observation: observationStatus(t)
21348
+ }));
21349
+ return { waves, eligible: [...eligible], targets };
21350
+ }
21351
+ var CANDIDATE_DISCOVERY_GUIDANCE2 = [
21352
+ "Propose deprecation candidates from multiple sources: static references (code/routes/UI/jobs),",
21353
+ "the System Graph (dependencies and blast radius), Legacy Knowledge (risks/unknowns/modernization",
21354
+ "candidates), Project Resources (databases/external systems and their boundaries), runtime evidence",
21355
+ "when telemetry exists, and database metadata when reachable.",
21356
+ "",
21357
+ "Rules:",
21358
+ "- Discovery is a PROPOSAL, not a materialization: never create or retire anything automatically.",
21359
+ "- Always explain WHY something is a candidate, citing the signal(s) observed.",
21360
+ "- Absence of a reference is NOT evidence of no usage. If a source cannot be evaluated, record it",
21361
+ " as not-evaluated/unknown and require human investigation \u2014 never collapse it to no-usage.",
21362
+ "- A modernization candidate is not automatically a deprecation target.",
21363
+ "- Soft deprecation precedes physical removal; order retirements by dependency, dependents first."
21364
+ ].join("\n");
21365
+
21366
+ // ../cli/src/core/deprecation-write.ts
21367
+ import fs7 from "fs";
21368
+ import path6 from "path";
21369
+ import matter19 from "gray-matter";
21370
+ var DeprecationWriteError = class extends Error {
21371
+ constructor(code, message) {
21372
+ super(message);
21373
+ this.code = code;
21374
+ this.name = "DeprecationWriteError";
21375
+ }
21376
+ code;
21377
+ };
21378
+ function slug5(s) {
21379
+ return s.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
21380
+ }
21381
+ function atomicWrite2(filePath, content) {
21382
+ const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
21383
+ fs7.writeFileSync(tmp, content, "utf-8");
21384
+ fs7.renameSync(tmp, filePath);
21385
+ }
21386
+ function serialize3(data, body) {
21387
+ return matter19.stringify(`
21388
+ ${body.trim()}
21389
+ `, data);
21390
+ }
21391
+ function allPlanArtifacts(dir) {
21392
+ return discoverKnowledge(dir).filter((a2) => a2.type === DEPRECATION_ARTIFACT_TYPE);
21393
+ }
21394
+ function nextPlanId(dir, explicit) {
21395
+ if (explicit && explicit.trim()) return explicit.trim();
21396
+ let max = 0;
21397
+ for (const a2 of allPlanArtifacts(dir)) {
21398
+ const m2 = (a2.id || "").match(/^DEP-(\d+)$/i);
21399
+ if (m2) max = Math.max(max, parseInt(m2[1], 10));
21400
+ }
21401
+ return `DEP-${String(max + 1).padStart(3, "0")}`;
21402
+ }
21403
+ function nextTargetId(targetIds) {
21404
+ let max = 0;
21405
+ for (const id of targetIds) {
21406
+ const m2 = id.match(/^DEP-TGT-(\d+)$/i);
21407
+ if (m2) max = Math.max(max, parseInt(m2[1], 10));
21408
+ }
21409
+ return `DEP-TGT-${String(max + 1).padStart(3, "0")}`;
21410
+ }
21411
+ function buildTargetRaw(input, base = {}) {
21412
+ const out = { ...base };
21413
+ if (input.id != null) out.id = input.id;
21414
+ if (input.type != null) out.type = input.type;
21415
+ if (input.locator != null) out.locator = input.locator;
21416
+ if (input.title != null) out.title = input.title;
21417
+ if (input.lifecycle != null) out.lifecycle = input.lifecycle;
21418
+ if (input.environments != null) out.environments = input.environments;
21419
+ if (input.assessment != null) {
21420
+ const a2 = { ...typeof base.assessment === "object" && base.assessment ? base.assessment : {} };
21421
+ if (input.assessment.unusedConfidence != null) a2.unused_confidence = input.assessment.unusedConfidence;
21422
+ if (input.assessment.blastRadius != null) a2.blast_radius = input.assessment.blastRadius;
21423
+ if (input.assessment.reversibility != null) a2.reversibility = input.assessment.reversibility;
21424
+ out.assessment = a2;
21425
+ }
21426
+ if (input.dependsOnRemoval != null) out.depends_on_removal = input.dependsOnRemoval;
21427
+ if (input.resource != null) out.resource = input.resource;
21428
+ if (input.module != null) out.module = input.module;
21429
+ return out;
21430
+ }
21431
+ function validateTargetInput(dir, raw) {
21432
+ const findings = [];
21433
+ const parsed = parseTarget(raw);
21434
+ if (!parsed) {
21435
+ findings.push({ level: "error", message: "A target id is required." });
21436
+ return findings;
21437
+ }
21438
+ for (const f3 of validateTarget(parsed)) {
21439
+ findings.push({ level: f3.severity === "error" ? "error" : "warning", message: f3.message });
21440
+ }
21441
+ const mapped = loadMappedModules(dir).map((m2) => m2.id);
21442
+ if (parsed.module && mapped.length > 0 && !mapped.includes(parsed.module)) {
21443
+ findings.push({ level: "warning", message: `Module "${parsed.module}" is not in .kaddo/modules.yml.` });
21444
+ }
21445
+ if (parsed.resource && !getResource(dir, parsed.resource)) {
21446
+ findings.push({ level: "warning", message: `Project Resource "${parsed.resource}" not found.` });
21447
+ }
21448
+ return findings;
21449
+ }
21450
+ function loadPlanRaw(dir, planId) {
21451
+ const match = allPlanArtifacts(dir).find((a2) => a2.id === planId || a2.title === planId);
21452
+ if (!match) throw new DeprecationWriteError("NOT_FOUND", `Deprecation Plan "${planId}" not found.`);
21453
+ const parsed = matter19(readFile(match.filePath));
21454
+ const data = { ...parsed.data ?? {} };
21455
+ return { filePath: match.filePath, data, body: parsed.content.trim() };
21456
+ }
21457
+ function rawTargets(data) {
21458
+ return Array.isArray(data.targets) ? data.targets : [];
21459
+ }
21460
+ function createPlan(dir, input) {
21461
+ const title = (input.title ?? "").trim();
21462
+ if (!title) throw new DeprecationWriteError("INVALID_INPUT", "A plan title is required.");
21463
+ const existing = allPlanArtifacts(dir);
21464
+ if (input.id && existing.some((a2) => (a2.id || "").toUpperCase() === input.id.toUpperCase())) {
21465
+ throw new DeprecationWriteError("DUPLICATE", `A plan with id "${input.id}" already exists.`);
21466
+ }
21467
+ if (existing.some((a2) => (a2.title || "").toLowerCase() === title.toLowerCase())) {
21468
+ throw new DeprecationWriteError("DUPLICATE", `A plan titled "${title}" already exists.`);
21469
+ }
21470
+ const id = nextPlanId(dir, input.id);
21471
+ const data = {
21472
+ type: DEPRECATION_ARTIFACT_TYPE,
21473
+ id,
21474
+ title,
21475
+ status: (input.status ?? "draft").trim() || "draft",
21476
+ targets: []
21477
+ };
21478
+ const body = `# Purpose
21479
+
21480
+ ${(input.purpose ?? "").trim() || "TODO: describe what this plan retires and why."}`;
21481
+ const relPath = `${DEPRECATION_PLANS_DIR}/${id}-${slug5(title) || "plan"}.md`;
21482
+ const filePath = join(dir, relPath);
21483
+ if (exists(filePath)) throw new DeprecationWriteError("DUPLICATE", `Plan file already exists: ${relPath}`);
21484
+ ensureDir(path6.dirname(filePath));
21485
+ atomicWrite2(filePath, serialize3(data, body));
21486
+ return { id, path: relPath, findings: [] };
21487
+ }
21488
+ function addTarget(dir, planId, input) {
21489
+ const { filePath, data, body } = loadPlanRaw(dir, planId);
21490
+ const targets = rawTargets(data);
21491
+ const existingIds = targets.map((t) => String(t.id ?? ""));
21492
+ const targetId = (input.id ?? "").trim() || nextTargetId(existingIds);
21493
+ if (existingIds.some((id) => id.toUpperCase() === targetId.toUpperCase())) {
21494
+ throw new DeprecationWriteError("DUPLICATE", `Target "${targetId}" already exists in ${planId}.`);
21495
+ }
21496
+ const raw = buildTargetRaw({ ...input, id: targetId, lifecycle: input.lifecycle ?? "candidate" });
21497
+ const findings = validateTargetInput(dir, raw);
21498
+ if (findings.some((f3) => f3.level === "error")) {
21499
+ throw new DeprecationWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
21500
+ }
21501
+ data.targets = [...targets, raw];
21502
+ atomicWrite2(filePath, serialize3(data, body));
21503
+ return { planId, targetId, path: `${DEPRECATION_PLANS_DIR}/${path6.basename(filePath)}`, findings };
21504
+ }
21505
+ function updateTarget(dir, planId, targetId, patch) {
21506
+ const { filePath, data, body } = loadPlanRaw(dir, planId);
21507
+ const targets = rawTargets(data);
21508
+ const idx = targets.findIndex((t) => String(t.id ?? "").toUpperCase() === targetId.toUpperCase());
21509
+ if (idx === -1) throw new DeprecationWriteError("NOT_FOUND", `Target "${targetId}" not found in ${planId}.`);
21510
+ const merged = buildTargetRaw({ ...patch, id: String(targets[idx].id) }, targets[idx]);
21511
+ const findings = validateTargetInput(dir, merged);
21512
+ if (findings.some((f3) => f3.level === "error")) {
21513
+ throw new DeprecationWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
21514
+ }
21515
+ const next = [...targets];
21516
+ next[idx] = merged;
21517
+ data.targets = next;
21518
+ atomicWrite2(filePath, serialize3(data, body));
21519
+ return { planId, targetId: String(targets[idx].id), path: `${DEPRECATION_PLANS_DIR}/${path6.basename(filePath)}`, findings };
21520
+ }
21521
+ function removeTarget(dir, planId, targetId, opts = {}) {
21522
+ const { filePath, data, body } = loadPlanRaw(dir, planId);
21523
+ const targets = rawTargets(data);
21524
+ const exists3 = targets.some((t) => String(t.id ?? "").toUpperCase() === targetId.toUpperCase());
21525
+ if (!exists3) throw new DeprecationWriteError("NOT_FOUND", `Target "${targetId}" not found in ${planId}.`);
21526
+ const dependents = targets.filter((t) => (Array.isArray(t.depends_on_removal) ? t.depends_on_removal.map(String) : []).some((d3) => d3.toUpperCase() === targetId.toUpperCase())).map((t) => String(t.id));
21527
+ if (!opts.confirm) {
21528
+ return { removed: false, preview: { planId, targetId, dependents } };
21529
+ }
21530
+ data.targets = targets.filter((t) => String(t.id ?? "").toUpperCase() !== targetId.toUpperCase());
21531
+ atomicWrite2(filePath, serialize3(data, body));
21532
+ return { removed: true, path: `${DEPRECATION_PLANS_DIR}/${path6.basename(filePath)}` };
21533
+ }
21534
+
20591
21535
  // src/tools.ts
20592
21536
  var ok = (data) => ({ ok: true, data });
20593
21537
  var fail = (message) => ({ ok: false, message });
@@ -20694,12 +21638,12 @@ function markWorkItemReady(root, id, confirm2) {
20694
21638
  }
20695
21639
  const raw = readText(root, item.path);
20696
21640
  if (!raw) return fail(`Could not read Work Item file: ${item.path}`);
20697
- const parsed = matter16(raw);
21641
+ const parsed = matter20(raw);
20698
21642
  const data = { ...parsed.data };
20699
21643
  const today3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
20700
21644
  data.status = "ready";
20701
21645
  data.ready_at = today3;
20702
- const newContent = matter16.stringify(parsed.content, data);
21646
+ const newContent = matter20.stringify(parsed.content, data);
20703
21647
  const posixPath = item.path.replace(/\\/g, "/");
20704
21648
  let newRelPath = item.path;
20705
21649
  if (posixPath.includes("/work-items/draft/")) {
@@ -20923,11 +21867,78 @@ function deleteResourceTool(root, id, confirm2) {
20923
21867
  return fail(e instanceof ResourceWriteError ? e.message : String(e));
20924
21868
  }
20925
21869
  }
21870
+ function listDeprecationsTool(root) {
21871
+ return ok(getDeprecationPlans(root));
21872
+ }
21873
+ function getDeprecationTool(root, id) {
21874
+ const plan = getDeprecationPlan(root, id);
21875
+ if (!plan) return fail(`Deprecation Plan "${id}" not found.`);
21876
+ return ok(plan);
21877
+ }
21878
+ function planDeprecationTool(root, id) {
21879
+ const plan = getDeprecationPlan(root, id);
21880
+ if (!plan) return fail(`Deprecation Plan "${id}" not found.`);
21881
+ return ok(deriveRemovalWaves(plan));
21882
+ }
21883
+ function analyzeDeprecationTool(root, id) {
21884
+ const plan = getDeprecationPlan(root, id);
21885
+ if (!plan) return fail(`Deprecation Plan "${id}" not found.`);
21886
+ return ok(planIntelligence(plan));
21887
+ }
21888
+ function createDeprecationTool(root, args, confirm2) {
21889
+ if (!confirm2) {
21890
+ return ok({ status: "needs_confirmation", action: "create", proposal: args, message: "Re-call with confirm=true to create this Deprecation Plan." });
21891
+ }
21892
+ try {
21893
+ return ok({ status: "created", ...createPlan(root, args) });
21894
+ } catch (e) {
21895
+ return fail(e instanceof DeprecationWriteError ? e.message : String(e));
21896
+ }
21897
+ }
21898
+ function addDeprecationTargetTool(root, planId, args, confirm2) {
21899
+ if (!getDeprecationPlan(root, planId)) return fail(`Deprecation Plan "${planId}" not found.`);
21900
+ if (!confirm2) {
21901
+ const findings = validateTargetInput(root, { ...args, id: args.id ?? "DEP-TGT-NEW", lifecycle: args.lifecycle ?? "candidate" });
21902
+ return ok({ status: "needs_confirmation", action: "add_target", planId, proposal: args, findings, message: "Re-call with confirm=true to add this target." });
21903
+ }
21904
+ try {
21905
+ return ok({ status: "added", ...addTarget(root, planId, args) });
21906
+ } catch (e) {
21907
+ return fail(e instanceof DeprecationWriteError ? e.message : String(e));
21908
+ }
21909
+ }
21910
+ function updateDeprecationTargetTool(root, planId, targetId, args, confirm2) {
21911
+ if (!getDeprecationPlan(root, planId)) return fail(`Deprecation Plan "${planId}" not found.`);
21912
+ if (!confirm2) {
21913
+ return ok({ status: "needs_confirmation", action: "update_target", planId, targetId, changes: args, message: "Re-call with confirm=true to apply this update." });
21914
+ }
21915
+ try {
21916
+ return ok({ status: "updated", ...updateTarget(root, planId, targetId, args) });
21917
+ } catch (e) {
21918
+ return fail(e instanceof DeprecationWriteError ? e.message : String(e));
21919
+ }
21920
+ }
21921
+ function removeDeprecationTargetTool(root, planId, targetId, confirm2) {
21922
+ if (!getDeprecationPlan(root, planId)) return fail(`Deprecation Plan "${planId}" not found.`);
21923
+ if (!confirm2) {
21924
+ try {
21925
+ const preview = removeTarget(root, planId, targetId);
21926
+ return ok({ status: "needs_confirmation", action: "remove_target", ...preview.preview, message: "Re-call with confirm=true to remove. Dependents\u2019 depends_on_removal are NOT rewritten." });
21927
+ } catch (e) {
21928
+ return fail(e instanceof DeprecationWriteError ? e.message : String(e));
21929
+ }
21930
+ }
21931
+ try {
21932
+ return ok({ status: "removed", ...removeTarget(root, planId, targetId, { confirm: true }) });
21933
+ } catch (e) {
21934
+ return fail(e instanceof DeprecationWriteError ? e.message : String(e));
21935
+ }
21936
+ }
20926
21937
 
20927
21938
  // src/multirepo.ts
20928
- import fs7 from "fs";
20929
- import path6 from "path";
20930
- import matter17 from "gray-matter";
21939
+ import fs8 from "fs";
21940
+ import path7 from "path";
21941
+ import matter21 from "gray-matter";
20931
21942
  import { parse as parseYaml11, stringify as stringifyYaml8 } from "yaml";
20932
21943
  var ok2 = (data) => ({ ok: true, data });
20933
21944
  var fail2 = (message) => ({ ok: false, message });
@@ -20947,24 +21958,24 @@ function isCore2(config) {
20947
21958
  return projectRole2(config) === "core";
20948
21959
  }
20949
21960
  function readModuleFile(root, repoPath, relFile) {
20950
- const moduleDir = path6.resolve(root, repoPath);
20951
- const abs = path6.resolve(moduleDir, relFile);
21961
+ const moduleDir = path7.resolve(root, repoPath);
21962
+ const abs = path7.resolve(moduleDir, relFile);
20952
21963
  if (!abs.startsWith(moduleDir)) return null;
20953
- if (!fs7.existsSync(abs) || !fs7.statSync(abs).isFile()) return null;
20954
- return fs7.readFileSync(abs, "utf-8");
21964
+ if (!fs8.existsSync(abs) || !fs8.statSync(abs).isFile()) return null;
21965
+ return fs8.readFileSync(abs, "utf-8");
20955
21966
  }
20956
21967
  function detectModuleStatus2(root, repoPath) {
20957
- const moduleDir = path6.resolve(root, repoPath);
20958
- if (!fs7.existsSync(moduleDir)) {
21968
+ const moduleDir = path7.resolve(root, repoPath);
21969
+ if (!fs8.existsSync(moduleDir)) {
20959
21970
  return { status: "missing", configured: false, moduleContext: false, currentState: false, codebase: false, warning: `Directory \`${repoPath}\` does not exist.` };
20960
21971
  }
20961
- const configPath = path6.join(moduleDir, ".kaddo", "config.yml");
20962
- if (!fs7.existsSync(configPath)) {
21972
+ const configPath = path7.join(moduleDir, ".kaddo", "config.yml");
21973
+ if (!fs8.existsSync(configPath)) {
20963
21974
  return { status: "not_configured", configured: false, moduleContext: false, currentState: false, codebase: false, warning: `Run \`kaddo init\` inside \`${repoPath}\` and select multirepo \u2192 module.` };
20964
21975
  }
20965
21976
  let parsed = null;
20966
21977
  try {
20967
- parsed = parseYaml11(fs7.readFileSync(configPath, "utf-8"));
21978
+ parsed = parseYaml11(fs8.readFileSync(configPath, "utf-8"));
20968
21979
  } catch {
20969
21980
  }
20970
21981
  const role = parsed?.project?.role ?? parsed?.multirepo?.role;
@@ -20975,14 +21986,14 @@ function detectModuleStatus2(root, repoPath) {
20975
21986
  status: "configured",
20976
21987
  configured: true,
20977
21988
  role: "module",
20978
- moduleContext: fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "module", "module-context.md")) || fs7.existsSync(path6.join(moduleDir, "knowledge", "module", "module-context.md")),
20979
- currentState: fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "current-state.md")),
20980
- codebase: fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "codebase.md"))
21989
+ moduleContext: fs8.existsSync(path7.join(moduleDir, "knowledge", "tech", "module", "module-context.md")) || fs8.existsSync(path7.join(moduleDir, "knowledge", "module", "module-context.md")),
21990
+ currentState: fs8.existsSync(path7.join(moduleDir, "knowledge", "tech", "current-state.md")),
21991
+ codebase: fs8.existsSync(path7.join(moduleDir, "knowledge", "tech", "codebase.md"))
20981
21992
  };
20982
21993
  }
20983
21994
  function resolveModuleContextPath(root, repoPath) {
20984
- const moduleDir = path6.resolve(root, repoPath);
20985
- if (fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "module", "module-context.md"))) {
21995
+ const moduleDir = path7.resolve(root, repoPath);
21996
+ if (fs8.existsSync(path7.join(moduleDir, "knowledge", "tech", "module", "module-context.md"))) {
20986
21997
  return "knowledge/tech/module/module-context.md";
20987
21998
  }
20988
21999
  return "knowledge/module/module-context.md";
@@ -21079,7 +22090,7 @@ function validateWorkItemModulesTool(root, args) {
21079
22090
  if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
21080
22091
  const raw = readText(root, wi.path);
21081
22092
  if (!raw) return fail2(`Could not read Work Item file.`);
21082
- const parsed = matter17(raw);
22093
+ const parsed = matter21(raw);
21083
22094
  const data = parsed.data;
21084
22095
  const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
21085
22096
  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) : [] })) : [];
@@ -21132,7 +22143,7 @@ function getWorkItemContextTool(root, args) {
21132
22143
  if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
21133
22144
  const raw = readText(root, wi.path);
21134
22145
  if (!raw) return fail2(`Could not read Work Item file.`);
21135
- const parsed = matter17(raw);
22146
+ const parsed = matter21(raw);
21136
22147
  const data = parsed.data;
21137
22148
  const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
21138
22149
  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) : [] })) : [];
@@ -21186,15 +22197,15 @@ function extractSection(content, heading) {
21186
22197
  return out;
21187
22198
  }
21188
22199
  function buildBranchStrategy(wi, affectedModules, codeEntries, descriptor) {
21189
- const slug6 = slugify4(wi.title);
22200
+ const slug7 = slugify4(wi.title);
21190
22201
  const wiLower = wi.id.toLowerCase();
21191
- const coreBranch = `feature/${wiLower}-${slug6}`;
22202
+ const coreBranch = `feature/${wiLower}-${slug7}`;
21192
22203
  const moduleBranches = {};
21193
22204
  const commitMessages = {};
21194
22205
  for (const modId of affectedModules) {
21195
22206
  const mod = (descriptor.modules ?? []).find((m2) => m2.id === modId);
21196
22207
  const repo = mod ? moduleRepoPath(mod) || modId : modId;
21197
- moduleBranches[repo] = `feature/${wiLower}-${modId}-${slug6.slice(0, 20)}`;
22208
+ moduleBranches[repo] = `feature/${wiLower}-${modId}-${slug7.slice(0, 20)}`;
21198
22209
  commitMessages[repo] = `fix(${wiLower}): update ${modId}`;
21199
22210
  }
21200
22211
  commitMessages["core"] = `docs(${wiLower}): capture cross-repo implementation summary`;
@@ -21218,7 +22229,7 @@ function suggestBranchStrategyTool(root, args) {
21218
22229
  if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
21219
22230
  const raw = readText(root, wi.path);
21220
22231
  if (!raw) return fail2(`Could not read Work Item file.`);
21221
- const parsed = matter17(raw);
22232
+ const parsed = matter21(raw);
21222
22233
  const data = parsed.data;
21223
22234
  const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
21224
22235
  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) : [] })) : [];
@@ -21367,31 +22378,31 @@ function modulesDiscoverTool(root, args) {
21367
22378
  const sysName = config.system?.name ?? config.project?.name ?? "";
21368
22379
  const workspaceRoots2 = config.multirepo?.workspace_roots ?? [".."];
21369
22380
  const IGNORED = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "coverage", "vendor", ".next", "out", "tmp", "temp", ".kaddo"]);
21370
- const coreAbs = path6.resolve(root);
22381
+ const coreAbs = path7.resolve(root);
21371
22382
  const discovered = [];
21372
22383
  for (const wr of workspaceRoots2) {
21373
- const rootAbs = path6.resolve(root, wr);
21374
- if (!fs7.existsSync(rootAbs) || !fs7.statSync(rootAbs).isDirectory()) continue;
22384
+ const rootAbs = path7.resolve(root, wr);
22385
+ if (!fs8.existsSync(rootAbs) || !fs8.statSync(rootAbs).isDirectory()) continue;
21375
22386
  let entries;
21376
22387
  try {
21377
- entries = fs7.readdirSync(rootAbs);
22388
+ entries = fs8.readdirSync(rootAbs);
21378
22389
  } catch {
21379
22390
  continue;
21380
22391
  }
21381
22392
  for (const entry of entries) {
21382
22393
  if (IGNORED.has(entry)) continue;
21383
- const candidateAbs = path6.resolve(rootAbs, entry);
21384
- if (!fs7.statSync(candidateAbs).isDirectory()) continue;
22394
+ const candidateAbs = path7.resolve(rootAbs, entry);
22395
+ if (!fs8.statSync(candidateAbs).isDirectory()) continue;
21385
22396
  if (candidateAbs === coreAbs) continue;
21386
- const relPath = path6.relative(root, candidateAbs).replace(/\\/g, "/");
21387
- const cfgPath = path6.join(candidateAbs, ".kaddo", "config.yml");
21388
- if (!fs7.existsSync(cfgPath)) {
22397
+ const relPath = path7.relative(root, candidateAbs).replace(/\\/g, "/");
22398
+ const cfgPath = path7.join(candidateAbs, ".kaddo", "config.yml");
22399
+ if (!fs8.existsSync(cfgPath)) {
21389
22400
  discovered.push({ id: entry, path: relPath, status: "not_configured", eligibleForMapping: false });
21390
22401
  continue;
21391
22402
  }
21392
22403
  let parsed = null;
21393
22404
  try {
21394
- parsed = parseYaml11(fs7.readFileSync(cfgPath, "utf-8"));
22405
+ parsed = parseYaml11(fs8.readFileSync(cfgPath, "utf-8"));
21395
22406
  } catch {
21396
22407
  }
21397
22408
  const role = parsed?.project?.role ?? parsed?.multirepo?.role;
@@ -21465,8 +22476,8 @@ ${list3(data.owners)}
21465
22476
 
21466
22477
  // ../cli/src/core/system-topology.ts
21467
22478
  import { parse as parseYaml12, stringify as stringifyYaml9 } from "yaml";
21468
- import fs8 from "fs";
21469
- import path7 from "path";
22479
+ import fs9 from "fs";
22480
+ import path8 from "path";
21470
22481
  import crypto from "crypto";
21471
22482
  var TOPOLOGY_FILE2 = "knowledge/tech/system-topology.yml";
21472
22483
  var SYSTEM_ENTITY_KINDS2 = /* @__PURE__ */ new Set([
@@ -21838,16 +22849,16 @@ function findSystemPaths(dir, source3, target, opts = {}) {
21838
22849
  out.get(r2.source).push(r2.target);
21839
22850
  }
21840
22851
  const paths = [];
21841
- const walk = (node, path9) => {
22852
+ const walk = (node, path10) => {
21842
22853
  if (paths.length >= 20) return;
21843
- if (node === target && path9.length > 1) {
21844
- paths.push([...path9]);
22854
+ if (node === target && path10.length > 1) {
22855
+ paths.push([...path10]);
21845
22856
  return;
21846
22857
  }
21847
- if (path9.length > maxDepth) return;
22858
+ if (path10.length > maxDepth) return;
21848
22859
  for (const nxt of out.get(node) ?? []) {
21849
- if (path9.includes(nxt)) continue;
21850
- walk(nxt, [...path9, nxt]);
22860
+ if (path10.includes(nxt)) continue;
22861
+ walk(nxt, [...path10, nxt]);
21851
22862
  }
21852
22863
  };
21853
22864
  walk(source3, [source3]);
@@ -21872,14 +22883,14 @@ function getImpactCandidates(dir, seedIds, opts = {}) {
21872
22883
  existing.reason.push(reason);
21873
22884
  continue;
21874
22885
  }
21875
- const path9 = findSystemPaths(dir, seed, n.id, { maxDepth: opts.maxDepth ?? DEFAULT_MAX_DEPTH })[0];
22886
+ const path10 = findSystemPaths(dir, seed, n.id, { maxDepth: opts.maxDepth ?? DEFAULT_MAX_DEPTH })[0];
21876
22887
  candidates.set(n.id, {
21877
22888
  nodeId: n.id,
21878
22889
  label: n.label,
21879
22890
  kind: n.type,
21880
22891
  ...n.moduleId ? { moduleId: n.moduleId } : {},
21881
22892
  reason: [reason],
21882
- ...path9 ? { graphPath: path9.map((id) => byId.get(id)?.label ?? id) } : {},
22893
+ ...path10 ? { graphPath: path10.map((id) => byId.get(id)?.label ?? id) } : {},
21883
22894
  ...n.implementationRefs?.length ? { implementationRefs: n.implementationRefs } : {},
21884
22895
  ...n.knowledgeRefs?.length ? { knowledgeRefs: n.knowledgeRefs } : {},
21885
22896
  status: "candidate",
@@ -21978,14 +22989,14 @@ function systemImpactCandidatesTool(root, args) {
21978
22989
  }
21979
22990
 
21980
22991
  // ../cli/src/services/integrations.ts
21981
- import matter19 from "gray-matter";
22992
+ import matter24 from "gray-matter";
21982
22993
  import { parse as parseYaml13, stringify as stringifyYaml10 } from "yaml";
21983
22994
 
21984
22995
  // ../cli/src/core/work-item-write.ts
21985
- import fs9 from "fs";
21986
- import path8 from "path";
22996
+ import fs10 from "fs";
22997
+ import path9 from "path";
21987
22998
  import crypto4 from "crypto";
21988
- import matter18 from "gray-matter";
22999
+ import matter23 from "gray-matter";
21989
23000
  var SECTION_ORDER2 = [
21990
23001
  "Actor",
21991
23002
  "Outcome",
@@ -22653,8 +23664,8 @@ async function handleJiraResponse2(res) {
22653
23664
  }
22654
23665
  return await res.json();
22655
23666
  }
22656
- async function jiraFetch2(baseUrl, path9, auth, timeoutMs, params) {
22657
- const url = new URL(path9, baseUrl + "/");
23667
+ async function jiraFetch2(baseUrl, path10, auth, timeoutMs, params) {
23668
+ const url = new URL(path10, baseUrl + "/");
22658
23669
  if (params) for (const [k2, v] of Object.entries(params)) url.searchParams.set(k2, v);
22659
23670
  const controller = new AbortController();
22660
23671
  const timer = setTimeout(() => controller.abort(), timeoutMs);
@@ -22672,8 +23683,8 @@ async function jiraFetch2(baseUrl, path9, auth, timeoutMs, params) {
22672
23683
  clearTimeout(timer);
22673
23684
  }
22674
23685
  }
22675
- async function jiraPost2(baseUrl, path9, auth, timeoutMs, body) {
22676
- const url = new URL(path9, baseUrl + "/");
23686
+ async function jiraPost2(baseUrl, path10, auth, timeoutMs, body) {
23687
+ const url = new URL(path10, baseUrl + "/");
22677
23688
  const controller = new AbortController();
22678
23689
  const timer = setTimeout(() => controller.abort(), timeoutMs);
22679
23690
  try {
@@ -23033,7 +24044,7 @@ async function integrationsWorkItemTool(root, args) {
23033
24044
  }
23034
24045
 
23035
24046
  // ../cli/src/core/context-pack.ts
23036
- import matter20 from "gray-matter";
24047
+ import matter25 from "gray-matter";
23037
24048
  var CONTEXT_PACK_VERSION = "1";
23038
24049
  var ARCH_DIR4 = "knowledge";
23039
24050
  function readScanJson(dir) {
@@ -23046,7 +24057,7 @@ function readScanJson(dir) {
23046
24057
  }
23047
24058
  }
23048
24059
  function firstParagraph4(markdown) {
23049
- const body = matter20(markdown).content.trim();
24060
+ const body = matter25(markdown).content.trim();
23050
24061
  const para = body.split("\n\n").map((p) => p.trim()).find((p) => p && !p.startsWith("#"));
23051
24062
  return para ?? "";
23052
24063
  }
@@ -24038,7 +25049,7 @@ function requireKnowledge(root) {
24038
25049
  throw new KaddoMcpError("Knowledge repository not found. Run `kaddo bootstrap` first.");
24039
25050
  }
24040
25051
  }
24041
- function slug5(s) {
25052
+ function slug6(s) {
24042
25053
  return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
24043
25054
  }
24044
25055
  function generateContext(root) {
@@ -24171,7 +25182,7 @@ function generateCapsuleDraft(root) {
24171
25182
  const config = requireConfig(root);
24172
25183
  requireKnowledge(root);
24173
25184
  const capsule = buildCapsule(root, config);
24174
- const name = slug5(config.project.name);
25185
+ const name = slug6(config.project.name);
24175
25186
  const md = `.kaddo/exports/${name}.capsule.md`;
24176
25187
  const json = `.kaddo/exports/${name}.capsule.json`;
24177
25188
  writeDerived(root, md, renderCapsuleMarkdown(capsule));
@@ -24534,6 +25545,74 @@ function createServer(root) {
24534
25545
  },
24535
25546
  async (args) => toolText(guarded(root, () => deleteResourceTool(root, args.id, args.confirm)))
24536
25547
  );
25548
+ server.registerTool(
25549
+ "kaddo_list_deprecations",
25550
+ { title: "List Deprecation Plans", description: "List Controlled Deprecation Plans under knowledge/legacy/deprecations/ (read-only).", inputSchema: {} },
25551
+ async () => toolText(guarded(root, () => listDeprecationsTool(root)))
25552
+ );
25553
+ server.registerTool(
25554
+ "kaddo_get_deprecation",
25555
+ { title: "Get Deprecation Plan", description: "Get one Deprecation Plan by ID: purpose, typed targets, lifecycle and per-environment state.", inputSchema: { id: z4.string() } },
25556
+ async (args) => toolText(guarded(root, () => getDeprecationTool(root, args.id)))
25557
+ );
25558
+ server.registerTool(
25559
+ "kaddo_plan_deprecation",
25560
+ { title: "Deprecation Removal Waves", description: "Derive the dependency-ordered removal waves (dependents first) and blocked targets for a plan. Explainable, not a manual priority.", inputSchema: { id: z4.string() } },
25561
+ async (args) => toolText(guarded(root, () => planDeprecationTool(root, args.id)))
25562
+ );
25563
+ server.registerTool(
25564
+ "kaddo_analyze_deprecation",
25565
+ { title: "Analyze Deprecation Plan", description: "Per-target unused-confidence from usage evidence + removal eligibility. Absence of a reference is never treated as no-usage; removal always requires human confirmation.", inputSchema: { id: z4.string() } },
25566
+ async (args) => toolText(guarded(root, () => analyzeDeprecationTool(root, args.id)))
25567
+ );
25568
+ const targetFields = {
25569
+ id: z4.string().optional(),
25570
+ type: z4.string().optional(),
25571
+ locator: z4.string().optional(),
25572
+ title: z4.string().optional(),
25573
+ lifecycle: z4.string().optional(),
25574
+ environments: z4.record(z4.string()).optional(),
25575
+ assessment: z4.object({ unusedConfidence: z4.string().optional(), blastRadius: z4.string().optional(), reversibility: z4.string().optional() }).optional(),
25576
+ dependsOnRemoval: z4.array(z4.string()).optional(),
25577
+ resource: z4.string().optional(),
25578
+ module: z4.string().optional()
25579
+ };
25580
+ server.registerTool(
25581
+ "kaddo_create_deprecation",
25582
+ {
25583
+ title: "Create Deprecation Plan",
25584
+ description: "Propose creating a Deprecation Plan. Without confirm, returns a preview (writes nothing); with confirm=true, Core persists it as Markdown.",
25585
+ inputSchema: { title: z4.string().optional(), purpose: z4.string().optional(), status: z4.string().optional(), confirm: z4.boolean().optional() }
25586
+ },
25587
+ async (args) => toolText(guarded(root, () => createDeprecationTool(root, args, args.confirm)))
25588
+ );
25589
+ server.registerTool(
25590
+ "kaddo_add_deprecation_target",
25591
+ {
25592
+ title: "Add Deprecation Target",
25593
+ description: "Propose adding a typed target to a plan. Without confirm, returns a preview + validation findings; with confirm=true, Core appends it (no-lossy).",
25594
+ inputSchema: { planId: z4.string(), ...targetFields, confirm: z4.boolean().optional() }
25595
+ },
25596
+ async (args) => toolText(guarded(root, () => addDeprecationTargetTool(root, args.planId, args, args.confirm)))
25597
+ );
25598
+ server.registerTool(
25599
+ "kaddo_update_deprecation_target",
25600
+ {
25601
+ title: "Update Deprecation Target",
25602
+ description: "Propose updating a target in place (no-lossy). Without confirm, returns a preview; with confirm=true, Core applies it.",
25603
+ inputSchema: { planId: z4.string(), targetId: z4.string(), ...targetFields, confirm: z4.boolean().optional() }
25604
+ },
25605
+ async (args) => toolText(guarded(root, () => updateDeprecationTargetTool(root, args.planId, args.targetId, args, args.confirm)))
25606
+ );
25607
+ server.registerTool(
25608
+ "kaddo_remove_deprecation_target",
25609
+ {
25610
+ title: "Remove Deprecation Target",
25611
+ description: "Remove a target. Without confirm, returns what depends on it (writes nothing); with confirm=true, Core removes it \u2014 dependents\u2019 depends_on_removal are left intact.",
25612
+ inputSchema: { planId: z4.string(), targetId: z4.string(), confirm: z4.boolean().optional() }
25613
+ },
25614
+ async (args) => toolText(guarded(root, () => removeDeprecationTargetTool(root, args.planId, args.targetId, args.confirm)))
25615
+ );
24537
25616
  server.registerTool(
24538
25617
  "kaddo_list_capsules",
24539
25618
  { title: "List Knowledge Capsules", description: "List registered external Knowledge Capsules.", inputSchema: {} },