@kaddo/mcp 3.134.0 → 3.135.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 +683 -130
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2903,7 +2903,7 @@ ${items.map((i2) => `- ${i2}`).join("\n")}
|
|
|
2903
2903
|
`;
|
|
2904
2904
|
}
|
|
2905
2905
|
function renderCapsuleMarkdown(c3) {
|
|
2906
|
-
const
|
|
2906
|
+
const fm2 = [
|
|
2907
2907
|
"---",
|
|
2908
2908
|
"type: knowledge-capsule",
|
|
2909
2909
|
`system: ${c3.system}`,
|
|
@@ -2915,7 +2915,7 @@ function renderCapsuleMarkdown(c3) {
|
|
|
2915
2915
|
"---"
|
|
2916
2916
|
].join("\n");
|
|
2917
2917
|
const parts = [
|
|
2918
|
-
|
|
2918
|
+
fm2,
|
|
2919
2919
|
"",
|
|
2920
2920
|
`# ${c3.system} \u2014 Knowledge Capsule`,
|
|
2921
2921
|
"",
|
|
@@ -3365,40 +3365,40 @@ function purposeFromBody(body) {
|
|
|
3365
3365
|
}
|
|
3366
3366
|
function toDetail(filePath, relPath, raw) {
|
|
3367
3367
|
const parsed = matter7(raw);
|
|
3368
|
-
const
|
|
3368
|
+
const fm2 = parsed.data ?? {};
|
|
3369
3369
|
const body = parsed.content.trim();
|
|
3370
|
-
const id = str(
|
|
3370
|
+
const id = str(fm2.id) ?? "";
|
|
3371
3371
|
return {
|
|
3372
3372
|
id,
|
|
3373
|
-
title: str(
|
|
3374
|
-
resourceType: str(
|
|
3375
|
-
provider: str(
|
|
3376
|
-
environments: strArray(
|
|
3377
|
-
scope: parseScope(
|
|
3378
|
-
modules: strArray(
|
|
3373
|
+
title: str(fm2.title) ?? id,
|
|
3374
|
+
resourceType: str(fm2.resource_type),
|
|
3375
|
+
provider: str(fm2.provider),
|
|
3376
|
+
environments: strArray(fm2.environments),
|
|
3377
|
+
scope: parseScope(fm2.scope),
|
|
3378
|
+
modules: strArray(fm2.modules),
|
|
3379
3379
|
path: relPath,
|
|
3380
|
-
purpose: str(
|
|
3381
|
-
interfaces: parseInterfaces(
|
|
3382
|
-
boundaries: parseBoundaries(
|
|
3380
|
+
purpose: str(fm2.purpose) ?? purposeFromBody(body),
|
|
3381
|
+
interfaces: parseInterfaces(fm2.access_interfaces),
|
|
3382
|
+
boundaries: parseBoundaries(fm2.access_boundaries),
|
|
3383
3383
|
...(() => {
|
|
3384
|
-
const a3 = parseAuth(
|
|
3384
|
+
const a3 = parseAuth(fm2.authentication);
|
|
3385
3385
|
return { authRefs: a3.refs, authMode: a3.mode };
|
|
3386
3386
|
})(),
|
|
3387
|
-
connectionProfiles: parseConnectionProfiles(
|
|
3387
|
+
connectionProfiles: parseConnectionProfiles(fm2.connection_profiles),
|
|
3388
3388
|
markdownBody: body
|
|
3389
3389
|
};
|
|
3390
3390
|
}
|
|
3391
3391
|
function getResources(dir) {
|
|
3392
3392
|
return discoverKnowledge(dir).filter((a3) => a3.type === RESOURCE_ARTIFACT_TYPE).map((a3) => {
|
|
3393
|
-
const
|
|
3393
|
+
const fm2 = a3.rawFrontmatter;
|
|
3394
3394
|
return {
|
|
3395
|
-
id: a3.id || str(
|
|
3396
|
-
title: a3.title || str(
|
|
3397
|
-
resourceType: str(
|
|
3398
|
-
provider: str(
|
|
3399
|
-
environments: strArray(
|
|
3400
|
-
scope: parseScope(
|
|
3401
|
-
modules: strArray(
|
|
3395
|
+
id: a3.id || str(fm2.id) || a3.title,
|
|
3396
|
+
title: a3.title || str(fm2.title) || a3.id,
|
|
3397
|
+
resourceType: str(fm2.resource_type),
|
|
3398
|
+
provider: str(fm2.provider),
|
|
3399
|
+
environments: strArray(fm2.environments),
|
|
3400
|
+
scope: parseScope(fm2.scope),
|
|
3401
|
+
modules: strArray(fm2.modules),
|
|
3402
3402
|
path: a3.relPath
|
|
3403
3403
|
};
|
|
3404
3404
|
}).sort((x2, y) => x2.id.localeCompare(y.id));
|
|
@@ -3411,8 +3411,8 @@ function getResource(dir, id) {
|
|
|
3411
3411
|
const raw = readFile(match.filePath);
|
|
3412
3412
|
return toDetail(match.filePath, match.relPath, raw);
|
|
3413
3413
|
}
|
|
3414
|
-
function parseWorkItemResources(
|
|
3415
|
-
const v =
|
|
3414
|
+
function parseWorkItemResources(fm2) {
|
|
3415
|
+
const v = fm2.resources;
|
|
3416
3416
|
if (!Array.isArray(v)) return [];
|
|
3417
3417
|
const out = [];
|
|
3418
3418
|
for (const raw of v) {
|
|
@@ -3464,8 +3464,8 @@ var EVIDENCE_SIGNALS = [
|
|
|
3464
3464
|
function str2(v) {
|
|
3465
3465
|
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
3466
3466
|
}
|
|
3467
|
-
function parseWorkItemDeprecationTargets(
|
|
3468
|
-
const v =
|
|
3467
|
+
function parseWorkItemDeprecationTargets(fm2) {
|
|
3468
|
+
const v = fm2.deprecation_targets;
|
|
3469
3469
|
if (!Array.isArray(v)) return [];
|
|
3470
3470
|
const out = [];
|
|
3471
3471
|
for (const raw of v) {
|
|
@@ -3599,13 +3599,13 @@ function purposeFromBody2(body) {
|
|
|
3599
3599
|
}
|
|
3600
3600
|
function getDeprecationPlans(dir) {
|
|
3601
3601
|
return discoverKnowledge(dir).filter((a3) => a3.type === DEPRECATION_ARTIFACT_TYPE).map((a3) => {
|
|
3602
|
-
const
|
|
3603
|
-
const targets = parseTargets(
|
|
3602
|
+
const fm2 = a3.rawFrontmatter;
|
|
3603
|
+
const targets = parseTargets(fm2.targets);
|
|
3604
3604
|
return {
|
|
3605
|
-
id: a3.id || str2(
|
|
3606
|
-
title: a3.title || str2(
|
|
3605
|
+
id: a3.id || str2(fm2.id) || a3.title,
|
|
3606
|
+
title: a3.title || str2(fm2.title) || a3.id,
|
|
3607
3607
|
path: a3.relPath,
|
|
3608
|
-
status: str2(
|
|
3608
|
+
status: str2(fm2.status),
|
|
3609
3609
|
targetCount: targets.length,
|
|
3610
3610
|
lifecycleCounts: lifecycleCountsOf(targets)
|
|
3611
3611
|
};
|
|
@@ -3618,17 +3618,17 @@ function getDeprecationPlan(dir, id) {
|
|
|
3618
3618
|
if (!match) return null;
|
|
3619
3619
|
const raw = readFile(match.filePath);
|
|
3620
3620
|
const parsed = matter8(raw);
|
|
3621
|
-
const
|
|
3621
|
+
const fm2 = parsed.data ?? {};
|
|
3622
3622
|
const body = parsed.content.trim();
|
|
3623
|
-
const targets = parseTargets(
|
|
3623
|
+
const targets = parseTargets(fm2.targets);
|
|
3624
3624
|
return {
|
|
3625
|
-
id: str2(
|
|
3626
|
-
title: str2(
|
|
3625
|
+
id: str2(fm2.id) ?? match.id,
|
|
3626
|
+
title: str2(fm2.title) ?? match.title,
|
|
3627
3627
|
path: match.relPath,
|
|
3628
|
-
status: str2(
|
|
3628
|
+
status: str2(fm2.status),
|
|
3629
3629
|
targetCount: targets.length,
|
|
3630
3630
|
lifecycleCounts: lifecycleCountsOf(targets),
|
|
3631
|
-
purpose: str2(
|
|
3631
|
+
purpose: str2(fm2.purpose) ?? purposeFromBody2(body),
|
|
3632
3632
|
targets,
|
|
3633
3633
|
markdownBody: body
|
|
3634
3634
|
};
|
|
@@ -4154,9 +4154,9 @@ var QUALITY_NOTE = {
|
|
|
4154
4154
|
sparse: "The graph has many nodes but few meaningful edges.",
|
|
4155
4155
|
empty: "The graph has almost no relationships."
|
|
4156
4156
|
};
|
|
4157
|
-
function yamlBlock(
|
|
4157
|
+
function yamlBlock(fm2) {
|
|
4158
4158
|
const lines = ["```yaml"];
|
|
4159
|
-
for (const [k2, vals] of Object.entries(
|
|
4159
|
+
for (const [k2, vals] of Object.entries(fm2)) {
|
|
4160
4160
|
lines.push(`${k2}:`);
|
|
4161
4161
|
for (const v of vals) lines.push(` - ${v}`);
|
|
4162
4162
|
}
|
|
@@ -9348,7 +9348,7 @@ var AGENT_PROMPTS = [
|
|
|
9348
9348
|
// ../cli/src/modules/agents.ts
|
|
9349
9349
|
function withAgentFrontMatter(fileName, content) {
|
|
9350
9350
|
const name = fileName.replace(/\.md$/, "");
|
|
9351
|
-
const
|
|
9351
|
+
const fm2 = [
|
|
9352
9352
|
"---",
|
|
9353
9353
|
"type: agent",
|
|
9354
9354
|
`name: ${name}`,
|
|
@@ -9357,7 +9357,7 @@ function withAgentFrontMatter(fileName, content) {
|
|
|
9357
9357
|
"---",
|
|
9358
9358
|
""
|
|
9359
9359
|
].join("\n");
|
|
9360
|
-
return
|
|
9360
|
+
return fm2 + content.replace(/^\s+/, "");
|
|
9361
9361
|
}
|
|
9362
9362
|
var agentReadme = {
|
|
9363
9363
|
path: "knowledge/agents/README.md",
|
|
@@ -10385,8 +10385,8 @@ function analyzeMetadataHealth(dir) {
|
|
|
10385
10385
|
if (isFile(p) && entry.endsWith(".md")) {
|
|
10386
10386
|
try {
|
|
10387
10387
|
const raw = readFile(p);
|
|
10388
|
-
const
|
|
10389
|
-
if (
|
|
10388
|
+
const fm2 = matter12(raw).data;
|
|
10389
|
+
if (fm2.status === "done") {
|
|
10390
10390
|
const rel = p.replace(dir + "/", "").replace(dir + "\\", "").replace(/\\/g, "/");
|
|
10391
10391
|
findings.push({
|
|
10392
10392
|
file: rel,
|
|
@@ -10719,8 +10719,8 @@ function buildProjectExplanation(dir) {
|
|
|
10719
10719
|
};
|
|
10720
10720
|
}),
|
|
10721
10721
|
implementationEvidence: workItemArtifacts.filter((a3) => a3.affectedModules.length > 0).map((a3) => {
|
|
10722
|
-
const
|
|
10723
|
-
const evidence =
|
|
10722
|
+
const fm2 = a3.rawFrontmatter;
|
|
10723
|
+
const evidence = fm2.implementation_evidence;
|
|
10724
10724
|
const repos = evidence?.repositories ?? {};
|
|
10725
10725
|
const repoEvidence = {};
|
|
10726
10726
|
for (const [repoId, data] of Object.entries(repos)) {
|
|
@@ -10738,8 +10738,8 @@ function buildProjectExplanation(dir) {
|
|
|
10738
10738
|
validationStatus: a3.validationStatus || historicalDefault,
|
|
10739
10739
|
releaseStatus: a3.releaseStatus || "not-assessed",
|
|
10740
10740
|
affectedModules: a3.affectedModules,
|
|
10741
|
-
releaseGates: Array.isArray(
|
|
10742
|
-
completionExceptions: Array.isArray(
|
|
10741
|
+
releaseGates: Array.isArray(fm2.release_gates) ? fm2.release_gates : [],
|
|
10742
|
+
completionExceptions: Array.isArray(fm2.completion_exceptions) ? fm2.completion_exceptions : [],
|
|
10743
10743
|
repoEvidence
|
|
10744
10744
|
};
|
|
10745
10745
|
})
|
|
@@ -13025,6 +13025,7 @@ import { parse as parseYaml32 } from "yaml";
|
|
|
13025
13025
|
import { parse as parseYaml52, stringify as stringifyYaml32 } from "yaml";
|
|
13026
13026
|
import { parse as parseYaml42, stringify as stringifyYaml22 } from "yaml";
|
|
13027
13027
|
import matter32 from "gray-matter";
|
|
13028
|
+
import { createHash as createHash3 } from "crypto";
|
|
13028
13029
|
import matter42 from "gray-matter";
|
|
13029
13030
|
import matter52 from "gray-matter";
|
|
13030
13031
|
import matter62 from "gray-matter";
|
|
@@ -21579,6 +21580,18 @@ var DELIVERY_PLANS_DIR2 = "knowledge/delivery/plans";
|
|
|
21579
21580
|
var EPICS_DIR2 = "knowledge/delivery/epics";
|
|
21580
21581
|
var PLAN_STATES2 = ["draft", "in-progress", "completed", "cancelled"];
|
|
21581
21582
|
var EPIC_STATES2 = ["planned", "in-progress", "completed", "cancelled"];
|
|
21583
|
+
var PLAN_TRANSITIONS = {
|
|
21584
|
+
draft: ["in-progress", "cancelled"],
|
|
21585
|
+
"in-progress": ["completed", "cancelled"],
|
|
21586
|
+
completed: [],
|
|
21587
|
+
cancelled: []
|
|
21588
|
+
};
|
|
21589
|
+
var EPIC_TRANSITIONS = {
|
|
21590
|
+
planned: ["in-progress", "cancelled"],
|
|
21591
|
+
"in-progress": ["completed", "cancelled"],
|
|
21592
|
+
completed: [],
|
|
21593
|
+
cancelled: []
|
|
21594
|
+
};
|
|
21582
21595
|
function toPosix32(p) {
|
|
21583
21596
|
return p.replace(/\\/g, "/");
|
|
21584
21597
|
}
|
|
@@ -21859,6 +21872,9 @@ function validatePlanningGraph(dir) {
|
|
|
21859
21872
|
findings.push({ level: "warning", kind: "pending_dependency", subject: e.id, message: `Epic ${e.id} is blocked by unfinished dependency ${d3}.` });
|
|
21860
21873
|
return findings;
|
|
21861
21874
|
}
|
|
21875
|
+
function revisionOf(content) {
|
|
21876
|
+
return createHash3("sha256").update(content, "utf8").digest("hex").slice(0, 16);
|
|
21877
|
+
}
|
|
21862
21878
|
var RESOURCE_ARTIFACT_TYPE2 = "project-resource";
|
|
21863
21879
|
var REDACTED_COORDINATE2 = "\u2022\u2022\u2022\u2022 (hidden \u2014 store as an authentication reference)";
|
|
21864
21880
|
var SECRET_KEY_RE2 = /(pass(word|phrase)?|secret|token|api[_-]?key|\bkey\b|credential|private|auth[_-]?token|access[_-]?key)/i;
|
|
@@ -21963,40 +21979,40 @@ function purposeFromBody3(body) {
|
|
|
21963
21979
|
}
|
|
21964
21980
|
function toDetail2(filePath, relPath, raw) {
|
|
21965
21981
|
const parsed = matter52(raw);
|
|
21966
|
-
const
|
|
21982
|
+
const fm2 = parsed.data ?? {};
|
|
21967
21983
|
const body = parsed.content.trim();
|
|
21968
|
-
const id = str3(
|
|
21984
|
+
const id = str3(fm2.id) ?? "";
|
|
21969
21985
|
return {
|
|
21970
21986
|
id,
|
|
21971
|
-
title: str3(
|
|
21972
|
-
resourceType: str3(
|
|
21973
|
-
provider: str3(
|
|
21974
|
-
environments: strArray4(
|
|
21975
|
-
scope: parseScope2(
|
|
21976
|
-
modules: strArray4(
|
|
21987
|
+
title: str3(fm2.title) ?? id,
|
|
21988
|
+
resourceType: str3(fm2.resource_type),
|
|
21989
|
+
provider: str3(fm2.provider),
|
|
21990
|
+
environments: strArray4(fm2.environments),
|
|
21991
|
+
scope: parseScope2(fm2.scope),
|
|
21992
|
+
modules: strArray4(fm2.modules),
|
|
21977
21993
|
path: relPath,
|
|
21978
|
-
purpose: str3(
|
|
21979
|
-
interfaces: parseInterfaces2(
|
|
21980
|
-
boundaries: parseBoundaries2(
|
|
21994
|
+
purpose: str3(fm2.purpose) ?? purposeFromBody3(body),
|
|
21995
|
+
interfaces: parseInterfaces2(fm2.access_interfaces),
|
|
21996
|
+
boundaries: parseBoundaries2(fm2.access_boundaries),
|
|
21981
21997
|
...(() => {
|
|
21982
|
-
const a3 = parseAuth2(
|
|
21998
|
+
const a3 = parseAuth2(fm2.authentication);
|
|
21983
21999
|
return { authRefs: a3.refs, authMode: a3.mode };
|
|
21984
22000
|
})(),
|
|
21985
|
-
connectionProfiles: parseConnectionProfiles2(
|
|
22001
|
+
connectionProfiles: parseConnectionProfiles2(fm2.connection_profiles),
|
|
21986
22002
|
markdownBody: body
|
|
21987
22003
|
};
|
|
21988
22004
|
}
|
|
21989
22005
|
function getResources2(dir) {
|
|
21990
22006
|
return discoverKnowledge2(dir).filter((a3) => a3.type === RESOURCE_ARTIFACT_TYPE2).map((a3) => {
|
|
21991
|
-
const
|
|
22007
|
+
const fm2 = a3.rawFrontmatter;
|
|
21992
22008
|
return {
|
|
21993
|
-
id: a3.id || str3(
|
|
21994
|
-
title: a3.title || str3(
|
|
21995
|
-
resourceType: str3(
|
|
21996
|
-
provider: str3(
|
|
21997
|
-
environments: strArray4(
|
|
21998
|
-
scope: parseScope2(
|
|
21999
|
-
modules: strArray4(
|
|
22009
|
+
id: a3.id || str3(fm2.id) || a3.title,
|
|
22010
|
+
title: a3.title || str3(fm2.title) || a3.id,
|
|
22011
|
+
resourceType: str3(fm2.resource_type),
|
|
22012
|
+
provider: str3(fm2.provider),
|
|
22013
|
+
environments: strArray4(fm2.environments),
|
|
22014
|
+
scope: parseScope2(fm2.scope),
|
|
22015
|
+
modules: strArray4(fm2.modules),
|
|
22000
22016
|
path: a3.relPath
|
|
22001
22017
|
};
|
|
22002
22018
|
}).sort((x2, y) => x2.id.localeCompare(y.id));
|
|
@@ -22009,8 +22025,8 @@ function getResource2(dir, id) {
|
|
|
22009
22025
|
const raw = readFile2(match.filePath);
|
|
22010
22026
|
return toDetail2(match.filePath, match.relPath, raw);
|
|
22011
22027
|
}
|
|
22012
|
-
function parseWorkItemResources2(
|
|
22013
|
-
const v =
|
|
22028
|
+
function parseWorkItemResources2(fm2) {
|
|
22029
|
+
const v = fm2.resources;
|
|
22014
22030
|
if (!Array.isArray(v)) return [];
|
|
22015
22031
|
const out = [];
|
|
22016
22032
|
for (const raw of v) {
|
|
@@ -22034,8 +22050,8 @@ var EVIDENCE_SIGNALS2 = [
|
|
|
22034
22050
|
function str22(v) {
|
|
22035
22051
|
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
22036
22052
|
}
|
|
22037
|
-
function parseWorkItemDeprecationTargets2(
|
|
22038
|
-
const v =
|
|
22053
|
+
function parseWorkItemDeprecationTargets2(fm2) {
|
|
22054
|
+
const v = fm2.deprecation_targets;
|
|
22039
22055
|
if (!Array.isArray(v)) return [];
|
|
22040
22056
|
const out = [];
|
|
22041
22057
|
for (const raw of v) {
|
|
@@ -22160,13 +22176,13 @@ function purposeFromBody22(body) {
|
|
|
22160
22176
|
}
|
|
22161
22177
|
function getDeprecationPlans2(dir) {
|
|
22162
22178
|
return discoverKnowledge2(dir).filter((a3) => a3.type === DEPRECATION_ARTIFACT_TYPE2).map((a3) => {
|
|
22163
|
-
const
|
|
22164
|
-
const targets = parseTargets2(
|
|
22179
|
+
const fm2 = a3.rawFrontmatter;
|
|
22180
|
+
const targets = parseTargets2(fm2.targets);
|
|
22165
22181
|
return {
|
|
22166
|
-
id: a3.id || str22(
|
|
22167
|
-
title: a3.title || str22(
|
|
22182
|
+
id: a3.id || str22(fm2.id) || a3.title,
|
|
22183
|
+
title: a3.title || str22(fm2.title) || a3.id,
|
|
22168
22184
|
path: a3.relPath,
|
|
22169
|
-
status: str22(
|
|
22185
|
+
status: str22(fm2.status),
|
|
22170
22186
|
targetCount: targets.length,
|
|
22171
22187
|
lifecycleCounts: lifecycleCountsOf2(targets)
|
|
22172
22188
|
};
|
|
@@ -22179,17 +22195,17 @@ function getDeprecationPlan2(dir, id) {
|
|
|
22179
22195
|
if (!match) return null;
|
|
22180
22196
|
const raw = readFile2(match.filePath);
|
|
22181
22197
|
const parsed = matter62(raw);
|
|
22182
|
-
const
|
|
22198
|
+
const fm2 = parsed.data ?? {};
|
|
22183
22199
|
const body = parsed.content.trim();
|
|
22184
|
-
const targets = parseTargets2(
|
|
22200
|
+
const targets = parseTargets2(fm2.targets);
|
|
22185
22201
|
return {
|
|
22186
|
-
id: str22(
|
|
22187
|
-
title: str22(
|
|
22202
|
+
id: str22(fm2.id) ?? match.id,
|
|
22203
|
+
title: str22(fm2.title) ?? match.title,
|
|
22188
22204
|
path: match.relPath,
|
|
22189
|
-
status: str22(
|
|
22205
|
+
status: str22(fm2.status),
|
|
22190
22206
|
targetCount: targets.length,
|
|
22191
22207
|
lifecycleCounts: lifecycleCountsOf2(targets),
|
|
22192
|
-
purpose: str22(
|
|
22208
|
+
purpose: str22(fm2.purpose) ?? purposeFromBody22(body),
|
|
22193
22209
|
targets,
|
|
22194
22210
|
markdownBody: body
|
|
22195
22211
|
};
|
|
@@ -26206,7 +26222,7 @@ var AGENT_PROMPTS2 = [
|
|
|
26206
26222
|
].map((p) => ({ fileName: p.fileName, content: withResponsibilityTrace2(p.fileName, p.content) }));
|
|
26207
26223
|
function withAgentFrontMatter2(fileName, content) {
|
|
26208
26224
|
const name = fileName.replace(/\.md$/, "");
|
|
26209
|
-
const
|
|
26225
|
+
const fm2 = [
|
|
26210
26226
|
"---",
|
|
26211
26227
|
"type: agent",
|
|
26212
26228
|
`name: ${name}`,
|
|
@@ -26215,7 +26231,7 @@ function withAgentFrontMatter2(fileName, content) {
|
|
|
26215
26231
|
"---",
|
|
26216
26232
|
""
|
|
26217
26233
|
].join("\n");
|
|
26218
|
-
return
|
|
26234
|
+
return fm2 + content.replace(/^\s+/, "");
|
|
26219
26235
|
}
|
|
26220
26236
|
var agentReadme2 = {
|
|
26221
26237
|
path: "knowledge/agents/README.md",
|
|
@@ -26978,12 +26994,267 @@ function materializeCandidate(dir, id, candidateId) {
|
|
|
26978
26994
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after materialization.");
|
|
26979
26995
|
return { workItemId: wiId, fileName, initiative: updated };
|
|
26980
26996
|
}
|
|
26997
|
+
function normalizeCriterion(text22) {
|
|
26998
|
+
return text22.trim().toLowerCase().replace(/\s+/g, " ");
|
|
26999
|
+
}
|
|
27000
|
+
var STATUSES = ["passed", "failed", "not-verified", "manual-review-required"];
|
|
27001
|
+
function optStr42(v) {
|
|
27002
|
+
if (typeof v === "string" && v.trim()) return v.trim();
|
|
27003
|
+
if (typeof v === "number") return String(v);
|
|
27004
|
+
return void 0;
|
|
27005
|
+
}
|
|
27006
|
+
function parseEvidenceList(raw) {
|
|
27007
|
+
if (!Array.isArray(raw)) return [];
|
|
27008
|
+
const out = [];
|
|
27009
|
+
for (const entry of raw) {
|
|
27010
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
|
|
27011
|
+
const o3 = entry;
|
|
27012
|
+
const criterion = optStr42(o3.criterion);
|
|
27013
|
+
if (!criterion) continue;
|
|
27014
|
+
const statusRaw = optStr42(o3.status);
|
|
27015
|
+
const status = STATUSES.includes(statusRaw ?? "") ? statusRaw : "not-verified";
|
|
27016
|
+
const method = optStr42(o3.method);
|
|
27017
|
+
out.push({
|
|
27018
|
+
criterion,
|
|
27019
|
+
status,
|
|
27020
|
+
...optStr42(o3.evidence) ? { evidence: optStr42(o3.evidence) } : {},
|
|
27021
|
+
...optStr42(o3.reference) ? { reference: optStr42(o3.reference) } : {},
|
|
27022
|
+
...method === "automated" || method === "manual" ? { method } : {},
|
|
27023
|
+
...optStr42(o3.verified_at) ? { verified_at: optStr42(o3.verified_at) } : {},
|
|
27024
|
+
...optStr42(o3.verified_by) ? { verified_by: optStr42(o3.verified_by) } : {}
|
|
27025
|
+
});
|
|
27026
|
+
}
|
|
27027
|
+
return out;
|
|
27028
|
+
}
|
|
27029
|
+
function parseExceptions(raw) {
|
|
27030
|
+
if (!Array.isArray(raw)) return [];
|
|
27031
|
+
const out = [];
|
|
27032
|
+
for (const entry of raw) {
|
|
27033
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
|
|
27034
|
+
const o3 = entry;
|
|
27035
|
+
const id = optStr42(o3.id);
|
|
27036
|
+
if (!id) continue;
|
|
27037
|
+
const st = optStr42(o3.status);
|
|
27038
|
+
const status = st === "accepted" || st === "rejected" ? st : "proposed";
|
|
27039
|
+
out.push({
|
|
27040
|
+
id,
|
|
27041
|
+
status,
|
|
27042
|
+
...optStr42(o3.criterion) ? { criterion: optStr42(o3.criterion) } : {},
|
|
27043
|
+
...optStr42(o3.reason) ? { reason: optStr42(o3.reason) } : {}
|
|
27044
|
+
});
|
|
27045
|
+
}
|
|
27046
|
+
return out;
|
|
27047
|
+
}
|
|
27048
|
+
function buildOutcomeVerification(declared, evidence, exceptions = []) {
|
|
27049
|
+
const byCriterion = /* @__PURE__ */ new Map();
|
|
27050
|
+
for (const e of evidence) byCriterion.set(normalizeCriterion(e.criterion), e);
|
|
27051
|
+
const declaredKeys = new Set(declared.map(normalizeCriterion));
|
|
27052
|
+
const counts = /* @__PURE__ */ new Map();
|
|
27053
|
+
for (const c3 of declared) counts.set(normalizeCriterion(c3), (counts.get(normalizeCriterion(c3)) ?? 0) + 1);
|
|
27054
|
+
const ambiguousKeys = new Set([...counts.entries()].filter(([, n2]) => n2 > 1).map(([k2]) => k2));
|
|
27055
|
+
const waived = new Set(
|
|
27056
|
+
exceptions.filter((x2) => x2.status === "accepted" && x2.reason?.trim() && x2.criterion && declaredKeys.has(normalizeCriterion(x2.criterion)) && !ambiguousKeys.has(normalizeCriterion(x2.criterion))).map((x2) => normalizeCriterion(x2.criterion))
|
|
27057
|
+
);
|
|
27058
|
+
const criteria = declared.map((criterion) => {
|
|
27059
|
+
const key = normalizeCriterion(criterion);
|
|
27060
|
+
const ambiguous = ambiguousKeys.has(key);
|
|
27061
|
+
const e = ambiguous ? void 0 : byCriterion.get(key);
|
|
27062
|
+
return {
|
|
27063
|
+
criterion,
|
|
27064
|
+
status: e?.status ?? "not-verified",
|
|
27065
|
+
...e?.evidence ? { evidence: e.evidence } : {},
|
|
27066
|
+
...e?.reference ? { reference: e.reference } : {},
|
|
27067
|
+
...e?.method ? { method: e.method } : {},
|
|
27068
|
+
...e?.verified_at ? { verifiedAt: e.verified_at } : {},
|
|
27069
|
+
...e?.verified_by ? { verifiedBy: e.verified_by } : {},
|
|
27070
|
+
...!ambiguous && waived.has(key) ? { waived: true } : {},
|
|
27071
|
+
...ambiguous ? { ambiguous: true } : {}
|
|
27072
|
+
};
|
|
27073
|
+
});
|
|
27074
|
+
const stale = evidence.filter((e) => !declaredKeys.has(normalizeCriterion(e.criterion)));
|
|
27075
|
+
return {
|
|
27076
|
+
total: criteria.length,
|
|
27077
|
+
// An ambiguous criterion is never counted as passed/waived; it counts as unverified until disambiguated.
|
|
27078
|
+
passed: criteria.filter((c3) => !c3.ambiguous && (c3.status === "passed" || c3.waived)).length,
|
|
27079
|
+
failed: criteria.filter((c3) => !c3.ambiguous && c3.status === "failed" && !c3.waived).length,
|
|
27080
|
+
notVerified: criteria.filter((c3) => c3.ambiguous || c3.status === "not-verified" && !c3.waived).length,
|
|
27081
|
+
manualReview: criteria.filter((c3) => !c3.ambiguous && c3.status === "manual-review-required" && !c3.waived).length,
|
|
27082
|
+
ambiguous: criteria.filter((c3) => c3.ambiguous).length,
|
|
27083
|
+
criteria,
|
|
27084
|
+
stale
|
|
27085
|
+
};
|
|
27086
|
+
}
|
|
27087
|
+
function fm(entity, key) {
|
|
27088
|
+
return entity.rawFrontmatter?.[key];
|
|
27089
|
+
}
|
|
27090
|
+
function epicOutcomeVerification(epic) {
|
|
27091
|
+
return buildOutcomeVerification(
|
|
27092
|
+
epic.successCriteria,
|
|
27093
|
+
parseEvidenceList(fm(epic, "outcome_verifications")),
|
|
27094
|
+
parseExceptions(fm(epic, "completion_exceptions"))
|
|
27095
|
+
);
|
|
27096
|
+
}
|
|
27097
|
+
function planOutcomeVerification(plan) {
|
|
27098
|
+
return buildOutcomeVerification(
|
|
27099
|
+
plan.successCriteria,
|
|
27100
|
+
parseEvidenceList(fm(plan, "outcome_verifications")),
|
|
27101
|
+
parseExceptions(fm(plan, "completion_exceptions"))
|
|
27102
|
+
);
|
|
27103
|
+
}
|
|
27104
|
+
function planMilestoneVerifications(plan) {
|
|
27105
|
+
const rawMs = Array.isArray(fm(plan, "milestones")) ? fm(plan, "milestones") : [];
|
|
27106
|
+
return plan.milestones.map((m2) => {
|
|
27107
|
+
const rawEntry = rawMs.find((r2) => r2 && typeof r2 === "object" && optStr42(r2.id)?.toLowerCase() === m2.id.toLowerCase());
|
|
27108
|
+
const evidence = parseEvidenceList(rawEntry?.exit_verifications);
|
|
27109
|
+
return { id: m2.id, title: m2.title, outcome: buildOutcomeVerification(m2.exitCriteria, evidence) };
|
|
27110
|
+
});
|
|
27111
|
+
}
|
|
27112
|
+
function decide(blockers, softBlocks, hasAcceptedExceptions) {
|
|
27113
|
+
if (blockers.length > 0) return "BLOCKED";
|
|
27114
|
+
if (softBlocks.length > 0) return "NEEDS_WORK";
|
|
27115
|
+
return hasAcceptedExceptions ? "READY_WITH_EXCEPTIONS" : "READY_TO_COMPLETE";
|
|
27116
|
+
}
|
|
27117
|
+
function evaluateEpicCompletion(dir, epic) {
|
|
27118
|
+
const prog = computeEpicProgress(dir, epic);
|
|
27119
|
+
const exceptions = parseExceptions(fm(epic, "completion_exceptions"));
|
|
27120
|
+
const outcome = epicOutcomeVerification(epic);
|
|
27121
|
+
const blockers = [];
|
|
27122
|
+
const softBlocks = [];
|
|
27123
|
+
const reasons = [];
|
|
27124
|
+
const warnings = [];
|
|
27125
|
+
for (const d3 of prog.readiness.blockingDependencies) blockers.push(`Blocked by unfinished dependency ${d3}.`);
|
|
27126
|
+
for (const f3 of integrityBlockers(dir, epic.id)) blockers.push(f3);
|
|
27127
|
+
evaluateOutcomeInto(epic.outcome, true, outcome, blockers, softBlocks, reasons, warnings);
|
|
27128
|
+
if (prog.delivery.total > 0 && prog.delivery.completed < prog.delivery.total) {
|
|
27129
|
+
softBlocks.push("committed-work-incomplete");
|
|
27130
|
+
reasons.push(`Committed work is incomplete (${prog.delivery.completed}/${prog.delivery.total} Work Items completed).`);
|
|
27131
|
+
}
|
|
27132
|
+
const readiness = decide(blockers, softBlocks, exceptions.some((x2) => x2.status === "accepted"));
|
|
27133
|
+
pushReadinessReason(readiness, reasons);
|
|
27134
|
+
return {
|
|
27135
|
+
id: epic.id,
|
|
27136
|
+
kind: "epic",
|
|
27137
|
+
status: epic.status,
|
|
27138
|
+
readiness,
|
|
27139
|
+
reasons,
|
|
27140
|
+
blockers,
|
|
27141
|
+
warnings,
|
|
27142
|
+
outcome,
|
|
27143
|
+
delivery: { total: prog.delivery.total, completed: prog.delivery.completed, blocked: prog.delivery.blocked },
|
|
27144
|
+
blockingDependencies: prog.readiness.blockingDependencies,
|
|
27145
|
+
exceptions
|
|
27146
|
+
};
|
|
27147
|
+
}
|
|
27148
|
+
function evaluatePlanCompletion(dir, plan) {
|
|
27149
|
+
const prog = computePlanProgress(dir, plan);
|
|
27150
|
+
const exceptions = parseExceptions(fm(plan, "completion_exceptions"));
|
|
27151
|
+
const outcome = planOutcomeVerification(plan);
|
|
27152
|
+
const milestones = planMilestoneVerifications(plan);
|
|
27153
|
+
const epics = epicsInPlan(dir, plan.id).map((e) => ({ id: e.id, status: e.status }));
|
|
27154
|
+
const blockers = [];
|
|
27155
|
+
const softBlocks = [];
|
|
27156
|
+
const reasons = [];
|
|
27157
|
+
const warnings = [];
|
|
27158
|
+
for (const d3 of prog.readiness.blockingDependencies) blockers.push(`Blocked by unfinished dependency ${d3}.`);
|
|
27159
|
+
for (const f3 of integrityBlockers(dir, plan.id)) blockers.push(f3);
|
|
27160
|
+
evaluateOutcomeInto(plan.objective, false, outcome, blockers, softBlocks, reasons, warnings);
|
|
27161
|
+
if (!plan.objective) warnings.push("No objective is declared for this Plan (optional, but recommended for completion review).");
|
|
27162
|
+
if (prog.delivery.total > 0 && prog.delivery.completed < prog.delivery.total) {
|
|
27163
|
+
softBlocks.push("committed-work-incomplete");
|
|
27164
|
+
reasons.push(`Committed work is incomplete (${prog.delivery.completed}/${prog.delivery.total} Work Items completed).`);
|
|
27165
|
+
}
|
|
27166
|
+
if (epics.length === 0 && prog.delivery.total === 0) {
|
|
27167
|
+
softBlocks.push("no-committed-work");
|
|
27168
|
+
reasons.push("No Epics and no committed Work Items \u2014 the Plan has no verifiable deliverables yet.");
|
|
27169
|
+
}
|
|
27170
|
+
const openEpics = epics.filter((e) => e.status !== "completed" && e.status !== "cancelled");
|
|
27171
|
+
if (openEpics.length > 0) {
|
|
27172
|
+
softBlocks.push("epics-open");
|
|
27173
|
+
reasons.push(`${openEpics.length} Epic(s) not yet completed: ${openEpics.map((e) => e.id).join(", ")}.`);
|
|
27174
|
+
}
|
|
27175
|
+
for (const m2 of milestones) {
|
|
27176
|
+
if (m2.outcome.failed > 0) {
|
|
27177
|
+
blockers.push(`Milestone ${m2.id} has a failed exit criterion.`);
|
|
27178
|
+
}
|
|
27179
|
+
const pending = m2.outcome.notVerified + m2.outcome.manualReview;
|
|
27180
|
+
if (pending > 0) {
|
|
27181
|
+
softBlocks.push(`milestone-${m2.id}-pending`);
|
|
27182
|
+
reasons.push(`Milestone ${m2.id}: ${pending} exit criterion(s) not verified.`);
|
|
27183
|
+
}
|
|
27184
|
+
if (m2.outcome.stale.length > 0) warnings.push(`Milestone ${m2.id} has stale exit-criteria evidence.`);
|
|
27185
|
+
}
|
|
27186
|
+
const readiness = decide(blockers, softBlocks, exceptions.some((x2) => x2.status === "accepted"));
|
|
27187
|
+
pushReadinessReason(readiness, reasons);
|
|
27188
|
+
return {
|
|
27189
|
+
id: plan.id,
|
|
27190
|
+
kind: "plan",
|
|
27191
|
+
status: plan.status,
|
|
27192
|
+
readiness,
|
|
27193
|
+
reasons,
|
|
27194
|
+
blockers,
|
|
27195
|
+
warnings,
|
|
27196
|
+
outcome,
|
|
27197
|
+
delivery: { total: prog.delivery.total, completed: prog.delivery.completed, blocked: prog.delivery.blocked },
|
|
27198
|
+
blockingDependencies: prog.readiness.blockingDependencies,
|
|
27199
|
+
milestones,
|
|
27200
|
+
epics,
|
|
27201
|
+
exceptions
|
|
27202
|
+
};
|
|
27203
|
+
}
|
|
27204
|
+
function integrityBlockers(dir, subjectId) {
|
|
27205
|
+
return validatePlanningGraph(dir).filter((f3) => f3.level === "error" && f3.subject.toLowerCase() === subjectId.toLowerCase()).map((f3) => f3.message);
|
|
27206
|
+
}
|
|
27207
|
+
function evaluateOutcomeInto(declaredOutcome, requireDeclaredOutcome, outcome, blockers, softBlocks, reasons, warnings) {
|
|
27208
|
+
if (requireDeclaredOutcome && !declaredOutcome) {
|
|
27209
|
+
softBlocks.push("no-outcome");
|
|
27210
|
+
reasons.push("No expected outcome is declared.");
|
|
27211
|
+
}
|
|
27212
|
+
if (outcome.total === 0) {
|
|
27213
|
+
softBlocks.push("no-success-criteria");
|
|
27214
|
+
reasons.push("No success criteria to verify \u2014 completion cannot be judged by outcome.");
|
|
27215
|
+
}
|
|
27216
|
+
if (outcome.ambiguous > 0) {
|
|
27217
|
+
softBlocks.push("criteria-ambiguous");
|
|
27218
|
+
reasons.push(`${outcome.ambiguous} success criterion(s) collide in text \u2014 rename to disambiguate before verifying.`);
|
|
27219
|
+
}
|
|
27220
|
+
if (outcome.failed > 0) {
|
|
27221
|
+
softBlocks.push("criteria-failed");
|
|
27222
|
+
reasons.push(`${outcome.failed} success criterion(s) failed verification.`);
|
|
27223
|
+
}
|
|
27224
|
+
const pending = outcome.notVerified + outcome.manualReview - outcome.ambiguous;
|
|
27225
|
+
if (pending > 0) {
|
|
27226
|
+
softBlocks.push("criteria-unverified");
|
|
27227
|
+
reasons.push(`${pending} success criterion(s) not verified (no or insufficient evidence).`);
|
|
27228
|
+
}
|
|
27229
|
+
if (outcome.stale.length > 0) warnings.push(`${outcome.stale.length} evidence record(s) no longer match a declared criterion (stale).`);
|
|
27230
|
+
}
|
|
27231
|
+
function pushReadinessReason(readiness, reasons) {
|
|
27232
|
+
if (readiness === "READY_TO_COMPLETE") reasons.unshift("All success criteria are verified and committed work is complete. Human approval is still required to close.");
|
|
27233
|
+
else if (readiness === "READY_WITH_EXCEPTIONS") reasons.unshift("Remaining gaps are covered by accepted exceptions. Human approval is still required to close.");
|
|
27234
|
+
}
|
|
26981
27235
|
var DeliveryPlanningWriteError = class extends Error {
|
|
26982
27236
|
constructor(message) {
|
|
26983
27237
|
super(message);
|
|
26984
27238
|
this.name = "DeliveryPlanningWriteError";
|
|
26985
27239
|
}
|
|
26986
27240
|
};
|
|
27241
|
+
var PlanningConflictError = class extends Error {
|
|
27242
|
+
constructor(id, expectedRevision, actualRevision) {
|
|
27243
|
+
super(`"${id}" was modified since it was loaded (expected revision ${expectedRevision}, found ${actualRevision}). Reload and try again.`);
|
|
27244
|
+
this.id = id;
|
|
27245
|
+
this.expectedRevision = expectedRevision;
|
|
27246
|
+
this.actualRevision = actualRevision;
|
|
27247
|
+
this.name = "PlanningConflictError";
|
|
27248
|
+
}
|
|
27249
|
+
id;
|
|
27250
|
+
expectedRevision;
|
|
27251
|
+
actualRevision;
|
|
27252
|
+
};
|
|
27253
|
+
function assertFreshRevision(id, currentContent, opts) {
|
|
27254
|
+
if (!opts?.expectedRevision) return;
|
|
27255
|
+
const actual = revisionOf(currentContent);
|
|
27256
|
+
if (actual !== opts.expectedRevision) throw new PlanningConflictError(id, opts.expectedRevision, actual);
|
|
27257
|
+
}
|
|
26987
27258
|
function slugify3(s) {
|
|
26988
27259
|
return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "item";
|
|
26989
27260
|
}
|
|
@@ -27044,6 +27315,163 @@ function associateInitiativeWithEpic(dir, initiativeId, epicId) {
|
|
|
27044
27315
|
if (!getEpic(dir, epicId)) throw new DeliveryPlanningWriteError(`Epic ${epicId} does not exist.`);
|
|
27045
27316
|
return updateInitiative(dir, ini.id, { epic: epicId });
|
|
27046
27317
|
}
|
|
27318
|
+
function today22() {
|
|
27319
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
27320
|
+
}
|
|
27321
|
+
var PlanningNotReadyError = class extends DeliveryPlanningWriteError {
|
|
27322
|
+
constructor(evaluation) {
|
|
27323
|
+
super(`${evaluation.kind === "plan" ? "Plan" : "Epic"} ${evaluation.id} is not ready to complete (${evaluation.readiness}).`);
|
|
27324
|
+
this.evaluation = evaluation;
|
|
27325
|
+
this.name = "PlanningNotReadyError";
|
|
27326
|
+
}
|
|
27327
|
+
evaluation;
|
|
27328
|
+
};
|
|
27329
|
+
function transitionDeliveryPlan(dir, id, to, opts) {
|
|
27330
|
+
const plan = getPlan(dir, id);
|
|
27331
|
+
if (!plan) throw new DeliveryPlanningWriteError(`Plan ${id} does not exist.`);
|
|
27332
|
+
if (!PLAN_STATES2.includes(to)) throw new DeliveryPlanningWriteError(`Invalid plan status: ${to}.`);
|
|
27333
|
+
if (to === "completed") throw new DeliveryPlanningWriteError(`Completing Plan ${id} requires evidence-based review \u2014 use completeDeliveryPlan.`);
|
|
27334
|
+
const raw = readFile2(plan.filePath);
|
|
27335
|
+
assertFreshRevision(plan.id, raw, opts);
|
|
27336
|
+
if (plan.status === to) return plan;
|
|
27337
|
+
if (!PLAN_TRANSITIONS[plan.status].includes(to)) throw new DeliveryPlanningWriteError(`Invalid transition ${plan.status} \u2192 ${to} for Plan ${id}.`);
|
|
27338
|
+
const { data, content } = matter132(raw, {});
|
|
27339
|
+
data.status = to;
|
|
27340
|
+
if (to === "completed") data.completed_at = today22();
|
|
27341
|
+
const newContent = matter132.stringify(content, data);
|
|
27342
|
+
writeFile2(plan.filePath, newContent);
|
|
27343
|
+
const updated = parseDeliveryPlan2(dir, plan.filePath, newContent);
|
|
27344
|
+
if (!updated) throw new DeliveryPlanningWriteError("Failed to parse the Plan after transition.");
|
|
27345
|
+
return updated;
|
|
27346
|
+
}
|
|
27347
|
+
function transitionEpic(dir, id, to, opts) {
|
|
27348
|
+
const epic = getEpic(dir, id);
|
|
27349
|
+
if (!epic) throw new DeliveryPlanningWriteError(`Epic ${id} does not exist.`);
|
|
27350
|
+
if (!EPIC_STATES2.includes(to)) throw new DeliveryPlanningWriteError(`Invalid epic status: ${to}.`);
|
|
27351
|
+
if (to === "completed") throw new DeliveryPlanningWriteError(`Completing Epic ${id} requires evidence-based review \u2014 use completeEpic.`);
|
|
27352
|
+
const raw = readFile2(epic.filePath);
|
|
27353
|
+
assertFreshRevision(epic.id, raw, opts);
|
|
27354
|
+
if (epic.status === to) return epic;
|
|
27355
|
+
if (!EPIC_TRANSITIONS[epic.status].includes(to)) throw new DeliveryPlanningWriteError(`Invalid transition ${epic.status} \u2192 ${to} for Epic ${id}.`);
|
|
27356
|
+
const { data, content } = matter132(raw, {});
|
|
27357
|
+
data.status = to;
|
|
27358
|
+
if (to === "completed") data.completed_at = today22();
|
|
27359
|
+
const newContent = matter132.stringify(content, data);
|
|
27360
|
+
writeFile2(epic.filePath, newContent);
|
|
27361
|
+
const updated = parseEpic2(dir, epic.filePath, newContent);
|
|
27362
|
+
if (!updated) throw new DeliveryPlanningWriteError("Failed to parse the Epic after transition.");
|
|
27363
|
+
return updated;
|
|
27364
|
+
}
|
|
27365
|
+
function assertCriterionDeclared(declared, criterion, kind) {
|
|
27366
|
+
const key = normalizeCriterion(criterion);
|
|
27367
|
+
const matches = declared.filter((c3) => normalizeCriterion(c3) === key).length;
|
|
27368
|
+
if (matches === 0) throw new DeliveryPlanningWriteError(`"${criterion}" is not a declared ${kind} criterion.`);
|
|
27369
|
+
if (matches > 1) throw new DeliveryPlanningWriteError(`"${criterion}" matches more than one declared ${kind} criterion \u2014 rename them to disambiguate before recording evidence.`);
|
|
27370
|
+
}
|
|
27371
|
+
function upsertEvidence(existing, input) {
|
|
27372
|
+
const list22 = Array.isArray(existing) ? existing.filter((e) => e && typeof e === "object" && !Array.isArray(e)) : [];
|
|
27373
|
+
const key = normalizeCriterion(input.criterion);
|
|
27374
|
+
const kept = list22.filter((e) => normalizeCriterion(String(e.criterion ?? "")) !== key);
|
|
27375
|
+
const obj = { criterion: input.criterion.trim(), status: input.status };
|
|
27376
|
+
if (input.evidence?.trim()) obj.evidence = input.evidence.trim();
|
|
27377
|
+
if (input.reference?.trim()) obj.reference = input.reference.trim();
|
|
27378
|
+
if (input.method) obj.method = input.method;
|
|
27379
|
+
obj.verified_at = today22();
|
|
27380
|
+
if (input.verifiedBy?.trim()) obj.verified_by = input.verifiedBy.trim();
|
|
27381
|
+
return [...kept, obj];
|
|
27382
|
+
}
|
|
27383
|
+
function recordEpicOutcomeVerification(dir, id, input, opts) {
|
|
27384
|
+
const epic = getEpic(dir, id);
|
|
27385
|
+
if (!epic) throw new DeliveryPlanningWriteError(`Epic ${id} does not exist.`);
|
|
27386
|
+
assertCriterionDeclared(epic.successCriteria, input.criterion, "success");
|
|
27387
|
+
const raw = readFile2(epic.filePath);
|
|
27388
|
+
assertFreshRevision(epic.id, raw, opts);
|
|
27389
|
+
const { data, content } = matter132(raw, {});
|
|
27390
|
+
data.outcome_verifications = upsertEvidence(data.outcome_verifications, input);
|
|
27391
|
+
const newContent = matter132.stringify(content, data);
|
|
27392
|
+
writeFile2(epic.filePath, newContent);
|
|
27393
|
+
const updated = parseEpic2(dir, epic.filePath, newContent);
|
|
27394
|
+
if (!updated) throw new DeliveryPlanningWriteError("Failed to parse the Epic after recording evidence.");
|
|
27395
|
+
return updated;
|
|
27396
|
+
}
|
|
27397
|
+
function recordPlanOutcomeVerification(dir, id, input, opts) {
|
|
27398
|
+
const plan = getPlan(dir, id);
|
|
27399
|
+
if (!plan) throw new DeliveryPlanningWriteError(`Plan ${id} does not exist.`);
|
|
27400
|
+
assertCriterionDeclared(plan.successCriteria, input.criterion, "success");
|
|
27401
|
+
const raw = readFile2(plan.filePath);
|
|
27402
|
+
assertFreshRevision(plan.id, raw, opts);
|
|
27403
|
+
const { data, content } = matter132(raw, {});
|
|
27404
|
+
data.outcome_verifications = upsertEvidence(data.outcome_verifications, input);
|
|
27405
|
+
const newContent = matter132.stringify(content, data);
|
|
27406
|
+
writeFile2(plan.filePath, newContent);
|
|
27407
|
+
const updated = parseDeliveryPlan2(dir, plan.filePath, newContent);
|
|
27408
|
+
if (!updated) throw new DeliveryPlanningWriteError("Failed to parse the Plan after recording evidence.");
|
|
27409
|
+
return updated;
|
|
27410
|
+
}
|
|
27411
|
+
function recordMilestoneExitVerification(dir, planId, milestoneId, input, opts) {
|
|
27412
|
+
const plan = getPlan(dir, planId);
|
|
27413
|
+
if (!plan) throw new DeliveryPlanningWriteError(`Plan ${planId} does not exist.`);
|
|
27414
|
+
const milestone = plan.milestones.find((m2) => m2.id.toLowerCase() === milestoneId.toLowerCase());
|
|
27415
|
+
if (!milestone) throw new DeliveryPlanningWriteError(`Milestone ${milestoneId} is not defined in ${planId}.`);
|
|
27416
|
+
assertCriterionDeclared(milestone.exitCriteria, input.criterion, "exit");
|
|
27417
|
+
const raw = readFile2(plan.filePath);
|
|
27418
|
+
assertFreshRevision(plan.id, raw, opts);
|
|
27419
|
+
const { data, content } = matter132(raw, {});
|
|
27420
|
+
const rawMs = Array.isArray(data.milestones) ? data.milestones : [];
|
|
27421
|
+
const entry = rawMs.find((m2) => m2 && typeof m2 === "object" && String(m2.id ?? "").toLowerCase() === milestoneId.toLowerCase());
|
|
27422
|
+
if (!entry) throw new DeliveryPlanningWriteError(`Milestone ${milestoneId} entry not found in ${planId} frontmatter.`);
|
|
27423
|
+
entry.exit_verifications = upsertEvidence(entry.exit_verifications, input);
|
|
27424
|
+
data.milestones = rawMs;
|
|
27425
|
+
const newContent = matter132.stringify(content, data);
|
|
27426
|
+
writeFile2(plan.filePath, newContent);
|
|
27427
|
+
const updated = parseDeliveryPlan2(dir, plan.filePath, newContent);
|
|
27428
|
+
if (!updated) throw new DeliveryPlanningWriteError("Failed to parse the Plan after recording milestone evidence.");
|
|
27429
|
+
return updated;
|
|
27430
|
+
}
|
|
27431
|
+
function completionDecisionRecord(evaluation, input) {
|
|
27432
|
+
const rec = { readiness: evaluation.readiness, decided_at: today22(), reasons: evaluation.reasons };
|
|
27433
|
+
if (input.decidedBy?.trim()) rec.decided_by = input.decidedBy.trim();
|
|
27434
|
+
if (input.note?.trim()) rec.note = input.note.trim();
|
|
27435
|
+
const acceptedExceptions = evaluation.exceptions.filter((x2) => x2.status === "accepted");
|
|
27436
|
+
if (acceptedExceptions.length > 0) rec.exceptions = acceptedExceptions;
|
|
27437
|
+
return rec;
|
|
27438
|
+
}
|
|
27439
|
+
function completeEpic(dir, id, input = {}, opts) {
|
|
27440
|
+
const epic = getEpic(dir, id);
|
|
27441
|
+
if (!epic) throw new DeliveryPlanningWriteError(`Epic ${id} does not exist.`);
|
|
27442
|
+
const evaluation = evaluateEpicCompletion(dir, epic);
|
|
27443
|
+
if (evaluation.readiness !== "READY_TO_COMPLETE" && evaluation.readiness !== "READY_WITH_EXCEPTIONS") throw new PlanningNotReadyError(evaluation);
|
|
27444
|
+
if (!EPIC_TRANSITIONS[epic.status].includes("completed")) throw new DeliveryPlanningWriteError(`Epic ${id} cannot move from ${epic.status} to completed.`);
|
|
27445
|
+
const raw = readFile2(epic.filePath);
|
|
27446
|
+
assertFreshRevision(epic.id, raw, opts);
|
|
27447
|
+
const { data, content } = matter132(raw, {});
|
|
27448
|
+
data.status = "completed";
|
|
27449
|
+
data.completed_at = today22();
|
|
27450
|
+
data.completion_decision = completionDecisionRecord(evaluation, input);
|
|
27451
|
+
const newContent = matter132.stringify(content, data);
|
|
27452
|
+
writeFile2(epic.filePath, newContent);
|
|
27453
|
+
const updated = parseEpic2(dir, epic.filePath, newContent);
|
|
27454
|
+
if (!updated) throw new DeliveryPlanningWriteError("Failed to parse the Epic after completion.");
|
|
27455
|
+
return updated;
|
|
27456
|
+
}
|
|
27457
|
+
function completeDeliveryPlan(dir, id, input = {}, opts) {
|
|
27458
|
+
const plan = getPlan(dir, id);
|
|
27459
|
+
if (!plan) throw new DeliveryPlanningWriteError(`Plan ${id} does not exist.`);
|
|
27460
|
+
const evaluation = evaluatePlanCompletion(dir, plan);
|
|
27461
|
+
if (evaluation.readiness !== "READY_TO_COMPLETE" && evaluation.readiness !== "READY_WITH_EXCEPTIONS") throw new PlanningNotReadyError(evaluation);
|
|
27462
|
+
if (!PLAN_TRANSITIONS[plan.status].includes("completed")) throw new DeliveryPlanningWriteError(`Plan ${id} cannot move from ${plan.status} to completed.`);
|
|
27463
|
+
const raw = readFile2(plan.filePath);
|
|
27464
|
+
assertFreshRevision(plan.id, raw, opts);
|
|
27465
|
+
const { data, content } = matter132(raw, {});
|
|
27466
|
+
data.status = "completed";
|
|
27467
|
+
data.completed_at = today22();
|
|
27468
|
+
data.completion_decision = completionDecisionRecord(evaluation, input);
|
|
27469
|
+
const newContent = matter132.stringify(content, data);
|
|
27470
|
+
writeFile2(plan.filePath, newContent);
|
|
27471
|
+
const updated = parseDeliveryPlan2(dir, plan.filePath, newContent);
|
|
27472
|
+
if (!updated) throw new DeliveryPlanningWriteError("Failed to parse the Plan after completion.");
|
|
27473
|
+
return updated;
|
|
27474
|
+
}
|
|
27047
27475
|
var USER_FACING_KEYWORDS_EN = [
|
|
27048
27476
|
"user",
|
|
27049
27477
|
"visitor",
|
|
@@ -27356,7 +27784,7 @@ function getWorkItem(dir, workItemId) {
|
|
|
27356
27784
|
const base = toListItem(match);
|
|
27357
27785
|
const body = readBody(match.filePath);
|
|
27358
27786
|
const sections = splitSections(body);
|
|
27359
|
-
const
|
|
27787
|
+
const fm2 = match.rawFrontmatter;
|
|
27360
27788
|
const knowledge = discoverKnowledge2(dir).filter((a3) => !a3.isWorkItem);
|
|
27361
27789
|
const knowledgeById = new Map(knowledge.filter((k2) => k2.id).map((k2) => [k2.id, k2]));
|
|
27362
27790
|
const detail = {
|
|
@@ -27373,21 +27801,21 @@ function getWorkItem(dir, workItemId) {
|
|
|
27373
27801
|
moduleCoverage: normalizeCoverage(match.moduleCoverage),
|
|
27374
27802
|
impactAnalysis: normalizeImpact(match.impactAnalysis),
|
|
27375
27803
|
acceptanceCriteria: parseAcceptanceCriteria(sections),
|
|
27376
|
-
implementationEvidence: parseEvidence(
|
|
27377
|
-
releaseGates: parseReleaseGates(
|
|
27378
|
-
completionExceptions:
|
|
27804
|
+
implementationEvidence: parseEvidence(fm2),
|
|
27805
|
+
releaseGates: parseReleaseGates(fm2),
|
|
27806
|
+
completionExceptions: parseExceptions2(fm2),
|
|
27379
27807
|
decisions: parseDecisions(match.decisions, knowledgeById),
|
|
27380
|
-
relatedKnowledge: parseRelatedKnowledge(
|
|
27381
|
-
source: parseWorkItemSource2(
|
|
27382
|
-
originalSnapshot: parseOriginalSnapshot(
|
|
27808
|
+
relatedKnowledge: parseRelatedKnowledge(fm2, knowledgeById),
|
|
27809
|
+
source: parseWorkItemSource2(fm2),
|
|
27810
|
+
originalSnapshot: parseOriginalSnapshot(fm2),
|
|
27383
27811
|
path: match.relPath,
|
|
27384
27812
|
// Faithful source of truth: the artifact body already read above, not reconstructed (WI-025).
|
|
27385
27813
|
markdownBody: body.trim(),
|
|
27386
27814
|
// External systems this WI relates to (WI-030), separate from affectedModules (code).
|
|
27387
|
-
resources: parseWorkItemResources2(
|
|
27815
|
+
resources: parseWorkItemResources2(fm2),
|
|
27388
27816
|
// Deprecation Targets this WI retires (WI-050), referenced not copied.
|
|
27389
|
-
deprecationTargets: parseWorkItemDeprecationTargets2(
|
|
27390
|
-
...parseSystemImpact(dir,
|
|
27817
|
+
deprecationTargets: parseWorkItemDeprecationTargets2(fm2),
|
|
27818
|
+
...parseSystemImpact(dir, fm2)
|
|
27391
27819
|
};
|
|
27392
27820
|
return { ...detail, refinement: computeRefinementStatus(detail) };
|
|
27393
27821
|
}
|
|
@@ -27410,14 +27838,14 @@ function parseExplain(o3) {
|
|
|
27410
27838
|
function emptyExplain() {
|
|
27411
27839
|
return { reason: null, graphReason: null, evidenceRefs: [], evidenceSummary: null };
|
|
27412
27840
|
}
|
|
27413
|
-
function parseSystemImpact(dir,
|
|
27841
|
+
function parseSystemImpact(dir, fm2) {
|
|
27414
27842
|
const topology = loadSystemTopology(dir);
|
|
27415
27843
|
const byId = new Map(topology.entities.map((e) => [e.id, e]));
|
|
27416
27844
|
const resolve = (id) => {
|
|
27417
27845
|
const e = byId.get(id);
|
|
27418
27846
|
return { id, nodeId: `sys:${id}`, label: e?.label ?? id, kind: e?.kind ?? "unknown", moduleId: e?.moduleId ?? null };
|
|
27419
27847
|
};
|
|
27420
|
-
const affected = Array.isArray(
|
|
27848
|
+
const affected = Array.isArray(fm2.affected_system_entities) ? fm2.affected_system_entities.map((raw) => {
|
|
27421
27849
|
if (typeof raw === "string") return raw ? { ...resolve(raw), ...emptyExplain() } : null;
|
|
27422
27850
|
if (raw && typeof raw === "object") {
|
|
27423
27851
|
const o3 = raw;
|
|
@@ -27426,13 +27854,13 @@ function parseSystemImpact(dir, fm) {
|
|
|
27426
27854
|
}
|
|
27427
27855
|
return null;
|
|
27428
27856
|
}).filter((e) => e != null) : [];
|
|
27429
|
-
const reviewed = Array.isArray(
|
|
27430
|
-
const graphRevision = typeof
|
|
27857
|
+
const reviewed = Array.isArray(fm2.reviewed_system_entities) ? fm2.reviewed_system_entities.filter((r2) => Boolean(r2) && typeof r2 === "object").map((r2) => ({ ...resolve(String(r2.id ?? "")), ...parseExplain(r2), status: String(r2.status ?? "unknown") })).filter((r2) => r2.id) : [];
|
|
27858
|
+
const graphRevision = typeof fm2.graph_revision === "string" && fm2.graph_revision.trim() ? fm2.graph_revision.trim() : null;
|
|
27431
27859
|
const graphCoverage = topology.entities.length === 0 ? "unavailable" : topology.relationships.length > 0 ? "available" : "partial";
|
|
27432
27860
|
return { affectedSystemEntities: affected, reviewedSystemEntities: reviewed, graphRevision, graphCoverage };
|
|
27433
27861
|
}
|
|
27434
|
-
function parseOriginalSnapshot(
|
|
27435
|
-
const raw =
|
|
27862
|
+
function parseOriginalSnapshot(fm2) {
|
|
27863
|
+
const raw = fm2.original_snapshot;
|
|
27436
27864
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
27437
27865
|
const o3 = raw;
|
|
27438
27866
|
const title = typeof o3.title === "string" ? o3.title : void 0;
|
|
@@ -27535,12 +27963,12 @@ function normalizeImpact(ia) {
|
|
|
27535
27963
|
...v.question ? { question: v.question } : {}
|
|
27536
27964
|
}));
|
|
27537
27965
|
}
|
|
27538
|
-
function
|
|
27966
|
+
function optStr52(v) {
|
|
27539
27967
|
if (typeof v === "string" && v.trim()) return v.trim();
|
|
27540
27968
|
return void 0;
|
|
27541
27969
|
}
|
|
27542
|
-
function parseEvidence(
|
|
27543
|
-
const evidence =
|
|
27970
|
+
function parseEvidence(fm2) {
|
|
27971
|
+
const evidence = fm2.implementation_evidence;
|
|
27544
27972
|
const repos = evidence?.repositories;
|
|
27545
27973
|
if (!repos || typeof repos !== "object" || Array.isArray(repos)) return [];
|
|
27546
27974
|
const out = [];
|
|
@@ -27550,13 +27978,13 @@ function parseEvidence(fm) {
|
|
|
27550
27978
|
const validations = Array.isArray(d3.validations) ? d3.validations.map((v) => ({
|
|
27551
27979
|
command: String(v.command ?? ""),
|
|
27552
27980
|
status: String(v.status ?? "unknown"),
|
|
27553
|
-
...
|
|
27981
|
+
...optStr52(v.reason) ? { reason: optStr52(v.reason) } : {}
|
|
27554
27982
|
})) : [];
|
|
27555
27983
|
const migrations = Array.isArray(d3.migrations) ? d3.migrations.map((m2) => ({
|
|
27556
27984
|
id: String(m2.id ?? ""),
|
|
27557
27985
|
environment: String(m2.environment ?? ""),
|
|
27558
27986
|
status: String(m2.status ?? "unknown"),
|
|
27559
|
-
...
|
|
27987
|
+
...optStr52(m2.reason) ? { reason: optStr52(m2.reason) } : {}
|
|
27560
27988
|
})) : [];
|
|
27561
27989
|
out.push({
|
|
27562
27990
|
module,
|
|
@@ -27569,23 +27997,23 @@ function parseEvidence(fm) {
|
|
|
27569
27997
|
}
|
|
27570
27998
|
return out;
|
|
27571
27999
|
}
|
|
27572
|
-
function parseReleaseGates(
|
|
27573
|
-
if (!Array.isArray(
|
|
27574
|
-
return
|
|
28000
|
+
function parseReleaseGates(fm2) {
|
|
28001
|
+
if (!Array.isArray(fm2.release_gates)) return [];
|
|
28002
|
+
return fm2.release_gates.filter((g) => g && typeof g === "object").map((g) => ({
|
|
27575
28003
|
id: String(g.id ?? ""),
|
|
27576
28004
|
status: String(g.status ?? "pending"),
|
|
27577
|
-
...
|
|
27578
|
-
...
|
|
28005
|
+
...optStr52(g.reason) ? { reason: optStr52(g.reason) } : {},
|
|
28006
|
+
...optStr52(g.required_for) ? { requiredFor: optStr52(g.required_for) } : {}
|
|
27579
28007
|
}));
|
|
27580
28008
|
}
|
|
27581
|
-
function
|
|
27582
|
-
if (!Array.isArray(
|
|
27583
|
-
return
|
|
28009
|
+
function parseExceptions2(fm2) {
|
|
28010
|
+
if (!Array.isArray(fm2.completion_exceptions)) return [];
|
|
28011
|
+
return fm2.completion_exceptions.filter((e) => e && typeof e === "object").map((e) => ({
|
|
27584
28012
|
id: String(e.id ?? ""),
|
|
27585
28013
|
status: String(e.status ?? "proposed"),
|
|
27586
|
-
...
|
|
27587
|
-
...
|
|
27588
|
-
...
|
|
28014
|
+
...optStr52(e.reason) ? { reason: optStr52(e.reason) } : {},
|
|
28015
|
+
...optStr52(e.category) ? { category: optStr52(e.category) } : {},
|
|
28016
|
+
...optStr52(e.impact) ? { impact: optStr52(e.impact) } : {}
|
|
27589
28017
|
}));
|
|
27590
28018
|
}
|
|
27591
28019
|
function parseDecisions(decisions, knowledgeById) {
|
|
@@ -27598,10 +28026,10 @@ function parseDecisions(decisions, knowledgeById) {
|
|
|
27598
28026
|
};
|
|
27599
28027
|
});
|
|
27600
28028
|
}
|
|
27601
|
-
function parseRelatedKnowledge(
|
|
28029
|
+
function parseRelatedKnowledge(fm2, knowledgeById) {
|
|
27602
28030
|
const refs = /* @__PURE__ */ new Set();
|
|
27603
28031
|
for (const field of ["related_knowledge", "knowledge", "related"]) {
|
|
27604
|
-
const v =
|
|
28032
|
+
const v = fm2[field];
|
|
27605
28033
|
if (Array.isArray(v)) {
|
|
27606
28034
|
for (const r2 of v) if (typeof r2 === "string" && r2.trim()) refs.add(r2.trim());
|
|
27607
28035
|
}
|
|
@@ -27769,7 +28197,7 @@ function createWorkItem(dir, opts) {
|
|
|
27769
28197
|
if (!type) throw new WorkItemWriteError("INVALID_INPUT", `Unknown Work Item type "${opts.type}".`);
|
|
27770
28198
|
const id = nextWorkItemId2(dir);
|
|
27771
28199
|
const title = intent.split(/\r?\n/)[0].trim().slice(0, 120);
|
|
27772
|
-
const
|
|
28200
|
+
const today3 = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
27773
28201
|
const summary = opts.summary?.trim() || intent;
|
|
27774
28202
|
const source22 = opts.source ? { ...opts.source, inferred: false } : { type: "manual", inferred: false };
|
|
27775
28203
|
const data = {
|
|
@@ -27778,7 +28206,7 @@ function createWorkItem(dir, opts) {
|
|
|
27778
28206
|
title,
|
|
27779
28207
|
status: "draft",
|
|
27780
28208
|
work_type: type,
|
|
27781
|
-
created_at:
|
|
28209
|
+
created_at: today3,
|
|
27782
28210
|
source: source22,
|
|
27783
28211
|
generated_by: "kaddo-admin",
|
|
27784
28212
|
affected_modules: [],
|
|
@@ -28469,11 +28897,11 @@ function verifyWorkItem(dir, workItemId, input) {
|
|
|
28469
28897
|
const artifacts = discoverWorkItems2(dir);
|
|
28470
28898
|
const match = artifacts.find((a3) => (a3.id || a3.title) === workItemId);
|
|
28471
28899
|
const lifecycle = lifecycleStateOf2({ status: match.status, filePath: match.filePath });
|
|
28472
|
-
const
|
|
28900
|
+
const fm2 = match.rawFrontmatter;
|
|
28473
28901
|
const acVerifications = mapACVerifications(input.acVerifications);
|
|
28474
28902
|
const acSummary = computeACSummary(acVerifications);
|
|
28475
|
-
const releaseGates = Array.isArray(
|
|
28476
|
-
const completionExceptions = Array.isArray(
|
|
28903
|
+
const releaseGates = Array.isArray(fm2.release_gates) ? fm2.release_gates : [];
|
|
28904
|
+
const completionExceptions = Array.isArray(fm2.completion_exceptions) ? fm2.completion_exceptions : [];
|
|
28477
28905
|
const plannedVsActual = computePlannedVsActual(match.affectedModules, input.repos);
|
|
28478
28906
|
const registeredModuleIds = loadMappedModules2(dir).map((m2) => m2.id);
|
|
28479
28907
|
const modifiedRepoIds = input.repos.map((r2) => r2.repoId);
|
|
@@ -28484,7 +28912,7 @@ function verifyWorkItem(dir, workItemId, input) {
|
|
|
28484
28912
|
validationStatus: match.validationStatus || "not-started",
|
|
28485
28913
|
releaseStatus: match.releaseStatus || "not-assessed",
|
|
28486
28914
|
affectedModules: match.affectedModules,
|
|
28487
|
-
rawFrontmatter:
|
|
28915
|
+
rawFrontmatter: fm2,
|
|
28488
28916
|
registeredModuleIds,
|
|
28489
28917
|
modifiedRepoIds
|
|
28490
28918
|
});
|
|
@@ -31264,10 +31692,10 @@ function modulesDiscoverTool(root, args) {
|
|
|
31264
31692
|
return ok2({ discovered, applied: false });
|
|
31265
31693
|
}
|
|
31266
31694
|
function renderCapsuleMd(data) {
|
|
31267
|
-
const
|
|
31695
|
+
const fm2 = ["---", `type: knowledge-capsule`, `system: ${data.system}`, `version: ${data.version}`, `updated_at: ${data.updated_at}`, "---", ""].join("\n");
|
|
31268
31696
|
const list3 = (items) => items.length === 0 ? "_To be completed._" : items.map((i2) => `- ${i2}`).join("\n");
|
|
31269
31697
|
return [
|
|
31270
|
-
|
|
31698
|
+
fm2,
|
|
31271
31699
|
`# ${data.system} \u2014 Knowledge Capsule
|
|
31272
31700
|
`,
|
|
31273
31701
|
`## Purpose
|
|
@@ -33867,7 +34295,7 @@ function renderUnderstand(plan) {
|
|
|
33867
34295
|
}
|
|
33868
34296
|
|
|
33869
34297
|
// ../cli/src/core/knowledge-consolidation.ts
|
|
33870
|
-
import { createHash as
|
|
34298
|
+
import { createHash as createHash4 } from "crypto";
|
|
33871
34299
|
import matter31 from "gray-matter";
|
|
33872
34300
|
var KNOWLEDGE7 = "knowledge";
|
|
33873
34301
|
var CONSOLIDATION_CATEGORIES = [
|
|
@@ -33887,7 +34315,7 @@ function relFromDir(dir, filePath) {
|
|
|
33887
34315
|
return f3.startsWith(d3 + "/") ? f3.slice(d3.length + 1) : f3;
|
|
33888
34316
|
}
|
|
33889
34317
|
function sha256(content) {
|
|
33890
|
-
return
|
|
34318
|
+
return createHash4("sha256").update(content, "utf-8").digest("hex");
|
|
33891
34319
|
}
|
|
33892
34320
|
var SHINGLE_SIZE = 6;
|
|
33893
34321
|
var MIN_SHARED_RUN = 6;
|
|
@@ -34739,6 +35167,14 @@ function completeInitiativeTool(root, args) {
|
|
|
34739
35167
|
}
|
|
34740
35168
|
|
|
34741
35169
|
// src/planning.ts
|
|
35170
|
+
var EVIDENCE_STATUSES = ["passed", "failed", "not-verified", "manual-review-required"];
|
|
35171
|
+
function toEvidence(a3) {
|
|
35172
|
+
if (!a3.criterion?.trim()) return "criterion is required.";
|
|
35173
|
+
const status = a3.status ?? "passed";
|
|
35174
|
+
if (!EVIDENCE_STATUSES.includes(status)) return `status must be one of: ${EVIDENCE_STATUSES.join(", ")}.`;
|
|
35175
|
+
const method = a3.method === "automated" || a3.method === "manual" ? a3.method : void 0;
|
|
35176
|
+
return { criterion: a3.criterion.trim(), status, evidence: a3.evidence, reference: a3.reference, method, verifiedBy: a3.verifiedBy };
|
|
35177
|
+
}
|
|
34742
35178
|
var ok6 = (data) => ({ ok: true, data });
|
|
34743
35179
|
var fail5 = (message) => ({ ok: false, message });
|
|
34744
35180
|
function previewOrApply2(confirm2, action, effect, apply) {
|
|
@@ -34835,6 +35271,82 @@ function associateInitiativeTool(root, args) {
|
|
|
34835
35271
|
() => associateInitiativeWithEpic(root, args.initiative, args.epic)
|
|
34836
35272
|
);
|
|
34837
35273
|
}
|
|
35274
|
+
function verifyPlanTool(root, id) {
|
|
35275
|
+
const plan = getPlan(root, id);
|
|
35276
|
+
if (!plan) return fail5(`Plan ${id} not found.`);
|
|
35277
|
+
return ok6(evaluatePlanCompletion(root, plan));
|
|
35278
|
+
}
|
|
35279
|
+
function verifyEpicTool(root, id) {
|
|
35280
|
+
const epic = getEpic(root, id);
|
|
35281
|
+
if (!epic) return fail5(`Epic ${id} not found.`);
|
|
35282
|
+
return ok6(evaluateEpicCompletion(root, epic));
|
|
35283
|
+
}
|
|
35284
|
+
function recordPlanEvidenceTool(root, args) {
|
|
35285
|
+
const input = toEvidence(args);
|
|
35286
|
+
if (typeof input === "string") return fail5(input);
|
|
35287
|
+
const target = args.milestone ? `milestone ${args.milestone}` : "Plan outcome";
|
|
35288
|
+
return previewOrApply2(
|
|
35289
|
+
args.confirm,
|
|
35290
|
+
"record-plan-evidence",
|
|
35291
|
+
`Evidence [${input.status}] for ${target} criterion "${input.criterion}" on ${args.id} will be recorded.`,
|
|
35292
|
+
() => args.milestone ? recordMilestoneExitVerification(root, args.id, args.milestone, input) : recordPlanOutcomeVerification(root, args.id, input)
|
|
35293
|
+
);
|
|
35294
|
+
}
|
|
35295
|
+
function recordEpicEvidenceTool(root, args) {
|
|
35296
|
+
const input = toEvidence(args);
|
|
35297
|
+
if (typeof input === "string") return fail5(input);
|
|
35298
|
+
return previewOrApply2(
|
|
35299
|
+
args.confirm,
|
|
35300
|
+
"record-epic-evidence",
|
|
35301
|
+
`Evidence [${input.status}] for Epic outcome criterion "${input.criterion}" on ${args.id} will be recorded.`,
|
|
35302
|
+
() => recordEpicOutcomeVerification(root, args.id, input)
|
|
35303
|
+
);
|
|
35304
|
+
}
|
|
35305
|
+
function transitionPlanTool(root, args) {
|
|
35306
|
+
if (args.to === "completed") return fail5("Use complete_plan for an evidence-based completion.");
|
|
35307
|
+
return previewOrApply2(
|
|
35308
|
+
args.confirm,
|
|
35309
|
+
"transition-plan",
|
|
35310
|
+
`Plan ${args.id} will transition to ${args.to}.`,
|
|
35311
|
+
() => transitionDeliveryPlan(root, args.id, args.to)
|
|
35312
|
+
);
|
|
35313
|
+
}
|
|
35314
|
+
function transitionEpicTool(root, args) {
|
|
35315
|
+
if (args.to === "completed") return fail5("Use complete_epic for an evidence-based completion.");
|
|
35316
|
+
return previewOrApply2(
|
|
35317
|
+
args.confirm,
|
|
35318
|
+
"transition-epic",
|
|
35319
|
+
`Epic ${args.id} will transition to ${args.to}.`,
|
|
35320
|
+
() => transitionEpic(root, args.id, args.to)
|
|
35321
|
+
);
|
|
35322
|
+
}
|
|
35323
|
+
var APPROVAL_NOTE = "confirm=true is a confirmation step, not by itself proof of human approval \u2014 only complete with genuine human authorization.";
|
|
35324
|
+
function completePlanTool(root, args) {
|
|
35325
|
+
const plan = getPlan(root, args.id);
|
|
35326
|
+
if (!plan) return fail5(`Plan ${args.id} not found.`);
|
|
35327
|
+
const evaluation = evaluatePlanCompletion(root, plan);
|
|
35328
|
+
const ready = evaluation.readiness === "READY_TO_COMPLETE" || evaluation.readiness === "READY_WITH_EXCEPTIONS";
|
|
35329
|
+
if (!args.confirm) return ok6({ action: "complete-plan", readiness: evaluation.readiness, evaluation, note: APPROVAL_NOTE, instruction: ready ? "Review the evaluation; call again with confirm=true to record the human completion decision." : `Not ready (${evaluation.readiness}). Resolve the blockers first.` });
|
|
35330
|
+
try {
|
|
35331
|
+
return ok6({ applied: true, result: completeDeliveryPlan(root, args.id, { decidedBy: args.decidedBy }) });
|
|
35332
|
+
} catch (err) {
|
|
35333
|
+
if (err instanceof PlanningNotReadyError || err instanceof DeliveryPlanningWriteError) return fail5(err.message);
|
|
35334
|
+
throw err;
|
|
35335
|
+
}
|
|
35336
|
+
}
|
|
35337
|
+
function completeEpicTool(root, args) {
|
|
35338
|
+
const epic = getEpic(root, args.id);
|
|
35339
|
+
if (!epic) return fail5(`Epic ${args.id} not found.`);
|
|
35340
|
+
const evaluation = evaluateEpicCompletion(root, epic);
|
|
35341
|
+
const ready = evaluation.readiness === "READY_TO_COMPLETE" || evaluation.readiness === "READY_WITH_EXCEPTIONS";
|
|
35342
|
+
if (!args.confirm) return ok6({ action: "complete-epic", readiness: evaluation.readiness, evaluation, note: APPROVAL_NOTE, instruction: ready ? "Review the evaluation; call again with confirm=true to record the human completion decision." : `Not ready (${evaluation.readiness}). Resolve the blockers first.` });
|
|
35343
|
+
try {
|
|
35344
|
+
return ok6({ applied: true, result: completeEpic(root, args.id, { decidedBy: args.decidedBy }) });
|
|
35345
|
+
} catch (err) {
|
|
35346
|
+
if (err instanceof PlanningNotReadyError || err instanceof DeliveryPlanningWriteError) return fail5(err.message);
|
|
35347
|
+
throw err;
|
|
35348
|
+
}
|
|
35349
|
+
}
|
|
34838
35350
|
|
|
34839
35351
|
// src/collaboration.ts
|
|
34840
35352
|
async function getWorkItemCollaborationTool(root, id) {
|
|
@@ -35487,6 +35999,47 @@ function createServer(root) {
|
|
|
35487
35999
|
{ title: "Associate initiative with epic", description: `Associate an existing Initiative with an Epic (validated). ${PLANNING_WRITE_NOTE}`, inputSchema: { epic: z4.string(), initiative: z4.string(), confirm: z4.boolean().optional() } },
|
|
35488
36000
|
async (args) => toolText(guarded(root, () => associateInitiativeTool(root, args)))
|
|
35489
36001
|
);
|
|
36002
|
+
const EVIDENCE_IN = { id: z4.string(), criterion: z4.string(), status: z4.string().optional(), evidence: z4.string().optional(), reference: z4.string().optional(), method: z4.string().optional(), verifiedBy: z4.string().optional(), confirm: z4.boolean().optional() };
|
|
36003
|
+
server.registerTool(
|
|
36004
|
+
"kaddo_verify_plan",
|
|
36005
|
+
{ title: "Verify plan outcome", description: `Evidence-based completion readiness of a Plan: success criteria, milestone exit criteria, dependencies and reasons. ${PLANNING_READ_NOTE}`, inputSchema: { id: z4.string() } },
|
|
36006
|
+
async (args) => toolText(guarded(root, () => verifyPlanTool(root, args.id)))
|
|
36007
|
+
);
|
|
36008
|
+
server.registerTool(
|
|
36009
|
+
"kaddo_verify_epic",
|
|
36010
|
+
{ title: "Verify epic outcome", description: `Evidence-based completion readiness of an Epic: success criteria, delivery, dependencies and reasons. ${PLANNING_READ_NOTE}`, inputSchema: { id: z4.string() } },
|
|
36011
|
+
async (args) => toolText(guarded(root, () => verifyEpicTool(root, args.id)))
|
|
36012
|
+
);
|
|
36013
|
+
server.registerTool(
|
|
36014
|
+
"kaddo_record_plan_evidence",
|
|
36015
|
+
{ title: "Record plan evidence", description: `Record outcome (or milestone exit) verification evidence for one declared criterion. ${PLANNING_WRITE_NOTE}`, inputSchema: { ...EVIDENCE_IN, milestone: z4.string().optional() } },
|
|
36016
|
+
async (args) => toolText(guarded(root, () => recordPlanEvidenceTool(root, args)))
|
|
36017
|
+
);
|
|
36018
|
+
server.registerTool(
|
|
36019
|
+
"kaddo_record_epic_evidence",
|
|
36020
|
+
{ title: "Record epic evidence", description: `Record outcome verification evidence for one declared success criterion. ${PLANNING_WRITE_NOTE}`, inputSchema: EVIDENCE_IN },
|
|
36021
|
+
async (args) => toolText(guarded(root, () => recordEpicEvidenceTool(root, args)))
|
|
36022
|
+
);
|
|
36023
|
+
server.registerTool(
|
|
36024
|
+
"kaddo_transition_plan",
|
|
36025
|
+
{ title: "Transition plan", description: `Transition a Delivery Plan (start/cancel). Completion uses kaddo_complete_plan. ${PLANNING_WRITE_NOTE}`, inputSchema: { id: z4.string(), to: z4.string(), confirm: z4.boolean().optional() } },
|
|
36026
|
+
async (args) => toolText(guarded(root, () => transitionPlanTool(root, args)))
|
|
36027
|
+
);
|
|
36028
|
+
server.registerTool(
|
|
36029
|
+
"kaddo_transition_epic",
|
|
36030
|
+
{ title: "Transition epic", description: `Transition an Epic (start/cancel). Completion uses kaddo_complete_epic. ${PLANNING_WRITE_NOTE}`, inputSchema: { id: z4.string(), to: z4.string(), confirm: z4.boolean().optional() } },
|
|
36031
|
+
async (args) => toolText(guarded(root, () => transitionEpicTool(root, args)))
|
|
36032
|
+
);
|
|
36033
|
+
server.registerTool(
|
|
36034
|
+
"kaddo_complete_plan",
|
|
36035
|
+
{ title: "Complete plan", description: `Review readiness, then (confirm=true) record a human completion decision for a Plan. Never completes automatically; confirm is not itself proof of human approval. ${PLANNING_WRITE_NOTE}`, inputSchema: { id: z4.string(), decidedBy: z4.string().optional(), confirm: z4.boolean().optional() } },
|
|
36036
|
+
async (args) => toolText(guarded(root, () => completePlanTool(root, args)))
|
|
36037
|
+
);
|
|
36038
|
+
server.registerTool(
|
|
36039
|
+
"kaddo_complete_epic",
|
|
36040
|
+
{ title: "Complete epic", description: `Review readiness, then (confirm=true) record a human completion decision for an Epic. Never completes automatically; confirm is not itself proof of human approval. ${PLANNING_WRITE_NOTE}`, inputSchema: { id: z4.string(), decidedBy: z4.string().optional(), confirm: z4.boolean().optional() } },
|
|
36041
|
+
async (args) => toolText(guarded(root, () => completeEpicTool(root, args)))
|
|
36042
|
+
);
|
|
35490
36043
|
const INITIATIVE_WRITE_NOTE = "Without confirm=true returns a preview. With confirm=true applies the change. Writes only under knowledge/delivery/.";
|
|
35491
36044
|
server.registerTool(
|
|
35492
36045
|
"kaddo_create_initiative",
|
package/package.json
CHANGED