@kaddo/mcp 3.105.0 → 3.107.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 +829 -17
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10828,6 +10828,9 @@ function writeFile2(filePath, content) {
10828
10828
  fs3.mkdirSync(path3.dirname(filePath), { recursive: true });
10829
10829
  fs3.writeFileSync(filePath, content, "utf-8");
10830
10830
  }
10831
+ function ensureDir2(dirPath) {
10832
+ fs3.mkdirSync(dirPath, { recursive: true });
10833
+ }
10831
10834
  function readDir2(dirPath) {
10832
10835
  if (!exists2(dirPath)) return [];
10833
10836
  return fs3.readdirSync(dirPath);
@@ -11071,6 +11074,9 @@ function lifecycleStateOf2(item) {
11071
11074
  if (folder) return folder;
11072
11075
  return "ready";
11073
11076
  }
11077
+ function emptyLifecycleCounts2() {
11078
+ return Object.fromEntries(LIFECYCLE_STATES2.map((s) => [s, 0]));
11079
+ }
11074
11080
  var ARCH_DIR3 = "knowledge";
11075
11081
  function toPosix6(p) {
11076
11082
  return p.replace(/\\/g, "/");
@@ -16967,7 +16973,7 @@ function createWorkItem(dir, opts) {
16967
16973
  if (!type) throw new WorkItemWriteError("INVALID_INPUT", `Unknown Work Item type "${opts.type}".`);
16968
16974
  const id = nextWorkItemId(dir);
16969
16975
  const title = intent.split(/\r?\n/)[0].trim().slice(0, 120);
16970
- const today2 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
16976
+ const today22 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
16971
16977
  const source = opts.source ? { ...opts.source, inferred: false } : { type: "manual", inferred: false };
16972
16978
  const data = {
16973
16979
  type,
@@ -16975,7 +16981,7 @@ function createWorkItem(dir, opts) {
16975
16981
  title,
16976
16982
  status: "draft",
16977
16983
  work_type: type,
16978
- created_at: today2,
16984
+ created_at: today22,
16979
16985
  source,
16980
16986
  generated_by: "kaddo-admin",
16981
16987
  affected_modules: [],
@@ -18581,6 +18587,7 @@ function getStatus(dir) {
18581
18587
  lastError: null
18582
18588
  };
18583
18589
  }
18590
+ var INITIATIVES_DIR = "knowledge/delivery/initiatives";
18584
18591
  var INITIATIVE_STATES = [
18585
18592
  "candidate",
18586
18593
  "planned",
@@ -18589,9 +18596,569 @@ var INITIATIVE_STATES = [
18589
18596
  "deferred",
18590
18597
  "cancelled"
18591
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
+ };
18592
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
+ }
18593
18785
  var WORK_ITEMS_DIR2 = "knowledge/delivery/work-items";
18594
18786
  var DRAFT_DIR = `${WORK_ITEMS_DIR2}/draft`;
18787
+ var InitiativeWriteError = class extends Error {
18788
+ constructor(message) {
18789
+ super(message);
18790
+ this.name = "InitiativeWriteError";
18791
+ }
18792
+ };
18793
+ function slugify3(s) {
18794
+ return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 50);
18795
+ }
18796
+ function today2() {
18797
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
18798
+ }
18799
+ function candidateToData(c) {
18800
+ const out = { id: c.id, title: c.title };
18801
+ if (c.type) out.type = c.type;
18802
+ if (c.suggestedKnowledgeLevel) out.suggested_knowledge_level = c.suggestedKnowledgeLevel;
18803
+ if (c.expectedValue) out.expected_value = c.expectedValue;
18804
+ if (c.notes) out.notes = c.notes;
18805
+ if (c.sourceSignals.length) out.source_signals = c.sourceSignals;
18806
+ if (c.dependencies.length) out.dependencies = c.dependencies;
18807
+ if (c.materializedAs) out.materialized_as = c.materializedAs;
18808
+ return out;
18809
+ }
18810
+ function externalLinkToData(l2) {
18811
+ const out = { integration: l2.integration, external_id: l2.externalId };
18812
+ if (l2.externalType) out.external_type = l2.externalType;
18813
+ if (l2.url) out.url = l2.url;
18814
+ return out;
18815
+ }
18816
+ function normalizeCandidate(input, fallbackId) {
18817
+ return {
18818
+ id: input.id?.trim() || fallbackId,
18819
+ title: input.title.trim(),
18820
+ type: input.type?.trim() || void 0,
18821
+ suggestedKnowledgeLevel: input.suggestedKnowledgeLevel?.trim() || void 0,
18822
+ expectedValue: input.expectedValue?.trim() || void 0,
18823
+ notes: input.notes?.trim() || void 0,
18824
+ sourceSignals: input.sourceSignals ?? [],
18825
+ dependencies: input.dependencies ?? [],
18826
+ materializedAs: input.materializedAs ?? null
18827
+ };
18828
+ }
18829
+ function defaultBody(title) {
18830
+ return [
18831
+ `# ${title}`,
18832
+ "",
18833
+ "## Goal",
18834
+ "",
18835
+ "_What outcome does this Initiative pursue?_",
18836
+ "",
18837
+ "## Expected Value",
18838
+ "",
18839
+ "_Why it matters._",
18840
+ "",
18841
+ "## Scope",
18842
+ "",
18843
+ "_What this Initiative covers._",
18844
+ "",
18845
+ "## Out of Scope",
18846
+ "",
18847
+ "_What it explicitly does not cover._",
18848
+ "",
18849
+ "## Success Criteria",
18850
+ "",
18851
+ '_Observable outcomes that define completion (not just "all Work Items done")._',
18852
+ "",
18853
+ "## Dependencies",
18854
+ "",
18855
+ "_Other Initiatives, modules, or external work this depends on._",
18856
+ "",
18857
+ "## Work Item Candidates",
18858
+ "",
18859
+ "_Candidates live in frontmatter `candidates`; summarize them here as they evolve._",
18860
+ "",
18861
+ "## Open Questions",
18862
+ "",
18863
+ "_Unresolved questions for this Initiative._",
18864
+ "",
18865
+ "## Learning",
18866
+ "",
18867
+ "_Captured on completion: what was delivered, what stayed out of scope, outcome reached._",
18868
+ ""
18869
+ ].join("\n");
18870
+ }
18871
+ function buildInitiativeContent(id, input) {
18872
+ const status = input.status ?? "planned";
18873
+ if (!isValidInitiativeStatus(status)) {
18874
+ throw new InitiativeWriteError(`Invalid initiative status: ${status}.`);
18875
+ }
18876
+ const candidates = (input.candidates ?? []).map(
18877
+ (c, i) => normalizeCandidate(c, `WI-CANDIDATE-${String(i + 1).padStart(3, "0")}`)
18878
+ );
18879
+ const data = {
18880
+ type: "initiative",
18881
+ id,
18882
+ title: input.title.trim(),
18883
+ status,
18884
+ knowledge_level: input.knowledgeLevel ?? "K2",
18885
+ domains: input.domains ?? [],
18886
+ related_capabilities: input.relatedCapabilities ?? [],
18887
+ created_at: today2(),
18888
+ external_links: (input.externalLinks ?? []).map(externalLinkToData),
18889
+ candidates: candidates.map(candidateToData)
18890
+ };
18891
+ if (input.horizon) data.horizon = input.horizon;
18892
+ if (input.priority) data.priority = input.priority;
18893
+ if (input.source) data.source = input.source;
18894
+ if (input.sourceId) data.source_id = input.sourceId;
18895
+ return matter11.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
18896
+ }
18897
+ function createInitiative(dir, input) {
18898
+ if (!input.title?.trim()) throw new InitiativeWriteError("Initiative title is required.");
18899
+ const id = nextInitiativeId(dir);
18900
+ const fileName = `${id}-${slugify3(input.title)}.md`;
18901
+ const iniDir = join3(dir, INITIATIVES_DIR);
18902
+ ensureDir2(iniDir);
18903
+ const filePath = join3(iniDir, fileName);
18904
+ const content = buildInitiativeContent(id, input);
18905
+ writeFile2(filePath, content);
18906
+ const ini = parseInitiative(dir, filePath, content);
18907
+ if (!ini) throw new InitiativeWriteError("Failed to parse the Initiative just written.");
18908
+ return ini;
18909
+ }
18910
+ function transitionInitiative(dir, id, to) {
18911
+ const ini = getInitiative(dir, id);
18912
+ if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
18913
+ if (!isValidInitiativeStatus(to)) throw new InitiativeWriteError(`Invalid status: ${to}.`);
18914
+ if (ini.status === to) return ini;
18915
+ if (!isValidInitiativeTransition(ini.status, to)) {
18916
+ throw new InitiativeWriteError(`Invalid transition ${ini.status} \u2192 ${to}.`);
18917
+ }
18918
+ const raw = readFile2(ini.filePath);
18919
+ const { data, content } = matter11(raw, {});
18920
+ data.status = to;
18921
+ if (to === "completed") data.completed_at = today2();
18922
+ const newContent = matter11.stringify(content, data);
18923
+ writeFile2(ini.filePath, newContent);
18924
+ const updated = parseInitiative(dir, ini.filePath, newContent);
18925
+ if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after transition.");
18926
+ return updated;
18927
+ }
18928
+ function addCandidate(dir, id, candidate) {
18929
+ const ini = getInitiative(dir, id);
18930
+ if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
18931
+ const raw = readFile2(ini.filePath);
18932
+ const { data, content } = matter11(raw, {});
18933
+ const existing = Array.isArray(data.candidates) ? data.candidates : [];
18934
+ const fallbackId = `WI-CANDIDATE-${String(existing.length + 1).padStart(3, "0")}`;
18935
+ const normalized = normalizeCandidate(candidate, fallbackId);
18936
+ if (ini.candidates.some((c) => c.id === normalized.id)) {
18937
+ throw new InitiativeWriteError(`Candidate ${normalized.id} already exists in ${id}.`);
18938
+ }
18939
+ data.candidates = [...existing, candidateToData(normalized)];
18940
+ const newContent = matter11.stringify(content, data);
18941
+ writeFile2(ini.filePath, newContent);
18942
+ const updated = parseInitiative(dir, ini.filePath, newContent);
18943
+ if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding a candidate.");
18944
+ return updated;
18945
+ }
18946
+ function updateInitiative(dir, id, patch) {
18947
+ const ini = getInitiative(dir, id);
18948
+ if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
18949
+ const raw = readFile2(ini.filePath);
18950
+ const { data, content } = matter11(raw, {});
18951
+ if (patch.title !== void 0) {
18952
+ if (!patch.title.trim()) throw new InitiativeWriteError("Initiative title cannot be empty.");
18953
+ data.title = patch.title.trim();
18954
+ }
18955
+ if (patch.horizon !== void 0) data.horizon = patch.horizon;
18956
+ if (patch.priority !== void 0) data.priority = patch.priority;
18957
+ if (patch.knowledgeLevel !== void 0) data.knowledge_level = patch.knowledgeLevel;
18958
+ if (patch.domains !== void 0) data.domains = patch.domains;
18959
+ if (patch.relatedCapabilities !== void 0) data.related_capabilities = patch.relatedCapabilities;
18960
+ const newContent = matter11.stringify(content, data);
18961
+ writeFile2(ini.filePath, newContent);
18962
+ const updated = parseInitiative(dir, ini.filePath, newContent);
18963
+ if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after update.");
18964
+ return updated;
18965
+ }
18966
+ function nextWorkItemId2(dir) {
18967
+ const wiDir = join3(dir, WORK_ITEMS_DIR2);
18968
+ let max = 0;
18969
+ const walk = (d3) => {
18970
+ if (!exists2(d3)) return;
18971
+ for (const entry of readDir2(d3)) {
18972
+ const full = join3(d3, entry);
18973
+ if (isFile2(full)) {
18974
+ const m2 = entry.match(/WI-(\d+)/);
18975
+ if (m2) max = Math.max(max, parseInt(m2[1], 10));
18976
+ } else if (!entry.startsWith(".")) {
18977
+ walk(full);
18978
+ }
18979
+ }
18980
+ };
18981
+ walk(wiDir);
18982
+ return `WI-${String(max + 1).padStart(3, "0")}`;
18983
+ }
18984
+ function buildWorkItemContent(wiId, iniId, candidate) {
18985
+ const data = {
18986
+ type: candidate.type ?? "feature",
18987
+ id: wiId,
18988
+ title: candidate.title,
18989
+ knowledge_level: candidate.suggestedKnowledgeLevel ?? "K2",
18990
+ status: "draft",
18991
+ phase: "now",
18992
+ initiative: iniId,
18993
+ domains: [],
18994
+ code: [],
18995
+ created_at: today2(),
18996
+ source: "initiative",
18997
+ source_id: candidate.id,
18998
+ source_initiative: iniId,
18999
+ affected_modules: []
19000
+ };
19001
+ if (candidate.sourceSignals.length) data.source_signals = candidate.sourceSignals;
19002
+ if (candidate.dependencies.length) data.dependencies = candidate.dependencies;
19003
+ const body = [
19004
+ `# ${candidate.title}`,
19005
+ "",
19006
+ `> Materialized from Initiative ${iniId}, candidate ${candidate.id}.`,
19007
+ "",
19008
+ "## Problem",
19009
+ "",
19010
+ candidate.expectedValue ? `_Expected value:_ ${candidate.expectedValue}` : "_Describe the problem._",
19011
+ "",
19012
+ "## Acceptance Criteria",
19013
+ "",
19014
+ "- [ ] _Define at least one testable criterion._",
19015
+ "",
19016
+ "## Out of scope",
19017
+ "",
19018
+ "_Not included in this Work Item._",
19019
+ "",
19020
+ "## Validation",
19021
+ "",
19022
+ "_How will this be validated?_",
19023
+ "",
19024
+ "## Learning",
19025
+ "",
19026
+ "_What did we learn? Update after completion._",
19027
+ ""
19028
+ ].join("\n");
19029
+ return matter11.stringify("\n" + body, data);
19030
+ }
19031
+ function materializeCandidate(dir, id, candidateId) {
19032
+ const ini = getInitiative(dir, id);
19033
+ if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
19034
+ const candidate = ini.candidates.find((c) => c.id === candidateId);
19035
+ if (!candidate) throw new InitiativeWriteError(`Candidate ${candidateId} not found in ${id}.`);
19036
+ if (candidate.materializedAs) {
19037
+ throw new InitiativeWriteError(
19038
+ `Candidate ${candidateId} is already materialized as ${candidate.materializedAs}.`
19039
+ );
19040
+ }
19041
+ const wiId = nextWorkItemId2(dir);
19042
+ const fileName = `${wiId}-${slugify3(candidate.title)}.md`;
19043
+ ensureDir2(join3(dir, DRAFT_DIR));
19044
+ writeFile2(join3(dir, DRAFT_DIR, fileName), buildWorkItemContent(wiId, ini.id, candidate));
19045
+ const raw = readFile2(ini.filePath);
19046
+ const { data, content } = matter11(raw, {});
19047
+ const cands = Array.isArray(data.candidates) ? data.candidates : [];
19048
+ for (const c of cands) {
19049
+ if (c && typeof c === "object" && String(c.id) === candidateId) c.materialized_as = wiId;
19050
+ }
19051
+ data.candidates = cands;
19052
+ const newContent = matter11.stringify(content, data);
19053
+ writeFile2(ini.filePath, newContent);
19054
+ const updated = parseInitiative(dir, ini.filePath, newContent);
19055
+ if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after materialization.");
19056
+ return { workItemId: wiId, fileName, initiative: updated };
19057
+ }
19058
+ var SUCCESS_HEADING_RE = /^#{1,6}\s*(success criteria|criterios de[ét]xito|criterios de éxito)\s*$/i;
19059
+ var HEADING_RE2 = /^#{1,6}\s+/;
19060
+ var CHECK_ITEM_RE = /^\s*[-*]\s+\[([ xX])\]\s+(.*)$/;
19061
+ var BULLET_RE2 = /^\s*[-*]\s+(.*)$/;
19062
+ var PLACEHOLDER_RE = /^_.*_$/;
19063
+ function parseSuccessCriteria(body) {
19064
+ const lines = body.split(/\r?\n/);
19065
+ const out = [];
19066
+ let capture = false;
19067
+ for (const line of lines) {
19068
+ if (capture && HEADING_RE2.test(line)) break;
19069
+ if (!capture) {
19070
+ if (SUCCESS_HEADING_RE.test(line.trim())) capture = true;
19071
+ continue;
19072
+ }
19073
+ const check = line.match(CHECK_ITEM_RE);
19074
+ if (check) {
19075
+ const text22 = check[2].trim();
19076
+ if (text22 && !PLACEHOLDER_RE.test(text22)) {
19077
+ out.push({ text: text22, checked: check[1].toLowerCase() === "x" });
19078
+ }
19079
+ continue;
19080
+ }
19081
+ const bullet = line.match(BULLET_RE2);
19082
+ if (bullet) {
19083
+ const text22 = bullet[1].trim();
19084
+ if (text22 && !PLACEHOLDER_RE.test(text22)) out.push({ text: text22, checked: null });
19085
+ }
19086
+ }
19087
+ return out;
19088
+ }
19089
+ function analyzeInitiative(dir, ini) {
19090
+ const progress = computeInitiativeProgress(dir, ini);
19091
+ const successCriteria = parseSuccessCriteria(ini.body);
19092
+ const pendingCandidates = ini.candidates.filter((c) => !c.materializedAs);
19093
+ const associated = associatedWorkItems(dir, ini.id);
19094
+ const findings = [];
19095
+ if (successCriteria.length === 0) {
19096
+ findings.push({
19097
+ code: "no-success-criteria",
19098
+ severity: "warning",
19099
+ message: "The Initiative has no explicit Success Criteria; completion cannot be judged by outcome, only by delivery."
19100
+ });
19101
+ } else {
19102
+ const uncovered = successCriteria.filter((c) => c.checked === false);
19103
+ if (uncovered.length > 0) {
19104
+ findings.push({
19105
+ code: "uncovered-success-criteria",
19106
+ severity: "blocking",
19107
+ message: `${uncovered.length} success criterion/criteria remain uncovered.`,
19108
+ items: uncovered.map((c) => c.text)
19109
+ });
19110
+ }
19111
+ }
19112
+ if (pendingCandidates.length > 0) {
19113
+ findings.push({
19114
+ code: "pending-candidates",
19115
+ severity: "blocking",
19116
+ message: `${pendingCandidates.length} committed candidate(s) not yet materialized into Work Items.`,
19117
+ items: pendingCandidates.map((c) => `${c.id}: ${c.title}`)
19118
+ });
19119
+ }
19120
+ const notCompleted = associated.filter((w3) => w3.status !== "completed" && w3.status !== "archived");
19121
+ if (associated.length === 0) {
19122
+ findings.push({
19123
+ code: "no-delivery",
19124
+ severity: "info",
19125
+ message: "No Work Items are associated with this Initiative yet."
19126
+ });
19127
+ } else if (notCompleted.length > 0) {
19128
+ findings.push({
19129
+ code: "incomplete-delivery",
19130
+ severity: "blocking",
19131
+ message: `${notCompleted.length} associated Work Item(s) are not completed.`,
19132
+ items: notCompleted.map((w3) => `${w3.id} (${w3.status})`)
19133
+ });
19134
+ }
19135
+ return {
19136
+ initiative: ini.id,
19137
+ status: ini.status,
19138
+ progress,
19139
+ successCriteria,
19140
+ pendingCandidates,
19141
+ findings,
19142
+ suggestedCandidates: pendingCandidates
19143
+ };
19144
+ }
19145
+ function evaluateInitiativeCompletion(dir, ini) {
19146
+ const analysis = analyzeInitiative(dir, ini);
19147
+ const reasons = [];
19148
+ for (const f3 of analysis.findings) {
19149
+ if (f3.severity === "blocking") reasons.push(f3.message);
19150
+ }
19151
+ if (analysis.progress.delivery.total === 0 && analysis.pendingCandidates.length === 0) {
19152
+ reasons.push("Nothing has been delivered or committed under this Initiative.");
19153
+ }
19154
+ const ready = reasons.length === 0;
19155
+ return {
19156
+ initiative: ini.id,
19157
+ ready,
19158
+ readiness: ready ? "ready" : "not-ready",
19159
+ reasons
19160
+ };
19161
+ }
18595
19162
 
18596
19163
  // src/tools.ts
18597
19164
  var ok = (data) => ({ ok: true, data });
@@ -18690,9 +19257,9 @@ function markWorkItemReady(root, id, confirm2) {
18690
19257
  if (!raw) return fail(`Could not read Work Item file: ${item.path}`);
18691
19258
  const parsed = matter12(raw);
18692
19259
  const data = { ...parsed.data };
18693
- const today2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
19260
+ const today3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
18694
19261
  data.status = "ready";
18695
- data.ready_at = today2;
19262
+ data.ready_at = today3;
18696
19263
  const newContent = matter12.stringify(parsed.content, data);
18697
19264
  const posixPath = item.path.replace(/\\/g, "/");
18698
19265
  let newRelPath = item.path;
@@ -18709,7 +19276,7 @@ function markWorkItemReady(root, id, confirm2) {
18709
19276
  id: item.id,
18710
19277
  from: "draft",
18711
19278
  to: "ready",
18712
- ready_at: today2,
19279
+ ready_at: today3,
18713
19280
  moved: newRelPath !== item.path ? { from: item.path, to: newRelPath } : void 0,
18714
19281
  ...warnings.length > 0 ? { warnings } : {},
18715
19282
  next: ["Run kaddo context", "Run kaddo understand"]
@@ -18932,7 +19499,7 @@ function firstParagraph3(md) {
18932
19499
  }
18933
19500
  return "";
18934
19501
  }
18935
- function slugify3(s) {
19502
+ function slugify4(s) {
18936
19503
  return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
18937
19504
  }
18938
19505
  function modulesListTool(root, args) {
@@ -19119,7 +19686,7 @@ function extractSection(content, heading) {
19119
19686
  return out;
19120
19687
  }
19121
19688
  function buildBranchStrategy(wi, affectedModules, codeEntries, descriptor) {
19122
- const slug5 = slugify3(wi.title);
19689
+ const slug5 = slugify4(wi.title);
19123
19690
  const wiLower = wi.id.toLowerCase();
19124
19691
  const coreBranch = `feature/${wiLower}-${slug5}`;
19125
19692
  const moduleBranches = {};
@@ -19182,7 +19749,7 @@ function exportCapsuleTool(root, args) {
19182
19749
  if (!mc) warnings.push("module-context.md not found for this module.");
19183
19750
  if (mc && isPlaceholder2(mc)) warnings.push("module-context.md contains placeholder sections.");
19184
19751
  capsuleType = "module";
19185
- capsuleName = `${slugify3(config.project?.name ?? "project")}-${args.module}`;
19752
+ capsuleName = `${slugify4(config.project?.name ?? "project")}-${args.module}`;
19186
19753
  capsuleData = {
19187
19754
  type: "knowledge-capsule",
19188
19755
  system: `${config.project?.name ?? "unknown"}/${args.module}`,
@@ -19204,7 +19771,7 @@ function exportCapsuleTool(root, args) {
19204
19771
  if (mc && isPlaceholder2(mc)) warnings.push("module-context.md contains placeholder sections.");
19205
19772
  const modId = config.module?.id ?? args.module;
19206
19773
  capsuleType = "module";
19207
- capsuleName = slugify3(modId);
19774
+ capsuleName = slugify4(modId);
19208
19775
  capsuleData = {
19209
19776
  type: "knowledge-capsule",
19210
19777
  system: `${config.multirepo?.parent_system ?? "unknown"}/${modId}`,
@@ -19232,7 +19799,7 @@ function exportCapsuleTool(root, args) {
19232
19799
  responsibilities.push(mc ? `[${m2.id}] ${firstParagraph3(mc) || m2.name || m2.id}` : `[${m2.id}] ${m2.name || m2.id} (no module-context)`);
19233
19800
  }
19234
19801
  capsuleType = "system";
19235
- capsuleName = `${slugify3(config.project?.name ?? "project")}-system`;
19802
+ capsuleName = `${slugify4(config.project?.name ?? "project")}-system`;
19236
19803
  capsuleData = {
19237
19804
  type: "knowledge-capsule",
19238
19805
  system: config.project?.name ?? "unknown",
@@ -19253,7 +19820,7 @@ function exportCapsuleTool(root, args) {
19253
19820
  if (mc && isPlaceholder2(mc)) warnings.push("module-context.md contains placeholder sections.");
19254
19821
  const modId = config.module?.id ?? "module";
19255
19822
  capsuleType = "module";
19256
- capsuleName = slugify3(modId);
19823
+ capsuleName = slugify4(modId);
19257
19824
  capsuleData = {
19258
19825
  type: "knowledge-capsule",
19259
19826
  system: `${config.multirepo?.parent_system ?? "unknown"}/${modId}`,
@@ -19269,7 +19836,7 @@ function exportCapsuleTool(root, args) {
19269
19836
  };
19270
19837
  } else {
19271
19838
  capsuleType = "project";
19272
- capsuleName = slugify3(config.project?.name ?? "project");
19839
+ capsuleName = slugify4(config.project?.name ?? "project");
19273
19840
  capsuleData = {
19274
19841
  type: "knowledge-capsule",
19275
19842
  system: config.project?.name ?? "unknown",
@@ -19432,7 +19999,7 @@ var PROVENANCE2 = /* @__PURE__ */ new Set(["declared", "derived", "agent-reviewe
19432
19999
  function isRelative2(p) {
19433
20000
  return typeof p === "string" && p.trim() !== "" && !/^([a-zA-Z]:[\\/]|\/)/.test(p) && !p.includes("..");
19434
20001
  }
19435
- function strList2(v) {
20002
+ function strList3(v) {
19436
20003
  return Array.isArray(v) ? v.map((x2) => typeof x2 === "string" ? x2.trim() : "").filter(Boolean) : [];
19437
20004
  }
19438
20005
  function loadSystemTopology2(dir) {
@@ -19473,7 +20040,7 @@ function parseTopologyContent2(raw, dir) {
19473
20040
  const moduleRaw = typeof e.module === "string" ? e.module.trim() : void 0;
19474
20041
  if (moduleRaw && !validModules.has(moduleRaw)) findings.push({ level: "warning", message: `Entity "${id}" references unregistered module "${moduleRaw}".` });
19475
20042
  const rawImpl = e.implementation ?? e.implementation_refs;
19476
- const implementation = strList2(rawImpl).filter((p) => {
20043
+ const implementation = strList3(rawImpl).filter((p) => {
19477
20044
  if (isRelative2(p)) return true;
19478
20045
  findings.push({ level: "warning", message: `Entity "${id}" implementation ref "${p}" is not a safe relative path and was dropped.` });
19479
20046
  return false;
@@ -19481,7 +20048,7 @@ function parseTopologyContent2(raw, dir) {
19481
20048
  const prov = e.provenance;
19482
20049
  const provOrigin = typeof prov === "string" ? prov : prov && typeof prov === "object" && typeof prov.origin === "string" ? String(prov.origin) : void 0;
19483
20050
  const provenance = provOrigin && PROVENANCE2.has(provOrigin) ? provOrigin : void 0;
19484
- const evidence = strList2(e.evidence ?? (prov && typeof prov === "object" ? prov.evidence_refs : void 0)).filter(isRelative2);
20051
+ const evidence = strList3(e.evidence ?? (prov && typeof prov === "object" ? prov.evidence_refs : void 0)).filter(isRelative2);
19485
20052
  seenIds.add(id);
19486
20053
  entities.push({
19487
20054
  id,
@@ -19490,7 +20057,7 @@ function parseTopologyContent2(raw, dir) {
19490
20057
  ...typeof e.purpose === "string" && e.purpose.trim() ? { purpose: e.purpose.trim() } : {},
19491
20058
  ...moduleRaw && validModules.has(moduleRaw) ? { moduleId: moduleRaw } : {},
19492
20059
  implementationRefs: implementation,
19493
- knowledgeRefs: strList2(e.knowledge ?? e.knowledge_refs),
20060
+ knowledgeRefs: strList3(e.knowledge ?? e.knowledge_refs),
19494
20061
  ...provenance ? { provenance } : {},
19495
20062
  evidence
19496
20063
  });
@@ -19524,7 +20091,7 @@ function parseTopologyContent2(raw, dir) {
19524
20091
  }
19525
20092
  seenRels.add(key);
19526
20093
  const prov = r2.provenance;
19527
- relationships.push({ from, to, type, evidence: strList2(r2.evidence ?? prov?.evidence_refs).filter(isRelative2) });
20094
+ relationships.push({ from, to, type, evidence: strList3(r2.evidence ?? prov?.evidence_refs).filter(isRelative2) });
19528
20095
  }
19529
20096
  return { entities, relationships, findings, ...sourceRevision ? { sourceRevision } : {} };
19530
20097
  }
@@ -22094,6 +22661,147 @@ function generateCapsuleDraft(root) {
22094
22661
  };
22095
22662
  }
22096
22663
 
22664
+ // src/initiatives.ts
22665
+ var ok5 = (data) => ({ ok: true, data });
22666
+ var fail4 = (message) => ({ ok: false, message });
22667
+ function listInitiativesTool(root) {
22668
+ const all = discoverInitiatives(root).map((ini) => {
22669
+ const p = computeInitiativeProgress(root, ini);
22670
+ return {
22671
+ id: ini.id,
22672
+ title: ini.title,
22673
+ status: ini.status,
22674
+ planning: p.planning,
22675
+ delivery: p.delivery
22676
+ };
22677
+ });
22678
+ return ok5(all);
22679
+ }
22680
+ function getInitiativeTool(root, id) {
22681
+ const ini = getInitiative(root, id);
22682
+ if (!ini) return fail4(`Initiative ${id} not found.`);
22683
+ return ok5({
22684
+ id: ini.id,
22685
+ title: ini.title,
22686
+ status: ini.status,
22687
+ horizon: ini.horizon,
22688
+ priority: ini.priority,
22689
+ knowledgeLevel: ini.knowledgeLevel,
22690
+ domains: ini.domains,
22691
+ relatedCapabilities: ini.relatedCapabilities,
22692
+ source: ini.source,
22693
+ sourceId: ini.sourceId,
22694
+ externalLinks: ini.externalLinks,
22695
+ candidates: ini.candidates,
22696
+ body: ini.body
22697
+ });
22698
+ }
22699
+ function getInitiativeContextTool(root, id) {
22700
+ const ini = getInitiative(root, id);
22701
+ if (!ini) return fail4(`Initiative ${id} not found.`);
22702
+ return ok5(getInitiativeContext(root, ini));
22703
+ }
22704
+ function getInitiativeProgressTool(root, id) {
22705
+ const ini = getInitiative(root, id);
22706
+ if (!ini) return fail4(`Initiative ${id} not found.`);
22707
+ return ok5(computeInitiativeProgress(root, ini));
22708
+ }
22709
+ function previewOrApply(confirm2, action, effect, apply) {
22710
+ if (!confirm2) {
22711
+ return ok5({ action, effect, instruction: "Call again with confirm=true to apply." });
22712
+ }
22713
+ try {
22714
+ return ok5({ applied: true, result: apply() });
22715
+ } catch (err) {
22716
+ if (err instanceof InitiativeWriteError) return fail4(err.message);
22717
+ throw err;
22718
+ }
22719
+ }
22720
+ function createInitiativeTool(root, args) {
22721
+ if (!args.title?.trim()) return fail4("title is required.");
22722
+ return previewOrApply(
22723
+ args.confirm,
22724
+ "create-initiative",
22725
+ `A new Initiative "${args.title.trim()}" will be created under knowledge/delivery/initiatives/.`,
22726
+ () => createInitiative(root, {
22727
+ title: args.title,
22728
+ domains: args.domains,
22729
+ relatedCapabilities: args.relatedCapabilities,
22730
+ horizon: args.horizon,
22731
+ priority: args.priority
22732
+ })
22733
+ );
22734
+ }
22735
+ function updateInitiativeTool(root, args) {
22736
+ if (!getInitiative(root, args.id)) return fail4(`Initiative ${args.id} not found.`);
22737
+ const changingStatus = typeof args.status === "string";
22738
+ const effect = changingStatus ? `Initiative ${args.id} status will transition to "${args.status}" (valid transitions only).` : `Initiative ${args.id} fields will be updated.`;
22739
+ return previewOrApply(args.confirm, "update-initiative", effect, () => {
22740
+ if (changingStatus) {
22741
+ return transitionInitiative(root, args.id, args.status);
22742
+ }
22743
+ return updateInitiative(root, args.id, {
22744
+ title: args.title,
22745
+ horizon: args.horizon,
22746
+ priority: args.priority,
22747
+ domains: args.domains,
22748
+ relatedCapabilities: args.relatedCapabilities
22749
+ });
22750
+ });
22751
+ }
22752
+ function addInitiativeCandidateTool(root, args) {
22753
+ if (!args.title?.trim()) return fail4("candidate title is required.");
22754
+ if (!getInitiative(root, args.id)) return fail4(`Initiative ${args.id} not found.`);
22755
+ return previewOrApply(
22756
+ args.confirm,
22757
+ "add-initiative-candidate",
22758
+ `A Work Item candidate "${args.title.trim()}" will be added to ${args.id}.`,
22759
+ () => addCandidate(root, args.id, {
22760
+ title: args.title,
22761
+ type: args.type,
22762
+ suggestedKnowledgeLevel: args.suggestedKnowledgeLevel,
22763
+ expectedValue: args.expectedValue,
22764
+ notes: args.notes
22765
+ })
22766
+ );
22767
+ }
22768
+ function materializeInitiativeCandidateTool(root, args) {
22769
+ const ini = getInitiative(root, args.id);
22770
+ if (!ini) return fail4(`Initiative ${args.id} not found.`);
22771
+ const candidate = ini.candidates.find((c) => c.id === args.candidateId);
22772
+ if (!candidate) return fail4(`Candidate ${args.candidateId} not found in ${args.id}.`);
22773
+ return previewOrApply(
22774
+ args.confirm,
22775
+ "materialize-initiative-candidate",
22776
+ `Candidate ${args.candidateId} ("${candidate.title}") will be materialized into a draft Work Item associated to ${args.id}.`,
22777
+ () => materializeCandidate(root, args.id, args.candidateId)
22778
+ );
22779
+ }
22780
+ function analyzeInitiativeTool(root, id) {
22781
+ const ini = getInitiative(root, id);
22782
+ if (!ini) return fail4(`Initiative ${id} not found.`);
22783
+ return ok5(analyzeInitiative(root, ini));
22784
+ }
22785
+ function completeInitiativeTool(root, args) {
22786
+ const ini = getInitiative(root, args.id);
22787
+ if (!ini) return fail4(`Initiative ${args.id} not found.`);
22788
+ const readiness = evaluateInitiativeCompletion(root, ini);
22789
+ if (!args.confirm) {
22790
+ return ok5({
22791
+ action: "complete-initiative",
22792
+ readiness,
22793
+ instruction: readiness.ready ? "Initiative looks ready. Call again with confirm=true to complete (human decision)." : "Initiative is NOT ready (see readiness.reasons). Completion requires an explicit human decision: call again with confirm=true only if the human chooses to complete anyway."
22794
+ });
22795
+ }
22796
+ try {
22797
+ const done = transitionInitiative(root, args.id, "completed");
22798
+ return ok5({ applied: true, readiness, result: { id: done.id, status: done.status } });
22799
+ } catch (err) {
22800
+ if (err instanceof InitiativeWriteError) return fail4(err.message);
22801
+ throw err;
22802
+ }
22803
+ }
22804
+
22097
22805
  // src/server.ts
22098
22806
  var SERVER_NAME = "kaddo";
22099
22807
  var require2 = createRequire(import.meta.url);
@@ -22490,6 +23198,110 @@ function createServer(root) {
22490
23198
  },
22491
23199
  async (args) => toolText(guarded(root, () => verifyWorkItemTool(root, args)))
22492
23200
  );
23201
+ const INITIATIVE_READ_NOTE = "Read-only. Initiatives are an optional outcome/traceability layer over Work Items; a Work Item never requires one.";
23202
+ server.registerTool(
23203
+ "kaddo_list_initiatives",
23204
+ { title: "List initiatives", description: `List initiatives with planning coverage and delivery progress. ${INITIATIVE_READ_NOTE}`, inputSchema: {} },
23205
+ async () => toolText(guarded(root, () => listInitiativesTool(root)))
23206
+ );
23207
+ server.registerTool(
23208
+ "kaddo_get_initiative",
23209
+ { title: "Get initiative", description: `Get one initiative: metadata, candidates and body. ${INITIATIVE_READ_NOTE}`, inputSchema: { id: z4.string() } },
23210
+ async (args) => toolText(guarded(root, () => getInitiativeTool(root, args.id)))
23211
+ );
23212
+ server.registerTool(
23213
+ "kaddo_get_initiative_context",
23214
+ {
23215
+ title: "Get initiative context",
23216
+ description: `Focused, initiative-scoped context for analysis: the initiative, progress, candidates (materialized/pending), associated Work Items and external links. Does NOT load the whole project. ${INITIATIVE_READ_NOTE}`,
23217
+ inputSchema: { id: z4.string() }
23218
+ },
23219
+ async (args) => toolText(guarded(root, () => getInitiativeContextTool(root, args.id)))
23220
+ );
23221
+ server.registerTool(
23222
+ "kaddo_get_initiative_progress",
23223
+ { title: "Get initiative progress", description: `Planning coverage (candidates materialized/total) and delivery progress (Work Items by lifecycle state). ${INITIATIVE_READ_NOTE}`, inputSchema: { id: z4.string() } },
23224
+ async (args) => toolText(guarded(root, () => getInitiativeProgressTool(root, args.id)))
23225
+ );
23226
+ const INITIATIVE_WRITE_NOTE = "Without confirm=true returns a preview. With confirm=true applies the change. Writes only under knowledge/delivery/.";
23227
+ server.registerTool(
23228
+ "kaddo_create_initiative",
23229
+ {
23230
+ title: "Create initiative",
23231
+ description: `Create a new initiative. ${INITIATIVE_WRITE_NOTE}`,
23232
+ inputSchema: {
23233
+ title: z4.string(),
23234
+ domains: z4.array(z4.string()).optional(),
23235
+ relatedCapabilities: z4.array(z4.string()).optional(),
23236
+ horizon: z4.string().optional(),
23237
+ priority: z4.string().optional(),
23238
+ confirm: z4.boolean().optional()
23239
+ }
23240
+ },
23241
+ async (args) => toolText(guarded(root, () => createInitiativeTool(root, args)))
23242
+ );
23243
+ server.registerTool(
23244
+ "kaddo_update_initiative",
23245
+ {
23246
+ title: "Update initiative",
23247
+ description: `Update an initiative's status (valid lifecycle transitions only) or non-lifecycle fields. ${INITIATIVE_WRITE_NOTE}`,
23248
+ inputSchema: {
23249
+ id: z4.string(),
23250
+ status: z4.enum(["planned", "in-progress", "completed", "deferred", "cancelled"]).optional(),
23251
+ title: z4.string().optional(),
23252
+ horizon: z4.string().optional(),
23253
+ priority: z4.string().optional(),
23254
+ domains: z4.array(z4.string()).optional(),
23255
+ relatedCapabilities: z4.array(z4.string()).optional(),
23256
+ confirm: z4.boolean().optional()
23257
+ }
23258
+ },
23259
+ async (args) => toolText(guarded(root, () => updateInitiativeTool(root, args)))
23260
+ );
23261
+ server.registerTool(
23262
+ "kaddo_add_initiative_candidate",
23263
+ {
23264
+ title: "Add initiative candidate",
23265
+ description: `Add a Work Item candidate to an initiative (not yet materialized). ${INITIATIVE_WRITE_NOTE}`,
23266
+ inputSchema: {
23267
+ id: z4.string(),
23268
+ title: z4.string(),
23269
+ type: z4.string().optional(),
23270
+ suggestedKnowledgeLevel: z4.string().optional(),
23271
+ expectedValue: z4.string().optional(),
23272
+ notes: z4.string().optional(),
23273
+ confirm: z4.boolean().optional()
23274
+ }
23275
+ },
23276
+ async (args) => toolText(guarded(root, () => addInitiativeCandidateTool(root, args)))
23277
+ );
23278
+ server.registerTool(
23279
+ "kaddo_materialize_initiative_candidate",
23280
+ {
23281
+ title: "Materialize initiative candidate",
23282
+ description: `Materialize an initiative candidate into a draft Work Item associated via \`initiative: INI-xxx\`. ${INITIATIVE_WRITE_NOTE}`,
23283
+ inputSchema: { id: z4.string(), candidateId: z4.string(), confirm: z4.boolean().optional() }
23284
+ },
23285
+ async (args) => toolText(guarded(root, () => materializeInitiativeCandidateTool(root, args)))
23286
+ );
23287
+ server.registerTool(
23288
+ "kaddo_analyze_initiative",
23289
+ {
23290
+ title: "Analyze initiative",
23291
+ description: `Read-only gap analysis: success-criteria coverage, pending candidates, delivery status, grounded findings, and the Initiative's own pending candidates as suggestions. Never invents work. ${INITIATIVE_READ_NOTE}`,
23292
+ inputSchema: { id: z4.string() }
23293
+ },
23294
+ async (args) => toolText(guarded(root, () => analyzeInitiativeTool(root, args.id)))
23295
+ );
23296
+ server.registerTool(
23297
+ "kaddo_complete_initiative",
23298
+ {
23299
+ title: "Complete initiative",
23300
+ description: "Human-gated completion. Without confirm returns completion readiness (does NOT complete). With confirm=true transitions the Initiative to completed; readiness findings are returned either way so uncovered scope stays visible. Never completes autonomously.",
23301
+ inputSchema: { id: z4.string(), confirm: z4.boolean().optional() }
23302
+ },
23303
+ async (args) => toolText(guarded(root, () => completeInitiativeTool(root, args)))
23304
+ );
22493
23305
  server.registerTool(
22494
23306
  "kaddo_telemetry_status",
22495
23307
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/mcp",
3
- "version": "3.105.0",
3
+ "version": "3.107.0",
4
4
  "description": "MCP server for Kaddo project knowledge: read-only resources/tools/prompts plus safe derived generation under .kaddo/.",
5
5
  "license": "MIT",
6
6
  "repository": {