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