@kaddo/mcp 3.107.0 → 3.109.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +592 -331
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -386,7 +386,7 @@ function join(...parts) {
386
386
  }
387
387
 
388
388
  // ../cli/src/core/impact-report.ts
389
- import matter8 from "gray-matter";
389
+ import matter9 from "gray-matter";
390
390
 
391
391
  // ../cli/src/core/config.ts
392
392
  import { z } from "zod";
@@ -2472,6 +2472,124 @@ function loadExternalCapsules(dir) {
2472
2472
  return out;
2473
2473
  }
2474
2474
 
2475
+ // ../cli/src/core/initiative.ts
2476
+ import matter5 from "gray-matter";
2477
+ var INITIATIVES_DIR = "knowledge/delivery/initiatives";
2478
+ var INITIATIVE_STATES = [
2479
+ "candidate",
2480
+ "planned",
2481
+ "in-progress",
2482
+ "completed",
2483
+ "deferred",
2484
+ "cancelled"
2485
+ ];
2486
+ var STATUS_SET = new Set(INITIATIVE_STATES);
2487
+ function isValidInitiativeStatus(s) {
2488
+ return STATUS_SET.has(s);
2489
+ }
2490
+ function toPosix3(p) {
2491
+ return p.replace(/\\/g, "/");
2492
+ }
2493
+ function optStr(v) {
2494
+ if (typeof v === "string" && v.trim()) return v.trim();
2495
+ if (typeof v === "number") return String(v);
2496
+ return void 0;
2497
+ }
2498
+ function strList(v) {
2499
+ if (!Array.isArray(v)) return [];
2500
+ return v.map((x2) => typeof x2 === "string" ? x2.trim() : String(x2)).filter(Boolean);
2501
+ }
2502
+ function parseCandidates(data) {
2503
+ const raw = data.candidates;
2504
+ if (!Array.isArray(raw)) return [];
2505
+ const out = [];
2506
+ for (const entry of raw) {
2507
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
2508
+ const o2 = entry;
2509
+ const id = optStr(o2.id);
2510
+ const title = optStr(o2.title);
2511
+ if (!id || !title) continue;
2512
+ out.push({
2513
+ id,
2514
+ title,
2515
+ type: optStr(o2.type),
2516
+ suggestedKnowledgeLevel: optStr(o2.suggested_knowledge_level ?? o2.knowledge_level),
2517
+ expectedValue: optStr(o2.expected_value),
2518
+ notes: optStr(o2.notes),
2519
+ sourceSignals: strList(o2.source_signals),
2520
+ dependencies: strList(o2.dependencies),
2521
+ materializedAs: optStr(o2.materialized_as) ?? null
2522
+ });
2523
+ }
2524
+ return out;
2525
+ }
2526
+ function parseExternalLinks(data) {
2527
+ const raw = data.external_links;
2528
+ if (!Array.isArray(raw)) return [];
2529
+ const out = [];
2530
+ for (const entry of raw) {
2531
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
2532
+ const o2 = entry;
2533
+ const integration = optStr(o2.integration);
2534
+ const externalId = optStr(o2.external_id);
2535
+ if (!integration || !externalId) continue;
2536
+ out.push({
2537
+ integration,
2538
+ externalId,
2539
+ externalType: optStr(o2.external_type),
2540
+ url: optStr(o2.url),
2541
+ externalStatus: optStr(o2.external_status)
2542
+ });
2543
+ }
2544
+ return out;
2545
+ }
2546
+ function parseInitiative(dir, filePath, raw) {
2547
+ let parsed;
2548
+ try {
2549
+ parsed = matter5(raw, {});
2550
+ } catch {
2551
+ return null;
2552
+ }
2553
+ const data = parsed.data;
2554
+ if (optStr(data.type) !== "initiative") return null;
2555
+ const id = optStr(data.id);
2556
+ if (!id) return null;
2557
+ const statusRaw = optStr(data.status) ?? "planned";
2558
+ const status = isValidInitiativeStatus(statusRaw) ? statusRaw : "planned";
2559
+ return {
2560
+ id,
2561
+ title: optStr(data.title) ?? id,
2562
+ status,
2563
+ horizon: optStr(data.horizon) ?? null,
2564
+ priority: optStr(data.priority) ?? null,
2565
+ knowledgeLevel: optStr(data.knowledge_level) ?? null,
2566
+ domains: strList(data.domains),
2567
+ relatedCapabilities: strList(data.related_capabilities),
2568
+ source: optStr(data.source) ?? null,
2569
+ sourceId: optStr(data.source_id) ?? null,
2570
+ createdAt: optStr(data.created_at) ?? null,
2571
+ externalLinks: parseExternalLinks(data),
2572
+ candidates: parseCandidates(data),
2573
+ filePath,
2574
+ relPath: toPosix3(filePath).startsWith(toPosix3(dir) + "/") ? toPosix3(filePath).slice(toPosix3(dir).length + 1) : toPosix3(filePath),
2575
+ body: parsed.content.trim(),
2576
+ rawFrontmatter: data
2577
+ };
2578
+ }
2579
+ function discoverInitiatives(dir) {
2580
+ const iniDir = join(dir, INITIATIVES_DIR);
2581
+ if (!exists(iniDir)) return [];
2582
+ const out = [];
2583
+ for (const entry of readDir(iniDir)) {
2584
+ if (!entry.endsWith(".md")) continue;
2585
+ const full = join(iniDir, entry);
2586
+ if (!isFile(full)) continue;
2587
+ const ini = parseInitiative(dir, full, readFile(full));
2588
+ if (ini) out.push(ini);
2589
+ }
2590
+ return out.sort((a2, b2) => a2.id.localeCompare(b2.id));
2591
+ }
2592
+
2475
2593
  // ../cli/src/core/graph.ts
2476
2594
  var KNOWLEDGE3 = "knowledge";
2477
2595
  var ACTIVE_STATUSES = ["draft", "ready", "in-progress", "blocked"];
@@ -2479,14 +2597,14 @@ var ALL_STATUSES = ["draft", "ready", "in-progress", "blocked", "completed"];
2479
2597
  function scopeStatuses(scope) {
2480
2598
  return scope === "all" ? { included: ALL_STATUSES, excluded: ["archived"] } : { included: ACTIVE_STATUSES, excluded: ["completed", "archived"] };
2481
2599
  }
2482
- function toPosix3(p) {
2600
+ function toPosix4(p) {
2483
2601
  return p.replace(/\\/g, "/");
2484
2602
  }
2485
2603
  function slug(s) {
2486
2604
  return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
2487
2605
  }
2488
2606
  function isAdr(a2) {
2489
- return toPosix3(a2.filePath).includes("/tech/decisions/") && Boolean(a2.type);
2607
+ return toPosix4(a2.filePath).includes("/tech/decisions/") && Boolean(a2.type);
2490
2608
  }
2491
2609
  function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
2492
2610
  const scope = opts.scope ?? "active";
@@ -2525,6 +2643,33 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
2525
2643
  const includedSet = new Set(included);
2526
2644
  const activeWICount = workItems.filter((a2) => a2.lifecycle && isActiveState(a2.lifecycle)).length;
2527
2645
  const selectedWIs = workItems.filter((a2) => a2.lifecycle && includedSet.has(a2.lifecycle));
2646
+ for (const ini of discoverInitiatives(dir)) {
2647
+ const iniNodeId = `initiative:${slug(ini.id) || ini.id}`;
2648
+ addNode({
2649
+ id: iniNodeId,
2650
+ type: "initiative",
2651
+ label: `${ini.id} ${ini.title}`.trim(),
2652
+ path: ini.relPath,
2653
+ status: ini.status
2654
+ });
2655
+ for (const cap of ini.relatedCapabilities) {
2656
+ if (!cap.trim()) continue;
2657
+ const capId = `capability:${slug(cap) || cap}`;
2658
+ addNode({ id: capId, type: "capability", label: cap });
2659
+ addEdge(iniNodeId, capId, "targets");
2660
+ }
2661
+ for (const link of ini.externalLinks) {
2662
+ const extId = `external-item:${slug(link.integration) || link.integration}:${slug(link.externalId) || link.externalId}`;
2663
+ addNode({ id: extId, type: "external-item", label: `${link.integration}:${link.externalId}`, path: link.url });
2664
+ addEdge(iniNodeId, extId, "references_external");
2665
+ }
2666
+ for (const cand of ini.candidates) {
2667
+ const candId = `candidate:${cand.id}`;
2668
+ addNode({ id: candId, type: "roadmap-candidate", label: cand.id });
2669
+ addEdge(candId, iniNodeId, "belongs_to");
2670
+ if (cand.materializedAs) addEdge(candId, `wi:${cand.materializedAs}`, "materialized_as");
2671
+ }
2672
+ }
2528
2673
  for (const wi of selectedWIs) {
2529
2674
  const id = wi.id || wi.title;
2530
2675
  if (!id || !id.trim()) continue;
@@ -2685,14 +2830,14 @@ function loadGraphSummary(dir) {
2685
2830
 
2686
2831
  // ../cli/src/core/graph-hints.ts
2687
2832
  var KNOWLEDGE4 = "knowledge";
2688
- function toPosix4(p) {
2833
+ function toPosix5(p) {
2689
2834
  return p.replace(/\\/g, "/");
2690
2835
  }
2691
2836
  function slug2(s) {
2692
2837
  return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
2693
2838
  }
2694
2839
  function isAdr2(a2) {
2695
- return toPosix4(a2.filePath).includes("/tech/decisions/") && Boolean(a2.type);
2840
+ return toPosix5(a2.filePath).includes("/tech/decisions/") && Boolean(a2.type);
2696
2841
  }
2697
2842
  function capabilityHeadings(dir) {
2698
2843
  const p = join(dir, KNOWLEDGE4, "product", "capabilities.md");
@@ -2927,7 +3072,7 @@ function loadGraphHints(dir) {
2927
3072
  }
2928
3073
 
2929
3074
  // ../cli/src/services/installed-skills.ts
2930
- import matter5 from "gray-matter";
3075
+ import matter6 from "gray-matter";
2931
3076
  var SKILLS_DIR = join("knowledge", "skills");
2932
3077
  function discoverInstalledSkills(dir) {
2933
3078
  const base = join(dir, SKILLS_DIR);
@@ -2937,7 +3082,7 @@ function discoverInstalledSkills(dir) {
2937
3082
  const skillFile = join(base, entry, "skill.md");
2938
3083
  if (!isFile(skillFile)) continue;
2939
3084
  try {
2940
- const { data } = matter5(readFile(skillFile));
3085
+ const { data } = matter6(readFile(skillFile));
2941
3086
  if (data.type && String(data.type) !== "skill") continue;
2942
3087
  const id = String(data.id ?? entry);
2943
3088
  out.push({
@@ -4526,7 +4671,7 @@ function deriveDeliveryCompletedStatus(dir) {
4526
4671
  }
4527
4672
 
4528
4673
  // ../cli/src/core/assets.ts
4529
- import matter6 from "gray-matter";
4674
+ import matter7 from "gray-matter";
4530
4675
 
4531
4676
  // ../cli/src/core/version.ts
4532
4677
  import { fileURLToPath } from "url";
@@ -7732,7 +7877,7 @@ function canonicalSkills() {
7732
7877
  }
7733
7878
  function versionOf(content) {
7734
7879
  try {
7735
- const v = matter6(content).data.version;
7880
+ const v = matter7(content).data.version;
7736
7881
  return v === void 0 || v === null ? null : String(v);
7737
7882
  } catch {
7738
7883
  return null;
@@ -7924,19 +8069,19 @@ function parseWorkItemSource(frontmatter) {
7924
8069
  const rawSource = frontmatter.source;
7925
8070
  if (rawSource != null && typeof rawSource === "object" && !Array.isArray(rawSource)) {
7926
8071
  const obj = rawSource;
7927
- const t = optStr(obj.type);
8072
+ const t = optStr2(obj.type);
7928
8073
  const type = t && isValidSource(t) ? t : "unknown";
7929
8074
  return {
7930
8075
  type,
7931
- id: optStr(obj.id) ?? optStr(frontmatter.source_id),
7932
- title: optStr(obj.title) ?? optStr(frontmatter.source_title),
7933
- context: optStr(obj.context) ?? optStr(frontmatter.source_context),
7934
- provider: optStr(obj.provider) ?? optStr(frontmatter.source_provider),
7935
- integration: optStr(obj.integration) ?? optStr(frontmatter.source_integration),
7936
- url: optStr(obj.url) ?? optStr(frontmatter.source_url),
7937
- imported_at: optStr(obj.imported_at) ?? optStr(frontmatter.source_imported_at),
7938
- external_updated_at: optStr(obj.external_updated_at) ?? optStr(frontmatter.source_external_updated_at),
7939
- synced_at: optStr(obj.synced_at) ?? optStr(frontmatter.source_synced_at),
8076
+ id: optStr2(obj.id) ?? optStr2(frontmatter.source_id),
8077
+ title: optStr2(obj.title) ?? optStr2(frontmatter.source_title),
8078
+ context: optStr2(obj.context) ?? optStr2(frontmatter.source_context),
8079
+ provider: optStr2(obj.provider) ?? optStr2(frontmatter.source_provider),
8080
+ integration: optStr2(obj.integration) ?? optStr2(frontmatter.source_integration),
8081
+ url: optStr2(obj.url) ?? optStr2(frontmatter.source_url),
8082
+ imported_at: optStr2(obj.imported_at) ?? optStr2(frontmatter.source_imported_at),
8083
+ external_updated_at: optStr2(obj.external_updated_at) ?? optStr2(frontmatter.source_external_updated_at),
8084
+ synced_at: optStr2(obj.synced_at) ?? optStr2(frontmatter.source_synced_at),
7940
8085
  inferred: obj.inferred === true || obj.inferred === "true",
7941
8086
  reason: t && !isValidSource(t) ? `Invalid source type in object: "${t}".` : void 0
7942
8087
  };
@@ -7945,28 +8090,28 @@ function parseWorkItemSource(frontmatter) {
7945
8090
  if (raw && isValidSource(raw)) {
7946
8091
  return {
7947
8092
  type: raw,
7948
- id: optStr(frontmatter.source_id),
7949
- title: optStr(frontmatter.source_title),
7950
- context: optStr(frontmatter.source_context),
7951
- provider: optStr(frontmatter.source_provider),
7952
- url: optStr(frontmatter.source_url),
7953
- imported_at: optStr(frontmatter.source_imported_at),
7954
- external_updated_at: optStr(frontmatter.source_external_updated_at),
7955
- synced_at: optStr(frontmatter.source_synced_at),
8093
+ id: optStr2(frontmatter.source_id),
8094
+ title: optStr2(frontmatter.source_title),
8095
+ context: optStr2(frontmatter.source_context),
8096
+ provider: optStr2(frontmatter.source_provider),
8097
+ url: optStr2(frontmatter.source_url),
8098
+ imported_at: optStr2(frontmatter.source_imported_at),
8099
+ external_updated_at: optStr2(frontmatter.source_external_updated_at),
8100
+ synced_at: optStr2(frontmatter.source_synced_at),
7956
8101
  inferred: false
7957
8102
  };
7958
8103
  }
7959
8104
  if (raw && !isValidSource(raw)) {
7960
8105
  return {
7961
8106
  type: "unknown",
7962
- id: optStr(frontmatter.source_id),
7963
- title: optStr(frontmatter.source_title),
7964
- context: optStr(frontmatter.source_context),
7965
- provider: optStr(frontmatter.source_provider),
7966
- url: optStr(frontmatter.source_url),
7967
- imported_at: optStr(frontmatter.source_imported_at),
7968
- external_updated_at: optStr(frontmatter.source_external_updated_at),
7969
- synced_at: optStr(frontmatter.source_synced_at),
8107
+ id: optStr2(frontmatter.source_id),
8108
+ title: optStr2(frontmatter.source_title),
8109
+ context: optStr2(frontmatter.source_context),
8110
+ provider: optStr2(frontmatter.source_provider),
8111
+ url: optStr2(frontmatter.source_url),
8112
+ imported_at: optStr2(frontmatter.source_imported_at),
8113
+ external_updated_at: optStr2(frontmatter.source_external_updated_at),
8114
+ synced_at: optStr2(frontmatter.source_synced_at),
7970
8115
  inferred: true,
7971
8116
  reason: `Invalid source value: "${raw}".`
7972
8117
  };
@@ -7974,8 +8119,8 @@ function parseWorkItemSource(frontmatter) {
7974
8119
  if (frontmatter.source_work_item_candidate || frontmatter.source_roadmap_initiative) {
7975
8120
  return {
7976
8121
  type: "roadmap",
7977
- id: optStr(frontmatter.source_id) ?? optStr(frontmatter.source_work_item_candidate),
7978
- title: optStr(frontmatter.source_initiative_title),
8122
+ id: optStr2(frontmatter.source_id) ?? optStr2(frontmatter.source_work_item_candidate),
8123
+ title: optStr2(frontmatter.source_initiative_title),
7979
8124
  inferred: true,
7980
8125
  reason: "Inferred from legacy roadmap fields."
7981
8126
  };
@@ -7983,7 +8128,7 @@ function parseWorkItemSource(frontmatter) {
7983
8128
  if (frontmatter.source_id) {
7984
8129
  return {
7985
8130
  type: "unknown",
7986
- id: optStr(frontmatter.source_id),
8131
+ id: optStr2(frontmatter.source_id),
7987
8132
  inferred: true,
7988
8133
  reason: "Has source_id but no source type."
7989
8134
  };
@@ -7999,7 +8144,7 @@ function renderSourceCompact(source) {
7999
8144
  if (source.id) parts.push(source.id);
8000
8145
  return parts.join(" \xB7 ");
8001
8146
  }
8002
- function optStr(v) {
8147
+ function optStr2(v) {
8003
8148
  if (typeof v === "string" && v.trim()) return v.trim();
8004
8149
  return void 0;
8005
8150
  }
@@ -8504,7 +8649,7 @@ function renderRouteCompact(route) {
8504
8649
  }
8505
8650
 
8506
8651
  // ../cli/src/core/metadata-health.ts
8507
- import matter7 from "gray-matter";
8652
+ import matter8 from "gray-matter";
8508
8653
  var KNOWLEDGE_FILES2 = [
8509
8654
  "knowledge/business/business.md",
8510
8655
  "knowledge/product/product.md",
@@ -8524,7 +8669,7 @@ function analyzeMetadataHealth(dir) {
8524
8669
  let data;
8525
8670
  try {
8526
8671
  const raw = readFile(p);
8527
- data = matter7(raw).data;
8672
+ data = matter8(raw).data;
8528
8673
  } catch {
8529
8674
  continue;
8530
8675
  }
@@ -8556,7 +8701,7 @@ function analyzeMetadataHealth(dir) {
8556
8701
  if (isFile(p) && entry.endsWith(".md")) {
8557
8702
  try {
8558
8703
  const raw = readFile(p);
8559
- const fm = matter7(raw).data;
8704
+ const fm = matter8(raw).data;
8560
8705
  if (fm.status === "done") {
8561
8706
  const rel = p.replace(dir + "/", "").replace(dir + "\\", "").replace(/\\/g, "/");
8562
8707
  findings.push({
@@ -9323,7 +9468,7 @@ function renderExplanationAgent(exp) {
9323
9468
  var HISTORY_DIR = ".kaddo/history";
9324
9469
  var RUNS_PATH = `${HISTORY_DIR}/guard-runs.jsonl`;
9325
9470
  var SUMMARY_PATH = `${HISTORY_DIR}/guard-summary.json`;
9326
- function toPosix5(p) {
9471
+ function toPosix6(p) {
9327
9472
  return p.replace(/\\/g, "/");
9328
9473
  }
9329
9474
  function loadGuardRuns(dir) {
@@ -9360,7 +9505,7 @@ function resolveDriftThreads(runs) {
9360
9505
  let resolvedAt;
9361
9506
  for (const r2 of sorted) {
9362
9507
  if (r2.generated_at <= lastWarn) continue;
9363
- const touched = r2.touched_files.some((f3) => toPosix5(f3) === codePath);
9508
+ const touched = r2.touched_files.some((f3) => toPosix6(f3) === codePath);
9364
9509
  const warnedHere = r2.warnings.some((x2) => x2.code_path === codePath);
9365
9510
  const updatedRelated = r2.updated_artifacts.some((a2) => related.has(a2));
9366
9511
  if (touched && updatedRelated && !warnedHere) {
@@ -9511,7 +9656,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
9511
9656
  }
9512
9657
  }
9513
9658
  try {
9514
- const body = matter8(readFile(wi.filePath)).content;
9659
+ const body = matter9(readFile(wi.filePath)).content;
9515
9660
  if (hasSection(body, /acceptance|criterios de aceptaci/i)) withAcceptance++;
9516
9661
  else gaps.missing_acceptance_criteria.push(gapItem(wi, "Add an `## Acceptance Criteria` section."));
9517
9662
  if (hasSection(body, /definition of done|^#{1,6}\s*dod\b|definici[oó]n de (terminado|hecho)/i)) withDoD++;
@@ -10224,25 +10369,25 @@ function serializeDriftJson(r2) {
10224
10369
  }
10225
10370
 
10226
10371
  // src/workitems.ts
10227
- import matter9 from "gray-matter";
10372
+ import matter10 from "gray-matter";
10228
10373
  var VALID_SOURCES2 = /* @__PURE__ */ new Set(["manual", "roadmap", "jira", "github", "notion", "xlsx", "csv", "api", "external", "unknown"]);
10229
- function optStr2(v) {
10374
+ function optStr3(v) {
10230
10375
  return typeof v === "string" && v.trim() ? v.trim() : void 0;
10231
10376
  }
10232
10377
  function parseSource(data) {
10233
10378
  const rawSource = data.source;
10234
10379
  if (rawSource != null && typeof rawSource === "object" && !Array.isArray(rawSource)) {
10235
10380
  const obj = rawSource;
10236
- const t = optStr2(obj.type);
10381
+ const t = optStr3(obj.type);
10237
10382
  const type = t && VALID_SOURCES2.has(t) ? t : "unknown";
10238
- return { type, id: optStr2(obj.id) ?? optStr2(data.source_id), title: optStr2(obj.title) ?? optStr2(data.source_title), url: optStr2(obj.url) ?? optStr2(data.source_url), provider: optStr2(obj.provider) ?? optStr2(data.source_provider), inferred: obj.inferred === true || obj.inferred === "true" };
10383
+ return { type, id: optStr3(obj.id) ?? optStr3(data.source_id), title: optStr3(obj.title) ?? optStr3(data.source_title), url: optStr3(obj.url) ?? optStr3(data.source_url), provider: optStr3(obj.provider) ?? optStr3(data.source_provider), inferred: obj.inferred === true || obj.inferred === "true" };
10239
10384
  }
10240
10385
  const raw = rawSource ? String(rawSource) : "";
10241
10386
  if (raw && VALID_SOURCES2.has(raw)) {
10242
- return { type: raw, id: optStr2(data.source_id), title: optStr2(data.source_title), url: optStr2(data.source_url), provider: optStr2(data.source_provider), inferred: false };
10387
+ return { type: raw, id: optStr3(data.source_id), title: optStr3(data.source_title), url: optStr3(data.source_url), provider: optStr3(data.source_provider), inferred: false };
10243
10388
  }
10244
10389
  if (data.source_work_item_candidate || data.source_roadmap_initiative) {
10245
- return { type: "roadmap", id: optStr2(data.source_id) ?? optStr2(data.source_work_item_candidate), title: optStr2(data.source_initiative_title), inferred: true };
10390
+ return { type: "roadmap", id: optStr3(data.source_id) ?? optStr3(data.source_work_item_candidate), title: optStr3(data.source_initiative_title), inferred: true };
10246
10391
  }
10247
10392
  return { type: "unknown", inferred: true };
10248
10393
  }
@@ -10315,7 +10460,7 @@ function listWorkItems(root) {
10315
10460
  if (raw === null) continue;
10316
10461
  let parsed;
10317
10462
  try {
10318
- parsed = matter9(raw);
10463
+ parsed = matter10(raw);
10319
10464
  } catch {
10320
10465
  continue;
10321
10466
  }
@@ -10790,7 +10935,7 @@ import fs3 from "fs";
10790
10935
  import path3 from "path";
10791
10936
  import { z as z3 } from "zod";
10792
10937
  import { parse as parseYaml10 } from "yaml";
10793
- import matter10 from "gray-matter";
10938
+ import matter11 from "gray-matter";
10794
10939
  import matter22 from "gray-matter";
10795
10940
  import { parse as parseYaml62, stringify as stringifyYaml32 } from "yaml";
10796
10941
  import { parse as parseYaml22 } from "yaml";
@@ -10798,26 +10943,26 @@ import { parse as parseYaml32 } from "yaml";
10798
10943
  import { parse as parseYaml52, stringify as stringifyYaml22 } from "yaml";
10799
10944
  import { parse as parseYaml42, stringify as stringifyYaml4 } from "yaml";
10800
10945
  import matter32 from "gray-matter";
10801
- import { parse as parseYaml72 } from "yaml";
10802
10946
  import matter42 from "gray-matter";
10947
+ import { parse as parseYaml72 } from "yaml";
10948
+ import matter52 from "gray-matter";
10803
10949
  import { fileURLToPath as fileURLToPath2 } from "url";
10804
10950
  import { dirname as dirname2, join as join22, parse as parse2 } from "path";
10805
10951
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
10806
- import matter52 from "gray-matter";
10807
10952
  import matter62 from "gray-matter";
10953
+ import matter72 from "gray-matter";
10808
10954
  import { parse as parseYaml82, stringify as stringifyYaml42 } from "yaml";
10809
10955
  import fs32 from "fs";
10810
10956
  import path32 from "path";
10811
10957
  import crypto2 from "crypto";
10812
- import matter72 from "gray-matter";
10958
+ import matter82 from "gray-matter";
10813
10959
  import crypto3 from "crypto";
10814
10960
  import fs4 from "fs";
10815
- import matter82 from "gray-matter";
10816
10961
  import matter92 from "gray-matter";
10962
+ import matter102 from "gray-matter";
10817
10963
  import { parse as parseYaml92, stringify as stringifyYaml5 } from "yaml";
10818
10964
  import { parse as parseYaml102, stringify as stringifyYaml6 } from "yaml";
10819
- import matter102 from "gray-matter";
10820
- import matter11 from "gray-matter";
10965
+ import matter112 from "gray-matter";
10821
10966
  function exists2(p) {
10822
10967
  return fs3.existsSync(p);
10823
10968
  }
@@ -10985,7 +11130,7 @@ function parseImpactAnalysis2(data) {
10985
11130
  }
10986
11131
  function parseArtifact2(filePath, raw) {
10987
11132
  try {
10988
- const { data } = matter10(raw);
11133
+ const { data } = matter11(raw);
10989
11134
  const globs = Array.isArray(data.code) ? data.code.filter(Boolean) : [];
10990
11135
  return {
10991
11136
  filePath,
@@ -11078,16 +11223,16 @@ function emptyLifecycleCounts2() {
11078
11223
  return Object.fromEntries(LIFECYCLE_STATES2.map((s) => [s, 0]));
11079
11224
  }
11080
11225
  var ARCH_DIR3 = "knowledge";
11081
- function toPosix6(p) {
11226
+ function toPosix7(p) {
11082
11227
  return p.replace(/\\/g, "/");
11083
11228
  }
11084
11229
  function relativeTo2(dir, filePath) {
11085
- const base = toPosix6(dir).replace(/\/+$/, "");
11086
- const full = toPosix6(filePath);
11230
+ const base = toPosix7(dir).replace(/\/+$/, "");
11231
+ const full = toPosix7(filePath);
11087
11232
  return full.startsWith(base + "/") ? full.slice(base.length + 1) : full;
11088
11233
  }
11089
11234
  function isWorkItemArtifact2(a2) {
11090
- return toPosix6(a2.filePath).includes("/delivery/work-items/") && Boolean(a2.type);
11235
+ return toPosix7(a2.filePath).includes("/delivery/work-items/") && Boolean(a2.type);
11091
11236
  }
11092
11237
  function classifyLayer2(posixPath) {
11093
11238
  if (posixPath.includes("/knowledge/business/")) return "business";
@@ -11099,7 +11244,7 @@ function classifyLayer2(posixPath) {
11099
11244
  return "unknown";
11100
11245
  }
11101
11246
  function enrich2(dir, a2) {
11102
- const posix = toPosix6(a2.filePath);
11247
+ const posix = toPosix7(a2.filePath);
11103
11248
  const isWi = isWorkItemArtifact2(a2);
11104
11249
  return {
11105
11250
  ...a2,
@@ -12333,20 +12478,226 @@ function loadExternalRegistry2(dir) {
12333
12478
  return [];
12334
12479
  }
12335
12480
  }
12481
+ var INITIATIVES_DIR2 = "knowledge/delivery/initiatives";
12482
+ var INITIATIVE_STATES2 = [
12483
+ "candidate",
12484
+ "planned",
12485
+ "in-progress",
12486
+ "completed",
12487
+ "deferred",
12488
+ "cancelled"
12489
+ ];
12490
+ var INITIATIVE_TRANSITIONS = {
12491
+ candidate: ["planned", "cancelled"],
12492
+ planned: ["in-progress", "deferred", "cancelled"],
12493
+ "in-progress": ["completed", "deferred", "cancelled"],
12494
+ deferred: ["planned", "cancelled"],
12495
+ completed: [],
12496
+ cancelled: []
12497
+ };
12498
+ var STATUS_SET2 = new Set(INITIATIVE_STATES2);
12499
+ function isValidInitiativeStatus2(s) {
12500
+ return STATUS_SET2.has(s);
12501
+ }
12502
+ function isValidInitiativeTransition(from, to) {
12503
+ return INITIATIVE_TRANSITIONS[from].includes(to);
12504
+ }
12505
+ function toPosix22(p) {
12506
+ return p.replace(/\\/g, "/");
12507
+ }
12508
+ function optStr4(v) {
12509
+ if (typeof v === "string" && v.trim()) return v.trim();
12510
+ if (typeof v === "number") return String(v);
12511
+ return void 0;
12512
+ }
12513
+ function strList2(v) {
12514
+ if (!Array.isArray(v)) return [];
12515
+ return v.map((x2) => typeof x2 === "string" ? x2.trim() : String(x2)).filter(Boolean);
12516
+ }
12517
+ function parseCandidates2(data) {
12518
+ const raw = data.candidates;
12519
+ if (!Array.isArray(raw)) return [];
12520
+ const out = [];
12521
+ for (const entry of raw) {
12522
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
12523
+ const o2 = entry;
12524
+ const id = optStr4(o2.id);
12525
+ const title = optStr4(o2.title);
12526
+ if (!id || !title) continue;
12527
+ out.push({
12528
+ id,
12529
+ title,
12530
+ type: optStr4(o2.type),
12531
+ suggestedKnowledgeLevel: optStr4(o2.suggested_knowledge_level ?? o2.knowledge_level),
12532
+ expectedValue: optStr4(o2.expected_value),
12533
+ notes: optStr4(o2.notes),
12534
+ sourceSignals: strList2(o2.source_signals),
12535
+ dependencies: strList2(o2.dependencies),
12536
+ materializedAs: optStr4(o2.materialized_as) ?? null
12537
+ });
12538
+ }
12539
+ return out;
12540
+ }
12541
+ function parseExternalLinks2(data) {
12542
+ const raw = data.external_links;
12543
+ if (!Array.isArray(raw)) return [];
12544
+ const out = [];
12545
+ for (const entry of raw) {
12546
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
12547
+ const o2 = entry;
12548
+ const integration = optStr4(o2.integration);
12549
+ const externalId = optStr4(o2.external_id);
12550
+ if (!integration || !externalId) continue;
12551
+ out.push({
12552
+ integration,
12553
+ externalId,
12554
+ externalType: optStr4(o2.external_type),
12555
+ url: optStr4(o2.url),
12556
+ externalStatus: optStr4(o2.external_status)
12557
+ });
12558
+ }
12559
+ return out;
12560
+ }
12561
+ function parseInitiative2(dir, filePath, raw) {
12562
+ let parsed;
12563
+ try {
12564
+ parsed = matter32(raw, {});
12565
+ } catch {
12566
+ return null;
12567
+ }
12568
+ const data = parsed.data;
12569
+ if (optStr4(data.type) !== "initiative") return null;
12570
+ const id = optStr4(data.id);
12571
+ if (!id) return null;
12572
+ const statusRaw = optStr4(data.status) ?? "planned";
12573
+ const status = isValidInitiativeStatus2(statusRaw) ? statusRaw : "planned";
12574
+ return {
12575
+ id,
12576
+ title: optStr4(data.title) ?? id,
12577
+ status,
12578
+ horizon: optStr4(data.horizon) ?? null,
12579
+ priority: optStr4(data.priority) ?? null,
12580
+ knowledgeLevel: optStr4(data.knowledge_level) ?? null,
12581
+ domains: strList2(data.domains),
12582
+ relatedCapabilities: strList2(data.related_capabilities),
12583
+ source: optStr4(data.source) ?? null,
12584
+ sourceId: optStr4(data.source_id) ?? null,
12585
+ createdAt: optStr4(data.created_at) ?? null,
12586
+ externalLinks: parseExternalLinks2(data),
12587
+ candidates: parseCandidates2(data),
12588
+ filePath,
12589
+ relPath: toPosix22(filePath).startsWith(toPosix22(dir) + "/") ? toPosix22(filePath).slice(toPosix22(dir).length + 1) : toPosix22(filePath),
12590
+ body: parsed.content.trim(),
12591
+ rawFrontmatter: data
12592
+ };
12593
+ }
12594
+ function discoverInitiatives2(dir) {
12595
+ const iniDir = join3(dir, INITIATIVES_DIR2);
12596
+ if (!exists2(iniDir)) return [];
12597
+ const out = [];
12598
+ for (const entry of readDir2(iniDir)) {
12599
+ if (!entry.endsWith(".md")) continue;
12600
+ const full = join3(iniDir, entry);
12601
+ if (!isFile2(full)) continue;
12602
+ const ini = parseInitiative2(dir, full, readFile2(full));
12603
+ if (ini) out.push(ini);
12604
+ }
12605
+ return out.sort((a2, b2) => a2.id.localeCompare(b2.id));
12606
+ }
12607
+ function getInitiative(dir, id) {
12608
+ const want = id.trim().toLowerCase();
12609
+ return discoverInitiatives2(dir).find((i) => i.id.toLowerCase() === want) ?? null;
12610
+ }
12611
+ function nextInitiativeId(dir) {
12612
+ const iniDir = join3(dir, INITIATIVES_DIR2);
12613
+ let max = 0;
12614
+ if (exists2(iniDir)) {
12615
+ for (const entry of readDir2(iniDir)) {
12616
+ const m2 = entry.match(/INI-(\d+)/);
12617
+ if (m2) max = Math.max(max, parseInt(m2[1], 10));
12618
+ }
12619
+ }
12620
+ return `INI-${String(max + 1).padStart(3, "0")}`;
12621
+ }
12622
+ function workItemBelongsToInitiative(workItemInitiative, iniId) {
12623
+ const v = (workItemInitiative ?? "").trim().toLowerCase();
12624
+ return v !== "" && v === iniId.trim().toLowerCase();
12625
+ }
12626
+ function associatedWorkItems(dir, iniId) {
12627
+ return discoverWorkItems2(dir).filter((wi) => workItemBelongsToInitiative(wi.initiative, iniId)).map((wi) => ({
12628
+ id: wi.id,
12629
+ title: wi.title,
12630
+ status: wi.lifecycle ?? "ready"
12631
+ })).sort((a2, b2) => a2.id.localeCompare(b2.id));
12632
+ }
12633
+ function computeInitiativeProgress(dir, ini) {
12634
+ const totalCandidates = ini.candidates.length;
12635
+ const materialized = ini.candidates.filter((c) => c.materializedAs).length;
12636
+ const byState = emptyLifecycleCounts2();
12637
+ const associated = associatedWorkItems(dir, ini.id);
12638
+ for (const wi of associated) byState[wi.status]++;
12639
+ return {
12640
+ initiative: ini.id,
12641
+ status: ini.status,
12642
+ planning: {
12643
+ totalCandidates,
12644
+ materialized,
12645
+ remaining: Math.max(0, totalCandidates - materialized)
12646
+ },
12647
+ delivery: {
12648
+ total: associated.length,
12649
+ byState
12650
+ }
12651
+ };
12652
+ }
12653
+ function getInitiativeContext(dir, ini) {
12654
+ return {
12655
+ initiative: {
12656
+ id: ini.id,
12657
+ title: ini.title,
12658
+ status: ini.status,
12659
+ horizon: ini.horizon,
12660
+ priority: ini.priority,
12661
+ knowledgeLevel: ini.knowledgeLevel,
12662
+ domains: ini.domains,
12663
+ relatedCapabilities: ini.relatedCapabilities,
12664
+ source: ini.source,
12665
+ sourceId: ini.sourceId
12666
+ },
12667
+ progress: computeInitiativeProgress(dir, ini),
12668
+ candidates: {
12669
+ total: ini.candidates.length,
12670
+ materialized: ini.candidates.filter((c) => c.materializedAs),
12671
+ pending: ini.candidates.filter((c) => !c.materializedAs)
12672
+ },
12673
+ workItems: associatedWorkItems(dir, ini.id),
12674
+ externalLinks: ini.externalLinks
12675
+ };
12676
+ }
12677
+ function suggestInitiativesForExternalItem(dir, integration, externalId) {
12678
+ const wantIntegration = integration.trim().toLowerCase();
12679
+ const wantId = externalId.trim().toLowerCase();
12680
+ if (!wantIntegration || !wantId) return [];
12681
+ return discoverInitiatives2(dir).filter(
12682
+ (ini) => ini.externalLinks.some(
12683
+ (l2) => l2.integration.trim().toLowerCase() === wantIntegration && l2.externalId.trim().toLowerCase() === wantId
12684
+ )
12685
+ );
12686
+ }
12336
12687
  var KNOWLEDGE22 = "knowledge";
12337
12688
  var ACTIVE_STATUSES2 = ["draft", "ready", "in-progress", "blocked"];
12338
12689
  var ALL_STATUSES2 = ["draft", "ready", "in-progress", "blocked", "completed"];
12339
12690
  function scopeStatuses2(scope) {
12340
12691
  return scope === "all" ? { included: ALL_STATUSES2, excluded: ["archived"] } : { included: ACTIVE_STATUSES2, excluded: ["completed", "archived"] };
12341
12692
  }
12342
- function toPosix22(p) {
12693
+ function toPosix32(p) {
12343
12694
  return p.replace(/\\/g, "/");
12344
12695
  }
12345
12696
  function slug3(s) {
12346
12697
  return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
12347
12698
  }
12348
12699
  function isAdr3(a2) {
12349
- return toPosix22(a2.filePath).includes("/tech/decisions/") && Boolean(a2.type);
12700
+ return toPosix32(a2.filePath).includes("/tech/decisions/") && Boolean(a2.type);
12350
12701
  }
12351
12702
  function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
12352
12703
  const scope = opts.scope ?? "active";
@@ -12385,6 +12736,33 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
12385
12736
  const includedSet = new Set(included);
12386
12737
  const activeWICount = workItems.filter((a2) => a2.lifecycle && isActiveState2(a2.lifecycle)).length;
12387
12738
  const selectedWIs = workItems.filter((a2) => a2.lifecycle && includedSet.has(a2.lifecycle));
12739
+ for (const ini of discoverInitiatives2(dir)) {
12740
+ const iniNodeId = `initiative:${slug3(ini.id) || ini.id}`;
12741
+ addNode({
12742
+ id: iniNodeId,
12743
+ type: "initiative",
12744
+ label: `${ini.id} ${ini.title}`.trim(),
12745
+ path: ini.relPath,
12746
+ status: ini.status
12747
+ });
12748
+ for (const cap of ini.relatedCapabilities) {
12749
+ if (!cap.trim()) continue;
12750
+ const capId = `capability:${slug3(cap) || cap}`;
12751
+ addNode({ id: capId, type: "capability", label: cap });
12752
+ addEdge(iniNodeId, capId, "targets");
12753
+ }
12754
+ for (const link of ini.externalLinks) {
12755
+ const extId = `external-item:${slug3(link.integration) || link.integration}:${slug3(link.externalId) || link.externalId}`;
12756
+ addNode({ id: extId, type: "external-item", label: `${link.integration}:${link.externalId}`, path: link.url });
12757
+ addEdge(iniNodeId, extId, "references_external");
12758
+ }
12759
+ for (const cand of ini.candidates) {
12760
+ const candId = `candidate:${cand.id}`;
12761
+ addNode({ id: candId, type: "roadmap-candidate", label: cand.id });
12762
+ addEdge(candId, iniNodeId, "belongs_to");
12763
+ if (cand.materializedAs) addEdge(candId, `wi:${cand.materializedAs}`, "materialized_as");
12764
+ }
12765
+ }
12388
12766
  for (const wi of selectedWIs) {
12389
12767
  const id = wi.id || wi.title;
12390
12768
  if (!id || !id.trim()) continue;
@@ -12478,14 +12856,14 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
12478
12856
  };
12479
12857
  }
12480
12858
  var KNOWLEDGE32 = "knowledge";
12481
- function toPosix32(p) {
12859
+ function toPosix42(p) {
12482
12860
  return p.replace(/\\/g, "/");
12483
12861
  }
12484
12862
  function slug22(s) {
12485
12863
  return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
12486
12864
  }
12487
12865
  function isAdr22(a2) {
12488
- return toPosix32(a2.filePath).includes("/tech/decisions/") && Boolean(a2.type);
12866
+ return toPosix42(a2.filePath).includes("/tech/decisions/") && Boolean(a2.type);
12489
12867
  }
12490
12868
  function capabilityHeadings2(dir) {
12491
12869
  const p = join3(dir, KNOWLEDGE32, "product", "capabilities.md");
@@ -15950,19 +16328,19 @@ function parseWorkItemSource2(frontmatter) {
15950
16328
  const rawSource = frontmatter.source;
15951
16329
  if (rawSource != null && typeof rawSource === "object" && !Array.isArray(rawSource)) {
15952
16330
  const obj = rawSource;
15953
- const t = optStr3(obj.type);
16331
+ const t = optStr22(obj.type);
15954
16332
  const type = t && isValidSource2(t) ? t : "unknown";
15955
16333
  return {
15956
16334
  type,
15957
- id: optStr3(obj.id) ?? optStr3(frontmatter.source_id),
15958
- title: optStr3(obj.title) ?? optStr3(frontmatter.source_title),
15959
- context: optStr3(obj.context) ?? optStr3(frontmatter.source_context),
15960
- provider: optStr3(obj.provider) ?? optStr3(frontmatter.source_provider),
15961
- integration: optStr3(obj.integration) ?? optStr3(frontmatter.source_integration),
15962
- url: optStr3(obj.url) ?? optStr3(frontmatter.source_url),
15963
- imported_at: optStr3(obj.imported_at) ?? optStr3(frontmatter.source_imported_at),
15964
- external_updated_at: optStr3(obj.external_updated_at) ?? optStr3(frontmatter.source_external_updated_at),
15965
- synced_at: optStr3(obj.synced_at) ?? optStr3(frontmatter.source_synced_at),
16335
+ id: optStr22(obj.id) ?? optStr22(frontmatter.source_id),
16336
+ title: optStr22(obj.title) ?? optStr22(frontmatter.source_title),
16337
+ context: optStr22(obj.context) ?? optStr22(frontmatter.source_context),
16338
+ provider: optStr22(obj.provider) ?? optStr22(frontmatter.source_provider),
16339
+ integration: optStr22(obj.integration) ?? optStr22(frontmatter.source_integration),
16340
+ url: optStr22(obj.url) ?? optStr22(frontmatter.source_url),
16341
+ imported_at: optStr22(obj.imported_at) ?? optStr22(frontmatter.source_imported_at),
16342
+ external_updated_at: optStr22(obj.external_updated_at) ?? optStr22(frontmatter.source_external_updated_at),
16343
+ synced_at: optStr22(obj.synced_at) ?? optStr22(frontmatter.source_synced_at),
15966
16344
  inferred: obj.inferred === true || obj.inferred === "true",
15967
16345
  reason: t && !isValidSource2(t) ? `Invalid source type in object: "${t}".` : void 0
15968
16346
  };
@@ -15971,28 +16349,28 @@ function parseWorkItemSource2(frontmatter) {
15971
16349
  if (raw && isValidSource2(raw)) {
15972
16350
  return {
15973
16351
  type: raw,
15974
- id: optStr3(frontmatter.source_id),
15975
- title: optStr3(frontmatter.source_title),
15976
- context: optStr3(frontmatter.source_context),
15977
- provider: optStr3(frontmatter.source_provider),
15978
- url: optStr3(frontmatter.source_url),
15979
- imported_at: optStr3(frontmatter.source_imported_at),
15980
- external_updated_at: optStr3(frontmatter.source_external_updated_at),
15981
- synced_at: optStr3(frontmatter.source_synced_at),
16352
+ id: optStr22(frontmatter.source_id),
16353
+ title: optStr22(frontmatter.source_title),
16354
+ context: optStr22(frontmatter.source_context),
16355
+ provider: optStr22(frontmatter.source_provider),
16356
+ url: optStr22(frontmatter.source_url),
16357
+ imported_at: optStr22(frontmatter.source_imported_at),
16358
+ external_updated_at: optStr22(frontmatter.source_external_updated_at),
16359
+ synced_at: optStr22(frontmatter.source_synced_at),
15982
16360
  inferred: false
15983
16361
  };
15984
16362
  }
15985
16363
  if (raw && !isValidSource2(raw)) {
15986
16364
  return {
15987
16365
  type: "unknown",
15988
- id: optStr3(frontmatter.source_id),
15989
- title: optStr3(frontmatter.source_title),
15990
- context: optStr3(frontmatter.source_context),
15991
- provider: optStr3(frontmatter.source_provider),
15992
- url: optStr3(frontmatter.source_url),
15993
- imported_at: optStr3(frontmatter.source_imported_at),
15994
- external_updated_at: optStr3(frontmatter.source_external_updated_at),
15995
- synced_at: optStr3(frontmatter.source_synced_at),
16366
+ id: optStr22(frontmatter.source_id),
16367
+ title: optStr22(frontmatter.source_title),
16368
+ context: optStr22(frontmatter.source_context),
16369
+ provider: optStr22(frontmatter.source_provider),
16370
+ url: optStr22(frontmatter.source_url),
16371
+ imported_at: optStr22(frontmatter.source_imported_at),
16372
+ external_updated_at: optStr22(frontmatter.source_external_updated_at),
16373
+ synced_at: optStr22(frontmatter.source_synced_at),
15996
16374
  inferred: true,
15997
16375
  reason: `Invalid source value: "${raw}".`
15998
16376
  };
@@ -16000,8 +16378,8 @@ function parseWorkItemSource2(frontmatter) {
16000
16378
  if (frontmatter.source_work_item_candidate || frontmatter.source_roadmap_initiative) {
16001
16379
  return {
16002
16380
  type: "roadmap",
16003
- id: optStr3(frontmatter.source_id) ?? optStr3(frontmatter.source_work_item_candidate),
16004
- title: optStr3(frontmatter.source_initiative_title),
16381
+ id: optStr22(frontmatter.source_id) ?? optStr22(frontmatter.source_work_item_candidate),
16382
+ title: optStr22(frontmatter.source_initiative_title),
16005
16383
  inferred: true,
16006
16384
  reason: "Inferred from legacy roadmap fields."
16007
16385
  };
@@ -16009,7 +16387,7 @@ function parseWorkItemSource2(frontmatter) {
16009
16387
  if (frontmatter.source_id) {
16010
16388
  return {
16011
16389
  type: "unknown",
16012
- id: optStr3(frontmatter.source_id),
16390
+ id: optStr22(frontmatter.source_id),
16013
16391
  inferred: true,
16014
16392
  reason: "Has source_id but no source type."
16015
16393
  };
@@ -16020,7 +16398,7 @@ function parseWorkItemSource2(frontmatter) {
16020
16398
  reason: "No source metadata found."
16021
16399
  };
16022
16400
  }
16023
- function optStr3(v) {
16401
+ function optStr22(v) {
16024
16402
  if (typeof v === "string" && v.trim()) return v.trim();
16025
16403
  return void 0;
16026
16404
  }
@@ -16438,7 +16816,7 @@ var PROVENANCE = /* @__PURE__ */ new Set(["declared", "derived", "agent-reviewed
16438
16816
  function isRelative(p) {
16439
16817
  return typeof p === "string" && p.trim() !== "" && !/^([a-zA-Z]:[\\/]|\/)/.test(p) && !p.includes("..");
16440
16818
  }
16441
- function strList(v) {
16819
+ function strList22(v) {
16442
16820
  return Array.isArray(v) ? v.map((x2) => typeof x2 === "string" ? x2.trim() : "").filter(Boolean) : [];
16443
16821
  }
16444
16822
  function loadSystemTopology(dir) {
@@ -16479,7 +16857,7 @@ function parseTopologyContent(raw, dir) {
16479
16857
  const moduleRaw = typeof e.module === "string" ? e.module.trim() : void 0;
16480
16858
  if (moduleRaw && !validModules.has(moduleRaw)) findings.push({ level: "warning", message: `Entity "${id}" references unregistered module "${moduleRaw}".` });
16481
16859
  const rawImpl = e.implementation ?? e.implementation_refs;
16482
- const implementation = strList(rawImpl).filter((p) => {
16860
+ const implementation = strList22(rawImpl).filter((p) => {
16483
16861
  if (isRelative(p)) return true;
16484
16862
  findings.push({ level: "warning", message: `Entity "${id}" implementation ref "${p}" is not a safe relative path and was dropped.` });
16485
16863
  return false;
@@ -16487,7 +16865,7 @@ function parseTopologyContent(raw, dir) {
16487
16865
  const prov = e.provenance;
16488
16866
  const provOrigin = typeof prov === "string" ? prov : prov && typeof prov === "object" && typeof prov.origin === "string" ? String(prov.origin) : void 0;
16489
16867
  const provenance = provOrigin && PROVENANCE.has(provOrigin) ? provOrigin : void 0;
16490
- const evidence = strList(e.evidence ?? (prov && typeof prov === "object" ? prov.evidence_refs : void 0)).filter(isRelative);
16868
+ const evidence = strList22(e.evidence ?? (prov && typeof prov === "object" ? prov.evidence_refs : void 0)).filter(isRelative);
16491
16869
  seenIds.add(id);
16492
16870
  entities.push({
16493
16871
  id,
@@ -16496,7 +16874,7 @@ function parseTopologyContent(raw, dir) {
16496
16874
  ...typeof e.purpose === "string" && e.purpose.trim() ? { purpose: e.purpose.trim() } : {},
16497
16875
  ...moduleRaw && validModules.has(moduleRaw) ? { moduleId: moduleRaw } : {},
16498
16876
  implementationRefs: implementation,
16499
- knowledgeRefs: strList(e.knowledge ?? e.knowledge_refs),
16877
+ knowledgeRefs: strList22(e.knowledge ?? e.knowledge_refs),
16500
16878
  ...provenance ? { provenance } : {},
16501
16879
  evidence
16502
16880
  });
@@ -16530,7 +16908,7 @@ function parseTopologyContent(raw, dir) {
16530
16908
  }
16531
16909
  seenRels.add(key);
16532
16910
  const prov = r2.provenance;
16533
- relationships.push({ from, to, type, evidence: strList(r2.evidence ?? prov?.evidence_refs).filter(isRelative) });
16911
+ relationships.push({ from, to, type, evidence: strList22(r2.evidence ?? prov?.evidence_refs).filter(isRelative) });
16534
16912
  }
16535
16913
  return { entities, relationships, findings, ...sourceRevision ? { sourceRevision } : {} };
16536
16914
  }
@@ -16663,7 +17041,7 @@ function parseOriginalSnapshot(fm) {
16663
17041
  function readBody(filePath) {
16664
17042
  try {
16665
17043
  const raw = readFile2(filePath);
16666
- return matter62(raw).content;
17044
+ return matter72(raw).content;
16667
17045
  } catch {
16668
17046
  return "";
16669
17047
  }
@@ -16747,7 +17125,7 @@ function normalizeImpact(ia) {
16747
17125
  ...v.question ? { question: v.question } : {}
16748
17126
  }));
16749
17127
  }
16750
- function optStr22(v) {
17128
+ function optStr32(v) {
16751
17129
  if (typeof v === "string" && v.trim()) return v.trim();
16752
17130
  return void 0;
16753
17131
  }
@@ -16762,13 +17140,13 @@ function parseEvidence(fm) {
16762
17140
  const validations = Array.isArray(d3.validations) ? d3.validations.map((v) => ({
16763
17141
  command: String(v.command ?? ""),
16764
17142
  status: String(v.status ?? "unknown"),
16765
- ...optStr22(v.reason) ? { reason: optStr22(v.reason) } : {}
17143
+ ...optStr32(v.reason) ? { reason: optStr32(v.reason) } : {}
16766
17144
  })) : [];
16767
17145
  const migrations = Array.isArray(d3.migrations) ? d3.migrations.map((m2) => ({
16768
17146
  id: String(m2.id ?? ""),
16769
17147
  environment: String(m2.environment ?? ""),
16770
17148
  status: String(m2.status ?? "unknown"),
16771
- ...optStr22(m2.reason) ? { reason: optStr22(m2.reason) } : {}
17149
+ ...optStr32(m2.reason) ? { reason: optStr32(m2.reason) } : {}
16772
17150
  })) : [];
16773
17151
  out.push({
16774
17152
  module,
@@ -16786,8 +17164,8 @@ function parseReleaseGates(fm) {
16786
17164
  return fm.release_gates.filter((g) => g && typeof g === "object").map((g) => ({
16787
17165
  id: String(g.id ?? ""),
16788
17166
  status: String(g.status ?? "pending"),
16789
- ...optStr22(g.reason) ? { reason: optStr22(g.reason) } : {},
16790
- ...optStr22(g.required_for) ? { requiredFor: optStr22(g.required_for) } : {}
17167
+ ...optStr32(g.reason) ? { reason: optStr32(g.reason) } : {},
17168
+ ...optStr32(g.required_for) ? { requiredFor: optStr32(g.required_for) } : {}
16791
17169
  }));
16792
17170
  }
16793
17171
  function parseExceptions(fm) {
@@ -16795,9 +17173,9 @@ function parseExceptions(fm) {
16795
17173
  return fm.completion_exceptions.filter((e) => e && typeof e === "object").map((e) => ({
16796
17174
  id: String(e.id ?? ""),
16797
17175
  status: String(e.status ?? "proposed"),
16798
- ...optStr22(e.reason) ? { reason: optStr22(e.reason) } : {},
16799
- ...optStr22(e.category) ? { category: optStr22(e.category) } : {},
16800
- ...optStr22(e.impact) ? { impact: optStr22(e.impact) } : {}
17176
+ ...optStr32(e.reason) ? { reason: optStr32(e.reason) } : {},
17177
+ ...optStr32(e.category) ? { category: optStr32(e.category) } : {},
17178
+ ...optStr32(e.impact) ? { impact: optStr32(e.impact) } : {}
16801
17179
  }));
16802
17180
  }
16803
17181
  function parseDecisions(decisions, knowledgeById) {
@@ -16940,7 +17318,7 @@ ${out.join("\n\n")}
16940
17318
  `.replace(/\n{3,}/g, "\n\n");
16941
17319
  }
16942
17320
  function serialize(data, body) {
16943
- return matter72.stringify(`
17321
+ return matter82.stringify(`
16944
17322
  ${body.trim()}
16945
17323
  `, data);
16946
17324
  }
@@ -17692,7 +18070,7 @@ function detectFormat(raw) {
17692
18070
  if (!trimmed) throw new ImportError("EMPTY_INPUT", "Import content is empty.");
17693
18071
  if (trimmed.startsWith("---")) {
17694
18072
  try {
17695
- const { data } = matter82(trimmed);
18073
+ const { data } = matter92(trimmed);
17696
18074
  if (data && typeof data === "object" && Object.keys(data).length > 0) {
17697
18075
  const d3 = data;
17698
18076
  if (d3.type === "work-item" || d3.id && typeof d3.id === "string" && /^WI-\d+/.test(d3.id)) {
@@ -17714,7 +18092,7 @@ function parseContent(raw) {
17714
18092
  let bodyText = trimmed;
17715
18093
  if (format === "kaddo-frontmatter" || format === "markdown-frontmatter") {
17716
18094
  try {
17717
- const parsed = matter82(trimmed);
18095
+ const parsed = matter92(trimmed);
17718
18096
  frontmatterFields = parsed.data ?? {};
17719
18097
  bodyText = parsed.content.trim();
17720
18098
  } catch {
@@ -17805,7 +18183,7 @@ function findDuplicateByHash(dir, hash) {
17805
18183
  const raw = readFile2(item.filePath);
17806
18184
  if (!raw) continue;
17807
18185
  try {
17808
- const { data } = matter82(raw);
18186
+ const { data } = matter92(raw);
17809
18187
  const source = data?.source;
17810
18188
  if (source && typeof source === "object" && !Array.isArray(source)) {
17811
18189
  const s = source;
@@ -18587,201 +18965,6 @@ function getStatus(dir) {
18587
18965
  lastError: null
18588
18966
  };
18589
18967
  }
18590
- var INITIATIVES_DIR = "knowledge/delivery/initiatives";
18591
- var INITIATIVE_STATES = [
18592
- "candidate",
18593
- "planned",
18594
- "in-progress",
18595
- "completed",
18596
- "deferred",
18597
- "cancelled"
18598
- ];
18599
- var INITIATIVE_TRANSITIONS = {
18600
- candidate: ["planned", "cancelled"],
18601
- planned: ["in-progress", "deferred", "cancelled"],
18602
- "in-progress": ["completed", "deferred", "cancelled"],
18603
- deferred: ["planned", "cancelled"],
18604
- completed: [],
18605
- cancelled: []
18606
- };
18607
- var STATUS_SET = new Set(INITIATIVE_STATES);
18608
- function isValidInitiativeStatus(s) {
18609
- return STATUS_SET.has(s);
18610
- }
18611
- function isValidInitiativeTransition(from, to) {
18612
- return INITIATIVE_TRANSITIONS[from].includes(to);
18613
- }
18614
- function toPosix42(p) {
18615
- return p.replace(/\\/g, "/");
18616
- }
18617
- function optStr32(v) {
18618
- if (typeof v === "string" && v.trim()) return v.trim();
18619
- if (typeof v === "number") return String(v);
18620
- return void 0;
18621
- }
18622
- function strList2(v) {
18623
- if (!Array.isArray(v)) return [];
18624
- return v.map((x2) => typeof x2 === "string" ? x2.trim() : String(x2)).filter(Boolean);
18625
- }
18626
- function parseCandidates(data) {
18627
- const raw = data.candidates;
18628
- if (!Array.isArray(raw)) return [];
18629
- const out = [];
18630
- for (const entry of raw) {
18631
- if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
18632
- const o2 = entry;
18633
- const id = optStr32(o2.id);
18634
- const title = optStr32(o2.title);
18635
- if (!id || !title) continue;
18636
- out.push({
18637
- id,
18638
- title,
18639
- type: optStr32(o2.type),
18640
- suggestedKnowledgeLevel: optStr32(o2.suggested_knowledge_level ?? o2.knowledge_level),
18641
- expectedValue: optStr32(o2.expected_value),
18642
- notes: optStr32(o2.notes),
18643
- sourceSignals: strList2(o2.source_signals),
18644
- dependencies: strList2(o2.dependencies),
18645
- materializedAs: optStr32(o2.materialized_as) ?? null
18646
- });
18647
- }
18648
- return out;
18649
- }
18650
- function parseExternalLinks(data) {
18651
- const raw = data.external_links;
18652
- if (!Array.isArray(raw)) return [];
18653
- const out = [];
18654
- for (const entry of raw) {
18655
- if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
18656
- const o2 = entry;
18657
- const integration = optStr32(o2.integration);
18658
- const externalId = optStr32(o2.external_id);
18659
- if (!integration || !externalId) continue;
18660
- out.push({
18661
- integration,
18662
- externalId,
18663
- externalType: optStr32(o2.external_type),
18664
- url: optStr32(o2.url)
18665
- });
18666
- }
18667
- return out;
18668
- }
18669
- function parseInitiative(dir, filePath, raw) {
18670
- let parsed;
18671
- try {
18672
- parsed = matter102(raw, {});
18673
- } catch {
18674
- return null;
18675
- }
18676
- const data = parsed.data;
18677
- if (optStr32(data.type) !== "initiative") return null;
18678
- const id = optStr32(data.id);
18679
- if (!id) return null;
18680
- const statusRaw = optStr32(data.status) ?? "planned";
18681
- const status = isValidInitiativeStatus(statusRaw) ? statusRaw : "planned";
18682
- return {
18683
- id,
18684
- title: optStr32(data.title) ?? id,
18685
- status,
18686
- horizon: optStr32(data.horizon) ?? null,
18687
- priority: optStr32(data.priority) ?? null,
18688
- knowledgeLevel: optStr32(data.knowledge_level) ?? null,
18689
- domains: strList2(data.domains),
18690
- relatedCapabilities: strList2(data.related_capabilities),
18691
- source: optStr32(data.source) ?? null,
18692
- sourceId: optStr32(data.source_id) ?? null,
18693
- createdAt: optStr32(data.created_at) ?? null,
18694
- externalLinks: parseExternalLinks(data),
18695
- candidates: parseCandidates(data),
18696
- filePath,
18697
- relPath: toPosix42(filePath).startsWith(toPosix42(dir) + "/") ? toPosix42(filePath).slice(toPosix42(dir).length + 1) : toPosix42(filePath),
18698
- body: parsed.content.trim(),
18699
- rawFrontmatter: data
18700
- };
18701
- }
18702
- function discoverInitiatives(dir) {
18703
- const iniDir = join3(dir, INITIATIVES_DIR);
18704
- if (!exists2(iniDir)) return [];
18705
- const out = [];
18706
- for (const entry of readDir2(iniDir)) {
18707
- if (!entry.endsWith(".md")) continue;
18708
- const full = join3(iniDir, entry);
18709
- if (!isFile2(full)) continue;
18710
- const ini = parseInitiative(dir, full, readFile2(full));
18711
- if (ini) out.push(ini);
18712
- }
18713
- return out.sort((a2, b2) => a2.id.localeCompare(b2.id));
18714
- }
18715
- function getInitiative(dir, id) {
18716
- const want = id.trim().toLowerCase();
18717
- return discoverInitiatives(dir).find((i) => i.id.toLowerCase() === want) ?? null;
18718
- }
18719
- function nextInitiativeId(dir) {
18720
- const iniDir = join3(dir, INITIATIVES_DIR);
18721
- let max = 0;
18722
- if (exists2(iniDir)) {
18723
- for (const entry of readDir2(iniDir)) {
18724
- const m2 = entry.match(/INI-(\d+)/);
18725
- if (m2) max = Math.max(max, parseInt(m2[1], 10));
18726
- }
18727
- }
18728
- return `INI-${String(max + 1).padStart(3, "0")}`;
18729
- }
18730
- function workItemBelongsToInitiative(workItemInitiative, iniId) {
18731
- const v = (workItemInitiative ?? "").trim().toLowerCase();
18732
- return v !== "" && v === iniId.trim().toLowerCase();
18733
- }
18734
- function associatedWorkItems(dir, iniId) {
18735
- return discoverWorkItems2(dir).filter((wi) => workItemBelongsToInitiative(wi.initiative, iniId)).map((wi) => ({
18736
- id: wi.id,
18737
- title: wi.title,
18738
- status: wi.lifecycle ?? "ready"
18739
- })).sort((a2, b2) => a2.id.localeCompare(b2.id));
18740
- }
18741
- function computeInitiativeProgress(dir, ini) {
18742
- const totalCandidates = ini.candidates.length;
18743
- const materialized = ini.candidates.filter((c) => c.materializedAs).length;
18744
- const byState = emptyLifecycleCounts2();
18745
- const associated = associatedWorkItems(dir, ini.id);
18746
- for (const wi of associated) byState[wi.status]++;
18747
- return {
18748
- initiative: ini.id,
18749
- status: ini.status,
18750
- planning: {
18751
- totalCandidates,
18752
- materialized,
18753
- remaining: Math.max(0, totalCandidates - materialized)
18754
- },
18755
- delivery: {
18756
- total: associated.length,
18757
- byState
18758
- }
18759
- };
18760
- }
18761
- function getInitiativeContext(dir, ini) {
18762
- return {
18763
- initiative: {
18764
- id: ini.id,
18765
- title: ini.title,
18766
- status: ini.status,
18767
- horizon: ini.horizon,
18768
- priority: ini.priority,
18769
- knowledgeLevel: ini.knowledgeLevel,
18770
- domains: ini.domains,
18771
- relatedCapabilities: ini.relatedCapabilities,
18772
- source: ini.source,
18773
- sourceId: ini.sourceId
18774
- },
18775
- progress: computeInitiativeProgress(dir, ini),
18776
- candidates: {
18777
- total: ini.candidates.length,
18778
- materialized: ini.candidates.filter((c) => c.materializedAs),
18779
- pending: ini.candidates.filter((c) => !c.materializedAs)
18780
- },
18781
- workItems: associatedWorkItems(dir, ini.id),
18782
- externalLinks: ini.externalLinks
18783
- };
18784
- }
18785
18968
  var WORK_ITEMS_DIR2 = "knowledge/delivery/work-items";
18786
18969
  var DRAFT_DIR = `${WORK_ITEMS_DIR2}/draft`;
18787
18970
  var InitiativeWriteError = class extends Error {
@@ -18811,6 +18994,7 @@ function externalLinkToData(l2) {
18811
18994
  const out = { integration: l2.integration, external_id: l2.externalId };
18812
18995
  if (l2.externalType) out.external_type = l2.externalType;
18813
18996
  if (l2.url) out.url = l2.url;
18997
+ if (l2.externalStatus) out.external_status = l2.externalStatus;
18814
18998
  return out;
18815
18999
  }
18816
19000
  function normalizeCandidate(input, fallbackId) {
@@ -18870,7 +19054,7 @@ function defaultBody(title) {
18870
19054
  }
18871
19055
  function buildInitiativeContent(id, input) {
18872
19056
  const status = input.status ?? "planned";
18873
- if (!isValidInitiativeStatus(status)) {
19057
+ if (!isValidInitiativeStatus2(status)) {
18874
19058
  throw new InitiativeWriteError(`Invalid initiative status: ${status}.`);
18875
19059
  }
18876
19060
  const candidates = (input.candidates ?? []).map(
@@ -18892,36 +19076,36 @@ function buildInitiativeContent(id, input) {
18892
19076
  if (input.priority) data.priority = input.priority;
18893
19077
  if (input.source) data.source = input.source;
18894
19078
  if (input.sourceId) data.source_id = input.sourceId;
18895
- return matter11.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
19079
+ return matter112.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
18896
19080
  }
18897
19081
  function createInitiative(dir, input) {
18898
19082
  if (!input.title?.trim()) throw new InitiativeWriteError("Initiative title is required.");
18899
19083
  const id = nextInitiativeId(dir);
18900
19084
  const fileName = `${id}-${slugify3(input.title)}.md`;
18901
- const iniDir = join3(dir, INITIATIVES_DIR);
19085
+ const iniDir = join3(dir, INITIATIVES_DIR2);
18902
19086
  ensureDir2(iniDir);
18903
19087
  const filePath = join3(iniDir, fileName);
18904
19088
  const content = buildInitiativeContent(id, input);
18905
19089
  writeFile2(filePath, content);
18906
- const ini = parseInitiative(dir, filePath, content);
19090
+ const ini = parseInitiative2(dir, filePath, content);
18907
19091
  if (!ini) throw new InitiativeWriteError("Failed to parse the Initiative just written.");
18908
19092
  return ini;
18909
19093
  }
18910
19094
  function transitionInitiative(dir, id, to) {
18911
19095
  const ini = getInitiative(dir, id);
18912
19096
  if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
18913
- if (!isValidInitiativeStatus(to)) throw new InitiativeWriteError(`Invalid status: ${to}.`);
19097
+ if (!isValidInitiativeStatus2(to)) throw new InitiativeWriteError(`Invalid status: ${to}.`);
18914
19098
  if (ini.status === to) return ini;
18915
19099
  if (!isValidInitiativeTransition(ini.status, to)) {
18916
19100
  throw new InitiativeWriteError(`Invalid transition ${ini.status} \u2192 ${to}.`);
18917
19101
  }
18918
19102
  const raw = readFile2(ini.filePath);
18919
- const { data, content } = matter11(raw, {});
19103
+ const { data, content } = matter112(raw, {});
18920
19104
  data.status = to;
18921
19105
  if (to === "completed") data.completed_at = today2();
18922
- const newContent = matter11.stringify(content, data);
19106
+ const newContent = matter112.stringify(content, data);
18923
19107
  writeFile2(ini.filePath, newContent);
18924
- const updated = parseInitiative(dir, ini.filePath, newContent);
19108
+ const updated = parseInitiative2(dir, ini.filePath, newContent);
18925
19109
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after transition.");
18926
19110
  return updated;
18927
19111
  }
@@ -18929,7 +19113,7 @@ function addCandidate(dir, id, candidate) {
18929
19113
  const ini = getInitiative(dir, id);
18930
19114
  if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
18931
19115
  const raw = readFile2(ini.filePath);
18932
- const { data, content } = matter11(raw, {});
19116
+ const { data, content } = matter112(raw, {});
18933
19117
  const existing = Array.isArray(data.candidates) ? data.candidates : [];
18934
19118
  const fallbackId = `WI-CANDIDATE-${String(existing.length + 1).padStart(3, "0")}`;
18935
19119
  const normalized = normalizeCandidate(candidate, fallbackId);
@@ -18937,9 +19121,9 @@ function addCandidate(dir, id, candidate) {
18937
19121
  throw new InitiativeWriteError(`Candidate ${normalized.id} already exists in ${id}.`);
18938
19122
  }
18939
19123
  data.candidates = [...existing, candidateToData(normalized)];
18940
- const newContent = matter11.stringify(content, data);
19124
+ const newContent = matter112.stringify(content, data);
18941
19125
  writeFile2(ini.filePath, newContent);
18942
- const updated = parseInitiative(dir, ini.filePath, newContent);
19126
+ const updated = parseInitiative2(dir, ini.filePath, newContent);
18943
19127
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding a candidate.");
18944
19128
  return updated;
18945
19129
  }
@@ -18947,7 +19131,7 @@ function updateInitiative(dir, id, patch) {
18947
19131
  const ini = getInitiative(dir, id);
18948
19132
  if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
18949
19133
  const raw = readFile2(ini.filePath);
18950
- const { data, content } = matter11(raw, {});
19134
+ const { data, content } = matter112(raw, {});
18951
19135
  if (patch.title !== void 0) {
18952
19136
  if (!patch.title.trim()) throw new InitiativeWriteError("Initiative title cannot be empty.");
18953
19137
  data.title = patch.title.trim();
@@ -18957,12 +19141,33 @@ function updateInitiative(dir, id, patch) {
18957
19141
  if (patch.knowledgeLevel !== void 0) data.knowledge_level = patch.knowledgeLevel;
18958
19142
  if (patch.domains !== void 0) data.domains = patch.domains;
18959
19143
  if (patch.relatedCapabilities !== void 0) data.related_capabilities = patch.relatedCapabilities;
18960
- const newContent = matter11.stringify(content, data);
19144
+ const newContent = matter112.stringify(content, data);
18961
19145
  writeFile2(ini.filePath, newContent);
18962
- const updated = parseInitiative(dir, ini.filePath, newContent);
19146
+ const updated = parseInitiative2(dir, ini.filePath, newContent);
18963
19147
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after update.");
18964
19148
  return updated;
18965
19149
  }
19150
+ function addExternalLink(dir, id, link) {
19151
+ const ini = getInitiative(dir, id);
19152
+ if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
19153
+ if (!link.integration?.trim() || !link.externalId?.trim()) {
19154
+ throw new InitiativeWriteError("External link requires integration and externalId.");
19155
+ }
19156
+ if (ini.externalLinks.some(
19157
+ (l2) => l2.integration.trim().toLowerCase() === link.integration.trim().toLowerCase() && l2.externalId.trim().toLowerCase() === link.externalId.trim().toLowerCase()
19158
+ )) {
19159
+ throw new InitiativeWriteError(`External link ${link.integration}:${link.externalId} already exists on ${id}.`);
19160
+ }
19161
+ const raw = readFile2(ini.filePath);
19162
+ const { data, content } = matter112(raw, {});
19163
+ const existing = Array.isArray(data.external_links) ? data.external_links : [];
19164
+ data.external_links = [...existing, externalLinkToData(link)];
19165
+ const newContent = matter112.stringify(content, data);
19166
+ writeFile2(ini.filePath, newContent);
19167
+ const updated = parseInitiative2(dir, ini.filePath, newContent);
19168
+ if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding an external link.");
19169
+ return updated;
19170
+ }
18966
19171
  function nextWorkItemId2(dir) {
18967
19172
  const wiDir = join3(dir, WORK_ITEMS_DIR2);
18968
19173
  let max = 0;
@@ -19026,7 +19231,7 @@ function buildWorkItemContent(wiId, iniId, candidate) {
19026
19231
  "_What did we learn? Update after completion._",
19027
19232
  ""
19028
19233
  ].join("\n");
19029
- return matter11.stringify("\n" + body, data);
19234
+ return matter112.stringify("\n" + body, data);
19030
19235
  }
19031
19236
  function materializeCandidate(dir, id, candidateId) {
19032
19237
  const ini = getInitiative(dir, id);
@@ -19043,15 +19248,15 @@ function materializeCandidate(dir, id, candidateId) {
19043
19248
  ensureDir2(join3(dir, DRAFT_DIR));
19044
19249
  writeFile2(join3(dir, DRAFT_DIR, fileName), buildWorkItemContent(wiId, ini.id, candidate));
19045
19250
  const raw = readFile2(ini.filePath);
19046
- const { data, content } = matter11(raw, {});
19251
+ const { data, content } = matter112(raw, {});
19047
19252
  const cands = Array.isArray(data.candidates) ? data.candidates : [];
19048
19253
  for (const c of cands) {
19049
19254
  if (c && typeof c === "object" && String(c.id) === candidateId) c.materialized_as = wiId;
19050
19255
  }
19051
19256
  data.candidates = cands;
19052
- const newContent = matter11.stringify(content, data);
19257
+ const newContent = matter112.stringify(content, data);
19053
19258
  writeFile2(ini.filePath, newContent);
19054
- const updated = parseInitiative(dir, ini.filePath, newContent);
19259
+ const updated = parseInitiative2(dir, ini.filePath, newContent);
19055
19260
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after materialization.");
19056
19261
  return { workItemId: wiId, fileName, initiative: updated };
19057
19262
  }
@@ -22665,7 +22870,7 @@ function generateCapsuleDraft(root) {
22665
22870
  var ok5 = (data) => ({ ok: true, data });
22666
22871
  var fail4 = (message) => ({ ok: false, message });
22667
22872
  function listInitiativesTool(root) {
22668
- const all = discoverInitiatives(root).map((ini) => {
22873
+ const all = discoverInitiatives2(root).map((ini) => {
22669
22874
  const p = computeInitiativeProgress(root, ini);
22670
22875
  return {
22671
22876
  id: ini.id,
@@ -22777,6 +22982,36 @@ function materializeInitiativeCandidateTool(root, args) {
22777
22982
  () => materializeCandidate(root, args.id, args.candidateId)
22778
22983
  );
22779
22984
  }
22985
+ function addInitiativeExternalLinkTool(root, args) {
22986
+ if (!args.integration?.trim() || !args.externalId?.trim()) {
22987
+ return fail4("integration and externalId are required.");
22988
+ }
22989
+ if (!getInitiative(root, args.id)) return fail4(`Initiative ${args.id} not found.`);
22990
+ return previewOrApply(
22991
+ args.confirm,
22992
+ "add-initiative-external-link",
22993
+ `A provider-neutral reference ${args.integration}:${args.externalId} will be added to ${args.id}. An external status is stored as a signal only and never changes the Initiative lifecycle.`,
22994
+ () => addExternalLink(root, args.id, {
22995
+ integration: args.integration,
22996
+ externalId: args.externalId,
22997
+ externalType: args.externalType,
22998
+ url: args.url,
22999
+ externalStatus: args.externalStatus
23000
+ })
23001
+ );
23002
+ }
23003
+ function suggestInitiativeForExternalItemTool(root, args) {
23004
+ if (!args.integration?.trim() || !args.externalId?.trim()) {
23005
+ return fail4("integration and externalId are required.");
23006
+ }
23007
+ const matches = suggestInitiativesForExternalItem(root, args.integration, args.externalId);
23008
+ return ok5({
23009
+ integration: args.integration,
23010
+ externalId: args.externalId,
23011
+ suggestions: matches.map((i) => ({ id: i.id, title: i.title, status: i.status })),
23012
+ note: "Suggestion only \u2014 associating a Work Item with an Initiative requires human confirmation."
23013
+ });
23014
+ }
22780
23015
  function analyzeInitiativeTool(root, id) {
22781
23016
  const ini = getInitiative(root, id);
22782
23017
  if (!ini) return fail4(`Initiative ${id} not found.`);
@@ -23284,6 +23519,32 @@ function createServer(root) {
23284
23519
  },
23285
23520
  async (args) => toolText(guarded(root, () => materializeInitiativeCandidateTool(root, args)))
23286
23521
  );
23522
+ server.registerTool(
23523
+ "kaddo_add_initiative_external_link",
23524
+ {
23525
+ title: "Add initiative external link",
23526
+ description: `Add a provider-neutral external planning reference (Jira/ADO/GitHub/\u2026) to an initiative. An external status is stored as a signal only and never changes the Initiative lifecycle. ${INITIATIVE_WRITE_NOTE}`,
23527
+ inputSchema: {
23528
+ id: z4.string(),
23529
+ integration: z4.string(),
23530
+ externalId: z4.string(),
23531
+ externalType: z4.string().optional(),
23532
+ url: z4.string().optional(),
23533
+ externalStatus: z4.string().optional(),
23534
+ confirm: z4.boolean().optional()
23535
+ }
23536
+ },
23537
+ async (args) => toolText(guarded(root, () => addInitiativeExternalLinkTool(root, args)))
23538
+ );
23539
+ server.registerTool(
23540
+ "kaddo_suggest_initiative_for_external_item",
23541
+ {
23542
+ title: "Suggest initiative for external item",
23543
+ description: `Given an external planning item (e.g. an imported Work Item's parent epic), suggest Initiatives whose external links reference it. Suggestion only \u2014 association requires human confirmation. ${INITIATIVE_READ_NOTE}`,
23544
+ inputSchema: { integration: z4.string(), externalId: z4.string() }
23545
+ },
23546
+ async (args) => toolText(guarded(root, () => suggestInitiativeForExternalItemTool(root, args)))
23547
+ );
23287
23548
  server.registerTool(
23288
23549
  "kaddo_analyze_initiative",
23289
23550
  {