@kaddo/mcp 3.104.0 → 3.106.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.
- package/dist/index.js +707 -28
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10783,7 +10783,7 @@ function getPrompt(root, name) {
|
|
|
10783
10783
|
}
|
|
10784
10784
|
|
|
10785
10785
|
// src/tools.ts
|
|
10786
|
-
import
|
|
10786
|
+
import matter12 from "gray-matter";
|
|
10787
10787
|
|
|
10788
10788
|
// ../cli/dist/core.js
|
|
10789
10789
|
import fs3 from "fs";
|
|
@@ -10816,6 +10816,8 @@ import matter82 from "gray-matter";
|
|
|
10816
10816
|
import matter92 from "gray-matter";
|
|
10817
10817
|
import { parse as parseYaml92, stringify as stringifyYaml5 } from "yaml";
|
|
10818
10818
|
import { parse as parseYaml102, stringify as stringifyYaml6 } from "yaml";
|
|
10819
|
+
import matter102 from "gray-matter";
|
|
10820
|
+
import matter11 from "gray-matter";
|
|
10819
10821
|
function exists2(p) {
|
|
10820
10822
|
return fs3.existsSync(p);
|
|
10821
10823
|
}
|
|
@@ -10826,6 +10828,9 @@ function writeFile2(filePath, content) {
|
|
|
10826
10828
|
fs3.mkdirSync(path3.dirname(filePath), { recursive: true });
|
|
10827
10829
|
fs3.writeFileSync(filePath, content, "utf-8");
|
|
10828
10830
|
}
|
|
10831
|
+
function ensureDir2(dirPath) {
|
|
10832
|
+
fs3.mkdirSync(dirPath, { recursive: true });
|
|
10833
|
+
}
|
|
10829
10834
|
function readDir2(dirPath) {
|
|
10830
10835
|
if (!exists2(dirPath)) return [];
|
|
10831
10836
|
return fs3.readdirSync(dirPath);
|
|
@@ -11069,6 +11074,9 @@ function lifecycleStateOf2(item) {
|
|
|
11069
11074
|
if (folder) return folder;
|
|
11070
11075
|
return "ready";
|
|
11071
11076
|
}
|
|
11077
|
+
function emptyLifecycleCounts2() {
|
|
11078
|
+
return Object.fromEntries(LIFECYCLE_STATES2.map((s) => [s, 0]));
|
|
11079
|
+
}
|
|
11072
11080
|
var ARCH_DIR3 = "knowledge";
|
|
11073
11081
|
function toPosix6(p) {
|
|
11074
11082
|
return p.replace(/\\/g, "/");
|
|
@@ -16965,7 +16973,7 @@ function createWorkItem(dir, opts) {
|
|
|
16965
16973
|
if (!type) throw new WorkItemWriteError("INVALID_INPUT", `Unknown Work Item type "${opts.type}".`);
|
|
16966
16974
|
const id = nextWorkItemId(dir);
|
|
16967
16975
|
const title = intent.split(/\r?\n/)[0].trim().slice(0, 120);
|
|
16968
|
-
const
|
|
16976
|
+
const today22 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
16969
16977
|
const source = opts.source ? { ...opts.source, inferred: false } : { type: "manual", inferred: false };
|
|
16970
16978
|
const data = {
|
|
16971
16979
|
type,
|
|
@@ -16973,7 +16981,7 @@ function createWorkItem(dir, opts) {
|
|
|
16973
16981
|
title,
|
|
16974
16982
|
status: "draft",
|
|
16975
16983
|
work_type: type,
|
|
16976
|
-
created_at:
|
|
16984
|
+
created_at: today22,
|
|
16977
16985
|
source,
|
|
16978
16986
|
generated_by: "kaddo-admin",
|
|
16979
16987
|
affected_modules: [],
|
|
@@ -18579,6 +18587,474 @@ function getStatus(dir) {
|
|
|
18579
18587
|
lastError: null
|
|
18580
18588
|
};
|
|
18581
18589
|
}
|
|
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
|
+
var WORK_ITEMS_DIR2 = "knowledge/delivery/work-items";
|
|
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
|
+
}
|
|
18582
19058
|
|
|
18583
19059
|
// src/tools.ts
|
|
18584
19060
|
var ok = (data) => ({ ok: true, data });
|
|
@@ -18675,12 +19151,12 @@ function markWorkItemReady(root, id, confirm2) {
|
|
|
18675
19151
|
}
|
|
18676
19152
|
const raw = readText(root, item.path);
|
|
18677
19153
|
if (!raw) return fail(`Could not read Work Item file: ${item.path}`);
|
|
18678
|
-
const parsed =
|
|
19154
|
+
const parsed = matter12(raw);
|
|
18679
19155
|
const data = { ...parsed.data };
|
|
18680
|
-
const
|
|
19156
|
+
const today3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
18681
19157
|
data.status = "ready";
|
|
18682
|
-
data.ready_at =
|
|
18683
|
-
const newContent =
|
|
19158
|
+
data.ready_at = today3;
|
|
19159
|
+
const newContent = matter12.stringify(parsed.content, data);
|
|
18684
19160
|
const posixPath = item.path.replace(/\\/g, "/");
|
|
18685
19161
|
let newRelPath = item.path;
|
|
18686
19162
|
if (posixPath.includes("/work-items/draft/")) {
|
|
@@ -18696,7 +19172,7 @@ function markWorkItemReady(root, id, confirm2) {
|
|
|
18696
19172
|
id: item.id,
|
|
18697
19173
|
from: "draft",
|
|
18698
19174
|
to: "ready",
|
|
18699
|
-
ready_at:
|
|
19175
|
+
ready_at: today3,
|
|
18700
19176
|
moved: newRelPath !== item.path ? { from: item.path, to: newRelPath } : void 0,
|
|
18701
19177
|
...warnings.length > 0 ? { warnings } : {},
|
|
18702
19178
|
next: ["Run kaddo context", "Run kaddo understand"]
|
|
@@ -18847,7 +19323,7 @@ function setTelemetryConsentTool(root, args) {
|
|
|
18847
19323
|
// src/multirepo.ts
|
|
18848
19324
|
import fs5 from "fs";
|
|
18849
19325
|
import path4 from "path";
|
|
18850
|
-
import
|
|
19326
|
+
import matter13 from "gray-matter";
|
|
18851
19327
|
import { parse as parseYaml11, stringify as stringifyYaml7 } from "yaml";
|
|
18852
19328
|
var ok2 = (data) => ({ ok: true, data });
|
|
18853
19329
|
var fail2 = (message) => ({ ok: false, message });
|
|
@@ -18919,7 +19395,7 @@ function firstParagraph3(md) {
|
|
|
18919
19395
|
}
|
|
18920
19396
|
return "";
|
|
18921
19397
|
}
|
|
18922
|
-
function
|
|
19398
|
+
function slugify4(s) {
|
|
18923
19399
|
return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
18924
19400
|
}
|
|
18925
19401
|
function modulesListTool(root, args) {
|
|
@@ -18999,7 +19475,7 @@ function validateWorkItemModulesTool(root, args) {
|
|
|
18999
19475
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
19000
19476
|
const raw = readText(root, wi.path);
|
|
19001
19477
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
19002
|
-
const parsed =
|
|
19478
|
+
const parsed = matter13(raw);
|
|
19003
19479
|
const data = parsed.data;
|
|
19004
19480
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
19005
19481
|
const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
|
|
@@ -19052,7 +19528,7 @@ function getWorkItemContextTool(root, args) {
|
|
|
19052
19528
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
19053
19529
|
const raw = readText(root, wi.path);
|
|
19054
19530
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
19055
|
-
const parsed =
|
|
19531
|
+
const parsed = matter13(raw);
|
|
19056
19532
|
const data = parsed.data;
|
|
19057
19533
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
19058
19534
|
const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
|
|
@@ -19106,7 +19582,7 @@ function extractSection(content, heading) {
|
|
|
19106
19582
|
return out;
|
|
19107
19583
|
}
|
|
19108
19584
|
function buildBranchStrategy(wi, affectedModules, codeEntries, descriptor) {
|
|
19109
|
-
const slug5 =
|
|
19585
|
+
const slug5 = slugify4(wi.title);
|
|
19110
19586
|
const wiLower = wi.id.toLowerCase();
|
|
19111
19587
|
const coreBranch = `feature/${wiLower}-${slug5}`;
|
|
19112
19588
|
const moduleBranches = {};
|
|
@@ -19138,7 +19614,7 @@ function suggestBranchStrategyTool(root, args) {
|
|
|
19138
19614
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
19139
19615
|
const raw = readText(root, wi.path);
|
|
19140
19616
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
19141
|
-
const parsed =
|
|
19617
|
+
const parsed = matter13(raw);
|
|
19142
19618
|
const data = parsed.data;
|
|
19143
19619
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
19144
19620
|
const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
|
|
@@ -19169,7 +19645,7 @@ function exportCapsuleTool(root, args) {
|
|
|
19169
19645
|
if (!mc) warnings.push("module-context.md not found for this module.");
|
|
19170
19646
|
if (mc && isPlaceholder2(mc)) warnings.push("module-context.md contains placeholder sections.");
|
|
19171
19647
|
capsuleType = "module";
|
|
19172
|
-
capsuleName = `${
|
|
19648
|
+
capsuleName = `${slugify4(config.project?.name ?? "project")}-${args.module}`;
|
|
19173
19649
|
capsuleData = {
|
|
19174
19650
|
type: "knowledge-capsule",
|
|
19175
19651
|
system: `${config.project?.name ?? "unknown"}/${args.module}`,
|
|
@@ -19191,7 +19667,7 @@ function exportCapsuleTool(root, args) {
|
|
|
19191
19667
|
if (mc && isPlaceholder2(mc)) warnings.push("module-context.md contains placeholder sections.");
|
|
19192
19668
|
const modId = config.module?.id ?? args.module;
|
|
19193
19669
|
capsuleType = "module";
|
|
19194
|
-
capsuleName =
|
|
19670
|
+
capsuleName = slugify4(modId);
|
|
19195
19671
|
capsuleData = {
|
|
19196
19672
|
type: "knowledge-capsule",
|
|
19197
19673
|
system: `${config.multirepo?.parent_system ?? "unknown"}/${modId}`,
|
|
@@ -19219,7 +19695,7 @@ function exportCapsuleTool(root, args) {
|
|
|
19219
19695
|
responsibilities.push(mc ? `[${m2.id}] ${firstParagraph3(mc) || m2.name || m2.id}` : `[${m2.id}] ${m2.name || m2.id} (no module-context)`);
|
|
19220
19696
|
}
|
|
19221
19697
|
capsuleType = "system";
|
|
19222
|
-
capsuleName = `${
|
|
19698
|
+
capsuleName = `${slugify4(config.project?.name ?? "project")}-system`;
|
|
19223
19699
|
capsuleData = {
|
|
19224
19700
|
type: "knowledge-capsule",
|
|
19225
19701
|
system: config.project?.name ?? "unknown",
|
|
@@ -19240,7 +19716,7 @@ function exportCapsuleTool(root, args) {
|
|
|
19240
19716
|
if (mc && isPlaceholder2(mc)) warnings.push("module-context.md contains placeholder sections.");
|
|
19241
19717
|
const modId = config.module?.id ?? "module";
|
|
19242
19718
|
capsuleType = "module";
|
|
19243
|
-
capsuleName =
|
|
19719
|
+
capsuleName = slugify4(modId);
|
|
19244
19720
|
capsuleData = {
|
|
19245
19721
|
type: "knowledge-capsule",
|
|
19246
19722
|
system: `${config.multirepo?.parent_system ?? "unknown"}/${modId}`,
|
|
@@ -19256,7 +19732,7 @@ function exportCapsuleTool(root, args) {
|
|
|
19256
19732
|
};
|
|
19257
19733
|
} else {
|
|
19258
19734
|
capsuleType = "project";
|
|
19259
|
-
capsuleName =
|
|
19735
|
+
capsuleName = slugify4(config.project?.name ?? "project");
|
|
19260
19736
|
capsuleData = {
|
|
19261
19737
|
type: "knowledge-capsule",
|
|
19262
19738
|
system: config.project?.name ?? "unknown",
|
|
@@ -19419,7 +19895,7 @@ var PROVENANCE2 = /* @__PURE__ */ new Set(["declared", "derived", "agent-reviewe
|
|
|
19419
19895
|
function isRelative2(p) {
|
|
19420
19896
|
return typeof p === "string" && p.trim() !== "" && !/^([a-zA-Z]:[\\/]|\/)/.test(p) && !p.includes("..");
|
|
19421
19897
|
}
|
|
19422
|
-
function
|
|
19898
|
+
function strList3(v) {
|
|
19423
19899
|
return Array.isArray(v) ? v.map((x2) => typeof x2 === "string" ? x2.trim() : "").filter(Boolean) : [];
|
|
19424
19900
|
}
|
|
19425
19901
|
function loadSystemTopology2(dir) {
|
|
@@ -19460,7 +19936,7 @@ function parseTopologyContent2(raw, dir) {
|
|
|
19460
19936
|
const moduleRaw = typeof e.module === "string" ? e.module.trim() : void 0;
|
|
19461
19937
|
if (moduleRaw && !validModules.has(moduleRaw)) findings.push({ level: "warning", message: `Entity "${id}" references unregistered module "${moduleRaw}".` });
|
|
19462
19938
|
const rawImpl = e.implementation ?? e.implementation_refs;
|
|
19463
|
-
const implementation =
|
|
19939
|
+
const implementation = strList3(rawImpl).filter((p) => {
|
|
19464
19940
|
if (isRelative2(p)) return true;
|
|
19465
19941
|
findings.push({ level: "warning", message: `Entity "${id}" implementation ref "${p}" is not a safe relative path and was dropped.` });
|
|
19466
19942
|
return false;
|
|
@@ -19468,7 +19944,7 @@ function parseTopologyContent2(raw, dir) {
|
|
|
19468
19944
|
const prov = e.provenance;
|
|
19469
19945
|
const provOrigin = typeof prov === "string" ? prov : prov && typeof prov === "object" && typeof prov.origin === "string" ? String(prov.origin) : void 0;
|
|
19470
19946
|
const provenance = provOrigin && PROVENANCE2.has(provOrigin) ? provOrigin : void 0;
|
|
19471
|
-
const evidence =
|
|
19947
|
+
const evidence = strList3(e.evidence ?? (prov && typeof prov === "object" ? prov.evidence_refs : void 0)).filter(isRelative2);
|
|
19472
19948
|
seenIds.add(id);
|
|
19473
19949
|
entities.push({
|
|
19474
19950
|
id,
|
|
@@ -19477,7 +19953,7 @@ function parseTopologyContent2(raw, dir) {
|
|
|
19477
19953
|
...typeof e.purpose === "string" && e.purpose.trim() ? { purpose: e.purpose.trim() } : {},
|
|
19478
19954
|
...moduleRaw && validModules.has(moduleRaw) ? { moduleId: moduleRaw } : {},
|
|
19479
19955
|
implementationRefs: implementation,
|
|
19480
|
-
knowledgeRefs:
|
|
19956
|
+
knowledgeRefs: strList3(e.knowledge ?? e.knowledge_refs),
|
|
19481
19957
|
...provenance ? { provenance } : {},
|
|
19482
19958
|
evidence
|
|
19483
19959
|
});
|
|
@@ -19511,7 +19987,7 @@ function parseTopologyContent2(raw, dir) {
|
|
|
19511
19987
|
}
|
|
19512
19988
|
seenRels.add(key);
|
|
19513
19989
|
const prov = r2.provenance;
|
|
19514
|
-
relationships.push({ from, to, type, evidence:
|
|
19990
|
+
relationships.push({ from, to, type, evidence: strList3(r2.evidence ?? prov?.evidence_refs).filter(isRelative2) });
|
|
19515
19991
|
}
|
|
19516
19992
|
return { entities, relationships, findings, ...sourceRevision ? { sourceRevision } : {} };
|
|
19517
19993
|
}
|
|
@@ -19898,14 +20374,14 @@ function systemImpactCandidatesTool(root, args) {
|
|
|
19898
20374
|
}
|
|
19899
20375
|
|
|
19900
20376
|
// ../cli/src/services/integrations.ts
|
|
19901
|
-
import
|
|
20377
|
+
import matter15 from "gray-matter";
|
|
19902
20378
|
import { parse as parseYaml13, stringify as stringifyYaml9 } from "yaml";
|
|
19903
20379
|
|
|
19904
20380
|
// ../cli/src/core/work-item-write.ts
|
|
19905
20381
|
import fs7 from "fs";
|
|
19906
20382
|
import path6 from "path";
|
|
19907
20383
|
import crypto4 from "crypto";
|
|
19908
|
-
import
|
|
20384
|
+
import matter14 from "gray-matter";
|
|
19909
20385
|
var SECTION_ORDER2 = [
|
|
19910
20386
|
"Actor",
|
|
19911
20387
|
"Outcome",
|
|
@@ -20953,7 +21429,7 @@ async function integrationsWorkItemTool(root, args) {
|
|
|
20953
21429
|
}
|
|
20954
21430
|
|
|
20955
21431
|
// ../cli/src/core/context-pack.ts
|
|
20956
|
-
import
|
|
21432
|
+
import matter16 from "gray-matter";
|
|
20957
21433
|
var CONTEXT_PACK_VERSION = "1";
|
|
20958
21434
|
var ARCH_DIR4 = "knowledge";
|
|
20959
21435
|
function readScanJson(dir) {
|
|
@@ -20966,7 +21442,7 @@ function readScanJson(dir) {
|
|
|
20966
21442
|
}
|
|
20967
21443
|
}
|
|
20968
21444
|
function firstParagraph4(markdown) {
|
|
20969
|
-
const body =
|
|
21445
|
+
const body = matter16(markdown).content.trim();
|
|
20970
21446
|
const para = body.split("\n\n").map((p) => p.trim()).find((p) => p && !p.startsWith("#"));
|
|
20971
21447
|
return para ?? "";
|
|
20972
21448
|
}
|
|
@@ -22081,6 +22557,123 @@ function generateCapsuleDraft(root) {
|
|
|
22081
22557
|
};
|
|
22082
22558
|
}
|
|
22083
22559
|
|
|
22560
|
+
// src/initiatives.ts
|
|
22561
|
+
var ok5 = (data) => ({ ok: true, data });
|
|
22562
|
+
var fail4 = (message) => ({ ok: false, message });
|
|
22563
|
+
function listInitiativesTool(root) {
|
|
22564
|
+
const all = discoverInitiatives(root).map((ini) => {
|
|
22565
|
+
const p = computeInitiativeProgress(root, ini);
|
|
22566
|
+
return {
|
|
22567
|
+
id: ini.id,
|
|
22568
|
+
title: ini.title,
|
|
22569
|
+
status: ini.status,
|
|
22570
|
+
planning: p.planning,
|
|
22571
|
+
delivery: p.delivery
|
|
22572
|
+
};
|
|
22573
|
+
});
|
|
22574
|
+
return ok5(all);
|
|
22575
|
+
}
|
|
22576
|
+
function getInitiativeTool(root, id) {
|
|
22577
|
+
const ini = getInitiative(root, id);
|
|
22578
|
+
if (!ini) return fail4(`Initiative ${id} not found.`);
|
|
22579
|
+
return ok5({
|
|
22580
|
+
id: ini.id,
|
|
22581
|
+
title: ini.title,
|
|
22582
|
+
status: ini.status,
|
|
22583
|
+
horizon: ini.horizon,
|
|
22584
|
+
priority: ini.priority,
|
|
22585
|
+
knowledgeLevel: ini.knowledgeLevel,
|
|
22586
|
+
domains: ini.domains,
|
|
22587
|
+
relatedCapabilities: ini.relatedCapabilities,
|
|
22588
|
+
source: ini.source,
|
|
22589
|
+
sourceId: ini.sourceId,
|
|
22590
|
+
externalLinks: ini.externalLinks,
|
|
22591
|
+
candidates: ini.candidates,
|
|
22592
|
+
body: ini.body
|
|
22593
|
+
});
|
|
22594
|
+
}
|
|
22595
|
+
function getInitiativeContextTool(root, id) {
|
|
22596
|
+
const ini = getInitiative(root, id);
|
|
22597
|
+
if (!ini) return fail4(`Initiative ${id} not found.`);
|
|
22598
|
+
return ok5(getInitiativeContext(root, ini));
|
|
22599
|
+
}
|
|
22600
|
+
function getInitiativeProgressTool(root, id) {
|
|
22601
|
+
const ini = getInitiative(root, id);
|
|
22602
|
+
if (!ini) return fail4(`Initiative ${id} not found.`);
|
|
22603
|
+
return ok5(computeInitiativeProgress(root, ini));
|
|
22604
|
+
}
|
|
22605
|
+
function previewOrApply(confirm2, action, effect, apply) {
|
|
22606
|
+
if (!confirm2) {
|
|
22607
|
+
return ok5({ action, effect, instruction: "Call again with confirm=true to apply." });
|
|
22608
|
+
}
|
|
22609
|
+
try {
|
|
22610
|
+
return ok5({ applied: true, result: apply() });
|
|
22611
|
+
} catch (err) {
|
|
22612
|
+
if (err instanceof InitiativeWriteError) return fail4(err.message);
|
|
22613
|
+
throw err;
|
|
22614
|
+
}
|
|
22615
|
+
}
|
|
22616
|
+
function createInitiativeTool(root, args) {
|
|
22617
|
+
if (!args.title?.trim()) return fail4("title is required.");
|
|
22618
|
+
return previewOrApply(
|
|
22619
|
+
args.confirm,
|
|
22620
|
+
"create-initiative",
|
|
22621
|
+
`A new Initiative "${args.title.trim()}" will be created under knowledge/delivery/initiatives/.`,
|
|
22622
|
+
() => createInitiative(root, {
|
|
22623
|
+
title: args.title,
|
|
22624
|
+
domains: args.domains,
|
|
22625
|
+
relatedCapabilities: args.relatedCapabilities,
|
|
22626
|
+
horizon: args.horizon,
|
|
22627
|
+
priority: args.priority
|
|
22628
|
+
})
|
|
22629
|
+
);
|
|
22630
|
+
}
|
|
22631
|
+
function updateInitiativeTool(root, args) {
|
|
22632
|
+
if (!getInitiative(root, args.id)) return fail4(`Initiative ${args.id} not found.`);
|
|
22633
|
+
const changingStatus = typeof args.status === "string";
|
|
22634
|
+
const effect = changingStatus ? `Initiative ${args.id} status will transition to "${args.status}" (valid transitions only).` : `Initiative ${args.id} fields will be updated.`;
|
|
22635
|
+
return previewOrApply(args.confirm, "update-initiative", effect, () => {
|
|
22636
|
+
if (changingStatus) {
|
|
22637
|
+
return transitionInitiative(root, args.id, args.status);
|
|
22638
|
+
}
|
|
22639
|
+
return updateInitiative(root, args.id, {
|
|
22640
|
+
title: args.title,
|
|
22641
|
+
horizon: args.horizon,
|
|
22642
|
+
priority: args.priority,
|
|
22643
|
+
domains: args.domains,
|
|
22644
|
+
relatedCapabilities: args.relatedCapabilities
|
|
22645
|
+
});
|
|
22646
|
+
});
|
|
22647
|
+
}
|
|
22648
|
+
function addInitiativeCandidateTool(root, args) {
|
|
22649
|
+
if (!args.title?.trim()) return fail4("candidate title is required.");
|
|
22650
|
+
if (!getInitiative(root, args.id)) return fail4(`Initiative ${args.id} not found.`);
|
|
22651
|
+
return previewOrApply(
|
|
22652
|
+
args.confirm,
|
|
22653
|
+
"add-initiative-candidate",
|
|
22654
|
+
`A Work Item candidate "${args.title.trim()}" will be added to ${args.id}.`,
|
|
22655
|
+
() => addCandidate(root, args.id, {
|
|
22656
|
+
title: args.title,
|
|
22657
|
+
type: args.type,
|
|
22658
|
+
suggestedKnowledgeLevel: args.suggestedKnowledgeLevel,
|
|
22659
|
+
expectedValue: args.expectedValue,
|
|
22660
|
+
notes: args.notes
|
|
22661
|
+
})
|
|
22662
|
+
);
|
|
22663
|
+
}
|
|
22664
|
+
function materializeInitiativeCandidateTool(root, args) {
|
|
22665
|
+
const ini = getInitiative(root, args.id);
|
|
22666
|
+
if (!ini) return fail4(`Initiative ${args.id} not found.`);
|
|
22667
|
+
const candidate = ini.candidates.find((c) => c.id === args.candidateId);
|
|
22668
|
+
if (!candidate) return fail4(`Candidate ${args.candidateId} not found in ${args.id}.`);
|
|
22669
|
+
return previewOrApply(
|
|
22670
|
+
args.confirm,
|
|
22671
|
+
"materialize-initiative-candidate",
|
|
22672
|
+
`Candidate ${args.candidateId} ("${candidate.title}") will be materialized into a draft Work Item associated to ${args.id}.`,
|
|
22673
|
+
() => materializeCandidate(root, args.id, args.candidateId)
|
|
22674
|
+
);
|
|
22675
|
+
}
|
|
22676
|
+
|
|
22084
22677
|
// src/server.ts
|
|
22085
22678
|
var SERVER_NAME = "kaddo";
|
|
22086
22679
|
var require2 = createRequire(import.meta.url);
|
|
@@ -22477,6 +23070,92 @@ function createServer(root) {
|
|
|
22477
23070
|
},
|
|
22478
23071
|
async (args) => toolText(guarded(root, () => verifyWorkItemTool(root, args)))
|
|
22479
23072
|
);
|
|
23073
|
+
const INITIATIVE_READ_NOTE = "Read-only. Initiatives are an optional outcome/traceability layer over Work Items; a Work Item never requires one.";
|
|
23074
|
+
server.registerTool(
|
|
23075
|
+
"kaddo_list_initiatives",
|
|
23076
|
+
{ title: "List initiatives", description: `List initiatives with planning coverage and delivery progress. ${INITIATIVE_READ_NOTE}`, inputSchema: {} },
|
|
23077
|
+
async () => toolText(guarded(root, () => listInitiativesTool(root)))
|
|
23078
|
+
);
|
|
23079
|
+
server.registerTool(
|
|
23080
|
+
"kaddo_get_initiative",
|
|
23081
|
+
{ title: "Get initiative", description: `Get one initiative: metadata, candidates and body. ${INITIATIVE_READ_NOTE}`, inputSchema: { id: z4.string() } },
|
|
23082
|
+
async (args) => toolText(guarded(root, () => getInitiativeTool(root, args.id)))
|
|
23083
|
+
);
|
|
23084
|
+
server.registerTool(
|
|
23085
|
+
"kaddo_get_initiative_context",
|
|
23086
|
+
{
|
|
23087
|
+
title: "Get initiative context",
|
|
23088
|
+
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}`,
|
|
23089
|
+
inputSchema: { id: z4.string() }
|
|
23090
|
+
},
|
|
23091
|
+
async (args) => toolText(guarded(root, () => getInitiativeContextTool(root, args.id)))
|
|
23092
|
+
);
|
|
23093
|
+
server.registerTool(
|
|
23094
|
+
"kaddo_get_initiative_progress",
|
|
23095
|
+
{ 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() } },
|
|
23096
|
+
async (args) => toolText(guarded(root, () => getInitiativeProgressTool(root, args.id)))
|
|
23097
|
+
);
|
|
23098
|
+
const INITIATIVE_WRITE_NOTE = "Without confirm=true returns a preview. With confirm=true applies the change. Writes only under knowledge/delivery/.";
|
|
23099
|
+
server.registerTool(
|
|
23100
|
+
"kaddo_create_initiative",
|
|
23101
|
+
{
|
|
23102
|
+
title: "Create initiative",
|
|
23103
|
+
description: `Create a new initiative. ${INITIATIVE_WRITE_NOTE}`,
|
|
23104
|
+
inputSchema: {
|
|
23105
|
+
title: z4.string(),
|
|
23106
|
+
domains: z4.array(z4.string()).optional(),
|
|
23107
|
+
relatedCapabilities: z4.array(z4.string()).optional(),
|
|
23108
|
+
horizon: z4.string().optional(),
|
|
23109
|
+
priority: z4.string().optional(),
|
|
23110
|
+
confirm: z4.boolean().optional()
|
|
23111
|
+
}
|
|
23112
|
+
},
|
|
23113
|
+
async (args) => toolText(guarded(root, () => createInitiativeTool(root, args)))
|
|
23114
|
+
);
|
|
23115
|
+
server.registerTool(
|
|
23116
|
+
"kaddo_update_initiative",
|
|
23117
|
+
{
|
|
23118
|
+
title: "Update initiative",
|
|
23119
|
+
description: `Update an initiative's status (valid lifecycle transitions only) or non-lifecycle fields. ${INITIATIVE_WRITE_NOTE}`,
|
|
23120
|
+
inputSchema: {
|
|
23121
|
+
id: z4.string(),
|
|
23122
|
+
status: z4.enum(["planned", "in-progress", "completed", "deferred", "cancelled"]).optional(),
|
|
23123
|
+
title: z4.string().optional(),
|
|
23124
|
+
horizon: z4.string().optional(),
|
|
23125
|
+
priority: z4.string().optional(),
|
|
23126
|
+
domains: z4.array(z4.string()).optional(),
|
|
23127
|
+
relatedCapabilities: z4.array(z4.string()).optional(),
|
|
23128
|
+
confirm: z4.boolean().optional()
|
|
23129
|
+
}
|
|
23130
|
+
},
|
|
23131
|
+
async (args) => toolText(guarded(root, () => updateInitiativeTool(root, args)))
|
|
23132
|
+
);
|
|
23133
|
+
server.registerTool(
|
|
23134
|
+
"kaddo_add_initiative_candidate",
|
|
23135
|
+
{
|
|
23136
|
+
title: "Add initiative candidate",
|
|
23137
|
+
description: `Add a Work Item candidate to an initiative (not yet materialized). ${INITIATIVE_WRITE_NOTE}`,
|
|
23138
|
+
inputSchema: {
|
|
23139
|
+
id: z4.string(),
|
|
23140
|
+
title: z4.string(),
|
|
23141
|
+
type: z4.string().optional(),
|
|
23142
|
+
suggestedKnowledgeLevel: z4.string().optional(),
|
|
23143
|
+
expectedValue: z4.string().optional(),
|
|
23144
|
+
notes: z4.string().optional(),
|
|
23145
|
+
confirm: z4.boolean().optional()
|
|
23146
|
+
}
|
|
23147
|
+
},
|
|
23148
|
+
async (args) => toolText(guarded(root, () => addInitiativeCandidateTool(root, args)))
|
|
23149
|
+
);
|
|
23150
|
+
server.registerTool(
|
|
23151
|
+
"kaddo_materialize_initiative_candidate",
|
|
23152
|
+
{
|
|
23153
|
+
title: "Materialize initiative candidate",
|
|
23154
|
+
description: `Materialize an initiative candidate into a draft Work Item associated via \`initiative: INI-xxx\`. ${INITIATIVE_WRITE_NOTE}`,
|
|
23155
|
+
inputSchema: { id: z4.string(), candidateId: z4.string(), confirm: z4.boolean().optional() }
|
|
23156
|
+
},
|
|
23157
|
+
async (args) => toolText(guarded(root, () => materializeInitiativeCandidateTool(root, args)))
|
|
23158
|
+
);
|
|
22480
23159
|
server.registerTool(
|
|
22481
23160
|
"kaddo_telemetry_status",
|
|
22482
23161
|
{
|
package/package.json
CHANGED