@kaddo/mcp 3.120.0 → 3.121.1
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 +1358 -187
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -389,7 +389,7 @@ function join(...parts) {
|
|
|
389
389
|
}
|
|
390
390
|
|
|
391
391
|
// ../cli/src/core/impact-report.ts
|
|
392
|
-
import
|
|
392
|
+
import matter12 from "gray-matter";
|
|
393
393
|
|
|
394
394
|
// ../cli/src/core/config.ts
|
|
395
395
|
import { z } from "zod";
|
|
@@ -499,8 +499,8 @@ function loadConfig(dir) {
|
|
|
499
499
|
const parsed = configSchema.safeParse(raw ?? {});
|
|
500
500
|
if (!parsed.success) {
|
|
501
501
|
const issues = parsed.error.issues.map((i) => {
|
|
502
|
-
const
|
|
503
|
-
return
|
|
502
|
+
const path10 = i.path.join(".");
|
|
503
|
+
return path10 ? ` - ${path10}: ${i.message}` : ` - ${i.message}`;
|
|
504
504
|
}).join("\n");
|
|
505
505
|
throw new ConfigError(`Invalid .kaddo/config.yml:
|
|
506
506
|
${issues}`);
|
|
@@ -941,11 +941,11 @@ function moduleArtifactCoverage(dir, id) {
|
|
|
941
941
|
};
|
|
942
942
|
}
|
|
943
943
|
function loadMappedModules(dir) {
|
|
944
|
-
const
|
|
945
|
-
if (!exists(
|
|
944
|
+
const path10 = join(dir, DESCRIPTOR_PATH);
|
|
945
|
+
if (!exists(path10)) return [];
|
|
946
946
|
let parsed;
|
|
947
947
|
try {
|
|
948
|
-
parsed = parseYaml4(readFile(
|
|
948
|
+
parsed = parseYaml4(readFile(path10));
|
|
949
949
|
} catch {
|
|
950
950
|
return [];
|
|
951
951
|
}
|
|
@@ -2452,7 +2452,7 @@ function sectionParagraph2(md, title) {
|
|
|
2452
2452
|
}
|
|
2453
2453
|
return "";
|
|
2454
2454
|
}
|
|
2455
|
-
function parseCapsule(id,
|
|
2455
|
+
function parseCapsule(id, path10, md) {
|
|
2456
2456
|
const { data } = matter4(md);
|
|
2457
2457
|
const updatedAt = data.updated_at ? String(data.updated_at) : void 0;
|
|
2458
2458
|
let ageDays = null;
|
|
@@ -2462,7 +2462,7 @@ function parseCapsule(id, path9, md) {
|
|
|
2462
2462
|
}
|
|
2463
2463
|
return {
|
|
2464
2464
|
id,
|
|
2465
|
-
path:
|
|
2465
|
+
path: path10,
|
|
2466
2466
|
system: data.system ? String(data.system) : id,
|
|
2467
2467
|
owner: data.owner ? String(data.owner) : void 0,
|
|
2468
2468
|
updatedAt,
|
|
@@ -2735,6 +2735,252 @@ function parseWorkItemResources(fm) {
|
|
|
2735
2735
|
return out;
|
|
2736
2736
|
}
|
|
2737
2737
|
|
|
2738
|
+
// ../cli/src/core/deprecation.ts
|
|
2739
|
+
import matter7 from "gray-matter";
|
|
2740
|
+
var DEPRECATION_ARTIFACT_TYPE = "deprecation-plan";
|
|
2741
|
+
var DEPRECATION_PLANS_DIR = "knowledge/legacy/deprecations";
|
|
2742
|
+
var TARGET_TYPES = [
|
|
2743
|
+
"module",
|
|
2744
|
+
"route",
|
|
2745
|
+
"function",
|
|
2746
|
+
"ui",
|
|
2747
|
+
"job",
|
|
2748
|
+
"database-object",
|
|
2749
|
+
"integration",
|
|
2750
|
+
"config",
|
|
2751
|
+
"other"
|
|
2752
|
+
];
|
|
2753
|
+
var LIFECYCLE_STATES2 = [
|
|
2754
|
+
"candidate",
|
|
2755
|
+
"usage-validated",
|
|
2756
|
+
"soft-deprecated",
|
|
2757
|
+
"disabled",
|
|
2758
|
+
"removed-devtest",
|
|
2759
|
+
"production-ready",
|
|
2760
|
+
"production-changed",
|
|
2761
|
+
"observing",
|
|
2762
|
+
"completed",
|
|
2763
|
+
"deprecated-retained"
|
|
2764
|
+
];
|
|
2765
|
+
var ENVIRONMENTS = ["development", "test", "production"];
|
|
2766
|
+
var ENVIRONMENT_STATES = ["active", "soft-deprecated", "disabled", "removed"];
|
|
2767
|
+
var EVIDENCE_SIGNALS = [
|
|
2768
|
+
"static",
|
|
2769
|
+
"runtime",
|
|
2770
|
+
"database",
|
|
2771
|
+
"external_consumers",
|
|
2772
|
+
"human_review"
|
|
2773
|
+
];
|
|
2774
|
+
function str2(v) {
|
|
2775
|
+
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
2776
|
+
}
|
|
2777
|
+
function parseWorkItemDeprecationTargets(fm) {
|
|
2778
|
+
const v = fm.deprecation_targets;
|
|
2779
|
+
if (!Array.isArray(v)) return [];
|
|
2780
|
+
const out = [];
|
|
2781
|
+
for (const raw of v) {
|
|
2782
|
+
if (typeof raw === "string") {
|
|
2783
|
+
const s = raw.trim();
|
|
2784
|
+
if (!s) continue;
|
|
2785
|
+
const slash = s.indexOf("/");
|
|
2786
|
+
if (slash > 0) out.push({ plan: s.slice(0, slash).trim(), target: s.slice(slash + 1).trim() });
|
|
2787
|
+
else out.push({ plan: null, target: s });
|
|
2788
|
+
continue;
|
|
2789
|
+
}
|
|
2790
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
2791
|
+
const o2 = raw;
|
|
2792
|
+
const target = str2(o2.target) ?? str2(o2.id);
|
|
2793
|
+
if (!target) continue;
|
|
2794
|
+
out.push({ plan: str2(o2.plan), target });
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
return out;
|
|
2798
|
+
}
|
|
2799
|
+
function strArray2(v) {
|
|
2800
|
+
if (Array.isArray(v)) return v.map((x2) => String(x2).trim()).filter(Boolean);
|
|
2801
|
+
if (typeof v === "string" && v.trim()) return [v.trim()];
|
|
2802
|
+
return [];
|
|
2803
|
+
}
|
|
2804
|
+
function isValidTargetType(t) {
|
|
2805
|
+
return TARGET_TYPES.includes(t);
|
|
2806
|
+
}
|
|
2807
|
+
function isValidLifecycleState(s) {
|
|
2808
|
+
return LIFECYCLE_STATES2.includes(s);
|
|
2809
|
+
}
|
|
2810
|
+
function isValidEnvironmentState(s) {
|
|
2811
|
+
return ENVIRONMENT_STATES.includes(s);
|
|
2812
|
+
}
|
|
2813
|
+
function parseAssessment(v) {
|
|
2814
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) {
|
|
2815
|
+
return { unusedConfidence: null, blastRadius: null, reversibility: null };
|
|
2816
|
+
}
|
|
2817
|
+
const o2 = v;
|
|
2818
|
+
return {
|
|
2819
|
+
unusedConfidence: str2(o2.unused_confidence),
|
|
2820
|
+
blastRadius: str2(o2.blast_radius),
|
|
2821
|
+
reversibility: str2(o2.reversibility)
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
function parseUsageEvidence(v) {
|
|
2825
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return {};
|
|
2826
|
+
const out = {};
|
|
2827
|
+
for (const signal of EVIDENCE_SIGNALS) {
|
|
2828
|
+
const raw = v[signal];
|
|
2829
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
2830
|
+
const o2 = raw;
|
|
2831
|
+
out[signal] = {
|
|
2832
|
+
status: str2(o2.status) ?? "unknown",
|
|
2833
|
+
quality: str2(o2.quality) ?? "unknown",
|
|
2834
|
+
detail: str2(o2.detail)
|
|
2835
|
+
};
|
|
2836
|
+
}
|
|
2837
|
+
return out;
|
|
2838
|
+
}
|
|
2839
|
+
function parseSoftDeprecation(v) {
|
|
2840
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return null;
|
|
2841
|
+
const o2 = v;
|
|
2842
|
+
const windowRaw = o2.observation_window_days;
|
|
2843
|
+
const window = typeof windowRaw === "number" && Number.isFinite(windowRaw) && windowRaw >= 0 ? Math.floor(windowRaw) : null;
|
|
2844
|
+
const exitCriteria = Array.isArray(o2.exit_criteria) ? o2.exit_criteria.map((c) => {
|
|
2845
|
+
if (typeof c === "string") return c.trim() ? { description: c.trim(), met: false } : null;
|
|
2846
|
+
if (c && typeof c === "object" && !Array.isArray(c)) {
|
|
2847
|
+
const co = c;
|
|
2848
|
+
const description = str2(co.description) ?? str2(co.criterion);
|
|
2849
|
+
if (!description) return null;
|
|
2850
|
+
return { description, met: co.met === true };
|
|
2851
|
+
}
|
|
2852
|
+
return null;
|
|
2853
|
+
}).filter((c) => Boolean(c)) : [];
|
|
2854
|
+
return {
|
|
2855
|
+
strategy: str2(o2.strategy),
|
|
2856
|
+
mechanism: str2(o2.mechanism),
|
|
2857
|
+
startedAt: str2(o2.started_at),
|
|
2858
|
+
observationWindowDays: window,
|
|
2859
|
+
exitCriteria
|
|
2860
|
+
};
|
|
2861
|
+
}
|
|
2862
|
+
function parseEnvironments(v) {
|
|
2863
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return {};
|
|
2864
|
+
const out = {};
|
|
2865
|
+
for (const env of ENVIRONMENTS) {
|
|
2866
|
+
const s = str2(v[env]);
|
|
2867
|
+
if (s) out[env] = s;
|
|
2868
|
+
}
|
|
2869
|
+
return out;
|
|
2870
|
+
}
|
|
2871
|
+
function parseTarget(raw) {
|
|
2872
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
2873
|
+
const o2 = raw;
|
|
2874
|
+
const id = str2(o2.id);
|
|
2875
|
+
if (!id) return null;
|
|
2876
|
+
return {
|
|
2877
|
+
id,
|
|
2878
|
+
type: str2(o2.type) ?? "other",
|
|
2879
|
+
locator: str2(o2.locator),
|
|
2880
|
+
title: str2(o2.title),
|
|
2881
|
+
lifecycle: str2(o2.lifecycle) ?? "candidate",
|
|
2882
|
+
environments: parseEnvironments(o2.environments),
|
|
2883
|
+
assessment: parseAssessment(o2.assessment),
|
|
2884
|
+
usageEvidence: parseUsageEvidence(o2.usage_evidence),
|
|
2885
|
+
dependsOnRemoval: strArray2(o2.depends_on_removal),
|
|
2886
|
+
resource: str2(o2.resource),
|
|
2887
|
+
module: str2(o2.module),
|
|
2888
|
+
softDeprecation: parseSoftDeprecation(o2.soft_deprecation)
|
|
2889
|
+
};
|
|
2890
|
+
}
|
|
2891
|
+
function parseTargets(v) {
|
|
2892
|
+
if (!Array.isArray(v)) return [];
|
|
2893
|
+
const out = [];
|
|
2894
|
+
for (const raw of v) {
|
|
2895
|
+
const t = parseTarget(raw);
|
|
2896
|
+
if (t) out.push(t);
|
|
2897
|
+
}
|
|
2898
|
+
return out;
|
|
2899
|
+
}
|
|
2900
|
+
function lifecycleCountsOf(targets) {
|
|
2901
|
+
const counts = {};
|
|
2902
|
+
for (const t of targets) counts[t.lifecycle] = (counts[t.lifecycle] ?? 0) + 1;
|
|
2903
|
+
return counts;
|
|
2904
|
+
}
|
|
2905
|
+
function purposeFromBody2(body) {
|
|
2906
|
+
const m2 = body.match(/^#{1,2}\s+Purpose\s*\n+([\s\S]*?)(?=\n#{1,2}\s|\s*$)/m);
|
|
2907
|
+
const text3 = m2 ? m2[1].trim() : "";
|
|
2908
|
+
return text3 || null;
|
|
2909
|
+
}
|
|
2910
|
+
function getDeprecationPlans(dir) {
|
|
2911
|
+
return discoverKnowledge(dir).filter((a2) => a2.type === DEPRECATION_ARTIFACT_TYPE).map((a2) => {
|
|
2912
|
+
const fm = a2.rawFrontmatter;
|
|
2913
|
+
const targets = parseTargets(fm.targets);
|
|
2914
|
+
return {
|
|
2915
|
+
id: a2.id || str2(fm.id) || a2.title,
|
|
2916
|
+
title: a2.title || str2(fm.title) || a2.id,
|
|
2917
|
+
path: a2.relPath,
|
|
2918
|
+
status: str2(fm.status),
|
|
2919
|
+
targetCount: targets.length,
|
|
2920
|
+
lifecycleCounts: lifecycleCountsOf(targets)
|
|
2921
|
+
};
|
|
2922
|
+
}).sort((x2, y) => x2.id.localeCompare(y.id));
|
|
2923
|
+
}
|
|
2924
|
+
function getDeprecationPlan(dir, id) {
|
|
2925
|
+
const match = discoverKnowledge(dir).find(
|
|
2926
|
+
(a2) => a2.type === DEPRECATION_ARTIFACT_TYPE && (a2.id === id || a2.title === id)
|
|
2927
|
+
);
|
|
2928
|
+
if (!match) return null;
|
|
2929
|
+
const raw = readFile(match.filePath);
|
|
2930
|
+
const parsed = matter7(raw);
|
|
2931
|
+
const fm = parsed.data ?? {};
|
|
2932
|
+
const body = parsed.content.trim();
|
|
2933
|
+
const targets = parseTargets(fm.targets);
|
|
2934
|
+
return {
|
|
2935
|
+
id: str2(fm.id) ?? match.id,
|
|
2936
|
+
title: str2(fm.title) ?? match.title,
|
|
2937
|
+
path: match.relPath,
|
|
2938
|
+
status: str2(fm.status),
|
|
2939
|
+
targetCount: targets.length,
|
|
2940
|
+
lifecycleCounts: lifecycleCountsOf(targets),
|
|
2941
|
+
purpose: str2(fm.purpose) ?? purposeFromBody2(body),
|
|
2942
|
+
targets,
|
|
2943
|
+
markdownBody: body
|
|
2944
|
+
};
|
|
2945
|
+
}
|
|
2946
|
+
function validateTarget(target) {
|
|
2947
|
+
const findings = [];
|
|
2948
|
+
const at = target.id;
|
|
2949
|
+
if (!isValidTargetType(String(target.type))) {
|
|
2950
|
+
findings.push({ severity: "error", target: at, message: `Unknown target type: ${target.type}` });
|
|
2951
|
+
}
|
|
2952
|
+
if (!isValidLifecycleState(String(target.lifecycle))) {
|
|
2953
|
+
findings.push({ severity: "error", target: at, message: `Unknown lifecycle state: ${target.lifecycle}` });
|
|
2954
|
+
}
|
|
2955
|
+
if (!target.locator) {
|
|
2956
|
+
findings.push({ severity: "warning", target: at, message: "Target has no locator \u2014 it may be ambiguous." });
|
|
2957
|
+
}
|
|
2958
|
+
for (const [env, state] of Object.entries(target.environments)) {
|
|
2959
|
+
if (!isValidEnvironmentState(String(state))) {
|
|
2960
|
+
findings.push({ severity: "error", target: at, message: `Unknown environment state for ${env}: ${state}` });
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
const prodActive = target.environments.production === "active";
|
|
2964
|
+
if (target.lifecycle === "completed") {
|
|
2965
|
+
const stillActive = ENVIRONMENTS.filter((e) => target.environments[e] === "active");
|
|
2966
|
+
if (stillActive.length > 0) {
|
|
2967
|
+
findings.push({
|
|
2968
|
+
severity: "error",
|
|
2969
|
+
target: at,
|
|
2970
|
+
message: `Target is 'completed' but still active in: ${stillActive.join(", ")}.`
|
|
2971
|
+
});
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
if ((target.lifecycle === "production-changed" || target.lifecycle === "observing") && prodActive) {
|
|
2975
|
+
findings.push({
|
|
2976
|
+
severity: "error",
|
|
2977
|
+
target: at,
|
|
2978
|
+
message: `Target is '${target.lifecycle}' but production is still 'active'.`
|
|
2979
|
+
});
|
|
2980
|
+
}
|
|
2981
|
+
return findings;
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2738
2984
|
// ../cli/src/core/graph.ts
|
|
2739
2985
|
var KNOWLEDGE3 = "knowledge";
|
|
2740
2986
|
var RESOURCE_ROLE_EDGE = {
|
|
@@ -2779,9 +3025,9 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
2779
3025
|
];
|
|
2780
3026
|
const presentLayers = [];
|
|
2781
3027
|
for (const layer2 of layerDocs) {
|
|
2782
|
-
const
|
|
2783
|
-
if (
|
|
2784
|
-
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path:
|
|
3028
|
+
const path10 = layer2.files.map((f3) => `${KNOWLEDGE3}/${f3}`).find((rel) => exists(join(dir, rel)));
|
|
3029
|
+
if (path10) {
|
|
3030
|
+
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path10 });
|
|
2785
3031
|
presentLayers.push(layer2.id);
|
|
2786
3032
|
}
|
|
2787
3033
|
}
|
|
@@ -2839,6 +3085,33 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
2839
3085
|
addEdge(modId, resId, "depends_on");
|
|
2840
3086
|
}
|
|
2841
3087
|
}
|
|
3088
|
+
for (const summary of getDeprecationPlans(dir)) {
|
|
3089
|
+
const plan = getDeprecationPlan(dir, summary.id);
|
|
3090
|
+
if (!plan) continue;
|
|
3091
|
+
const planId = `deprecation-plan:${plan.id}`;
|
|
3092
|
+
addNode({ id: planId, type: "deprecation-plan", label: `${plan.id} ${plan.title}`.trim(), path: plan.path, status: plan.status ?? void 0 });
|
|
3093
|
+
for (const target of plan.targets) {
|
|
3094
|
+
const targetId = `deprecation-target:${target.id}`;
|
|
3095
|
+
addNode({ id: targetId, type: "deprecation-target", label: target.title ? `${target.id} ${target.title}` : target.id, path: plan.path, status: target.lifecycle });
|
|
3096
|
+
addEdge(planId, targetId, "targets");
|
|
3097
|
+
if (target.module) {
|
|
3098
|
+
const moduleId = `module:${target.module}`;
|
|
3099
|
+
addNode({ id: moduleId, type: "module", label: target.module });
|
|
3100
|
+
addEdge(targetId, moduleId, "targets");
|
|
3101
|
+
}
|
|
3102
|
+
if (target.resource) {
|
|
3103
|
+
const resourceId = `resource:${target.resource}`;
|
|
3104
|
+
const title = resourceTitleById.get(target.resource);
|
|
3105
|
+
addNode({ id: resourceId, type: "project-resource", label: title ? `${target.resource} ${title}` : target.resource });
|
|
3106
|
+
addEdge(targetId, resourceId, "targets");
|
|
3107
|
+
}
|
|
3108
|
+
for (const dependency of target.dependsOnRemoval) {
|
|
3109
|
+
const dependencyId = `deprecation-target:${dependency}`;
|
|
3110
|
+
addNode({ id: dependencyId, type: "deprecation-target", label: dependency });
|
|
3111
|
+
addEdge(targetId, dependencyId, "depends_on_removal");
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
2842
3115
|
for (const wi of selectedWIs) {
|
|
2843
3116
|
const id = wi.id || wi.title;
|
|
2844
3117
|
if (!id || !id.trim()) continue;
|
|
@@ -2869,6 +3142,11 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
2869
3142
|
addNode({ id: resId, type: "project-resource", label: title ? `${rel.id} ${title}`.trim() : rel.id });
|
|
2870
3143
|
addEdge(wiNodeId, resId, RESOURCE_ROLE_EDGE[rel.role] ?? "uses");
|
|
2871
3144
|
}
|
|
3145
|
+
for (const ref of parseWorkItemDeprecationTargets(wi.rawFrontmatter)) {
|
|
3146
|
+
const targetId = `deprecation-target:${ref.target}`;
|
|
3147
|
+
addNode({ id: targetId, type: "deprecation-target", label: ref.target });
|
|
3148
|
+
addEdge(wiNodeId, targetId, "targets");
|
|
3149
|
+
}
|
|
2872
3150
|
for (const dec of wi.decisions) {
|
|
2873
3151
|
if (!dec || !dec.trim()) continue;
|
|
2874
3152
|
const adrId = `adr:${dec}`;
|
|
@@ -3247,7 +3525,7 @@ function loadGraphHints(dir) {
|
|
|
3247
3525
|
}
|
|
3248
3526
|
|
|
3249
3527
|
// ../cli/src/services/installed-skills.ts
|
|
3250
|
-
import
|
|
3528
|
+
import matter8 from "gray-matter";
|
|
3251
3529
|
var SKILLS_DIR = join("knowledge", "skills");
|
|
3252
3530
|
function discoverInstalledSkills(dir) {
|
|
3253
3531
|
const base = join(dir, SKILLS_DIR);
|
|
@@ -3257,7 +3535,7 @@ function discoverInstalledSkills(dir) {
|
|
|
3257
3535
|
const skillFile = join(base, entry, "skill.md");
|
|
3258
3536
|
if (!isFile(skillFile)) continue;
|
|
3259
3537
|
try {
|
|
3260
|
-
const { data } =
|
|
3538
|
+
const { data } = matter8(readFile(skillFile));
|
|
3261
3539
|
if (data.type && String(data.type) !== "skill") continue;
|
|
3262
3540
|
const id = String(data.id ?? entry);
|
|
3263
3541
|
out.push({
|
|
@@ -4337,13 +4615,13 @@ function meaningful(text3) {
|
|
|
4337
4615
|
});
|
|
4338
4616
|
}
|
|
4339
4617
|
function readPocSummary(dir) {
|
|
4340
|
-
const
|
|
4341
|
-
if (!exists(
|
|
4618
|
+
const path10 = join(dir, POC_ARTIFACT_PATH);
|
|
4619
|
+
if (!exists(path10)) {
|
|
4342
4620
|
return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
|
|
4343
4621
|
}
|
|
4344
4622
|
let markdown = "";
|
|
4345
4623
|
try {
|
|
4346
|
-
markdown = readFile(
|
|
4624
|
+
markdown = readFile(path10);
|
|
4347
4625
|
} catch {
|
|
4348
4626
|
return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
|
|
4349
4627
|
}
|
|
@@ -4362,7 +4640,7 @@ function readPocSummary(dir) {
|
|
|
4362
4640
|
|
|
4363
4641
|
// ../cli/src/core/poc-report.ts
|
|
4364
4642
|
import { createHash } from "crypto";
|
|
4365
|
-
import
|
|
4643
|
+
import matter9 from "gray-matter";
|
|
4366
4644
|
var POC_REPORT_PREFIX = "poc-report-v";
|
|
4367
4645
|
var SECRET_LINE = /(^|\s)(?:[A-Z][A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|API_KEY|PRIVATE_KEY)|(?:token|secret|password|api[_-]?key))\s*[:=]\s*\S+/i;
|
|
4368
4646
|
var reportName = /^poc-report-v(\d{3,})\.md$/;
|
|
@@ -4378,11 +4656,11 @@ function redactSecrets(value) {
|
|
|
4378
4656
|
return line.replace(/([:=]\s*)\S+.*/, "$1[redacted]");
|
|
4379
4657
|
}).join("\n");
|
|
4380
4658
|
}
|
|
4381
|
-
function source(dir,
|
|
4382
|
-
const absolute = join(dir,
|
|
4659
|
+
function source(dir, path10, kind) {
|
|
4660
|
+
const absolute = join(dir, path10);
|
|
4383
4661
|
if (!exists(absolute)) return null;
|
|
4384
4662
|
const raw = readFile(absolute);
|
|
4385
|
-
return { path: posix(
|
|
4663
|
+
return { path: posix(path10), kind, digest: sha(raw), content: redactSecrets(raw) };
|
|
4386
4664
|
}
|
|
4387
4665
|
function reportFiles(dir) {
|
|
4388
4666
|
const delivery = join(dir, "knowledge", "delivery");
|
|
@@ -4390,7 +4668,7 @@ function reportFiles(dir) {
|
|
|
4390
4668
|
const match = name.match(reportName);
|
|
4391
4669
|
if (!match) return [];
|
|
4392
4670
|
try {
|
|
4393
|
-
const parsed =
|
|
4671
|
+
const parsed = matter9(readFile(join(delivery, name)));
|
|
4394
4672
|
const fingerprint = typeof parsed.data.source_fingerprint === "string" ? parsed.data.source_fingerprint : "";
|
|
4395
4673
|
const manifest = parsed.data.source_manifest;
|
|
4396
4674
|
const sourceManifest = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? Object.fromEntries(Object.entries(manifest).filter(([, digest]) => typeof digest === "string")) : {};
|
|
@@ -4422,9 +4700,9 @@ function buildPocReportContext(dir) {
|
|
|
4422
4700
|
};
|
|
4423
4701
|
}
|
|
4424
4702
|
const sources = [];
|
|
4425
|
-
const add = (
|
|
4426
|
-
if (sources.some((item2) => item2.path === posix(
|
|
4427
|
-
const item = source(dir,
|
|
4703
|
+
const add = (path10, kind) => {
|
|
4704
|
+
if (sources.some((item2) => item2.path === posix(path10))) return;
|
|
4705
|
+
const item = source(dir, path10, kind);
|
|
4428
4706
|
if (item) sources.push(item);
|
|
4429
4707
|
};
|
|
4430
4708
|
add(POC_ARTIFACT_PATH, "poc");
|
|
@@ -4452,7 +4730,7 @@ function buildPocReportContext(dir) {
|
|
|
4452
4730
|
const sourceFingerprint = sha(sources.map((item) => `${item.path}\0${item.digest}`).join("\n"));
|
|
4453
4731
|
const status = !latestReport ? "missing" : latestReport.sourceFingerprint === sourceFingerprint ? "current" : "stale";
|
|
4454
4732
|
const currentManifest = Object.fromEntries(sources.map((item) => [item.path, item.digest]));
|
|
4455
|
-
const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((
|
|
4733
|
+
const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((path10) => currentManifest[path10] !== latestReport?.sourceManifest[path10]).sort() : [];
|
|
4456
4734
|
const sourceList = sources.map((item) => `- ${item.path} (${item.kind})`).join("\n") || "- No sources selected.";
|
|
4457
4735
|
return {
|
|
4458
4736
|
eligible: true,
|
|
@@ -4468,6 +4746,9 @@ function buildPocReportContext(dir) {
|
|
|
4468
4746
|
`Canonical conclusion: ${poc.conclusion}. Do not contradict it.`,
|
|
4469
4747
|
"Use the 15 canonical sections. Mark missing evidence as Not evaluated, Not applicable, or No evidence available.",
|
|
4470
4748
|
"Distinguish measured, calculated, estimated, and projected values. Never include secret values.",
|
|
4749
|
+
"Choose the representation that best communicates each grounded fact: Markdown tables for structured comparisons, Mermaid for grounded architecture, flows, or relationships, and prose for explanation and interpretation.",
|
|
4750
|
+
"Do not invent data, entities, relationships, priorities, or metrics to create a table or diagram. Do not use ASCII diagrams when Mermaid can represent the same grounded flow.",
|
|
4751
|
+
"In Traceability, use repository-relative Markdown links or paths. Never use file:/// URLs or local absolute filesystem paths.",
|
|
4471
4752
|
"",
|
|
4472
4753
|
"Selected sources:",
|
|
4473
4754
|
sourceList
|
|
@@ -4600,10 +4881,10 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
4600
4881
|
const q3 = (rel) => analyzeKnowledgeArtifact(dir, rel);
|
|
4601
4882
|
const resolveAgent = (agent) => {
|
|
4602
4883
|
const file = agent.endsWith(".md") ? agent : `${agent}.md`;
|
|
4603
|
-
const
|
|
4604
|
-
const installed = isFile(join(dir,
|
|
4884
|
+
const path10 = agentInstallPath(file);
|
|
4885
|
+
const installed = isFile(join(dir, path10));
|
|
4605
4886
|
const group = agentGroupOf(file);
|
|
4606
|
-
return { agentPath:
|
|
4887
|
+
return { agentPath: path10, agentInstalled: installed, installCommand: installed ? void 0 : `kaddo add agents --group ${group}` };
|
|
4607
4888
|
};
|
|
4608
4889
|
const moduleRepo = isModule(config);
|
|
4609
4890
|
const coreRepo = isCore(config);
|
|
@@ -5033,7 +5314,7 @@ function deriveDeliveryCompletedStatus(dir) {
|
|
|
5033
5314
|
}
|
|
5034
5315
|
|
|
5035
5316
|
// ../cli/src/core/assets.ts
|
|
5036
|
-
import
|
|
5317
|
+
import matter10 from "gray-matter";
|
|
5037
5318
|
|
|
5038
5319
|
// ../cli/src/core/version.ts
|
|
5039
5320
|
import { fileURLToPath } from "url";
|
|
@@ -5734,6 +6015,49 @@ After a POC has a canonical conclusion and Core has prepared its deterministic r
|
|
|
5734
6015
|
- Use \`Not evaluated in this POC.\`, \`Not applicable.\`, or \`No evidence available.\` when sources do not support a section.
|
|
5735
6016
|
- Return a proposal for human review; Core persists it only after confirmation.
|
|
5736
6017
|
|
|
6018
|
+
## Representation selection
|
|
6019
|
+
|
|
6020
|
+
- Use prose for context, interpretation, rationale, findings, and conclusions.
|
|
6021
|
+
- Use a Markdown table when supplied evidence has comparable rows, such as success criteria and
|
|
6022
|
+
outcomes, resources, experiments, measurements, costs, risks, recommendations, or traceability.
|
|
6023
|
+
- Use Mermaid only for architecture, topology, or flow relationships explicitly supported by the
|
|
6024
|
+
supplied sources. Prefer it to an ASCII diagram when both can represent the same grounded flow.
|
|
6025
|
+
- Tables and diagrams are communication tools, not completeness requirements. Do not produce one
|
|
6026
|
+
when the context does not support a useful representation.
|
|
6027
|
+
- Never invent table rows, diagram nodes or edges, statuses, priorities, metrics, or relationships
|
|
6028
|
+
to make a report look more complete.
|
|
6029
|
+
- Preserve measured, calculated, estimated, and projected semantics in every representation.
|
|
6030
|
+
- In Traceability, use repository-relative Markdown links or paths; never emit \`file:///\` URLs or
|
|
6031
|
+
local absolute filesystem paths.
|
|
6032
|
+
|
|
6033
|
+
## Section representation guide
|
|
6034
|
+
|
|
6035
|
+
| Section | Preferred representation when supported by evidence |
|
|
6036
|
+
|---|---|
|
|
6037
|
+
| Executive Summary | Snapshot table and short prose |
|
|
6038
|
+
| Problem and Objective | Prose |
|
|
6039
|
+
| Hypothesis and Success Criteria | Evaluation matrix |
|
|
6040
|
+
| Scope, Constraints and Non-goals | Compact bullets or table |
|
|
6041
|
+
| Technical Approach and Architecture | Mermaid and prose |
|
|
6042
|
+
| Infrastructure and Project Resources | Table, with optional Mermaid |
|
|
6043
|
+
| Implementation and Relevant Technical Decisions | Table or concise prose |
|
|
6044
|
+
| Experiments and Validation | Validation matrix |
|
|
6045
|
+
| Results and Measurements | Measurement tables |
|
|
6046
|
+
| Cost and Efficiency Analysis | Comparison table |
|
|
6047
|
+
| Observability and Operational Findings | Table or prose |
|
|
6048
|
+
| Findings, Learnings, Limitations and Risks | Structured matrix |
|
|
6049
|
+
| Conclusion | Status and concise prose |
|
|
6050
|
+
| Recommendation and Next Steps | Recommendation matrix only when priority is evidenced |
|
|
6051
|
+
| Traceability and Sources | Relative-link table or list |
|
|
6052
|
+
|
|
6053
|
+
## Quality checklist
|
|
6054
|
+
|
|
6055
|
+
- Structured evidence uses tables when clearer than prose.
|
|
6056
|
+
- Grounded architecture or flows use Mermaid when useful; no decorative or unsupported diagrams exist.
|
|
6057
|
+
- Tables and Mermaid preserve source grounding and measurement semantics.
|
|
6058
|
+
- Traceability contains repository-portable paths.
|
|
6059
|
+
- The report is scannable before reading the full narrative.
|
|
6060
|
+
|
|
5737
6061
|
## Required structure
|
|
5738
6062
|
|
|
5739
6063
|
1. Executive Summary
|
|
@@ -8301,7 +8625,7 @@ function canonicalSkills() {
|
|
|
8301
8625
|
}
|
|
8302
8626
|
function versionOf(content) {
|
|
8303
8627
|
try {
|
|
8304
|
-
const v =
|
|
8628
|
+
const v = matter10(content).data.version;
|
|
8305
8629
|
return v === void 0 || v === null ? null : String(v);
|
|
8306
8630
|
} catch {
|
|
8307
8631
|
return null;
|
|
@@ -9141,7 +9465,7 @@ function renderRouteCompact(route) {
|
|
|
9141
9465
|
}
|
|
9142
9466
|
|
|
9143
9467
|
// ../cli/src/core/metadata-health.ts
|
|
9144
|
-
import
|
|
9468
|
+
import matter11 from "gray-matter";
|
|
9145
9469
|
var KNOWLEDGE_FILES2 = [
|
|
9146
9470
|
"knowledge/business/business.md",
|
|
9147
9471
|
"knowledge/product/product.md",
|
|
@@ -9161,7 +9485,7 @@ function analyzeMetadataHealth(dir) {
|
|
|
9161
9485
|
let data;
|
|
9162
9486
|
try {
|
|
9163
9487
|
const raw = readFile(p);
|
|
9164
|
-
data =
|
|
9488
|
+
data = matter11(raw).data;
|
|
9165
9489
|
} catch {
|
|
9166
9490
|
continue;
|
|
9167
9491
|
}
|
|
@@ -9193,7 +9517,7 @@ function analyzeMetadataHealth(dir) {
|
|
|
9193
9517
|
if (isFile(p) && entry.endsWith(".md")) {
|
|
9194
9518
|
try {
|
|
9195
9519
|
const raw = readFile(p);
|
|
9196
|
-
const fm =
|
|
9520
|
+
const fm = matter11(raw).data;
|
|
9197
9521
|
if (fm.status === "done") {
|
|
9198
9522
|
const rel = p.replace(dir + "/", "").replace(dir + "\\", "").replace(/\\/g, "/");
|
|
9199
9523
|
findings.push({
|
|
@@ -10061,7 +10385,7 @@ function buildGuardHistory(dir) {
|
|
|
10061
10385
|
const d3 = hotspotDir(t.code_path);
|
|
10062
10386
|
hotspotMap.set(d3, (hotspotMap.get(d3) ?? 0) + 1);
|
|
10063
10387
|
}
|
|
10064
|
-
const hotspots = [...hotspotMap.entries()].map(([
|
|
10388
|
+
const hotspots = [...hotspotMap.entries()].map(([path10, warnings]) => ({ path: path10, warnings })).sort((a2, b2) => b2.warnings - a2.warnings);
|
|
10065
10389
|
return {
|
|
10066
10390
|
available: true,
|
|
10067
10391
|
total_runs: sorted.length,
|
|
@@ -10148,7 +10472,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10148
10472
|
}
|
|
10149
10473
|
}
|
|
10150
10474
|
try {
|
|
10151
|
-
const body =
|
|
10475
|
+
const body = matter12(readFile(wi.filePath)).content;
|
|
10152
10476
|
if (hasSection(body, /acceptance|criterios de aceptaci/i)) withAcceptance++;
|
|
10153
10477
|
else gaps.missing_acceptance_criteria.push(gapItem(wi, "Add an `## Acceptance Criteria` section."));
|
|
10154
10478
|
if (hasSection(body, /definition of done|^#{1,6}\s*dod\b|definici[oó]n de (terminado|hecho)/i)) withDoD++;
|
|
@@ -10861,7 +11185,7 @@ function serializeDriftJson(r2) {
|
|
|
10861
11185
|
}
|
|
10862
11186
|
|
|
10863
11187
|
// src/workitems.ts
|
|
10864
|
-
import
|
|
11188
|
+
import matter13 from "gray-matter";
|
|
10865
11189
|
var VALID_SOURCES2 = /* @__PURE__ */ new Set(["manual", "roadmap", "jira", "github", "notion", "xlsx", "csv", "api", "external", "unknown"]);
|
|
10866
11190
|
function optStr3(v) {
|
|
10867
11191
|
return typeof v === "string" && v.trim() ? v.trim() : void 0;
|
|
@@ -10884,7 +11208,7 @@ function parseSource(data) {
|
|
|
10884
11208
|
return { type: "unknown", inferred: true };
|
|
10885
11209
|
}
|
|
10886
11210
|
var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["draft", "ready", "in-progress", "blocked"]);
|
|
10887
|
-
function
|
|
11211
|
+
function strArray3(v) {
|
|
10888
11212
|
return Array.isArray(v) ? v.map((x2) => String(x2)).filter(Boolean) : [];
|
|
10889
11213
|
}
|
|
10890
11214
|
function lifecycleOf(data, relPath) {
|
|
@@ -10952,15 +11276,15 @@ function listWorkItems(root) {
|
|
|
10952
11276
|
if (raw === null) continue;
|
|
10953
11277
|
let parsed;
|
|
10954
11278
|
try {
|
|
10955
|
-
parsed =
|
|
11279
|
+
parsed = matter13(raw);
|
|
10956
11280
|
} catch {
|
|
10957
11281
|
continue;
|
|
10958
11282
|
}
|
|
10959
11283
|
const data = parsed.data;
|
|
10960
11284
|
if (!data.type) continue;
|
|
10961
11285
|
const status = lifecycleOf(data, rel);
|
|
10962
|
-
const code =
|
|
10963
|
-
const domains =
|
|
11286
|
+
const code = strArray3(data.code);
|
|
11287
|
+
const domains = strArray3(data.domains);
|
|
10964
11288
|
const source3 = parseSource(data);
|
|
10965
11289
|
const readiness = status === "draft" ? assessReadiness(data, parsed.content, code, domains, source3) : void 0;
|
|
10966
11290
|
items.push({
|
|
@@ -10972,9 +11296,9 @@ function listWorkItems(root) {
|
|
|
10972
11296
|
path: rel,
|
|
10973
11297
|
summary: String(data.summary ?? "") || firstParagraph2(parsed.content),
|
|
10974
11298
|
code,
|
|
10975
|
-
capabilities:
|
|
10976
|
-
decisions:
|
|
10977
|
-
capsules:
|
|
11299
|
+
capabilities: strArray3(data.capabilities),
|
|
11300
|
+
decisions: strArray3(data.decisions),
|
|
11301
|
+
capsules: strArray3(data.capsules),
|
|
10978
11302
|
source: source3,
|
|
10979
11303
|
domains,
|
|
10980
11304
|
readiness,
|
|
@@ -11451,14 +11775,14 @@ function getPrompt(root, name) {
|
|
|
11451
11775
|
}
|
|
11452
11776
|
|
|
11453
11777
|
// src/tools.ts
|
|
11454
|
-
import
|
|
11778
|
+
import matter20 from "gray-matter";
|
|
11455
11779
|
|
|
11456
11780
|
// ../cli/dist/core.js
|
|
11457
11781
|
import fs3 from "fs";
|
|
11458
11782
|
import path3 from "path";
|
|
11459
11783
|
import { z as z3 } from "zod";
|
|
11460
11784
|
import { parse as parseYaml10, stringify as stringifyYaml5 } from "yaml";
|
|
11461
|
-
import
|
|
11785
|
+
import matter14 from "gray-matter";
|
|
11462
11786
|
import matter22 from "gray-matter";
|
|
11463
11787
|
import { parse as parseYaml62, stringify as stringifyYaml42 } from "yaml";
|
|
11464
11788
|
import { parse as parseYaml22 } from "yaml";
|
|
@@ -11468,28 +11792,31 @@ import { parse as parseYaml42, stringify as stringifyYaml22 } from "yaml";
|
|
|
11468
11792
|
import matter32 from "gray-matter";
|
|
11469
11793
|
import matter42 from "gray-matter";
|
|
11470
11794
|
import matter52 from "gray-matter";
|
|
11795
|
+
import matter62 from "gray-matter";
|
|
11471
11796
|
import { parse as parseYaml72 } from "yaml";
|
|
11472
11797
|
import { createHash as createHash2 } from "crypto";
|
|
11473
|
-
import matter62 from "gray-matter";
|
|
11474
11798
|
import matter72 from "gray-matter";
|
|
11799
|
+
import matter82 from "gray-matter";
|
|
11475
11800
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
11476
11801
|
import { dirname as dirname2, join as join22, parse as parse2 } from "path";
|
|
11477
11802
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
11478
|
-
import matter82 from "gray-matter";
|
|
11479
11803
|
import matter92 from "gray-matter";
|
|
11480
11804
|
import matter102 from "gray-matter";
|
|
11805
|
+
import matter112 from "gray-matter";
|
|
11481
11806
|
import { parse as parseYaml82, stringify as stringifyYaml52 } from "yaml";
|
|
11482
11807
|
import fs4 from "fs";
|
|
11483
11808
|
import path4 from "path";
|
|
11484
11809
|
import crypto2 from "crypto";
|
|
11485
|
-
import
|
|
11810
|
+
import matter122 from "gray-matter";
|
|
11486
11811
|
import crypto3 from "crypto";
|
|
11487
11812
|
import fs5 from "fs";
|
|
11488
|
-
import matter122 from "gray-matter";
|
|
11489
11813
|
import matter132 from "gray-matter";
|
|
11814
|
+
import matter142 from "gray-matter";
|
|
11490
11815
|
import { parse as parseYaml92, stringify as stringifyYaml6 } from "yaml";
|
|
11491
11816
|
import { parse as parseYaml102, stringify as stringifyYaml7 } from "yaml";
|
|
11492
|
-
import
|
|
11817
|
+
import matter15 from "gray-matter";
|
|
11818
|
+
import matter16 from "gray-matter";
|
|
11819
|
+
import matter17 from "gray-matter";
|
|
11493
11820
|
function exists2(p) {
|
|
11494
11821
|
return fs3.existsSync(p);
|
|
11495
11822
|
}
|
|
@@ -11608,8 +11935,8 @@ function loadConfig2(dir) {
|
|
|
11608
11935
|
const parsed = configSchema2.safeParse(raw ?? {});
|
|
11609
11936
|
if (!parsed.success) {
|
|
11610
11937
|
const issues = parsed.error.issues.map((i) => {
|
|
11611
|
-
const
|
|
11612
|
-
return
|
|
11938
|
+
const path62 = i.path.join(".");
|
|
11939
|
+
return path62 ? ` - ${path62}: ${i.message}` : ` - ${i.message}`;
|
|
11613
11940
|
}).join("\n");
|
|
11614
11941
|
throw new ConfigError2(`Invalid .kaddo/config.yml:
|
|
11615
11942
|
${issues}`);
|
|
@@ -11665,7 +11992,7 @@ function parseImpactAnalysis2(data) {
|
|
|
11665
11992
|
}
|
|
11666
11993
|
function parseArtifact2(filePath, raw) {
|
|
11667
11994
|
try {
|
|
11668
|
-
const { data } =
|
|
11995
|
+
const { data } = matter14(raw);
|
|
11669
11996
|
const globs = Array.isArray(data.code) ? data.code.filter(Boolean) : [];
|
|
11670
11997
|
return {
|
|
11671
11998
|
filePath,
|
|
@@ -11718,7 +12045,7 @@ function readArtifacts2(archDir) {
|
|
|
11718
12045
|
walkDir(archDir);
|
|
11719
12046
|
return artifacts;
|
|
11720
12047
|
}
|
|
11721
|
-
var
|
|
12048
|
+
var LIFECYCLE_STATES3 = [
|
|
11722
12049
|
"draft",
|
|
11723
12050
|
"ready",
|
|
11724
12051
|
"in-progress",
|
|
@@ -11733,7 +12060,7 @@ var LEGACY_STATUS_MAP2 = {
|
|
|
11733
12060
|
canceled: "archived",
|
|
11734
12061
|
"in-progress": "in-progress"
|
|
11735
12062
|
};
|
|
11736
|
-
var STATE_SET2 = new Set(
|
|
12063
|
+
var STATE_SET2 = new Set(LIFECYCLE_STATES3);
|
|
11737
12064
|
function isLifecycleState2(s) {
|
|
11738
12065
|
return STATE_SET2.has(s);
|
|
11739
12066
|
}
|
|
@@ -11755,7 +12082,7 @@ function lifecycleStateOf2(item) {
|
|
|
11755
12082
|
return "ready";
|
|
11756
12083
|
}
|
|
11757
12084
|
function emptyLifecycleCounts2() {
|
|
11758
|
-
return Object.fromEntries(
|
|
12085
|
+
return Object.fromEntries(LIFECYCLE_STATES3.map((s) => [s, 0]));
|
|
11759
12086
|
}
|
|
11760
12087
|
var ARCH_DIR3 = "knowledge";
|
|
11761
12088
|
function toPosix7(p) {
|
|
@@ -11813,11 +12140,11 @@ function moduleArtifactCoverage2(dir, id) {
|
|
|
11813
12140
|
};
|
|
11814
12141
|
}
|
|
11815
12142
|
function loadMappedModules2(dir) {
|
|
11816
|
-
const
|
|
11817
|
-
if (!exists2(
|
|
12143
|
+
const path62 = join3(dir, DESCRIPTOR_PATH2);
|
|
12144
|
+
if (!exists2(path62)) return [];
|
|
11818
12145
|
let parsed;
|
|
11819
12146
|
try {
|
|
11820
|
-
parsed = parseYaml32(readFile2(
|
|
12147
|
+
parsed = parseYaml32(readFile2(path62));
|
|
11821
12148
|
} catch {
|
|
11822
12149
|
return [];
|
|
11823
12150
|
}
|
|
@@ -13220,10 +13547,10 @@ function suggestInitiativesForExternalItem(dir, integration, externalId) {
|
|
|
13220
13547
|
);
|
|
13221
13548
|
}
|
|
13222
13549
|
var RESOURCE_ARTIFACT_TYPE2 = "project-resource";
|
|
13223
|
-
function
|
|
13550
|
+
function str3(v) {
|
|
13224
13551
|
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
13225
13552
|
}
|
|
13226
|
-
function
|
|
13553
|
+
function strArray4(v) {
|
|
13227
13554
|
if (Array.isArray(v)) return v.map((x2) => String(x2).trim()).filter(Boolean);
|
|
13228
13555
|
if (typeof v === "string" && v.trim()) return [v.trim()];
|
|
13229
13556
|
return [];
|
|
@@ -13234,16 +13561,16 @@ function parseInterfaces2(v) {
|
|
|
13234
13561
|
for (const raw of v) {
|
|
13235
13562
|
if (!raw || typeof raw !== "object") continue;
|
|
13236
13563
|
const o2 = raw;
|
|
13237
|
-
const type =
|
|
13564
|
+
const type = str3(o2.type);
|
|
13238
13565
|
if (!type) continue;
|
|
13239
13566
|
out.push({
|
|
13240
13567
|
type,
|
|
13241
|
-
tool:
|
|
13242
|
-
provider:
|
|
13243
|
-
purpose:
|
|
13244
|
-
operations:
|
|
13245
|
-
environments:
|
|
13246
|
-
constraints:
|
|
13568
|
+
tool: str3(o2.tool),
|
|
13569
|
+
provider: str3(o2.provider),
|
|
13570
|
+
purpose: str3(o2.purpose),
|
|
13571
|
+
operations: strArray4(o2.operations),
|
|
13572
|
+
environments: strArray4(o2.environments),
|
|
13573
|
+
constraints: str3(o2.constraints)
|
|
13247
13574
|
});
|
|
13248
13575
|
}
|
|
13249
13576
|
return out;
|
|
@@ -13252,7 +13579,7 @@ function parseBoundaries2(v) {
|
|
|
13252
13579
|
if (!v || typeof v !== "object" || Array.isArray(v)) return {};
|
|
13253
13580
|
const out = {};
|
|
13254
13581
|
for (const [env, val] of Object.entries(v)) {
|
|
13255
|
-
const list22 =
|
|
13582
|
+
const list22 = strArray4(val);
|
|
13256
13583
|
if (list22.length > 0) out[env] = list22;
|
|
13257
13584
|
}
|
|
13258
13585
|
return out;
|
|
@@ -13260,17 +13587,17 @@ function parseBoundaries2(v) {
|
|
|
13260
13587
|
function parseAuth2(v) {
|
|
13261
13588
|
if (!v || typeof v !== "object" || Array.isArray(v)) return { mode: null, refs: [] };
|
|
13262
13589
|
const o2 = v;
|
|
13263
|
-
const refs =
|
|
13264
|
-
return { mode:
|
|
13590
|
+
const refs = strArray4(o2.refs).filter((r2) => !r2.includes("=") && /^[A-Za-z0-9_.-]+$/.test(r2));
|
|
13591
|
+
return { mode: str3(o2.mode), refs };
|
|
13265
13592
|
}
|
|
13266
13593
|
function parseScope2(v) {
|
|
13267
13594
|
if (!v || typeof v !== "object" || Array.isArray(v)) return null;
|
|
13268
13595
|
const o2 = v;
|
|
13269
|
-
const type =
|
|
13596
|
+
const type = str3(o2.type);
|
|
13270
13597
|
if (!type) return null;
|
|
13271
|
-
return { type, module:
|
|
13598
|
+
return { type, module: str3(o2.module) };
|
|
13272
13599
|
}
|
|
13273
|
-
function
|
|
13600
|
+
function purposeFromBody3(body) {
|
|
13274
13601
|
const m2 = body.match(/^#{1,2}\s+Purpose\s*\n+([\s\S]*?)(?=\n#{1,2}\s|\s*$)/m);
|
|
13275
13602
|
const text22 = m2 ? m2[1].trim() : "";
|
|
13276
13603
|
return text22 || null;
|
|
@@ -13279,17 +13606,17 @@ function toDetail2(filePath, relPath, raw) {
|
|
|
13279
13606
|
const parsed = matter42(raw);
|
|
13280
13607
|
const fm = parsed.data ?? {};
|
|
13281
13608
|
const body = parsed.content.trim();
|
|
13282
|
-
const id =
|
|
13609
|
+
const id = str3(fm.id) ?? "";
|
|
13283
13610
|
return {
|
|
13284
13611
|
id,
|
|
13285
|
-
title:
|
|
13286
|
-
resourceType:
|
|
13287
|
-
provider:
|
|
13288
|
-
environments:
|
|
13612
|
+
title: str3(fm.title) ?? id,
|
|
13613
|
+
resourceType: str3(fm.resource_type),
|
|
13614
|
+
provider: str3(fm.provider),
|
|
13615
|
+
environments: strArray4(fm.environments),
|
|
13289
13616
|
scope: parseScope2(fm.scope),
|
|
13290
|
-
modules:
|
|
13617
|
+
modules: strArray4(fm.modules),
|
|
13291
13618
|
path: relPath,
|
|
13292
|
-
purpose:
|
|
13619
|
+
purpose: str3(fm.purpose) ?? purposeFromBody3(body),
|
|
13293
13620
|
interfaces: parseInterfaces2(fm.access_interfaces),
|
|
13294
13621
|
boundaries: parseBoundaries2(fm.access_boundaries),
|
|
13295
13622
|
...(() => {
|
|
@@ -13303,13 +13630,13 @@ function getResources2(dir) {
|
|
|
13303
13630
|
return discoverKnowledge2(dir).filter((a2) => a2.type === RESOURCE_ARTIFACT_TYPE2).map((a2) => {
|
|
13304
13631
|
const fm = a2.rawFrontmatter;
|
|
13305
13632
|
return {
|
|
13306
|
-
id: a2.id ||
|
|
13307
|
-
title: a2.title ||
|
|
13308
|
-
resourceType:
|
|
13309
|
-
provider:
|
|
13310
|
-
environments:
|
|
13633
|
+
id: a2.id || str3(fm.id) || a2.title,
|
|
13634
|
+
title: a2.title || str3(fm.title) || a2.id,
|
|
13635
|
+
resourceType: str3(fm.resource_type),
|
|
13636
|
+
provider: str3(fm.provider),
|
|
13637
|
+
environments: strArray4(fm.environments),
|
|
13311
13638
|
scope: parseScope2(fm.scope),
|
|
13312
|
-
modules:
|
|
13639
|
+
modules: strArray4(fm.modules),
|
|
13313
13640
|
path: a2.relPath
|
|
13314
13641
|
};
|
|
13315
13642
|
}).sort((x2, y) => x2.id.localeCompare(y.id));
|
|
@@ -13329,12 +13656,184 @@ function parseWorkItemResources2(fm) {
|
|
|
13329
13656
|
for (const raw of v) {
|
|
13330
13657
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
13331
13658
|
const o2 = raw;
|
|
13332
|
-
const id =
|
|
13659
|
+
const id = str3(o2.id);
|
|
13333
13660
|
if (!id) continue;
|
|
13334
|
-
out.push({ id, role:
|
|
13661
|
+
out.push({ id, role: str3(o2.role) ?? "affected" });
|
|
13335
13662
|
}
|
|
13336
13663
|
return out;
|
|
13337
13664
|
}
|
|
13665
|
+
var DEPRECATION_ARTIFACT_TYPE2 = "deprecation-plan";
|
|
13666
|
+
var ENVIRONMENTS2 = ["development", "test", "production"];
|
|
13667
|
+
var EVIDENCE_SIGNALS2 = [
|
|
13668
|
+
"static",
|
|
13669
|
+
"runtime",
|
|
13670
|
+
"database",
|
|
13671
|
+
"external_consumers",
|
|
13672
|
+
"human_review"
|
|
13673
|
+
];
|
|
13674
|
+
function str22(v) {
|
|
13675
|
+
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
13676
|
+
}
|
|
13677
|
+
function parseWorkItemDeprecationTargets2(fm) {
|
|
13678
|
+
const v = fm.deprecation_targets;
|
|
13679
|
+
if (!Array.isArray(v)) return [];
|
|
13680
|
+
const out = [];
|
|
13681
|
+
for (const raw of v) {
|
|
13682
|
+
if (typeof raw === "string") {
|
|
13683
|
+
const s = raw.trim();
|
|
13684
|
+
if (!s) continue;
|
|
13685
|
+
const slash = s.indexOf("/");
|
|
13686
|
+
if (slash > 0) out.push({ plan: s.slice(0, slash).trim(), target: s.slice(slash + 1).trim() });
|
|
13687
|
+
else out.push({ plan: null, target: s });
|
|
13688
|
+
continue;
|
|
13689
|
+
}
|
|
13690
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
13691
|
+
const o2 = raw;
|
|
13692
|
+
const target = str22(o2.target) ?? str22(o2.id);
|
|
13693
|
+
if (!target) continue;
|
|
13694
|
+
out.push({ plan: str22(o2.plan), target });
|
|
13695
|
+
}
|
|
13696
|
+
}
|
|
13697
|
+
return out;
|
|
13698
|
+
}
|
|
13699
|
+
function strArray22(v) {
|
|
13700
|
+
if (Array.isArray(v)) return v.map((x2) => String(x2).trim()).filter(Boolean);
|
|
13701
|
+
if (typeof v === "string" && v.trim()) return [v.trim()];
|
|
13702
|
+
return [];
|
|
13703
|
+
}
|
|
13704
|
+
function parseAssessment2(v) {
|
|
13705
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) {
|
|
13706
|
+
return { unusedConfidence: null, blastRadius: null, reversibility: null };
|
|
13707
|
+
}
|
|
13708
|
+
const o2 = v;
|
|
13709
|
+
return {
|
|
13710
|
+
unusedConfidence: str22(o2.unused_confidence),
|
|
13711
|
+
blastRadius: str22(o2.blast_radius),
|
|
13712
|
+
reversibility: str22(o2.reversibility)
|
|
13713
|
+
};
|
|
13714
|
+
}
|
|
13715
|
+
function parseUsageEvidence2(v) {
|
|
13716
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return {};
|
|
13717
|
+
const out = {};
|
|
13718
|
+
for (const signal of EVIDENCE_SIGNALS2) {
|
|
13719
|
+
const raw = v[signal];
|
|
13720
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
13721
|
+
const o2 = raw;
|
|
13722
|
+
out[signal] = {
|
|
13723
|
+
status: str22(o2.status) ?? "unknown",
|
|
13724
|
+
quality: str22(o2.quality) ?? "unknown",
|
|
13725
|
+
detail: str22(o2.detail)
|
|
13726
|
+
};
|
|
13727
|
+
}
|
|
13728
|
+
return out;
|
|
13729
|
+
}
|
|
13730
|
+
function parseSoftDeprecation2(v) {
|
|
13731
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return null;
|
|
13732
|
+
const o2 = v;
|
|
13733
|
+
const windowRaw = o2.observation_window_days;
|
|
13734
|
+
const window = typeof windowRaw === "number" && Number.isFinite(windowRaw) && windowRaw >= 0 ? Math.floor(windowRaw) : null;
|
|
13735
|
+
const exitCriteria = Array.isArray(o2.exit_criteria) ? o2.exit_criteria.map((c) => {
|
|
13736
|
+
if (typeof c === "string") return c.trim() ? { description: c.trim(), met: false } : null;
|
|
13737
|
+
if (c && typeof c === "object" && !Array.isArray(c)) {
|
|
13738
|
+
const co = c;
|
|
13739
|
+
const description = str22(co.description) ?? str22(co.criterion);
|
|
13740
|
+
if (!description) return null;
|
|
13741
|
+
return { description, met: co.met === true };
|
|
13742
|
+
}
|
|
13743
|
+
return null;
|
|
13744
|
+
}).filter((c) => Boolean(c)) : [];
|
|
13745
|
+
return {
|
|
13746
|
+
strategy: str22(o2.strategy),
|
|
13747
|
+
mechanism: str22(o2.mechanism),
|
|
13748
|
+
startedAt: str22(o2.started_at),
|
|
13749
|
+
observationWindowDays: window,
|
|
13750
|
+
exitCriteria
|
|
13751
|
+
};
|
|
13752
|
+
}
|
|
13753
|
+
function parseEnvironments2(v) {
|
|
13754
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return {};
|
|
13755
|
+
const out = {};
|
|
13756
|
+
for (const env of ENVIRONMENTS2) {
|
|
13757
|
+
const s = str22(v[env]);
|
|
13758
|
+
if (s) out[env] = s;
|
|
13759
|
+
}
|
|
13760
|
+
return out;
|
|
13761
|
+
}
|
|
13762
|
+
function parseTarget2(raw) {
|
|
13763
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
13764
|
+
const o2 = raw;
|
|
13765
|
+
const id = str22(o2.id);
|
|
13766
|
+
if (!id) return null;
|
|
13767
|
+
return {
|
|
13768
|
+
id,
|
|
13769
|
+
type: str22(o2.type) ?? "other",
|
|
13770
|
+
locator: str22(o2.locator),
|
|
13771
|
+
title: str22(o2.title),
|
|
13772
|
+
lifecycle: str22(o2.lifecycle) ?? "candidate",
|
|
13773
|
+
environments: parseEnvironments2(o2.environments),
|
|
13774
|
+
assessment: parseAssessment2(o2.assessment),
|
|
13775
|
+
usageEvidence: parseUsageEvidence2(o2.usage_evidence),
|
|
13776
|
+
dependsOnRemoval: strArray22(o2.depends_on_removal),
|
|
13777
|
+
resource: str22(o2.resource),
|
|
13778
|
+
module: str22(o2.module),
|
|
13779
|
+
softDeprecation: parseSoftDeprecation2(o2.soft_deprecation)
|
|
13780
|
+
};
|
|
13781
|
+
}
|
|
13782
|
+
function parseTargets2(v) {
|
|
13783
|
+
if (!Array.isArray(v)) return [];
|
|
13784
|
+
const out = [];
|
|
13785
|
+
for (const raw of v) {
|
|
13786
|
+
const t = parseTarget2(raw);
|
|
13787
|
+
if (t) out.push(t);
|
|
13788
|
+
}
|
|
13789
|
+
return out;
|
|
13790
|
+
}
|
|
13791
|
+
function lifecycleCountsOf2(targets) {
|
|
13792
|
+
const counts = {};
|
|
13793
|
+
for (const t of targets) counts[t.lifecycle] = (counts[t.lifecycle] ?? 0) + 1;
|
|
13794
|
+
return counts;
|
|
13795
|
+
}
|
|
13796
|
+
function purposeFromBody22(body) {
|
|
13797
|
+
const m2 = body.match(/^#{1,2}\s+Purpose\s*\n+([\s\S]*?)(?=\n#{1,2}\s|\s*$)/m);
|
|
13798
|
+
const text22 = m2 ? m2[1].trim() : "";
|
|
13799
|
+
return text22 || null;
|
|
13800
|
+
}
|
|
13801
|
+
function getDeprecationPlans2(dir) {
|
|
13802
|
+
return discoverKnowledge2(dir).filter((a2) => a2.type === DEPRECATION_ARTIFACT_TYPE2).map((a2) => {
|
|
13803
|
+
const fm = a2.rawFrontmatter;
|
|
13804
|
+
const targets = parseTargets2(fm.targets);
|
|
13805
|
+
return {
|
|
13806
|
+
id: a2.id || str22(fm.id) || a2.title,
|
|
13807
|
+
title: a2.title || str22(fm.title) || a2.id,
|
|
13808
|
+
path: a2.relPath,
|
|
13809
|
+
status: str22(fm.status),
|
|
13810
|
+
targetCount: targets.length,
|
|
13811
|
+
lifecycleCounts: lifecycleCountsOf2(targets)
|
|
13812
|
+
};
|
|
13813
|
+
}).sort((x2, y) => x2.id.localeCompare(y.id));
|
|
13814
|
+
}
|
|
13815
|
+
function getDeprecationPlan2(dir, id) {
|
|
13816
|
+
const match = discoverKnowledge2(dir).find(
|
|
13817
|
+
(a2) => a2.type === DEPRECATION_ARTIFACT_TYPE2 && (a2.id === id || a2.title === id)
|
|
13818
|
+
);
|
|
13819
|
+
if (!match) return null;
|
|
13820
|
+
const raw = readFile2(match.filePath);
|
|
13821
|
+
const parsed = matter52(raw);
|
|
13822
|
+
const fm = parsed.data ?? {};
|
|
13823
|
+
const body = parsed.content.trim();
|
|
13824
|
+
const targets = parseTargets2(fm.targets);
|
|
13825
|
+
return {
|
|
13826
|
+
id: str22(fm.id) ?? match.id,
|
|
13827
|
+
title: str22(fm.title) ?? match.title,
|
|
13828
|
+
path: match.relPath,
|
|
13829
|
+
status: str22(fm.status),
|
|
13830
|
+
targetCount: targets.length,
|
|
13831
|
+
lifecycleCounts: lifecycleCountsOf2(targets),
|
|
13832
|
+
purpose: str22(fm.purpose) ?? purposeFromBody22(body),
|
|
13833
|
+
targets,
|
|
13834
|
+
markdownBody: body
|
|
13835
|
+
};
|
|
13836
|
+
}
|
|
13338
13837
|
var KNOWLEDGE22 = "knowledge";
|
|
13339
13838
|
var RESOURCE_ROLE_EDGE2 = {
|
|
13340
13839
|
affected: "affects",
|
|
@@ -13378,9 +13877,9 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
13378
13877
|
];
|
|
13379
13878
|
const presentLayers = [];
|
|
13380
13879
|
for (const layer2 of layerDocs) {
|
|
13381
|
-
const
|
|
13382
|
-
if (
|
|
13383
|
-
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path:
|
|
13880
|
+
const path62 = layer2.files.map((f3) => `${KNOWLEDGE22}/${f3}`).find((rel) => exists2(join3(dir, rel)));
|
|
13881
|
+
if (path62) {
|
|
13882
|
+
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path62 });
|
|
13384
13883
|
presentLayers.push(layer2.id);
|
|
13385
13884
|
}
|
|
13386
13885
|
}
|
|
@@ -13438,6 +13937,33 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
13438
13937
|
addEdge(modId, resId, "depends_on");
|
|
13439
13938
|
}
|
|
13440
13939
|
}
|
|
13940
|
+
for (const summary of getDeprecationPlans2(dir)) {
|
|
13941
|
+
const plan = getDeprecationPlan2(dir, summary.id);
|
|
13942
|
+
if (!plan) continue;
|
|
13943
|
+
const planId = `deprecation-plan:${plan.id}`;
|
|
13944
|
+
addNode({ id: planId, type: "deprecation-plan", label: `${plan.id} ${plan.title}`.trim(), path: plan.path, status: plan.status ?? void 0 });
|
|
13945
|
+
for (const target of plan.targets) {
|
|
13946
|
+
const targetId = `deprecation-target:${target.id}`;
|
|
13947
|
+
addNode({ id: targetId, type: "deprecation-target", label: target.title ? `${target.id} ${target.title}` : target.id, path: plan.path, status: target.lifecycle });
|
|
13948
|
+
addEdge(planId, targetId, "targets");
|
|
13949
|
+
if (target.module) {
|
|
13950
|
+
const moduleId = `module:${target.module}`;
|
|
13951
|
+
addNode({ id: moduleId, type: "module", label: target.module });
|
|
13952
|
+
addEdge(targetId, moduleId, "targets");
|
|
13953
|
+
}
|
|
13954
|
+
if (target.resource) {
|
|
13955
|
+
const resourceId = `resource:${target.resource}`;
|
|
13956
|
+
const title = resourceTitleById.get(target.resource);
|
|
13957
|
+
addNode({ id: resourceId, type: "project-resource", label: title ? `${target.resource} ${title}` : target.resource });
|
|
13958
|
+
addEdge(targetId, resourceId, "targets");
|
|
13959
|
+
}
|
|
13960
|
+
for (const dependency of target.dependsOnRemoval) {
|
|
13961
|
+
const dependencyId = `deprecation-target:${dependency}`;
|
|
13962
|
+
addNode({ id: dependencyId, type: "deprecation-target", label: dependency });
|
|
13963
|
+
addEdge(targetId, dependencyId, "depends_on_removal");
|
|
13964
|
+
}
|
|
13965
|
+
}
|
|
13966
|
+
}
|
|
13441
13967
|
for (const wi of selectedWIs) {
|
|
13442
13968
|
const id = wi.id || wi.title;
|
|
13443
13969
|
if (!id || !id.trim()) continue;
|
|
@@ -13468,6 +13994,11 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
13468
13994
|
addNode({ id: resId, type: "project-resource", label: title ? `${rel.id} ${title}`.trim() : rel.id });
|
|
13469
13995
|
addEdge(wiNodeId, resId, RESOURCE_ROLE_EDGE2[rel.role] ?? "uses");
|
|
13470
13996
|
}
|
|
13997
|
+
for (const ref of parseWorkItemDeprecationTargets2(wi.rawFrontmatter)) {
|
|
13998
|
+
const targetId = `deprecation-target:${ref.target}`;
|
|
13999
|
+
addNode({ id: targetId, type: "deprecation-target", label: ref.target });
|
|
14000
|
+
addEdge(wiNodeId, targetId, "targets");
|
|
14001
|
+
}
|
|
13471
14002
|
for (const dec of wi.decisions) {
|
|
13472
14003
|
if (!dec || !dec.trim()) continue;
|
|
13473
14004
|
const adrId = `adr:${dec}`;
|
|
@@ -13827,13 +14358,13 @@ function meaningful2(text22) {
|
|
|
13827
14358
|
});
|
|
13828
14359
|
}
|
|
13829
14360
|
function readPocSummary2(dir) {
|
|
13830
|
-
const
|
|
13831
|
-
if (!exists2(
|
|
14361
|
+
const path62 = join3(dir, POC_ARTIFACT_PATH2);
|
|
14362
|
+
if (!exists2(path62)) {
|
|
13832
14363
|
return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
|
|
13833
14364
|
}
|
|
13834
14365
|
let markdown = "";
|
|
13835
14366
|
try {
|
|
13836
|
-
markdown = readFile2(
|
|
14367
|
+
markdown = readFile2(path62);
|
|
13837
14368
|
} catch {
|
|
13838
14369
|
return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
|
|
13839
14370
|
}
|
|
@@ -13864,11 +14395,11 @@ function redactSecrets2(value) {
|
|
|
13864
14395
|
return line.replace(/([:=]\s*)\S+.*/, "$1[redacted]");
|
|
13865
14396
|
}).join("\n");
|
|
13866
14397
|
}
|
|
13867
|
-
function source2(dir,
|
|
13868
|
-
const absolute = join3(dir,
|
|
14398
|
+
function source2(dir, path62, kind) {
|
|
14399
|
+
const absolute = join3(dir, path62);
|
|
13869
14400
|
if (!exists2(absolute)) return null;
|
|
13870
14401
|
const raw = readFile2(absolute);
|
|
13871
|
-
return { path: posix2(
|
|
14402
|
+
return { path: posix2(path62), kind, digest: sha2(raw), content: redactSecrets2(raw) };
|
|
13872
14403
|
}
|
|
13873
14404
|
function reportFiles2(dir) {
|
|
13874
14405
|
const delivery = join3(dir, "knowledge", "delivery");
|
|
@@ -13876,7 +14407,7 @@ function reportFiles2(dir) {
|
|
|
13876
14407
|
const match = name.match(reportName2);
|
|
13877
14408
|
if (!match) return [];
|
|
13878
14409
|
try {
|
|
13879
|
-
const parsed =
|
|
14410
|
+
const parsed = matter72(readFile2(join3(delivery, name)));
|
|
13880
14411
|
const fingerprint = typeof parsed.data.source_fingerprint === "string" ? parsed.data.source_fingerprint : "";
|
|
13881
14412
|
const manifest = parsed.data.source_manifest;
|
|
13882
14413
|
const sourceManifest = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? Object.fromEntries(Object.entries(manifest).filter(([, digest]) => typeof digest === "string")) : {};
|
|
@@ -13908,9 +14439,9 @@ function buildPocReportContext2(dir) {
|
|
|
13908
14439
|
};
|
|
13909
14440
|
}
|
|
13910
14441
|
const sources = [];
|
|
13911
|
-
const add = (
|
|
13912
|
-
if (sources.some((item2) => item2.path === posix2(
|
|
13913
|
-
const item = source2(dir,
|
|
14442
|
+
const add = (path62, kind) => {
|
|
14443
|
+
if (sources.some((item2) => item2.path === posix2(path62))) return;
|
|
14444
|
+
const item = source2(dir, path62, kind);
|
|
13914
14445
|
if (item) sources.push(item);
|
|
13915
14446
|
};
|
|
13916
14447
|
add(POC_ARTIFACT_PATH2, "poc");
|
|
@@ -13938,7 +14469,7 @@ function buildPocReportContext2(dir) {
|
|
|
13938
14469
|
const sourceFingerprint = sha2(sources.map((item) => `${item.path}\0${item.digest}`).join("\n"));
|
|
13939
14470
|
const status = !latestReport ? "missing" : latestReport.sourceFingerprint === sourceFingerprint ? "current" : "stale";
|
|
13940
14471
|
const currentManifest = Object.fromEntries(sources.map((item) => [item.path, item.digest]));
|
|
13941
|
-
const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((
|
|
14472
|
+
const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((path62) => currentManifest[path62] !== latestReport?.sourceManifest[path62]).sort() : [];
|
|
13942
14473
|
const sourceList = sources.map((item) => `- ${item.path} (${item.kind})`).join("\n") || "- No sources selected.";
|
|
13943
14474
|
return {
|
|
13944
14475
|
eligible: true,
|
|
@@ -13954,6 +14485,9 @@ function buildPocReportContext2(dir) {
|
|
|
13954
14485
|
`Canonical conclusion: ${poc.conclusion}. Do not contradict it.`,
|
|
13955
14486
|
"Use the 15 canonical sections. Mark missing evidence as Not evaluated, Not applicable, or No evidence available.",
|
|
13956
14487
|
"Distinguish measured, calculated, estimated, and projected values. Never include secret values.",
|
|
14488
|
+
"Choose the representation that best communicates each grounded fact: Markdown tables for structured comparisons, Mermaid for grounded architecture, flows, or relationships, and prose for explanation and interpretation.",
|
|
14489
|
+
"Do not invent data, entities, relationships, priorities, or metrics to create a table or diagram. Do not use ASCII diagrams when Mermaid can represent the same grounded flow.",
|
|
14490
|
+
"In Traceability, use repository-relative Markdown links or paths. Never use file:/// URLs or local absolute filesystem paths.",
|
|
13957
14491
|
"",
|
|
13958
14492
|
"Selected sources:",
|
|
13959
14493
|
sourceList
|
|
@@ -13972,7 +14506,7 @@ function canonicalReport(context, proposal) {
|
|
|
13972
14506
|
source_manifest: Object.fromEntries(context.sources.map((item) => [item.path, item.digest]))
|
|
13973
14507
|
};
|
|
13974
14508
|
if (supersedes) data.supersedes = supersedes;
|
|
13975
|
-
return
|
|
14509
|
+
return matter72.stringify(redactSecrets2(proposal).trimEnd() + "\n", data);
|
|
13976
14510
|
}
|
|
13977
14511
|
function persistPocReport(dir, proposal, opts = {}) {
|
|
13978
14512
|
const context = buildPocReportContext2(dir);
|
|
@@ -14678,6 +15212,49 @@ After a POC has a canonical conclusion and Core has prepared its deterministic r
|
|
|
14678
15212
|
- Use \`Not evaluated in this POC.\`, \`Not applicable.\`, or \`No evidence available.\` when sources do not support a section.
|
|
14679
15213
|
- Return a proposal for human review; Core persists it only after confirmation.
|
|
14680
15214
|
|
|
15215
|
+
## Representation selection
|
|
15216
|
+
|
|
15217
|
+
- Use prose for context, interpretation, rationale, findings, and conclusions.
|
|
15218
|
+
- Use a Markdown table when supplied evidence has comparable rows, such as success criteria and
|
|
15219
|
+
outcomes, resources, experiments, measurements, costs, risks, recommendations, or traceability.
|
|
15220
|
+
- Use Mermaid only for architecture, topology, or flow relationships explicitly supported by the
|
|
15221
|
+
supplied sources. Prefer it to an ASCII diagram when both can represent the same grounded flow.
|
|
15222
|
+
- Tables and diagrams are communication tools, not completeness requirements. Do not produce one
|
|
15223
|
+
when the context does not support a useful representation.
|
|
15224
|
+
- Never invent table rows, diagram nodes or edges, statuses, priorities, metrics, or relationships
|
|
15225
|
+
to make a report look more complete.
|
|
15226
|
+
- Preserve measured, calculated, estimated, and projected semantics in every representation.
|
|
15227
|
+
- In Traceability, use repository-relative Markdown links or paths; never emit \`file:///\` URLs or
|
|
15228
|
+
local absolute filesystem paths.
|
|
15229
|
+
|
|
15230
|
+
## Section representation guide
|
|
15231
|
+
|
|
15232
|
+
| Section | Preferred representation when supported by evidence |
|
|
15233
|
+
|---|---|
|
|
15234
|
+
| Executive Summary | Snapshot table and short prose |
|
|
15235
|
+
| Problem and Objective | Prose |
|
|
15236
|
+
| Hypothesis and Success Criteria | Evaluation matrix |
|
|
15237
|
+
| Scope, Constraints and Non-goals | Compact bullets or table |
|
|
15238
|
+
| Technical Approach and Architecture | Mermaid and prose |
|
|
15239
|
+
| Infrastructure and Project Resources | Table, with optional Mermaid |
|
|
15240
|
+
| Implementation and Relevant Technical Decisions | Table or concise prose |
|
|
15241
|
+
| Experiments and Validation | Validation matrix |
|
|
15242
|
+
| Results and Measurements | Measurement tables |
|
|
15243
|
+
| Cost and Efficiency Analysis | Comparison table |
|
|
15244
|
+
| Observability and Operational Findings | Table or prose |
|
|
15245
|
+
| Findings, Learnings, Limitations and Risks | Structured matrix |
|
|
15246
|
+
| Conclusion | Status and concise prose |
|
|
15247
|
+
| Recommendation and Next Steps | Recommendation matrix only when priority is evidenced |
|
|
15248
|
+
| Traceability and Sources | Relative-link table or list |
|
|
15249
|
+
|
|
15250
|
+
## Quality checklist
|
|
15251
|
+
|
|
15252
|
+
- Structured evidence uses tables when clearer than prose.
|
|
15253
|
+
- Grounded architecture or flows use Mermaid when useful; no decorative or unsupported diagrams exist.
|
|
15254
|
+
- Tables and Mermaid preserve source grounding and measurement semantics.
|
|
15255
|
+
- Traceability contains repository-portable paths.
|
|
15256
|
+
- The report is scannable before reading the full narrative.
|
|
15257
|
+
|
|
14681
15258
|
## Required structure
|
|
14682
15259
|
|
|
14683
15260
|
1. Executive Summary
|
|
@@ -17895,6 +18472,8 @@ function getWorkItem(dir, workItemId) {
|
|
|
17895
18472
|
markdownBody: body.trim(),
|
|
17896
18473
|
// External systems this WI relates to (WI-030), separate from affectedModules (code).
|
|
17897
18474
|
resources: parseWorkItemResources2(fm),
|
|
18475
|
+
// Deprecation Targets this WI retires (WI-050), referenced not copied.
|
|
18476
|
+
deprecationTargets: parseWorkItemDeprecationTargets2(fm),
|
|
17898
18477
|
...parseSystemImpact(dir, fm)
|
|
17899
18478
|
};
|
|
17900
18479
|
return { ...detail, refinement: computeRefinementStatus(detail) };
|
|
@@ -17903,8 +18482,8 @@ function parseGraphReason(v) {
|
|
|
17903
18482
|
if (!v || typeof v !== "object") return null;
|
|
17904
18483
|
const o2 = v;
|
|
17905
18484
|
const relationship = typeof o2.relationship === "string" && o2.relationship.trim() ? o2.relationship.trim() : null;
|
|
17906
|
-
const
|
|
17907
|
-
return relationship ||
|
|
18485
|
+
const path62 = Array.isArray(o2.path) ? o2.path.map(String).filter(Boolean) : [];
|
|
18486
|
+
return relationship || path62.length ? { relationship, path: path62 } : null;
|
|
17908
18487
|
}
|
|
17909
18488
|
function parseExplain(o2) {
|
|
17910
18489
|
const refs = Array.isArray(o2.evidence) ? o2.evidence : Array.isArray(o2.evidence_refs) ? o2.evidence_refs : [];
|
|
@@ -17959,7 +18538,7 @@ function parseOriginalSnapshot(fm) {
|
|
|
17959
18538
|
function readBody(filePath) {
|
|
17960
18539
|
try {
|
|
17961
18540
|
const raw = readFile2(filePath);
|
|
17962
|
-
return
|
|
18541
|
+
return matter112(raw).content;
|
|
17963
18542
|
} catch {
|
|
17964
18543
|
return "";
|
|
17965
18544
|
}
|
|
@@ -18244,7 +18823,7 @@ function ensureH1(body, title) {
|
|
|
18244
18823
|
${trimmed}`;
|
|
18245
18824
|
}
|
|
18246
18825
|
function serialize2(data, body) {
|
|
18247
|
-
return
|
|
18826
|
+
return matter122.stringify(`
|
|
18248
18827
|
${body.trim()}
|
|
18249
18828
|
`, data);
|
|
18250
18829
|
}
|
|
@@ -18609,6 +19188,61 @@ function buildRefinementHandoff(dir, workItemId) {
|
|
|
18609
19188
|
text: lines.join("\n")
|
|
18610
19189
|
};
|
|
18611
19190
|
}
|
|
19191
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
19192
|
+
function isStrongNoUsage(sig) {
|
|
19193
|
+
return sig.status === "no-usage" && (sig.quality === "observed-fact" || sig.quality === "human-confirmed");
|
|
19194
|
+
}
|
|
19195
|
+
function deriveUnusedConfidence(evidence) {
|
|
19196
|
+
const present = EVIDENCE_SIGNALS2.map((name) => ({ name, sig: evidence[name] })).filter(
|
|
19197
|
+
(e) => Boolean(e.sig)
|
|
19198
|
+
);
|
|
19199
|
+
const usageFound = present.filter((e) => e.sig.status === "usage-found");
|
|
19200
|
+
if (usageFound.length > 0) {
|
|
19201
|
+
return {
|
|
19202
|
+
confidence: "in-use",
|
|
19203
|
+
requiresHumanConfirmation: false,
|
|
19204
|
+
rationale: [`Usage found via: ${usageFound.map((e) => e.name).join(", ")}.`]
|
|
19205
|
+
};
|
|
19206
|
+
}
|
|
19207
|
+
const strong = present.filter((e) => isStrongNoUsage(e.sig));
|
|
19208
|
+
const unknown = present.filter((e) => e.sig.status === "unknown");
|
|
19209
|
+
const notEvaluated = EVIDENCE_SIGNALS2.filter(
|
|
19210
|
+
(name) => !evidence[name] || evidence[name]?.status === "not-evaluated"
|
|
19211
|
+
);
|
|
19212
|
+
const rationale = [];
|
|
19213
|
+
if (strong.length > 0) rationale.push(`No usage observed via: ${strong.map((e) => e.name).join(", ")}.`);
|
|
19214
|
+
if (unknown.length > 0) {
|
|
19215
|
+
rationale.push(`Unknown (looked but could not determine): ${unknown.map((e) => e.name).join(", ")} \u2014 NOT treated as no-usage.`);
|
|
19216
|
+
}
|
|
19217
|
+
if (notEvaluated.length > 0) {
|
|
19218
|
+
rationale.push(`Not evaluated: ${notEvaluated.join(", ")} \u2014 absence of a reference is not evidence of no usage.`);
|
|
19219
|
+
}
|
|
19220
|
+
let confidence;
|
|
19221
|
+
if (strong.length === 0) {
|
|
19222
|
+
confidence = "insufficient-evidence";
|
|
19223
|
+
} else if (unknown.length > 0) {
|
|
19224
|
+
confidence = "medium";
|
|
19225
|
+
} else if (strong.length >= 2) {
|
|
19226
|
+
confidence = "high";
|
|
19227
|
+
} else {
|
|
19228
|
+
confidence = "medium";
|
|
19229
|
+
}
|
|
19230
|
+
return { confidence, requiresHumanConfirmation: true, rationale };
|
|
19231
|
+
}
|
|
19232
|
+
var CANDIDATE_DISCOVERY_GUIDANCE = [
|
|
19233
|
+
"Propose deprecation candidates from multiple sources: static references (code/routes/UI/jobs),",
|
|
19234
|
+
"the System Graph (dependencies and blast radius), Legacy Knowledge (risks/unknowns/modernization",
|
|
19235
|
+
"candidates), Project Resources (databases/external systems and their boundaries), runtime evidence",
|
|
19236
|
+
"when telemetry exists, and database metadata when reachable.",
|
|
19237
|
+
"",
|
|
19238
|
+
"Rules:",
|
|
19239
|
+
"- Discovery is a PROPOSAL, not a materialization: never create or retire anything automatically.",
|
|
19240
|
+
"- Always explain WHY something is a candidate, citing the signal(s) observed.",
|
|
19241
|
+
"- Absence of a reference is NOT evidence of no usage. If a source cannot be evaluated, record it",
|
|
19242
|
+
" as not-evaluated/unknown and require human investigation \u2014 never collapse it to no-usage.",
|
|
19243
|
+
"- A modernization candidate is not automatically a deprecation target.",
|
|
19244
|
+
"- Soft deprecation precedes physical removal; order retirements by dependency, dependents first."
|
|
19245
|
+
].join("\n");
|
|
18612
19246
|
var WorkItemNotReadyError = class extends Error {
|
|
18613
19247
|
constructor(workItemId, actualState) {
|
|
18614
19248
|
super(`Work Item "${workItemId}" must be in "ready" state to produce an Implementation Handoff, but is "${actualState}".`);
|
|
@@ -18639,6 +19273,16 @@ function buildImplementationHandoff(dir, workItemId) {
|
|
|
18639
19273
|
const domains = match.domains;
|
|
18640
19274
|
const scopeConfidence = match.scopeConfidence;
|
|
18641
19275
|
const wiResources = parseWorkItemResources2(match.rawFrontmatter);
|
|
19276
|
+
const wiDeprecationRefs = parseWorkItemDeprecationTargets2(match.rawFrontmatter);
|
|
19277
|
+
const resolveTarget = (ref) => {
|
|
19278
|
+
const plans = ref.plan ? [getDeprecationPlan2(dir, ref.plan)].filter(Boolean) : getDeprecationPlans2(dir).map((p) => getDeprecationPlan2(dir, p.id));
|
|
19279
|
+
for (const p of plans) {
|
|
19280
|
+
if (!p) continue;
|
|
19281
|
+
const t = p.targets.find((x2) => x2.id.toUpperCase() === ref.target.toUpperCase());
|
|
19282
|
+
if (t) return { planId: p.id, target: t };
|
|
19283
|
+
}
|
|
19284
|
+
return null;
|
|
19285
|
+
};
|
|
18642
19286
|
const lines = [
|
|
18643
19287
|
`Implement Work Item ${wiId} \u2014 "${wiTitle}" \u2014 in project "${projectName}" using Kaddo.`,
|
|
18644
19288
|
"",
|
|
@@ -18763,6 +19407,48 @@ function buildImplementationHandoff(dir, workItemId) {
|
|
|
18763
19407
|
"installed or connected. Do not execute any interface or migration without explicit human confirmation."
|
|
18764
19408
|
);
|
|
18765
19409
|
}
|
|
19410
|
+
if (wiDeprecationRefs.length > 0) {
|
|
19411
|
+
lines.push(
|
|
19412
|
+
"",
|
|
19413
|
+
"--- Relevant Deprecation Targets ---",
|
|
19414
|
+
"",
|
|
19415
|
+
"This Work Item is part of a Controlled Deprecation plan. It RETIRES the targets below. Soft",
|
|
19416
|
+
"deprecation precedes physical removal; in Dev/Test you may remove code/endpoints/dev-test data",
|
|
19417
|
+
"when the resource boundary allows and there is confirmation. A production change needs a Runbook",
|
|
19418
|
+
"and explicit human approval \u2014 never execute a destructive production operation autonomously."
|
|
19419
|
+
);
|
|
19420
|
+
for (const ref of wiDeprecationRefs) {
|
|
19421
|
+
const resolved = resolveTarget(ref);
|
|
19422
|
+
if (!resolved) {
|
|
19423
|
+
lines.push("", `- ${ref.target}${ref.plan ? ` (plan ${ref.plan})` : ""} \u2014 target not found in any Deprecation Plan.`);
|
|
19424
|
+
continue;
|
|
19425
|
+
}
|
|
19426
|
+
const { planId, target: t } = resolved;
|
|
19427
|
+
lines.push("", `- ${t.id} [${t.type}] \u2014 plan ${planId}, lifecycle: ${t.lifecycle}`);
|
|
19428
|
+
if (t.locator) lines.push(` locator: ${t.locator}`);
|
|
19429
|
+
const envs = Object.entries(t.environments).map(([e, s]) => `${e}=${s}`).join(" ");
|
|
19430
|
+
if (envs) lines.push(` environments: ${envs}`);
|
|
19431
|
+
const unused = deriveUnusedConfidence(t.usageEvidence);
|
|
19432
|
+
lines.push(` unused confidence: ${unused.confidence}${unused.requiresHumanConfirmation ? " (human confirmation required)" : ""}`);
|
|
19433
|
+
if (t.assessment.blastRadius) lines.push(` blast radius: ${t.assessment.blastRadius}`);
|
|
19434
|
+
if (t.assessment.reversibility) lines.push(` reversibility: ${t.assessment.reversibility}`);
|
|
19435
|
+
if (t.dependsOnRemoval.length > 0) lines.push(` depends_on_removal: ${t.dependsOnRemoval.join(", ")} (retire these first)`);
|
|
19436
|
+
if (t.resource) {
|
|
19437
|
+
const res = getResource2(dir, t.resource);
|
|
19438
|
+
if (res) {
|
|
19439
|
+
const bounds = Object.entries(res.boundaries).map(([env, ops]) => `${env}: ${ops.join("/")}`).join(" \xB7 ");
|
|
19440
|
+
lines.push(` resource: ${res.id} \u2014 ${res.title}${bounds ? ` (boundaries \u2014 ${bounds})` : ""}`);
|
|
19441
|
+
} else {
|
|
19442
|
+
lines.push(` resource: ${t.resource} \u2014 definition not found under knowledge/tech/resources/.`);
|
|
19443
|
+
}
|
|
19444
|
+
}
|
|
19445
|
+
}
|
|
19446
|
+
lines.push(
|
|
19447
|
+
"",
|
|
19448
|
+
"Record what you actually retired as Evidence. Prepare a rollback for every change; for production,",
|
|
19449
|
+
"the rollback/abort path lives in the Runbook, not here."
|
|
19450
|
+
);
|
|
19451
|
+
}
|
|
18766
19452
|
lines.push(
|
|
18767
19453
|
"",
|
|
18768
19454
|
"--- Implementation Plan ---",
|
|
@@ -18784,6 +19470,7 @@ function buildImplementationHandoff(dir, workItemId) {
|
|
|
18784
19470
|
lifecycle,
|
|
18785
19471
|
affectedModules,
|
|
18786
19472
|
resources: wiResources,
|
|
19473
|
+
deprecationTargets: wiDeprecationRefs,
|
|
18787
19474
|
domains,
|
|
18788
19475
|
scopeConfidence,
|
|
18789
19476
|
hasUnmaterializedDecisions: unmaterialized,
|
|
@@ -19035,7 +19722,7 @@ function detectFormat(raw) {
|
|
|
19035
19722
|
if (!trimmed) throw new ImportError("EMPTY_INPUT", "Import content is empty.");
|
|
19036
19723
|
if (trimmed.startsWith("---")) {
|
|
19037
19724
|
try {
|
|
19038
|
-
const { data } =
|
|
19725
|
+
const { data } = matter132(trimmed);
|
|
19039
19726
|
if (data && typeof data === "object" && Object.keys(data).length > 0) {
|
|
19040
19727
|
const d3 = data;
|
|
19041
19728
|
if (d3.type === "work-item" || d3.id && typeof d3.id === "string" && /^WI-\d+/.test(d3.id)) {
|
|
@@ -19057,7 +19744,7 @@ function parseContent(raw) {
|
|
|
19057
19744
|
let bodyText = trimmed;
|
|
19058
19745
|
if (format === "kaddo-frontmatter" || format === "markdown-frontmatter") {
|
|
19059
19746
|
try {
|
|
19060
|
-
const parsed =
|
|
19747
|
+
const parsed = matter132(trimmed);
|
|
19061
19748
|
frontmatterFields = parsed.data ?? {};
|
|
19062
19749
|
bodyText = parsed.content.trim();
|
|
19063
19750
|
} catch {
|
|
@@ -19154,7 +19841,7 @@ function findDuplicateByHash(dir, hash) {
|
|
|
19154
19841
|
const raw = readFile2(item.filePath);
|
|
19155
19842
|
if (!raw) continue;
|
|
19156
19843
|
try {
|
|
19157
|
-
const { data } =
|
|
19844
|
+
const { data } = matter132(raw);
|
|
19158
19845
|
const source22 = data?.source;
|
|
19159
19846
|
if (source22 && typeof source22 === "object" && !Array.isArray(source22)) {
|
|
19160
19847
|
const s = source22;
|
|
@@ -19663,8 +20350,8 @@ async function handleJiraResponse(res) {
|
|
|
19663
20350
|
}
|
|
19664
20351
|
return await res.json();
|
|
19665
20352
|
}
|
|
19666
|
-
async function jiraFetch(baseUrl,
|
|
19667
|
-
const url = new URL(
|
|
20353
|
+
async function jiraFetch(baseUrl, path62, auth, timeoutMs, params) {
|
|
20354
|
+
const url = new URL(path62, baseUrl + "/");
|
|
19668
20355
|
if (params) for (const [k2, v] of Object.entries(params)) url.searchParams.set(k2, v);
|
|
19669
20356
|
const controller = new AbortController();
|
|
19670
20357
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -19682,8 +20369,8 @@ async function jiraFetch(baseUrl, path52, auth, timeoutMs, params) {
|
|
|
19682
20369
|
clearTimeout(timer);
|
|
19683
20370
|
}
|
|
19684
20371
|
}
|
|
19685
|
-
async function jiraPost(baseUrl,
|
|
19686
|
-
const url = new URL(
|
|
20372
|
+
async function jiraPost(baseUrl, path62, auth, timeoutMs, body) {
|
|
20373
|
+
const url = new URL(path62, baseUrl + "/");
|
|
19687
20374
|
const controller = new AbortController();
|
|
19688
20375
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
19689
20376
|
try {
|
|
@@ -20049,7 +20736,7 @@ function buildInitiativeContent(id, input) {
|
|
|
20049
20736
|
if (input.priority) data.priority = input.priority;
|
|
20050
20737
|
if (input.source) data.source = input.source;
|
|
20051
20738
|
if (input.sourceId) data.source_id = input.sourceId;
|
|
20052
|
-
return
|
|
20739
|
+
return matter15.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
|
|
20053
20740
|
}
|
|
20054
20741
|
function createInitiative(dir, input) {
|
|
20055
20742
|
if (!input.title?.trim()) throw new InitiativeWriteError("Initiative title is required.");
|
|
@@ -20073,10 +20760,10 @@ function transitionInitiative(dir, id, to) {
|
|
|
20073
20760
|
throw new InitiativeWriteError(`Invalid transition ${ini.status} \u2192 ${to}.`);
|
|
20074
20761
|
}
|
|
20075
20762
|
const raw = readFile2(ini.filePath);
|
|
20076
|
-
const { data, content } =
|
|
20763
|
+
const { data, content } = matter15(raw, {});
|
|
20077
20764
|
data.status = to;
|
|
20078
20765
|
if (to === "completed") data.completed_at = today2();
|
|
20079
|
-
const newContent =
|
|
20766
|
+
const newContent = matter15.stringify(content, data);
|
|
20080
20767
|
writeFile2(ini.filePath, newContent);
|
|
20081
20768
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
20082
20769
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after transition.");
|
|
@@ -20086,7 +20773,7 @@ function addCandidate(dir, id, candidate) {
|
|
|
20086
20773
|
const ini = getInitiative(dir, id);
|
|
20087
20774
|
if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
|
|
20088
20775
|
const raw = readFile2(ini.filePath);
|
|
20089
|
-
const { data, content } =
|
|
20776
|
+
const { data, content } = matter15(raw, {});
|
|
20090
20777
|
const existing = Array.isArray(data.candidates) ? data.candidates : [];
|
|
20091
20778
|
const fallbackId = `WI-CANDIDATE-${String(existing.length + 1).padStart(3, "0")}`;
|
|
20092
20779
|
const normalized = normalizeCandidate(candidate, fallbackId);
|
|
@@ -20094,7 +20781,7 @@ function addCandidate(dir, id, candidate) {
|
|
|
20094
20781
|
throw new InitiativeWriteError(`Candidate ${normalized.id} already exists in ${id}.`);
|
|
20095
20782
|
}
|
|
20096
20783
|
data.candidates = [...existing, candidateToData(normalized)];
|
|
20097
|
-
const newContent =
|
|
20784
|
+
const newContent = matter15.stringify(content, data);
|
|
20098
20785
|
writeFile2(ini.filePath, newContent);
|
|
20099
20786
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
20100
20787
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding a candidate.");
|
|
@@ -20104,7 +20791,7 @@ function updateInitiative(dir, id, patch) {
|
|
|
20104
20791
|
const ini = getInitiative(dir, id);
|
|
20105
20792
|
if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
|
|
20106
20793
|
const raw = readFile2(ini.filePath);
|
|
20107
|
-
const { data, content } =
|
|
20794
|
+
const { data, content } = matter15(raw, {});
|
|
20108
20795
|
if (patch.title !== void 0) {
|
|
20109
20796
|
if (!patch.title.trim()) throw new InitiativeWriteError("Initiative title cannot be empty.");
|
|
20110
20797
|
data.title = patch.title.trim();
|
|
@@ -20114,7 +20801,7 @@ function updateInitiative(dir, id, patch) {
|
|
|
20114
20801
|
if (patch.knowledgeLevel !== void 0) data.knowledge_level = patch.knowledgeLevel;
|
|
20115
20802
|
if (patch.domains !== void 0) data.domains = patch.domains;
|
|
20116
20803
|
if (patch.relatedCapabilities !== void 0) data.related_capabilities = patch.relatedCapabilities;
|
|
20117
|
-
const newContent =
|
|
20804
|
+
const newContent = matter15.stringify(content, data);
|
|
20118
20805
|
writeFile2(ini.filePath, newContent);
|
|
20119
20806
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
20120
20807
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after update.");
|
|
@@ -20132,10 +20819,10 @@ function addExternalLink(dir, id, link) {
|
|
|
20132
20819
|
throw new InitiativeWriteError(`External link ${link.integration}:${link.externalId} already exists on ${id}.`);
|
|
20133
20820
|
}
|
|
20134
20821
|
const raw = readFile2(ini.filePath);
|
|
20135
|
-
const { data, content } =
|
|
20822
|
+
const { data, content } = matter15(raw, {});
|
|
20136
20823
|
const existing = Array.isArray(data.external_links) ? data.external_links : [];
|
|
20137
20824
|
data.external_links = [...existing, externalLinkToData(link)];
|
|
20138
|
-
const newContent =
|
|
20825
|
+
const newContent = matter15.stringify(content, data);
|
|
20139
20826
|
writeFile2(ini.filePath, newContent);
|
|
20140
20827
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
20141
20828
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding an external link.");
|
|
@@ -20204,7 +20891,7 @@ function buildWorkItemContent(wiId, iniId, candidate) {
|
|
|
20204
20891
|
"_What did we learn? Update after completion._",
|
|
20205
20892
|
""
|
|
20206
20893
|
].join("\n");
|
|
20207
|
-
return
|
|
20894
|
+
return matter15.stringify("\n" + body, data);
|
|
20208
20895
|
}
|
|
20209
20896
|
function materializeCandidate(dir, id, candidateId) {
|
|
20210
20897
|
const ini = getInitiative(dir, id);
|
|
@@ -20221,13 +20908,13 @@ function materializeCandidate(dir, id, candidateId) {
|
|
|
20221
20908
|
ensureDir2(join3(dir, DRAFT_DIR));
|
|
20222
20909
|
writeFile2(join3(dir, DRAFT_DIR, fileName), buildWorkItemContent(wiId, ini.id, candidate));
|
|
20223
20910
|
const raw = readFile2(ini.filePath);
|
|
20224
|
-
const { data, content } =
|
|
20911
|
+
const { data, content } = matter15(raw, {});
|
|
20225
20912
|
const cands = Array.isArray(data.candidates) ? data.candidates : [];
|
|
20226
20913
|
for (const c of cands) {
|
|
20227
20914
|
if (c && typeof c === "object" && String(c.id) === candidateId) c.materialized_as = wiId;
|
|
20228
20915
|
}
|
|
20229
20916
|
data.candidates = cands;
|
|
20230
|
-
const newContent =
|
|
20917
|
+
const newContent = matter15.stringify(content, data);
|
|
20231
20918
|
writeFile2(ini.filePath, newContent);
|
|
20232
20919
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
20233
20920
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after materialization.");
|
|
@@ -20341,7 +21028,7 @@ function evaluateInitiativeCompletion(dir, ini) {
|
|
|
20341
21028
|
// ../cli/src/core/resource-write.ts
|
|
20342
21029
|
import fs6 from "fs";
|
|
20343
21030
|
import path5 from "path";
|
|
20344
|
-
import
|
|
21031
|
+
import matter18 from "gray-matter";
|
|
20345
21032
|
var RESOURCES_DIR = "knowledge/tech/resources";
|
|
20346
21033
|
var ResourceWriteError = class extends Error {
|
|
20347
21034
|
constructor(code, message) {
|
|
@@ -20421,7 +21108,7 @@ function buildFrontmatter(id, input, base = {}) {
|
|
|
20421
21108
|
return data;
|
|
20422
21109
|
}
|
|
20423
21110
|
function serialize(data, body) {
|
|
20424
|
-
return
|
|
21111
|
+
return matter18.stringify(`
|
|
20425
21112
|
${body.trim()}
|
|
20426
21113
|
`, data);
|
|
20427
21114
|
}
|
|
@@ -20461,7 +21148,7 @@ function updateResource(dir, id, input) {
|
|
|
20461
21148
|
throw new ResourceWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
|
|
20462
21149
|
}
|
|
20463
21150
|
const raw = readFile(match.filePath);
|
|
20464
|
-
const parsed =
|
|
21151
|
+
const parsed = matter18(raw);
|
|
20465
21152
|
const base = parsed.data ?? {};
|
|
20466
21153
|
const data = buildFrontmatter(match.id || id, input, base);
|
|
20467
21154
|
let body = parsed.content.trim();
|
|
@@ -20496,6 +21183,355 @@ function deleteResource(dir, id, opts = {}) {
|
|
|
20496
21183
|
return { deleted: true, path: match.relPath };
|
|
20497
21184
|
}
|
|
20498
21185
|
|
|
21186
|
+
// ../cli/src/core/deprecation-observation.ts
|
|
21187
|
+
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
21188
|
+
function addDaysIso(startIso, days) {
|
|
21189
|
+
const start = Date.parse(startIso);
|
|
21190
|
+
if (Number.isNaN(start)) return null;
|
|
21191
|
+
return new Date(start + days * DAY_MS2).toISOString().slice(0, 10);
|
|
21192
|
+
}
|
|
21193
|
+
function observationStatus(target, now = /* @__PURE__ */ new Date()) {
|
|
21194
|
+
const sd = target.softDeprecation;
|
|
21195
|
+
const reasons = [];
|
|
21196
|
+
if (!sd) {
|
|
21197
|
+
return {
|
|
21198
|
+
declared: false,
|
|
21199
|
+
windowElapsed: false,
|
|
21200
|
+
windowEndsAt: null,
|
|
21201
|
+
criteriaTotal: 0,
|
|
21202
|
+
criteriaMet: 0,
|
|
21203
|
+
allCriteriaMet: false,
|
|
21204
|
+
eligibleForRemovalReview: false,
|
|
21205
|
+
reasons: ["No soft-deprecation/observation window declared \u2014 not eligible for removal review."]
|
|
21206
|
+
};
|
|
21207
|
+
}
|
|
21208
|
+
const criteriaTotal = sd.exitCriteria.length;
|
|
21209
|
+
const criteriaMet = sd.exitCriteria.filter((c) => c.met).length;
|
|
21210
|
+
const allCriteriaMet = criteriaTotal > 0 && criteriaMet === criteriaTotal;
|
|
21211
|
+
let windowElapsed = false;
|
|
21212
|
+
let windowEndsAt = null;
|
|
21213
|
+
if (sd.startedAt && sd.observationWindowDays != null) {
|
|
21214
|
+
windowEndsAt = addDaysIso(sd.startedAt, sd.observationWindowDays);
|
|
21215
|
+
if (windowEndsAt) {
|
|
21216
|
+
windowElapsed = now.getTime() >= Date.parse(windowEndsAt);
|
|
21217
|
+
}
|
|
21218
|
+
}
|
|
21219
|
+
if (!sd.startedAt) reasons.push("Observation has not started (no started_at).");
|
|
21220
|
+
else if (sd.observationWindowDays == null) reasons.push("No observation_window_days declared.");
|
|
21221
|
+
else if (!windowElapsed) reasons.push(`Observation window not elapsed (ends ${windowEndsAt ?? "unknown"}).`);
|
|
21222
|
+
if (criteriaTotal === 0) reasons.push("No exit criteria declared.");
|
|
21223
|
+
else if (!allCriteriaMet) reasons.push(`Exit criteria not all met (${criteriaMet}/${criteriaTotal}).`);
|
|
21224
|
+
const eligibleForRemovalReview = windowElapsed && allCriteriaMet;
|
|
21225
|
+
if (eligibleForRemovalReview) {
|
|
21226
|
+
reasons.length = 0;
|
|
21227
|
+
reasons.push("Observation window elapsed and all exit criteria met \u2014 eligible for removal REVIEW (human decision).");
|
|
21228
|
+
}
|
|
21229
|
+
return {
|
|
21230
|
+
declared: true,
|
|
21231
|
+
windowElapsed,
|
|
21232
|
+
windowEndsAt,
|
|
21233
|
+
criteriaTotal,
|
|
21234
|
+
criteriaMet,
|
|
21235
|
+
allCriteriaMet,
|
|
21236
|
+
eligibleForRemovalReview,
|
|
21237
|
+
reasons
|
|
21238
|
+
};
|
|
21239
|
+
}
|
|
21240
|
+
|
|
21241
|
+
// ../cli/src/core/deprecation-intelligence.ts
|
|
21242
|
+
function isStrongNoUsage2(sig) {
|
|
21243
|
+
return sig.status === "no-usage" && (sig.quality === "observed-fact" || sig.quality === "human-confirmed");
|
|
21244
|
+
}
|
|
21245
|
+
function deriveUnusedConfidence2(evidence) {
|
|
21246
|
+
const present = EVIDENCE_SIGNALS.map((name) => ({ name, sig: evidence[name] })).filter(
|
|
21247
|
+
(e) => Boolean(e.sig)
|
|
21248
|
+
);
|
|
21249
|
+
const usageFound = present.filter((e) => e.sig.status === "usage-found");
|
|
21250
|
+
if (usageFound.length > 0) {
|
|
21251
|
+
return {
|
|
21252
|
+
confidence: "in-use",
|
|
21253
|
+
requiresHumanConfirmation: false,
|
|
21254
|
+
rationale: [`Usage found via: ${usageFound.map((e) => e.name).join(", ")}.`]
|
|
21255
|
+
};
|
|
21256
|
+
}
|
|
21257
|
+
const strong = present.filter((e) => isStrongNoUsage2(e.sig));
|
|
21258
|
+
const unknown = present.filter((e) => e.sig.status === "unknown");
|
|
21259
|
+
const notEvaluated = EVIDENCE_SIGNALS.filter(
|
|
21260
|
+
(name) => !evidence[name] || evidence[name]?.status === "not-evaluated"
|
|
21261
|
+
);
|
|
21262
|
+
const rationale = [];
|
|
21263
|
+
if (strong.length > 0) rationale.push(`No usage observed via: ${strong.map((e) => e.name).join(", ")}.`);
|
|
21264
|
+
if (unknown.length > 0) {
|
|
21265
|
+
rationale.push(`Unknown (looked but could not determine): ${unknown.map((e) => e.name).join(", ")} \u2014 NOT treated as no-usage.`);
|
|
21266
|
+
}
|
|
21267
|
+
if (notEvaluated.length > 0) {
|
|
21268
|
+
rationale.push(`Not evaluated: ${notEvaluated.join(", ")} \u2014 absence of a reference is not evidence of no usage.`);
|
|
21269
|
+
}
|
|
21270
|
+
let confidence;
|
|
21271
|
+
if (strong.length === 0) {
|
|
21272
|
+
confidence = "insufficient-evidence";
|
|
21273
|
+
} else if (unknown.length > 0) {
|
|
21274
|
+
confidence = "medium";
|
|
21275
|
+
} else if (strong.length >= 2) {
|
|
21276
|
+
confidence = "high";
|
|
21277
|
+
} else {
|
|
21278
|
+
confidence = "medium";
|
|
21279
|
+
}
|
|
21280
|
+
return { confidence, requiresHumanConfirmation: true, rationale };
|
|
21281
|
+
}
|
|
21282
|
+
function deriveRemovalWaves(plan) {
|
|
21283
|
+
const ids = new Set(plan.targets.map((t) => t.id));
|
|
21284
|
+
const deps = /* @__PURE__ */ new Map();
|
|
21285
|
+
const blocked = [];
|
|
21286
|
+
for (const t of plan.targets) {
|
|
21287
|
+
const resolved = [];
|
|
21288
|
+
for (const d3 of t.dependsOnRemoval) {
|
|
21289
|
+
if (d3 === t.id) {
|
|
21290
|
+
blocked.push({ target: t.id, reason: "Target depends on its own removal." });
|
|
21291
|
+
continue;
|
|
21292
|
+
}
|
|
21293
|
+
if (!ids.has(d3)) {
|
|
21294
|
+
blocked.push({ target: t.id, reason: `depends_on_removal references unknown target: ${d3}.` });
|
|
21295
|
+
continue;
|
|
21296
|
+
}
|
|
21297
|
+
resolved.push(d3);
|
|
21298
|
+
}
|
|
21299
|
+
deps.set(t.id, resolved);
|
|
21300
|
+
}
|
|
21301
|
+
const placed = /* @__PURE__ */ new Set();
|
|
21302
|
+
const waves = [];
|
|
21303
|
+
let remaining = plan.targets.map((t) => t.id);
|
|
21304
|
+
while (remaining.length > 0) {
|
|
21305
|
+
const wave = remaining.filter((id) => (deps.get(id) ?? []).every((d3) => placed.has(d3)));
|
|
21306
|
+
if (wave.length === 0) break;
|
|
21307
|
+
for (const id of wave) placed.add(id);
|
|
21308
|
+
waves.push(wave);
|
|
21309
|
+
remaining = remaining.filter((id) => !placed.has(id));
|
|
21310
|
+
}
|
|
21311
|
+
for (const id of remaining) {
|
|
21312
|
+
blocked.push({ target: id, reason: "Involved in a removal dependency cycle \u2014 cannot be ordered." });
|
|
21313
|
+
}
|
|
21314
|
+
return { waves, blocked };
|
|
21315
|
+
}
|
|
21316
|
+
var RETIRED_STATES = /* @__PURE__ */ new Set([
|
|
21317
|
+
"removed-devtest",
|
|
21318
|
+
"production-changed",
|
|
21319
|
+
"observing",
|
|
21320
|
+
"completed",
|
|
21321
|
+
"deprecated-retained"
|
|
21322
|
+
]);
|
|
21323
|
+
function isRetiredLifecycle(state) {
|
|
21324
|
+
return RETIRED_STATES.has(state);
|
|
21325
|
+
}
|
|
21326
|
+
function targetsEligibleForRemoval(plan) {
|
|
21327
|
+
const byId = new Map(plan.targets.map((t) => [t.id, t]));
|
|
21328
|
+
return plan.targets.filter((t) => !isRetiredLifecycle(String(t.lifecycle))).filter(
|
|
21329
|
+
(t) => t.dependsOnRemoval.every((d3) => {
|
|
21330
|
+
const dep = byId.get(d3);
|
|
21331
|
+
return dep ? isRetiredLifecycle(String(dep.lifecycle)) : false;
|
|
21332
|
+
})
|
|
21333
|
+
).map((t) => t.id);
|
|
21334
|
+
}
|
|
21335
|
+
function planIntelligence(plan) {
|
|
21336
|
+
const waves = deriveRemovalWaves(plan);
|
|
21337
|
+
const eligible = new Set(targetsEligibleForRemoval(plan));
|
|
21338
|
+
const byId = new Map(plan.targets.map((t) => [t.id, t]));
|
|
21339
|
+
const targets = plan.targets.map((t) => ({
|
|
21340
|
+
id: t.id,
|
|
21341
|
+
unused: deriveUnusedConfidence2(t.usageEvidence),
|
|
21342
|
+
eligibleForRemovalReview: eligible.has(t.id),
|
|
21343
|
+
blockedBy: t.dependsOnRemoval.filter((d3) => {
|
|
21344
|
+
const dep = byId.get(d3);
|
|
21345
|
+
return !dep || !isRetiredLifecycle(String(dep.lifecycle));
|
|
21346
|
+
}),
|
|
21347
|
+
observation: observationStatus(t)
|
|
21348
|
+
}));
|
|
21349
|
+
return { waves, eligible: [...eligible], targets };
|
|
21350
|
+
}
|
|
21351
|
+
var CANDIDATE_DISCOVERY_GUIDANCE2 = [
|
|
21352
|
+
"Propose deprecation candidates from multiple sources: static references (code/routes/UI/jobs),",
|
|
21353
|
+
"the System Graph (dependencies and blast radius), Legacy Knowledge (risks/unknowns/modernization",
|
|
21354
|
+
"candidates), Project Resources (databases/external systems and their boundaries), runtime evidence",
|
|
21355
|
+
"when telemetry exists, and database metadata when reachable.",
|
|
21356
|
+
"",
|
|
21357
|
+
"Rules:",
|
|
21358
|
+
"- Discovery is a PROPOSAL, not a materialization: never create or retire anything automatically.",
|
|
21359
|
+
"- Always explain WHY something is a candidate, citing the signal(s) observed.",
|
|
21360
|
+
"- Absence of a reference is NOT evidence of no usage. If a source cannot be evaluated, record it",
|
|
21361
|
+
" as not-evaluated/unknown and require human investigation \u2014 never collapse it to no-usage.",
|
|
21362
|
+
"- A modernization candidate is not automatically a deprecation target.",
|
|
21363
|
+
"- Soft deprecation precedes physical removal; order retirements by dependency, dependents first."
|
|
21364
|
+
].join("\n");
|
|
21365
|
+
|
|
21366
|
+
// ../cli/src/core/deprecation-write.ts
|
|
21367
|
+
import fs7 from "fs";
|
|
21368
|
+
import path6 from "path";
|
|
21369
|
+
import matter19 from "gray-matter";
|
|
21370
|
+
var DeprecationWriteError = class extends Error {
|
|
21371
|
+
constructor(code, message) {
|
|
21372
|
+
super(message);
|
|
21373
|
+
this.code = code;
|
|
21374
|
+
this.name = "DeprecationWriteError";
|
|
21375
|
+
}
|
|
21376
|
+
code;
|
|
21377
|
+
};
|
|
21378
|
+
function slug5(s) {
|
|
21379
|
+
return s.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
|
|
21380
|
+
}
|
|
21381
|
+
function atomicWrite2(filePath, content) {
|
|
21382
|
+
const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
21383
|
+
fs7.writeFileSync(tmp, content, "utf-8");
|
|
21384
|
+
fs7.renameSync(tmp, filePath);
|
|
21385
|
+
}
|
|
21386
|
+
function serialize3(data, body) {
|
|
21387
|
+
return matter19.stringify(`
|
|
21388
|
+
${body.trim()}
|
|
21389
|
+
`, data);
|
|
21390
|
+
}
|
|
21391
|
+
function allPlanArtifacts(dir) {
|
|
21392
|
+
return discoverKnowledge(dir).filter((a2) => a2.type === DEPRECATION_ARTIFACT_TYPE);
|
|
21393
|
+
}
|
|
21394
|
+
function nextPlanId(dir, explicit) {
|
|
21395
|
+
if (explicit && explicit.trim()) return explicit.trim();
|
|
21396
|
+
let max = 0;
|
|
21397
|
+
for (const a2 of allPlanArtifacts(dir)) {
|
|
21398
|
+
const m2 = (a2.id || "").match(/^DEP-(\d+)$/i);
|
|
21399
|
+
if (m2) max = Math.max(max, parseInt(m2[1], 10));
|
|
21400
|
+
}
|
|
21401
|
+
return `DEP-${String(max + 1).padStart(3, "0")}`;
|
|
21402
|
+
}
|
|
21403
|
+
function nextTargetId(targetIds) {
|
|
21404
|
+
let max = 0;
|
|
21405
|
+
for (const id of targetIds) {
|
|
21406
|
+
const m2 = id.match(/^DEP-TGT-(\d+)$/i);
|
|
21407
|
+
if (m2) max = Math.max(max, parseInt(m2[1], 10));
|
|
21408
|
+
}
|
|
21409
|
+
return `DEP-TGT-${String(max + 1).padStart(3, "0")}`;
|
|
21410
|
+
}
|
|
21411
|
+
function buildTargetRaw(input, base = {}) {
|
|
21412
|
+
const out = { ...base };
|
|
21413
|
+
if (input.id != null) out.id = input.id;
|
|
21414
|
+
if (input.type != null) out.type = input.type;
|
|
21415
|
+
if (input.locator != null) out.locator = input.locator;
|
|
21416
|
+
if (input.title != null) out.title = input.title;
|
|
21417
|
+
if (input.lifecycle != null) out.lifecycle = input.lifecycle;
|
|
21418
|
+
if (input.environments != null) out.environments = input.environments;
|
|
21419
|
+
if (input.assessment != null) {
|
|
21420
|
+
const a2 = { ...typeof base.assessment === "object" && base.assessment ? base.assessment : {} };
|
|
21421
|
+
if (input.assessment.unusedConfidence != null) a2.unused_confidence = input.assessment.unusedConfidence;
|
|
21422
|
+
if (input.assessment.blastRadius != null) a2.blast_radius = input.assessment.blastRadius;
|
|
21423
|
+
if (input.assessment.reversibility != null) a2.reversibility = input.assessment.reversibility;
|
|
21424
|
+
out.assessment = a2;
|
|
21425
|
+
}
|
|
21426
|
+
if (input.dependsOnRemoval != null) out.depends_on_removal = input.dependsOnRemoval;
|
|
21427
|
+
if (input.resource != null) out.resource = input.resource;
|
|
21428
|
+
if (input.module != null) out.module = input.module;
|
|
21429
|
+
return out;
|
|
21430
|
+
}
|
|
21431
|
+
function validateTargetInput(dir, raw) {
|
|
21432
|
+
const findings = [];
|
|
21433
|
+
const parsed = parseTarget(raw);
|
|
21434
|
+
if (!parsed) {
|
|
21435
|
+
findings.push({ level: "error", message: "A target id is required." });
|
|
21436
|
+
return findings;
|
|
21437
|
+
}
|
|
21438
|
+
for (const f3 of validateTarget(parsed)) {
|
|
21439
|
+
findings.push({ level: f3.severity === "error" ? "error" : "warning", message: f3.message });
|
|
21440
|
+
}
|
|
21441
|
+
const mapped = loadMappedModules(dir).map((m2) => m2.id);
|
|
21442
|
+
if (parsed.module && mapped.length > 0 && !mapped.includes(parsed.module)) {
|
|
21443
|
+
findings.push({ level: "warning", message: `Module "${parsed.module}" is not in .kaddo/modules.yml.` });
|
|
21444
|
+
}
|
|
21445
|
+
if (parsed.resource && !getResource(dir, parsed.resource)) {
|
|
21446
|
+
findings.push({ level: "warning", message: `Project Resource "${parsed.resource}" not found.` });
|
|
21447
|
+
}
|
|
21448
|
+
return findings;
|
|
21449
|
+
}
|
|
21450
|
+
function loadPlanRaw(dir, planId) {
|
|
21451
|
+
const match = allPlanArtifacts(dir).find((a2) => a2.id === planId || a2.title === planId);
|
|
21452
|
+
if (!match) throw new DeprecationWriteError("NOT_FOUND", `Deprecation Plan "${planId}" not found.`);
|
|
21453
|
+
const parsed = matter19(readFile(match.filePath));
|
|
21454
|
+
const data = { ...parsed.data ?? {} };
|
|
21455
|
+
return { filePath: match.filePath, data, body: parsed.content.trim() };
|
|
21456
|
+
}
|
|
21457
|
+
function rawTargets(data) {
|
|
21458
|
+
return Array.isArray(data.targets) ? data.targets : [];
|
|
21459
|
+
}
|
|
21460
|
+
function createPlan(dir, input) {
|
|
21461
|
+
const title = (input.title ?? "").trim();
|
|
21462
|
+
if (!title) throw new DeprecationWriteError("INVALID_INPUT", "A plan title is required.");
|
|
21463
|
+
const existing = allPlanArtifacts(dir);
|
|
21464
|
+
if (input.id && existing.some((a2) => (a2.id || "").toUpperCase() === input.id.toUpperCase())) {
|
|
21465
|
+
throw new DeprecationWriteError("DUPLICATE", `A plan with id "${input.id}" already exists.`);
|
|
21466
|
+
}
|
|
21467
|
+
if (existing.some((a2) => (a2.title || "").toLowerCase() === title.toLowerCase())) {
|
|
21468
|
+
throw new DeprecationWriteError("DUPLICATE", `A plan titled "${title}" already exists.`);
|
|
21469
|
+
}
|
|
21470
|
+
const id = nextPlanId(dir, input.id);
|
|
21471
|
+
const data = {
|
|
21472
|
+
type: DEPRECATION_ARTIFACT_TYPE,
|
|
21473
|
+
id,
|
|
21474
|
+
title,
|
|
21475
|
+
status: (input.status ?? "draft").trim() || "draft",
|
|
21476
|
+
targets: []
|
|
21477
|
+
};
|
|
21478
|
+
const body = `# Purpose
|
|
21479
|
+
|
|
21480
|
+
${(input.purpose ?? "").trim() || "TODO: describe what this plan retires and why."}`;
|
|
21481
|
+
const relPath = `${DEPRECATION_PLANS_DIR}/${id}-${slug5(title) || "plan"}.md`;
|
|
21482
|
+
const filePath = join(dir, relPath);
|
|
21483
|
+
if (exists(filePath)) throw new DeprecationWriteError("DUPLICATE", `Plan file already exists: ${relPath}`);
|
|
21484
|
+
ensureDir(path6.dirname(filePath));
|
|
21485
|
+
atomicWrite2(filePath, serialize3(data, body));
|
|
21486
|
+
return { id, path: relPath, findings: [] };
|
|
21487
|
+
}
|
|
21488
|
+
function addTarget(dir, planId, input) {
|
|
21489
|
+
const { filePath, data, body } = loadPlanRaw(dir, planId);
|
|
21490
|
+
const targets = rawTargets(data);
|
|
21491
|
+
const existingIds = targets.map((t) => String(t.id ?? ""));
|
|
21492
|
+
const targetId = (input.id ?? "").trim() || nextTargetId(existingIds);
|
|
21493
|
+
if (existingIds.some((id) => id.toUpperCase() === targetId.toUpperCase())) {
|
|
21494
|
+
throw new DeprecationWriteError("DUPLICATE", `Target "${targetId}" already exists in ${planId}.`);
|
|
21495
|
+
}
|
|
21496
|
+
const raw = buildTargetRaw({ ...input, id: targetId, lifecycle: input.lifecycle ?? "candidate" });
|
|
21497
|
+
const findings = validateTargetInput(dir, raw);
|
|
21498
|
+
if (findings.some((f3) => f3.level === "error")) {
|
|
21499
|
+
throw new DeprecationWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
|
|
21500
|
+
}
|
|
21501
|
+
data.targets = [...targets, raw];
|
|
21502
|
+
atomicWrite2(filePath, serialize3(data, body));
|
|
21503
|
+
return { planId, targetId, path: `${DEPRECATION_PLANS_DIR}/${path6.basename(filePath)}`, findings };
|
|
21504
|
+
}
|
|
21505
|
+
function updateTarget(dir, planId, targetId, patch) {
|
|
21506
|
+
const { filePath, data, body } = loadPlanRaw(dir, planId);
|
|
21507
|
+
const targets = rawTargets(data);
|
|
21508
|
+
const idx = targets.findIndex((t) => String(t.id ?? "").toUpperCase() === targetId.toUpperCase());
|
|
21509
|
+
if (idx === -1) throw new DeprecationWriteError("NOT_FOUND", `Target "${targetId}" not found in ${planId}.`);
|
|
21510
|
+
const merged = buildTargetRaw({ ...patch, id: String(targets[idx].id) }, targets[idx]);
|
|
21511
|
+
const findings = validateTargetInput(dir, merged);
|
|
21512
|
+
if (findings.some((f3) => f3.level === "error")) {
|
|
21513
|
+
throw new DeprecationWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
|
|
21514
|
+
}
|
|
21515
|
+
const next = [...targets];
|
|
21516
|
+
next[idx] = merged;
|
|
21517
|
+
data.targets = next;
|
|
21518
|
+
atomicWrite2(filePath, serialize3(data, body));
|
|
21519
|
+
return { planId, targetId: String(targets[idx].id), path: `${DEPRECATION_PLANS_DIR}/${path6.basename(filePath)}`, findings };
|
|
21520
|
+
}
|
|
21521
|
+
function removeTarget(dir, planId, targetId, opts = {}) {
|
|
21522
|
+
const { filePath, data, body } = loadPlanRaw(dir, planId);
|
|
21523
|
+
const targets = rawTargets(data);
|
|
21524
|
+
const exists3 = targets.some((t) => String(t.id ?? "").toUpperCase() === targetId.toUpperCase());
|
|
21525
|
+
if (!exists3) throw new DeprecationWriteError("NOT_FOUND", `Target "${targetId}" not found in ${planId}.`);
|
|
21526
|
+
const dependents = targets.filter((t) => (Array.isArray(t.depends_on_removal) ? t.depends_on_removal.map(String) : []).some((d3) => d3.toUpperCase() === targetId.toUpperCase())).map((t) => String(t.id));
|
|
21527
|
+
if (!opts.confirm) {
|
|
21528
|
+
return { removed: false, preview: { planId, targetId, dependents } };
|
|
21529
|
+
}
|
|
21530
|
+
data.targets = targets.filter((t) => String(t.id ?? "").toUpperCase() !== targetId.toUpperCase());
|
|
21531
|
+
atomicWrite2(filePath, serialize3(data, body));
|
|
21532
|
+
return { removed: true, path: `${DEPRECATION_PLANS_DIR}/${path6.basename(filePath)}` };
|
|
21533
|
+
}
|
|
21534
|
+
|
|
20499
21535
|
// src/tools.ts
|
|
20500
21536
|
var ok = (data) => ({ ok: true, data });
|
|
20501
21537
|
var fail = (message) => ({ ok: false, message });
|
|
@@ -20602,12 +21638,12 @@ function markWorkItemReady(root, id, confirm2) {
|
|
|
20602
21638
|
}
|
|
20603
21639
|
const raw = readText(root, item.path);
|
|
20604
21640
|
if (!raw) return fail(`Could not read Work Item file: ${item.path}`);
|
|
20605
|
-
const parsed =
|
|
21641
|
+
const parsed = matter20(raw);
|
|
20606
21642
|
const data = { ...parsed.data };
|
|
20607
21643
|
const today3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
20608
21644
|
data.status = "ready";
|
|
20609
21645
|
data.ready_at = today3;
|
|
20610
|
-
const newContent =
|
|
21646
|
+
const newContent = matter20.stringify(parsed.content, data);
|
|
20611
21647
|
const posixPath = item.path.replace(/\\/g, "/");
|
|
20612
21648
|
let newRelPath = item.path;
|
|
20613
21649
|
if (posixPath.includes("/work-items/draft/")) {
|
|
@@ -20831,11 +21867,78 @@ function deleteResourceTool(root, id, confirm2) {
|
|
|
20831
21867
|
return fail(e instanceof ResourceWriteError ? e.message : String(e));
|
|
20832
21868
|
}
|
|
20833
21869
|
}
|
|
21870
|
+
function listDeprecationsTool(root) {
|
|
21871
|
+
return ok(getDeprecationPlans(root));
|
|
21872
|
+
}
|
|
21873
|
+
function getDeprecationTool(root, id) {
|
|
21874
|
+
const plan = getDeprecationPlan(root, id);
|
|
21875
|
+
if (!plan) return fail(`Deprecation Plan "${id}" not found.`);
|
|
21876
|
+
return ok(plan);
|
|
21877
|
+
}
|
|
21878
|
+
function planDeprecationTool(root, id) {
|
|
21879
|
+
const plan = getDeprecationPlan(root, id);
|
|
21880
|
+
if (!plan) return fail(`Deprecation Plan "${id}" not found.`);
|
|
21881
|
+
return ok(deriveRemovalWaves(plan));
|
|
21882
|
+
}
|
|
21883
|
+
function analyzeDeprecationTool(root, id) {
|
|
21884
|
+
const plan = getDeprecationPlan(root, id);
|
|
21885
|
+
if (!plan) return fail(`Deprecation Plan "${id}" not found.`);
|
|
21886
|
+
return ok(planIntelligence(plan));
|
|
21887
|
+
}
|
|
21888
|
+
function createDeprecationTool(root, args, confirm2) {
|
|
21889
|
+
if (!confirm2) {
|
|
21890
|
+
return ok({ status: "needs_confirmation", action: "create", proposal: args, message: "Re-call with confirm=true to create this Deprecation Plan." });
|
|
21891
|
+
}
|
|
21892
|
+
try {
|
|
21893
|
+
return ok({ status: "created", ...createPlan(root, args) });
|
|
21894
|
+
} catch (e) {
|
|
21895
|
+
return fail(e instanceof DeprecationWriteError ? e.message : String(e));
|
|
21896
|
+
}
|
|
21897
|
+
}
|
|
21898
|
+
function addDeprecationTargetTool(root, planId, args, confirm2) {
|
|
21899
|
+
if (!getDeprecationPlan(root, planId)) return fail(`Deprecation Plan "${planId}" not found.`);
|
|
21900
|
+
if (!confirm2) {
|
|
21901
|
+
const findings = validateTargetInput(root, { ...args, id: args.id ?? "DEP-TGT-NEW", lifecycle: args.lifecycle ?? "candidate" });
|
|
21902
|
+
return ok({ status: "needs_confirmation", action: "add_target", planId, proposal: args, findings, message: "Re-call with confirm=true to add this target." });
|
|
21903
|
+
}
|
|
21904
|
+
try {
|
|
21905
|
+
return ok({ status: "added", ...addTarget(root, planId, args) });
|
|
21906
|
+
} catch (e) {
|
|
21907
|
+
return fail(e instanceof DeprecationWriteError ? e.message : String(e));
|
|
21908
|
+
}
|
|
21909
|
+
}
|
|
21910
|
+
function updateDeprecationTargetTool(root, planId, targetId, args, confirm2) {
|
|
21911
|
+
if (!getDeprecationPlan(root, planId)) return fail(`Deprecation Plan "${planId}" not found.`);
|
|
21912
|
+
if (!confirm2) {
|
|
21913
|
+
return ok({ status: "needs_confirmation", action: "update_target", planId, targetId, changes: args, message: "Re-call with confirm=true to apply this update." });
|
|
21914
|
+
}
|
|
21915
|
+
try {
|
|
21916
|
+
return ok({ status: "updated", ...updateTarget(root, planId, targetId, args) });
|
|
21917
|
+
} catch (e) {
|
|
21918
|
+
return fail(e instanceof DeprecationWriteError ? e.message : String(e));
|
|
21919
|
+
}
|
|
21920
|
+
}
|
|
21921
|
+
function removeDeprecationTargetTool(root, planId, targetId, confirm2) {
|
|
21922
|
+
if (!getDeprecationPlan(root, planId)) return fail(`Deprecation Plan "${planId}" not found.`);
|
|
21923
|
+
if (!confirm2) {
|
|
21924
|
+
try {
|
|
21925
|
+
const preview = removeTarget(root, planId, targetId);
|
|
21926
|
+
return ok({ status: "needs_confirmation", action: "remove_target", ...preview.preview, message: "Re-call with confirm=true to remove. Dependents\u2019 depends_on_removal are NOT rewritten." });
|
|
21927
|
+
} catch (e) {
|
|
21928
|
+
return fail(e instanceof DeprecationWriteError ? e.message : String(e));
|
|
21929
|
+
}
|
|
21930
|
+
}
|
|
21931
|
+
try {
|
|
21932
|
+
return ok({ status: "removed", ...removeTarget(root, planId, targetId, { confirm: true }) });
|
|
21933
|
+
} catch (e) {
|
|
21934
|
+
return fail(e instanceof DeprecationWriteError ? e.message : String(e));
|
|
21935
|
+
}
|
|
21936
|
+
}
|
|
20834
21937
|
|
|
20835
21938
|
// src/multirepo.ts
|
|
20836
|
-
import
|
|
20837
|
-
import
|
|
20838
|
-
import
|
|
21939
|
+
import fs8 from "fs";
|
|
21940
|
+
import path7 from "path";
|
|
21941
|
+
import matter21 from "gray-matter";
|
|
20839
21942
|
import { parse as parseYaml11, stringify as stringifyYaml8 } from "yaml";
|
|
20840
21943
|
var ok2 = (data) => ({ ok: true, data });
|
|
20841
21944
|
var fail2 = (message) => ({ ok: false, message });
|
|
@@ -20855,24 +21958,24 @@ function isCore2(config) {
|
|
|
20855
21958
|
return projectRole2(config) === "core";
|
|
20856
21959
|
}
|
|
20857
21960
|
function readModuleFile(root, repoPath, relFile) {
|
|
20858
|
-
const moduleDir =
|
|
20859
|
-
const abs =
|
|
21961
|
+
const moduleDir = path7.resolve(root, repoPath);
|
|
21962
|
+
const abs = path7.resolve(moduleDir, relFile);
|
|
20860
21963
|
if (!abs.startsWith(moduleDir)) return null;
|
|
20861
|
-
if (!
|
|
20862
|
-
return
|
|
21964
|
+
if (!fs8.existsSync(abs) || !fs8.statSync(abs).isFile()) return null;
|
|
21965
|
+
return fs8.readFileSync(abs, "utf-8");
|
|
20863
21966
|
}
|
|
20864
21967
|
function detectModuleStatus2(root, repoPath) {
|
|
20865
|
-
const moduleDir =
|
|
20866
|
-
if (!
|
|
21968
|
+
const moduleDir = path7.resolve(root, repoPath);
|
|
21969
|
+
if (!fs8.existsSync(moduleDir)) {
|
|
20867
21970
|
return { status: "missing", configured: false, moduleContext: false, currentState: false, codebase: false, warning: `Directory \`${repoPath}\` does not exist.` };
|
|
20868
21971
|
}
|
|
20869
|
-
const configPath =
|
|
20870
|
-
if (!
|
|
21972
|
+
const configPath = path7.join(moduleDir, ".kaddo", "config.yml");
|
|
21973
|
+
if (!fs8.existsSync(configPath)) {
|
|
20871
21974
|
return { status: "not_configured", configured: false, moduleContext: false, currentState: false, codebase: false, warning: `Run \`kaddo init\` inside \`${repoPath}\` and select multirepo \u2192 module.` };
|
|
20872
21975
|
}
|
|
20873
21976
|
let parsed = null;
|
|
20874
21977
|
try {
|
|
20875
|
-
parsed = parseYaml11(
|
|
21978
|
+
parsed = parseYaml11(fs8.readFileSync(configPath, "utf-8"));
|
|
20876
21979
|
} catch {
|
|
20877
21980
|
}
|
|
20878
21981
|
const role = parsed?.project?.role ?? parsed?.multirepo?.role;
|
|
@@ -20883,14 +21986,14 @@ function detectModuleStatus2(root, repoPath) {
|
|
|
20883
21986
|
status: "configured",
|
|
20884
21987
|
configured: true,
|
|
20885
21988
|
role: "module",
|
|
20886
|
-
moduleContext:
|
|
20887
|
-
currentState:
|
|
20888
|
-
codebase:
|
|
21989
|
+
moduleContext: fs8.existsSync(path7.join(moduleDir, "knowledge", "tech", "module", "module-context.md")) || fs8.existsSync(path7.join(moduleDir, "knowledge", "module", "module-context.md")),
|
|
21990
|
+
currentState: fs8.existsSync(path7.join(moduleDir, "knowledge", "tech", "current-state.md")),
|
|
21991
|
+
codebase: fs8.existsSync(path7.join(moduleDir, "knowledge", "tech", "codebase.md"))
|
|
20889
21992
|
};
|
|
20890
21993
|
}
|
|
20891
21994
|
function resolveModuleContextPath(root, repoPath) {
|
|
20892
|
-
const moduleDir =
|
|
20893
|
-
if (
|
|
21995
|
+
const moduleDir = path7.resolve(root, repoPath);
|
|
21996
|
+
if (fs8.existsSync(path7.join(moduleDir, "knowledge", "tech", "module", "module-context.md"))) {
|
|
20894
21997
|
return "knowledge/tech/module/module-context.md";
|
|
20895
21998
|
}
|
|
20896
21999
|
return "knowledge/module/module-context.md";
|
|
@@ -20987,7 +22090,7 @@ function validateWorkItemModulesTool(root, args) {
|
|
|
20987
22090
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
20988
22091
|
const raw = readText(root, wi.path);
|
|
20989
22092
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
20990
|
-
const parsed =
|
|
22093
|
+
const parsed = matter21(raw);
|
|
20991
22094
|
const data = parsed.data;
|
|
20992
22095
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
20993
22096
|
const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
|
|
@@ -21040,7 +22143,7 @@ function getWorkItemContextTool(root, args) {
|
|
|
21040
22143
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
21041
22144
|
const raw = readText(root, wi.path);
|
|
21042
22145
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
21043
|
-
const parsed =
|
|
22146
|
+
const parsed = matter21(raw);
|
|
21044
22147
|
const data = parsed.data;
|
|
21045
22148
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
21046
22149
|
const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
|
|
@@ -21094,15 +22197,15 @@ function extractSection(content, heading) {
|
|
|
21094
22197
|
return out;
|
|
21095
22198
|
}
|
|
21096
22199
|
function buildBranchStrategy(wi, affectedModules, codeEntries, descriptor) {
|
|
21097
|
-
const
|
|
22200
|
+
const slug7 = slugify4(wi.title);
|
|
21098
22201
|
const wiLower = wi.id.toLowerCase();
|
|
21099
|
-
const coreBranch = `feature/${wiLower}-${
|
|
22202
|
+
const coreBranch = `feature/${wiLower}-${slug7}`;
|
|
21100
22203
|
const moduleBranches = {};
|
|
21101
22204
|
const commitMessages = {};
|
|
21102
22205
|
for (const modId of affectedModules) {
|
|
21103
22206
|
const mod = (descriptor.modules ?? []).find((m2) => m2.id === modId);
|
|
21104
22207
|
const repo = mod ? moduleRepoPath(mod) || modId : modId;
|
|
21105
|
-
moduleBranches[repo] = `feature/${wiLower}-${modId}-${
|
|
22208
|
+
moduleBranches[repo] = `feature/${wiLower}-${modId}-${slug7.slice(0, 20)}`;
|
|
21106
22209
|
commitMessages[repo] = `fix(${wiLower}): update ${modId}`;
|
|
21107
22210
|
}
|
|
21108
22211
|
commitMessages["core"] = `docs(${wiLower}): capture cross-repo implementation summary`;
|
|
@@ -21126,7 +22229,7 @@ function suggestBranchStrategyTool(root, args) {
|
|
|
21126
22229
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
21127
22230
|
const raw = readText(root, wi.path);
|
|
21128
22231
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
21129
|
-
const parsed =
|
|
22232
|
+
const parsed = matter21(raw);
|
|
21130
22233
|
const data = parsed.data;
|
|
21131
22234
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
21132
22235
|
const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
|
|
@@ -21275,31 +22378,31 @@ function modulesDiscoverTool(root, args) {
|
|
|
21275
22378
|
const sysName = config.system?.name ?? config.project?.name ?? "";
|
|
21276
22379
|
const workspaceRoots2 = config.multirepo?.workspace_roots ?? [".."];
|
|
21277
22380
|
const IGNORED = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "coverage", "vendor", ".next", "out", "tmp", "temp", ".kaddo"]);
|
|
21278
|
-
const coreAbs =
|
|
22381
|
+
const coreAbs = path7.resolve(root);
|
|
21279
22382
|
const discovered = [];
|
|
21280
22383
|
for (const wr of workspaceRoots2) {
|
|
21281
|
-
const rootAbs =
|
|
21282
|
-
if (!
|
|
22384
|
+
const rootAbs = path7.resolve(root, wr);
|
|
22385
|
+
if (!fs8.existsSync(rootAbs) || !fs8.statSync(rootAbs).isDirectory()) continue;
|
|
21283
22386
|
let entries;
|
|
21284
22387
|
try {
|
|
21285
|
-
entries =
|
|
22388
|
+
entries = fs8.readdirSync(rootAbs);
|
|
21286
22389
|
} catch {
|
|
21287
22390
|
continue;
|
|
21288
22391
|
}
|
|
21289
22392
|
for (const entry of entries) {
|
|
21290
22393
|
if (IGNORED.has(entry)) continue;
|
|
21291
|
-
const candidateAbs =
|
|
21292
|
-
if (!
|
|
22394
|
+
const candidateAbs = path7.resolve(rootAbs, entry);
|
|
22395
|
+
if (!fs8.statSync(candidateAbs).isDirectory()) continue;
|
|
21293
22396
|
if (candidateAbs === coreAbs) continue;
|
|
21294
|
-
const relPath =
|
|
21295
|
-
const cfgPath =
|
|
21296
|
-
if (!
|
|
22397
|
+
const relPath = path7.relative(root, candidateAbs).replace(/\\/g, "/");
|
|
22398
|
+
const cfgPath = path7.join(candidateAbs, ".kaddo", "config.yml");
|
|
22399
|
+
if (!fs8.existsSync(cfgPath)) {
|
|
21297
22400
|
discovered.push({ id: entry, path: relPath, status: "not_configured", eligibleForMapping: false });
|
|
21298
22401
|
continue;
|
|
21299
22402
|
}
|
|
21300
22403
|
let parsed = null;
|
|
21301
22404
|
try {
|
|
21302
|
-
parsed = parseYaml11(
|
|
22405
|
+
parsed = parseYaml11(fs8.readFileSync(cfgPath, "utf-8"));
|
|
21303
22406
|
} catch {
|
|
21304
22407
|
}
|
|
21305
22408
|
const role = parsed?.project?.role ?? parsed?.multirepo?.role;
|
|
@@ -21373,8 +22476,8 @@ ${list3(data.owners)}
|
|
|
21373
22476
|
|
|
21374
22477
|
// ../cli/src/core/system-topology.ts
|
|
21375
22478
|
import { parse as parseYaml12, stringify as stringifyYaml9 } from "yaml";
|
|
21376
|
-
import
|
|
21377
|
-
import
|
|
22479
|
+
import fs9 from "fs";
|
|
22480
|
+
import path8 from "path";
|
|
21378
22481
|
import crypto from "crypto";
|
|
21379
22482
|
var TOPOLOGY_FILE2 = "knowledge/tech/system-topology.yml";
|
|
21380
22483
|
var SYSTEM_ENTITY_KINDS2 = /* @__PURE__ */ new Set([
|
|
@@ -21746,16 +22849,16 @@ function findSystemPaths(dir, source3, target, opts = {}) {
|
|
|
21746
22849
|
out.get(r2.source).push(r2.target);
|
|
21747
22850
|
}
|
|
21748
22851
|
const paths = [];
|
|
21749
|
-
const walk = (node,
|
|
22852
|
+
const walk = (node, path10) => {
|
|
21750
22853
|
if (paths.length >= 20) return;
|
|
21751
|
-
if (node === target &&
|
|
21752
|
-
paths.push([...
|
|
22854
|
+
if (node === target && path10.length > 1) {
|
|
22855
|
+
paths.push([...path10]);
|
|
21753
22856
|
return;
|
|
21754
22857
|
}
|
|
21755
|
-
if (
|
|
22858
|
+
if (path10.length > maxDepth) return;
|
|
21756
22859
|
for (const nxt of out.get(node) ?? []) {
|
|
21757
|
-
if (
|
|
21758
|
-
walk(nxt, [...
|
|
22860
|
+
if (path10.includes(nxt)) continue;
|
|
22861
|
+
walk(nxt, [...path10, nxt]);
|
|
21759
22862
|
}
|
|
21760
22863
|
};
|
|
21761
22864
|
walk(source3, [source3]);
|
|
@@ -21780,14 +22883,14 @@ function getImpactCandidates(dir, seedIds, opts = {}) {
|
|
|
21780
22883
|
existing.reason.push(reason);
|
|
21781
22884
|
continue;
|
|
21782
22885
|
}
|
|
21783
|
-
const
|
|
22886
|
+
const path10 = findSystemPaths(dir, seed, n.id, { maxDepth: opts.maxDepth ?? DEFAULT_MAX_DEPTH })[0];
|
|
21784
22887
|
candidates.set(n.id, {
|
|
21785
22888
|
nodeId: n.id,
|
|
21786
22889
|
label: n.label,
|
|
21787
22890
|
kind: n.type,
|
|
21788
22891
|
...n.moduleId ? { moduleId: n.moduleId } : {},
|
|
21789
22892
|
reason: [reason],
|
|
21790
|
-
...
|
|
22893
|
+
...path10 ? { graphPath: path10.map((id) => byId.get(id)?.label ?? id) } : {},
|
|
21791
22894
|
...n.implementationRefs?.length ? { implementationRefs: n.implementationRefs } : {},
|
|
21792
22895
|
...n.knowledgeRefs?.length ? { knowledgeRefs: n.knowledgeRefs } : {},
|
|
21793
22896
|
status: "candidate",
|
|
@@ -21886,14 +22989,14 @@ function systemImpactCandidatesTool(root, args) {
|
|
|
21886
22989
|
}
|
|
21887
22990
|
|
|
21888
22991
|
// ../cli/src/services/integrations.ts
|
|
21889
|
-
import
|
|
22992
|
+
import matter24 from "gray-matter";
|
|
21890
22993
|
import { parse as parseYaml13, stringify as stringifyYaml10 } from "yaml";
|
|
21891
22994
|
|
|
21892
22995
|
// ../cli/src/core/work-item-write.ts
|
|
21893
|
-
import
|
|
21894
|
-
import
|
|
22996
|
+
import fs10 from "fs";
|
|
22997
|
+
import path9 from "path";
|
|
21895
22998
|
import crypto4 from "crypto";
|
|
21896
|
-
import
|
|
22999
|
+
import matter23 from "gray-matter";
|
|
21897
23000
|
var SECTION_ORDER2 = [
|
|
21898
23001
|
"Actor",
|
|
21899
23002
|
"Outcome",
|
|
@@ -22561,8 +23664,8 @@ async function handleJiraResponse2(res) {
|
|
|
22561
23664
|
}
|
|
22562
23665
|
return await res.json();
|
|
22563
23666
|
}
|
|
22564
|
-
async function jiraFetch2(baseUrl,
|
|
22565
|
-
const url = new URL(
|
|
23667
|
+
async function jiraFetch2(baseUrl, path10, auth, timeoutMs, params) {
|
|
23668
|
+
const url = new URL(path10, baseUrl + "/");
|
|
22566
23669
|
if (params) for (const [k2, v] of Object.entries(params)) url.searchParams.set(k2, v);
|
|
22567
23670
|
const controller = new AbortController();
|
|
22568
23671
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -22580,8 +23683,8 @@ async function jiraFetch2(baseUrl, path9, auth, timeoutMs, params) {
|
|
|
22580
23683
|
clearTimeout(timer);
|
|
22581
23684
|
}
|
|
22582
23685
|
}
|
|
22583
|
-
async function jiraPost2(baseUrl,
|
|
22584
|
-
const url = new URL(
|
|
23686
|
+
async function jiraPost2(baseUrl, path10, auth, timeoutMs, body) {
|
|
23687
|
+
const url = new URL(path10, baseUrl + "/");
|
|
22585
23688
|
const controller = new AbortController();
|
|
22586
23689
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
22587
23690
|
try {
|
|
@@ -22941,7 +24044,7 @@ async function integrationsWorkItemTool(root, args) {
|
|
|
22941
24044
|
}
|
|
22942
24045
|
|
|
22943
24046
|
// ../cli/src/core/context-pack.ts
|
|
22944
|
-
import
|
|
24047
|
+
import matter25 from "gray-matter";
|
|
22945
24048
|
var CONTEXT_PACK_VERSION = "1";
|
|
22946
24049
|
var ARCH_DIR4 = "knowledge";
|
|
22947
24050
|
function readScanJson(dir) {
|
|
@@ -22954,7 +24057,7 @@ function readScanJson(dir) {
|
|
|
22954
24057
|
}
|
|
22955
24058
|
}
|
|
22956
24059
|
function firstParagraph4(markdown) {
|
|
22957
|
-
const body =
|
|
24060
|
+
const body = matter25(markdown).content.trim();
|
|
22958
24061
|
const para = body.split("\n\n").map((p) => p.trim()).find((p) => p && !p.startsWith("#"));
|
|
22959
24062
|
return para ?? "";
|
|
22960
24063
|
}
|
|
@@ -23946,7 +25049,7 @@ function requireKnowledge(root) {
|
|
|
23946
25049
|
throw new KaddoMcpError("Knowledge repository not found. Run `kaddo bootstrap` first.");
|
|
23947
25050
|
}
|
|
23948
25051
|
}
|
|
23949
|
-
function
|
|
25052
|
+
function slug6(s) {
|
|
23950
25053
|
return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
23951
25054
|
}
|
|
23952
25055
|
function generateContext(root) {
|
|
@@ -24079,7 +25182,7 @@ function generateCapsuleDraft(root) {
|
|
|
24079
25182
|
const config = requireConfig(root);
|
|
24080
25183
|
requireKnowledge(root);
|
|
24081
25184
|
const capsule = buildCapsule(root, config);
|
|
24082
|
-
const name =
|
|
25185
|
+
const name = slug6(config.project.name);
|
|
24083
25186
|
const md = `.kaddo/exports/${name}.capsule.md`;
|
|
24084
25187
|
const json = `.kaddo/exports/${name}.capsule.json`;
|
|
24085
25188
|
writeDerived(root, md, renderCapsuleMarkdown(capsule));
|
|
@@ -24442,6 +25545,74 @@ function createServer(root) {
|
|
|
24442
25545
|
},
|
|
24443
25546
|
async (args) => toolText(guarded(root, () => deleteResourceTool(root, args.id, args.confirm)))
|
|
24444
25547
|
);
|
|
25548
|
+
server.registerTool(
|
|
25549
|
+
"kaddo_list_deprecations",
|
|
25550
|
+
{ title: "List Deprecation Plans", description: "List Controlled Deprecation Plans under knowledge/legacy/deprecations/ (read-only).", inputSchema: {} },
|
|
25551
|
+
async () => toolText(guarded(root, () => listDeprecationsTool(root)))
|
|
25552
|
+
);
|
|
25553
|
+
server.registerTool(
|
|
25554
|
+
"kaddo_get_deprecation",
|
|
25555
|
+
{ title: "Get Deprecation Plan", description: "Get one Deprecation Plan by ID: purpose, typed targets, lifecycle and per-environment state.", inputSchema: { id: z4.string() } },
|
|
25556
|
+
async (args) => toolText(guarded(root, () => getDeprecationTool(root, args.id)))
|
|
25557
|
+
);
|
|
25558
|
+
server.registerTool(
|
|
25559
|
+
"kaddo_plan_deprecation",
|
|
25560
|
+
{ title: "Deprecation Removal Waves", description: "Derive the dependency-ordered removal waves (dependents first) and blocked targets for a plan. Explainable, not a manual priority.", inputSchema: { id: z4.string() } },
|
|
25561
|
+
async (args) => toolText(guarded(root, () => planDeprecationTool(root, args.id)))
|
|
25562
|
+
);
|
|
25563
|
+
server.registerTool(
|
|
25564
|
+
"kaddo_analyze_deprecation",
|
|
25565
|
+
{ title: "Analyze Deprecation Plan", description: "Per-target unused-confidence from usage evidence + removal eligibility. Absence of a reference is never treated as no-usage; removal always requires human confirmation.", inputSchema: { id: z4.string() } },
|
|
25566
|
+
async (args) => toolText(guarded(root, () => analyzeDeprecationTool(root, args.id)))
|
|
25567
|
+
);
|
|
25568
|
+
const targetFields = {
|
|
25569
|
+
id: z4.string().optional(),
|
|
25570
|
+
type: z4.string().optional(),
|
|
25571
|
+
locator: z4.string().optional(),
|
|
25572
|
+
title: z4.string().optional(),
|
|
25573
|
+
lifecycle: z4.string().optional(),
|
|
25574
|
+
environments: z4.record(z4.string()).optional(),
|
|
25575
|
+
assessment: z4.object({ unusedConfidence: z4.string().optional(), blastRadius: z4.string().optional(), reversibility: z4.string().optional() }).optional(),
|
|
25576
|
+
dependsOnRemoval: z4.array(z4.string()).optional(),
|
|
25577
|
+
resource: z4.string().optional(),
|
|
25578
|
+
module: z4.string().optional()
|
|
25579
|
+
};
|
|
25580
|
+
server.registerTool(
|
|
25581
|
+
"kaddo_create_deprecation",
|
|
25582
|
+
{
|
|
25583
|
+
title: "Create Deprecation Plan",
|
|
25584
|
+
description: "Propose creating a Deprecation Plan. Without confirm, returns a preview (writes nothing); with confirm=true, Core persists it as Markdown.",
|
|
25585
|
+
inputSchema: { title: z4.string().optional(), purpose: z4.string().optional(), status: z4.string().optional(), confirm: z4.boolean().optional() }
|
|
25586
|
+
},
|
|
25587
|
+
async (args) => toolText(guarded(root, () => createDeprecationTool(root, args, args.confirm)))
|
|
25588
|
+
);
|
|
25589
|
+
server.registerTool(
|
|
25590
|
+
"kaddo_add_deprecation_target",
|
|
25591
|
+
{
|
|
25592
|
+
title: "Add Deprecation Target",
|
|
25593
|
+
description: "Propose adding a typed target to a plan. Without confirm, returns a preview + validation findings; with confirm=true, Core appends it (no-lossy).",
|
|
25594
|
+
inputSchema: { planId: z4.string(), ...targetFields, confirm: z4.boolean().optional() }
|
|
25595
|
+
},
|
|
25596
|
+
async (args) => toolText(guarded(root, () => addDeprecationTargetTool(root, args.planId, args, args.confirm)))
|
|
25597
|
+
);
|
|
25598
|
+
server.registerTool(
|
|
25599
|
+
"kaddo_update_deprecation_target",
|
|
25600
|
+
{
|
|
25601
|
+
title: "Update Deprecation Target",
|
|
25602
|
+
description: "Propose updating a target in place (no-lossy). Without confirm, returns a preview; with confirm=true, Core applies it.",
|
|
25603
|
+
inputSchema: { planId: z4.string(), targetId: z4.string(), ...targetFields, confirm: z4.boolean().optional() }
|
|
25604
|
+
},
|
|
25605
|
+
async (args) => toolText(guarded(root, () => updateDeprecationTargetTool(root, args.planId, args.targetId, args, args.confirm)))
|
|
25606
|
+
);
|
|
25607
|
+
server.registerTool(
|
|
25608
|
+
"kaddo_remove_deprecation_target",
|
|
25609
|
+
{
|
|
25610
|
+
title: "Remove Deprecation Target",
|
|
25611
|
+
description: "Remove a target. Without confirm, returns what depends on it (writes nothing); with confirm=true, Core removes it \u2014 dependents\u2019 depends_on_removal are left intact.",
|
|
25612
|
+
inputSchema: { planId: z4.string(), targetId: z4.string(), confirm: z4.boolean().optional() }
|
|
25613
|
+
},
|
|
25614
|
+
async (args) => toolText(guarded(root, () => removeDeprecationTargetTool(root, args.planId, args.targetId, args.confirm)))
|
|
25615
|
+
);
|
|
24445
25616
|
server.registerTool(
|
|
24446
25617
|
"kaddo_list_capsules",
|
|
24447
25618
|
{ title: "List Knowledge Capsules", description: "List registered external Knowledge Capsules.", inputSchema: {} },
|