@kaddo/mcp 3.121.0 → 3.121.2
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 +1266 -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,
|
|
@@ -4603,10 +4881,10 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
4603
4881
|
const q3 = (rel) => analyzeKnowledgeArtifact(dir, rel);
|
|
4604
4882
|
const resolveAgent = (agent) => {
|
|
4605
4883
|
const file = agent.endsWith(".md") ? agent : `${agent}.md`;
|
|
4606
|
-
const
|
|
4607
|
-
const installed = isFile(join(dir,
|
|
4884
|
+
const path10 = agentInstallPath(file);
|
|
4885
|
+
const installed = isFile(join(dir, path10));
|
|
4608
4886
|
const group = agentGroupOf(file);
|
|
4609
|
-
return { agentPath:
|
|
4887
|
+
return { agentPath: path10, agentInstalled: installed, installCommand: installed ? void 0 : `kaddo add agents --group ${group}` };
|
|
4610
4888
|
};
|
|
4611
4889
|
const moduleRepo = isModule(config);
|
|
4612
4890
|
const coreRepo = isCore(config);
|
|
@@ -5036,7 +5314,7 @@ function deriveDeliveryCompletedStatus(dir) {
|
|
|
5036
5314
|
}
|
|
5037
5315
|
|
|
5038
5316
|
// ../cli/src/core/assets.ts
|
|
5039
|
-
import
|
|
5317
|
+
import matter10 from "gray-matter";
|
|
5040
5318
|
|
|
5041
5319
|
// ../cli/src/core/version.ts
|
|
5042
5320
|
import { fileURLToPath } from "url";
|
|
@@ -8347,7 +8625,7 @@ function canonicalSkills() {
|
|
|
8347
8625
|
}
|
|
8348
8626
|
function versionOf(content) {
|
|
8349
8627
|
try {
|
|
8350
|
-
const v =
|
|
8628
|
+
const v = matter10(content).data.version;
|
|
8351
8629
|
return v === void 0 || v === null ? null : String(v);
|
|
8352
8630
|
} catch {
|
|
8353
8631
|
return null;
|
|
@@ -9187,7 +9465,7 @@ function renderRouteCompact(route) {
|
|
|
9187
9465
|
}
|
|
9188
9466
|
|
|
9189
9467
|
// ../cli/src/core/metadata-health.ts
|
|
9190
|
-
import
|
|
9468
|
+
import matter11 from "gray-matter";
|
|
9191
9469
|
var KNOWLEDGE_FILES2 = [
|
|
9192
9470
|
"knowledge/business/business.md",
|
|
9193
9471
|
"knowledge/product/product.md",
|
|
@@ -9207,7 +9485,7 @@ function analyzeMetadataHealth(dir) {
|
|
|
9207
9485
|
let data;
|
|
9208
9486
|
try {
|
|
9209
9487
|
const raw = readFile(p);
|
|
9210
|
-
data =
|
|
9488
|
+
data = matter11(raw).data;
|
|
9211
9489
|
} catch {
|
|
9212
9490
|
continue;
|
|
9213
9491
|
}
|
|
@@ -9239,7 +9517,7 @@ function analyzeMetadataHealth(dir) {
|
|
|
9239
9517
|
if (isFile(p) && entry.endsWith(".md")) {
|
|
9240
9518
|
try {
|
|
9241
9519
|
const raw = readFile(p);
|
|
9242
|
-
const fm =
|
|
9520
|
+
const fm = matter11(raw).data;
|
|
9243
9521
|
if (fm.status === "done") {
|
|
9244
9522
|
const rel = p.replace(dir + "/", "").replace(dir + "\\", "").replace(/\\/g, "/");
|
|
9245
9523
|
findings.push({
|
|
@@ -10107,7 +10385,7 @@ function buildGuardHistory(dir) {
|
|
|
10107
10385
|
const d3 = hotspotDir(t.code_path);
|
|
10108
10386
|
hotspotMap.set(d3, (hotspotMap.get(d3) ?? 0) + 1);
|
|
10109
10387
|
}
|
|
10110
|
-
const hotspots = [...hotspotMap.entries()].map(([
|
|
10388
|
+
const hotspots = [...hotspotMap.entries()].map(([path10, warnings]) => ({ path: path10, warnings })).sort((a2, b2) => b2.warnings - a2.warnings);
|
|
10111
10389
|
return {
|
|
10112
10390
|
available: true,
|
|
10113
10391
|
total_runs: sorted.length,
|
|
@@ -10194,7 +10472,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10194
10472
|
}
|
|
10195
10473
|
}
|
|
10196
10474
|
try {
|
|
10197
|
-
const body =
|
|
10475
|
+
const body = matter12(readFile(wi.filePath)).content;
|
|
10198
10476
|
if (hasSection(body, /acceptance|criterios de aceptaci/i)) withAcceptance++;
|
|
10199
10477
|
else gaps.missing_acceptance_criteria.push(gapItem(wi, "Add an `## Acceptance Criteria` section."));
|
|
10200
10478
|
if (hasSection(body, /definition of done|^#{1,6}\s*dod\b|definici[oó]n de (terminado|hecho)/i)) withDoD++;
|
|
@@ -10907,7 +11185,7 @@ function serializeDriftJson(r2) {
|
|
|
10907
11185
|
}
|
|
10908
11186
|
|
|
10909
11187
|
// src/workitems.ts
|
|
10910
|
-
import
|
|
11188
|
+
import matter13 from "gray-matter";
|
|
10911
11189
|
var VALID_SOURCES2 = /* @__PURE__ */ new Set(["manual", "roadmap", "jira", "github", "notion", "xlsx", "csv", "api", "external", "unknown"]);
|
|
10912
11190
|
function optStr3(v) {
|
|
10913
11191
|
return typeof v === "string" && v.trim() ? v.trim() : void 0;
|
|
@@ -10930,7 +11208,7 @@ function parseSource(data) {
|
|
|
10930
11208
|
return { type: "unknown", inferred: true };
|
|
10931
11209
|
}
|
|
10932
11210
|
var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["draft", "ready", "in-progress", "blocked"]);
|
|
10933
|
-
function
|
|
11211
|
+
function strArray3(v) {
|
|
10934
11212
|
return Array.isArray(v) ? v.map((x2) => String(x2)).filter(Boolean) : [];
|
|
10935
11213
|
}
|
|
10936
11214
|
function lifecycleOf(data, relPath) {
|
|
@@ -10998,15 +11276,15 @@ function listWorkItems(root) {
|
|
|
10998
11276
|
if (raw === null) continue;
|
|
10999
11277
|
let parsed;
|
|
11000
11278
|
try {
|
|
11001
|
-
parsed =
|
|
11279
|
+
parsed = matter13(raw);
|
|
11002
11280
|
} catch {
|
|
11003
11281
|
continue;
|
|
11004
11282
|
}
|
|
11005
11283
|
const data = parsed.data;
|
|
11006
11284
|
if (!data.type) continue;
|
|
11007
11285
|
const status = lifecycleOf(data, rel);
|
|
11008
|
-
const code =
|
|
11009
|
-
const domains =
|
|
11286
|
+
const code = strArray3(data.code);
|
|
11287
|
+
const domains = strArray3(data.domains);
|
|
11010
11288
|
const source3 = parseSource(data);
|
|
11011
11289
|
const readiness = status === "draft" ? assessReadiness(data, parsed.content, code, domains, source3) : void 0;
|
|
11012
11290
|
items.push({
|
|
@@ -11018,9 +11296,9 @@ function listWorkItems(root) {
|
|
|
11018
11296
|
path: rel,
|
|
11019
11297
|
summary: String(data.summary ?? "") || firstParagraph2(parsed.content),
|
|
11020
11298
|
code,
|
|
11021
|
-
capabilities:
|
|
11022
|
-
decisions:
|
|
11023
|
-
capsules:
|
|
11299
|
+
capabilities: strArray3(data.capabilities),
|
|
11300
|
+
decisions: strArray3(data.decisions),
|
|
11301
|
+
capsules: strArray3(data.capsules),
|
|
11024
11302
|
source: source3,
|
|
11025
11303
|
domains,
|
|
11026
11304
|
readiness,
|
|
@@ -11497,14 +11775,14 @@ function getPrompt(root, name) {
|
|
|
11497
11775
|
}
|
|
11498
11776
|
|
|
11499
11777
|
// src/tools.ts
|
|
11500
|
-
import
|
|
11778
|
+
import matter20 from "gray-matter";
|
|
11501
11779
|
|
|
11502
11780
|
// ../cli/dist/core.js
|
|
11503
11781
|
import fs3 from "fs";
|
|
11504
11782
|
import path3 from "path";
|
|
11505
11783
|
import { z as z3 } from "zod";
|
|
11506
11784
|
import { parse as parseYaml10, stringify as stringifyYaml5 } from "yaml";
|
|
11507
|
-
import
|
|
11785
|
+
import matter14 from "gray-matter";
|
|
11508
11786
|
import matter22 from "gray-matter";
|
|
11509
11787
|
import { parse as parseYaml62, stringify as stringifyYaml42 } from "yaml";
|
|
11510
11788
|
import { parse as parseYaml22 } from "yaml";
|
|
@@ -11514,28 +11792,31 @@ import { parse as parseYaml42, stringify as stringifyYaml22 } from "yaml";
|
|
|
11514
11792
|
import matter32 from "gray-matter";
|
|
11515
11793
|
import matter42 from "gray-matter";
|
|
11516
11794
|
import matter52 from "gray-matter";
|
|
11795
|
+
import matter62 from "gray-matter";
|
|
11517
11796
|
import { parse as parseYaml72 } from "yaml";
|
|
11518
11797
|
import { createHash as createHash2 } from "crypto";
|
|
11519
|
-
import matter62 from "gray-matter";
|
|
11520
11798
|
import matter72 from "gray-matter";
|
|
11799
|
+
import matter82 from "gray-matter";
|
|
11521
11800
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
11522
11801
|
import { dirname as dirname2, join as join22, parse as parse2 } from "path";
|
|
11523
11802
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
11524
|
-
import matter82 from "gray-matter";
|
|
11525
11803
|
import matter92 from "gray-matter";
|
|
11526
11804
|
import matter102 from "gray-matter";
|
|
11805
|
+
import matter112 from "gray-matter";
|
|
11527
11806
|
import { parse as parseYaml82, stringify as stringifyYaml52 } from "yaml";
|
|
11528
11807
|
import fs4 from "fs";
|
|
11529
11808
|
import path4 from "path";
|
|
11530
11809
|
import crypto2 from "crypto";
|
|
11531
|
-
import
|
|
11810
|
+
import matter122 from "gray-matter";
|
|
11532
11811
|
import crypto3 from "crypto";
|
|
11533
11812
|
import fs5 from "fs";
|
|
11534
|
-
import matter122 from "gray-matter";
|
|
11535
11813
|
import matter132 from "gray-matter";
|
|
11814
|
+
import matter142 from "gray-matter";
|
|
11536
11815
|
import { parse as parseYaml92, stringify as stringifyYaml6 } from "yaml";
|
|
11537
11816
|
import { parse as parseYaml102, stringify as stringifyYaml7 } from "yaml";
|
|
11538
|
-
import
|
|
11817
|
+
import matter15 from "gray-matter";
|
|
11818
|
+
import matter16 from "gray-matter";
|
|
11819
|
+
import matter17 from "gray-matter";
|
|
11539
11820
|
function exists2(p) {
|
|
11540
11821
|
return fs3.existsSync(p);
|
|
11541
11822
|
}
|
|
@@ -11654,8 +11935,8 @@ function loadConfig2(dir) {
|
|
|
11654
11935
|
const parsed = configSchema2.safeParse(raw ?? {});
|
|
11655
11936
|
if (!parsed.success) {
|
|
11656
11937
|
const issues = parsed.error.issues.map((i) => {
|
|
11657
|
-
const
|
|
11658
|
-
return
|
|
11938
|
+
const path62 = i.path.join(".");
|
|
11939
|
+
return path62 ? ` - ${path62}: ${i.message}` : ` - ${i.message}`;
|
|
11659
11940
|
}).join("\n");
|
|
11660
11941
|
throw new ConfigError2(`Invalid .kaddo/config.yml:
|
|
11661
11942
|
${issues}`);
|
|
@@ -11711,7 +11992,7 @@ function parseImpactAnalysis2(data) {
|
|
|
11711
11992
|
}
|
|
11712
11993
|
function parseArtifact2(filePath, raw) {
|
|
11713
11994
|
try {
|
|
11714
|
-
const { data } =
|
|
11995
|
+
const { data } = matter14(raw);
|
|
11715
11996
|
const globs = Array.isArray(data.code) ? data.code.filter(Boolean) : [];
|
|
11716
11997
|
return {
|
|
11717
11998
|
filePath,
|
|
@@ -11764,7 +12045,7 @@ function readArtifacts2(archDir) {
|
|
|
11764
12045
|
walkDir(archDir);
|
|
11765
12046
|
return artifacts;
|
|
11766
12047
|
}
|
|
11767
|
-
var
|
|
12048
|
+
var LIFECYCLE_STATES3 = [
|
|
11768
12049
|
"draft",
|
|
11769
12050
|
"ready",
|
|
11770
12051
|
"in-progress",
|
|
@@ -11779,7 +12060,7 @@ var LEGACY_STATUS_MAP2 = {
|
|
|
11779
12060
|
canceled: "archived",
|
|
11780
12061
|
"in-progress": "in-progress"
|
|
11781
12062
|
};
|
|
11782
|
-
var STATE_SET2 = new Set(
|
|
12063
|
+
var STATE_SET2 = new Set(LIFECYCLE_STATES3);
|
|
11783
12064
|
function isLifecycleState2(s) {
|
|
11784
12065
|
return STATE_SET2.has(s);
|
|
11785
12066
|
}
|
|
@@ -11801,7 +12082,7 @@ function lifecycleStateOf2(item) {
|
|
|
11801
12082
|
return "ready";
|
|
11802
12083
|
}
|
|
11803
12084
|
function emptyLifecycleCounts2() {
|
|
11804
|
-
return Object.fromEntries(
|
|
12085
|
+
return Object.fromEntries(LIFECYCLE_STATES3.map((s) => [s, 0]));
|
|
11805
12086
|
}
|
|
11806
12087
|
var ARCH_DIR3 = "knowledge";
|
|
11807
12088
|
function toPosix7(p) {
|
|
@@ -11859,11 +12140,11 @@ function moduleArtifactCoverage2(dir, id) {
|
|
|
11859
12140
|
};
|
|
11860
12141
|
}
|
|
11861
12142
|
function loadMappedModules2(dir) {
|
|
11862
|
-
const
|
|
11863
|
-
if (!exists2(
|
|
12143
|
+
const path62 = join3(dir, DESCRIPTOR_PATH2);
|
|
12144
|
+
if (!exists2(path62)) return [];
|
|
11864
12145
|
let parsed;
|
|
11865
12146
|
try {
|
|
11866
|
-
parsed = parseYaml32(readFile2(
|
|
12147
|
+
parsed = parseYaml32(readFile2(path62));
|
|
11867
12148
|
} catch {
|
|
11868
12149
|
return [];
|
|
11869
12150
|
}
|
|
@@ -13266,10 +13547,10 @@ function suggestInitiativesForExternalItem(dir, integration, externalId) {
|
|
|
13266
13547
|
);
|
|
13267
13548
|
}
|
|
13268
13549
|
var RESOURCE_ARTIFACT_TYPE2 = "project-resource";
|
|
13269
|
-
function
|
|
13550
|
+
function str3(v) {
|
|
13270
13551
|
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
13271
13552
|
}
|
|
13272
|
-
function
|
|
13553
|
+
function strArray4(v) {
|
|
13273
13554
|
if (Array.isArray(v)) return v.map((x2) => String(x2).trim()).filter(Boolean);
|
|
13274
13555
|
if (typeof v === "string" && v.trim()) return [v.trim()];
|
|
13275
13556
|
return [];
|
|
@@ -13280,16 +13561,16 @@ function parseInterfaces2(v) {
|
|
|
13280
13561
|
for (const raw of v) {
|
|
13281
13562
|
if (!raw || typeof raw !== "object") continue;
|
|
13282
13563
|
const o2 = raw;
|
|
13283
|
-
const type =
|
|
13564
|
+
const type = str3(o2.type);
|
|
13284
13565
|
if (!type) continue;
|
|
13285
13566
|
out.push({
|
|
13286
13567
|
type,
|
|
13287
|
-
tool:
|
|
13288
|
-
provider:
|
|
13289
|
-
purpose:
|
|
13290
|
-
operations:
|
|
13291
|
-
environments:
|
|
13292
|
-
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)
|
|
13293
13574
|
});
|
|
13294
13575
|
}
|
|
13295
13576
|
return out;
|
|
@@ -13298,7 +13579,7 @@ function parseBoundaries2(v) {
|
|
|
13298
13579
|
if (!v || typeof v !== "object" || Array.isArray(v)) return {};
|
|
13299
13580
|
const out = {};
|
|
13300
13581
|
for (const [env, val] of Object.entries(v)) {
|
|
13301
|
-
const list22 =
|
|
13582
|
+
const list22 = strArray4(val);
|
|
13302
13583
|
if (list22.length > 0) out[env] = list22;
|
|
13303
13584
|
}
|
|
13304
13585
|
return out;
|
|
@@ -13306,17 +13587,17 @@ function parseBoundaries2(v) {
|
|
|
13306
13587
|
function parseAuth2(v) {
|
|
13307
13588
|
if (!v || typeof v !== "object" || Array.isArray(v)) return { mode: null, refs: [] };
|
|
13308
13589
|
const o2 = v;
|
|
13309
|
-
const refs =
|
|
13310
|
-
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 };
|
|
13311
13592
|
}
|
|
13312
13593
|
function parseScope2(v) {
|
|
13313
13594
|
if (!v || typeof v !== "object" || Array.isArray(v)) return null;
|
|
13314
13595
|
const o2 = v;
|
|
13315
|
-
const type =
|
|
13596
|
+
const type = str3(o2.type);
|
|
13316
13597
|
if (!type) return null;
|
|
13317
|
-
return { type, module:
|
|
13598
|
+
return { type, module: str3(o2.module) };
|
|
13318
13599
|
}
|
|
13319
|
-
function
|
|
13600
|
+
function purposeFromBody3(body) {
|
|
13320
13601
|
const m2 = body.match(/^#{1,2}\s+Purpose\s*\n+([\s\S]*?)(?=\n#{1,2}\s|\s*$)/m);
|
|
13321
13602
|
const text22 = m2 ? m2[1].trim() : "";
|
|
13322
13603
|
return text22 || null;
|
|
@@ -13325,17 +13606,17 @@ function toDetail2(filePath, relPath, raw) {
|
|
|
13325
13606
|
const parsed = matter42(raw);
|
|
13326
13607
|
const fm = parsed.data ?? {};
|
|
13327
13608
|
const body = parsed.content.trim();
|
|
13328
|
-
const id =
|
|
13609
|
+
const id = str3(fm.id) ?? "";
|
|
13329
13610
|
return {
|
|
13330
13611
|
id,
|
|
13331
|
-
title:
|
|
13332
|
-
resourceType:
|
|
13333
|
-
provider:
|
|
13334
|
-
environments:
|
|
13612
|
+
title: str3(fm.title) ?? id,
|
|
13613
|
+
resourceType: str3(fm.resource_type),
|
|
13614
|
+
provider: str3(fm.provider),
|
|
13615
|
+
environments: strArray4(fm.environments),
|
|
13335
13616
|
scope: parseScope2(fm.scope),
|
|
13336
|
-
modules:
|
|
13617
|
+
modules: strArray4(fm.modules),
|
|
13337
13618
|
path: relPath,
|
|
13338
|
-
purpose:
|
|
13619
|
+
purpose: str3(fm.purpose) ?? purposeFromBody3(body),
|
|
13339
13620
|
interfaces: parseInterfaces2(fm.access_interfaces),
|
|
13340
13621
|
boundaries: parseBoundaries2(fm.access_boundaries),
|
|
13341
13622
|
...(() => {
|
|
@@ -13349,13 +13630,13 @@ function getResources2(dir) {
|
|
|
13349
13630
|
return discoverKnowledge2(dir).filter((a2) => a2.type === RESOURCE_ARTIFACT_TYPE2).map((a2) => {
|
|
13350
13631
|
const fm = a2.rawFrontmatter;
|
|
13351
13632
|
return {
|
|
13352
|
-
id: a2.id ||
|
|
13353
|
-
title: a2.title ||
|
|
13354
|
-
resourceType:
|
|
13355
|
-
provider:
|
|
13356
|
-
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),
|
|
13357
13638
|
scope: parseScope2(fm.scope),
|
|
13358
|
-
modules:
|
|
13639
|
+
modules: strArray4(fm.modules),
|
|
13359
13640
|
path: a2.relPath
|
|
13360
13641
|
};
|
|
13361
13642
|
}).sort((x2, y) => x2.id.localeCompare(y.id));
|
|
@@ -13375,12 +13656,184 @@ function parseWorkItemResources2(fm) {
|
|
|
13375
13656
|
for (const raw of v) {
|
|
13376
13657
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
13377
13658
|
const o2 = raw;
|
|
13378
|
-
const id =
|
|
13659
|
+
const id = str3(o2.id);
|
|
13379
13660
|
if (!id) continue;
|
|
13380
|
-
out.push({ id, role:
|
|
13661
|
+
out.push({ id, role: str3(o2.role) ?? "affected" });
|
|
13662
|
+
}
|
|
13663
|
+
return out;
|
|
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
|
+
}
|
|
13381
13696
|
}
|
|
13382
13697
|
return out;
|
|
13383
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
|
+
}
|
|
13384
13837
|
var KNOWLEDGE22 = "knowledge";
|
|
13385
13838
|
var RESOURCE_ROLE_EDGE2 = {
|
|
13386
13839
|
affected: "affects",
|
|
@@ -13424,9 +13877,9 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
13424
13877
|
];
|
|
13425
13878
|
const presentLayers = [];
|
|
13426
13879
|
for (const layer2 of layerDocs) {
|
|
13427
|
-
const
|
|
13428
|
-
if (
|
|
13429
|
-
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 });
|
|
13430
13883
|
presentLayers.push(layer2.id);
|
|
13431
13884
|
}
|
|
13432
13885
|
}
|
|
@@ -13484,6 +13937,33 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
13484
13937
|
addEdge(modId, resId, "depends_on");
|
|
13485
13938
|
}
|
|
13486
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
|
+
}
|
|
13487
13967
|
for (const wi of selectedWIs) {
|
|
13488
13968
|
const id = wi.id || wi.title;
|
|
13489
13969
|
if (!id || !id.trim()) continue;
|
|
@@ -13514,6 +13994,11 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
13514
13994
|
addNode({ id: resId, type: "project-resource", label: title ? `${rel.id} ${title}`.trim() : rel.id });
|
|
13515
13995
|
addEdge(wiNodeId, resId, RESOURCE_ROLE_EDGE2[rel.role] ?? "uses");
|
|
13516
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
|
+
}
|
|
13517
14002
|
for (const dec of wi.decisions) {
|
|
13518
14003
|
if (!dec || !dec.trim()) continue;
|
|
13519
14004
|
const adrId = `adr:${dec}`;
|
|
@@ -13873,13 +14358,13 @@ function meaningful2(text22) {
|
|
|
13873
14358
|
});
|
|
13874
14359
|
}
|
|
13875
14360
|
function readPocSummary2(dir) {
|
|
13876
|
-
const
|
|
13877
|
-
if (!exists2(
|
|
14361
|
+
const path62 = join3(dir, POC_ARTIFACT_PATH2);
|
|
14362
|
+
if (!exists2(path62)) {
|
|
13878
14363
|
return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
|
|
13879
14364
|
}
|
|
13880
14365
|
let markdown = "";
|
|
13881
14366
|
try {
|
|
13882
|
-
markdown = readFile2(
|
|
14367
|
+
markdown = readFile2(path62);
|
|
13883
14368
|
} catch {
|
|
13884
14369
|
return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
|
|
13885
14370
|
}
|
|
@@ -13910,11 +14395,11 @@ function redactSecrets2(value) {
|
|
|
13910
14395
|
return line.replace(/([:=]\s*)\S+.*/, "$1[redacted]");
|
|
13911
14396
|
}).join("\n");
|
|
13912
14397
|
}
|
|
13913
|
-
function source2(dir,
|
|
13914
|
-
const absolute = join3(dir,
|
|
14398
|
+
function source2(dir, path62, kind) {
|
|
14399
|
+
const absolute = join3(dir, path62);
|
|
13915
14400
|
if (!exists2(absolute)) return null;
|
|
13916
14401
|
const raw = readFile2(absolute);
|
|
13917
|
-
return { path: posix2(
|
|
14402
|
+
return { path: posix2(path62), kind, digest: sha2(raw), content: redactSecrets2(raw) };
|
|
13918
14403
|
}
|
|
13919
14404
|
function reportFiles2(dir) {
|
|
13920
14405
|
const delivery = join3(dir, "knowledge", "delivery");
|
|
@@ -13922,7 +14407,7 @@ function reportFiles2(dir) {
|
|
|
13922
14407
|
const match = name.match(reportName2);
|
|
13923
14408
|
if (!match) return [];
|
|
13924
14409
|
try {
|
|
13925
|
-
const parsed =
|
|
14410
|
+
const parsed = matter72(readFile2(join3(delivery, name)));
|
|
13926
14411
|
const fingerprint = typeof parsed.data.source_fingerprint === "string" ? parsed.data.source_fingerprint : "";
|
|
13927
14412
|
const manifest = parsed.data.source_manifest;
|
|
13928
14413
|
const sourceManifest = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? Object.fromEntries(Object.entries(manifest).filter(([, digest]) => typeof digest === "string")) : {};
|
|
@@ -13954,9 +14439,9 @@ function buildPocReportContext2(dir) {
|
|
|
13954
14439
|
};
|
|
13955
14440
|
}
|
|
13956
14441
|
const sources = [];
|
|
13957
|
-
const add = (
|
|
13958
|
-
if (sources.some((item2) => item2.path === posix2(
|
|
13959
|
-
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);
|
|
13960
14445
|
if (item) sources.push(item);
|
|
13961
14446
|
};
|
|
13962
14447
|
add(POC_ARTIFACT_PATH2, "poc");
|
|
@@ -13984,7 +14469,7 @@ function buildPocReportContext2(dir) {
|
|
|
13984
14469
|
const sourceFingerprint = sha2(sources.map((item) => `${item.path}\0${item.digest}`).join("\n"));
|
|
13985
14470
|
const status = !latestReport ? "missing" : latestReport.sourceFingerprint === sourceFingerprint ? "current" : "stale";
|
|
13986
14471
|
const currentManifest = Object.fromEntries(sources.map((item) => [item.path, item.digest]));
|
|
13987
|
-
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() : [];
|
|
13988
14473
|
const sourceList = sources.map((item) => `- ${item.path} (${item.kind})`).join("\n") || "- No sources selected.";
|
|
13989
14474
|
return {
|
|
13990
14475
|
eligible: true,
|
|
@@ -14021,7 +14506,7 @@ function canonicalReport(context, proposal) {
|
|
|
14021
14506
|
source_manifest: Object.fromEntries(context.sources.map((item) => [item.path, item.digest]))
|
|
14022
14507
|
};
|
|
14023
14508
|
if (supersedes) data.supersedes = supersedes;
|
|
14024
|
-
return
|
|
14509
|
+
return matter72.stringify(redactSecrets2(proposal).trimEnd() + "\n", data);
|
|
14025
14510
|
}
|
|
14026
14511
|
function persistPocReport(dir, proposal, opts = {}) {
|
|
14027
14512
|
const context = buildPocReportContext2(dir);
|
|
@@ -17987,6 +18472,8 @@ function getWorkItem(dir, workItemId) {
|
|
|
17987
18472
|
markdownBody: body.trim(),
|
|
17988
18473
|
// External systems this WI relates to (WI-030), separate from affectedModules (code).
|
|
17989
18474
|
resources: parseWorkItemResources2(fm),
|
|
18475
|
+
// Deprecation Targets this WI retires (WI-050), referenced not copied.
|
|
18476
|
+
deprecationTargets: parseWorkItemDeprecationTargets2(fm),
|
|
17990
18477
|
...parseSystemImpact(dir, fm)
|
|
17991
18478
|
};
|
|
17992
18479
|
return { ...detail, refinement: computeRefinementStatus(detail) };
|
|
@@ -17995,8 +18482,8 @@ function parseGraphReason(v) {
|
|
|
17995
18482
|
if (!v || typeof v !== "object") return null;
|
|
17996
18483
|
const o2 = v;
|
|
17997
18484
|
const relationship = typeof o2.relationship === "string" && o2.relationship.trim() ? o2.relationship.trim() : null;
|
|
17998
|
-
const
|
|
17999
|
-
return relationship ||
|
|
18485
|
+
const path62 = Array.isArray(o2.path) ? o2.path.map(String).filter(Boolean) : [];
|
|
18486
|
+
return relationship || path62.length ? { relationship, path: path62 } : null;
|
|
18000
18487
|
}
|
|
18001
18488
|
function parseExplain(o2) {
|
|
18002
18489
|
const refs = Array.isArray(o2.evidence) ? o2.evidence : Array.isArray(o2.evidence_refs) ? o2.evidence_refs : [];
|
|
@@ -18051,7 +18538,7 @@ function parseOriginalSnapshot(fm) {
|
|
|
18051
18538
|
function readBody(filePath) {
|
|
18052
18539
|
try {
|
|
18053
18540
|
const raw = readFile2(filePath);
|
|
18054
|
-
return
|
|
18541
|
+
return matter112(raw).content;
|
|
18055
18542
|
} catch {
|
|
18056
18543
|
return "";
|
|
18057
18544
|
}
|
|
@@ -18336,7 +18823,7 @@ function ensureH1(body, title) {
|
|
|
18336
18823
|
${trimmed}`;
|
|
18337
18824
|
}
|
|
18338
18825
|
function serialize2(data, body) {
|
|
18339
|
-
return
|
|
18826
|
+
return matter122.stringify(`
|
|
18340
18827
|
${body.trim()}
|
|
18341
18828
|
`, data);
|
|
18342
18829
|
}
|
|
@@ -18701,6 +19188,61 @@ function buildRefinementHandoff(dir, workItemId) {
|
|
|
18701
19188
|
text: lines.join("\n")
|
|
18702
19189
|
};
|
|
18703
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");
|
|
18704
19246
|
var WorkItemNotReadyError = class extends Error {
|
|
18705
19247
|
constructor(workItemId, actualState) {
|
|
18706
19248
|
super(`Work Item "${workItemId}" must be in "ready" state to produce an Implementation Handoff, but is "${actualState}".`);
|
|
@@ -18731,6 +19273,16 @@ function buildImplementationHandoff(dir, workItemId) {
|
|
|
18731
19273
|
const domains = match.domains;
|
|
18732
19274
|
const scopeConfidence = match.scopeConfidence;
|
|
18733
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
|
+
};
|
|
18734
19286
|
const lines = [
|
|
18735
19287
|
`Implement Work Item ${wiId} \u2014 "${wiTitle}" \u2014 in project "${projectName}" using Kaddo.`,
|
|
18736
19288
|
"",
|
|
@@ -18855,6 +19407,48 @@ function buildImplementationHandoff(dir, workItemId) {
|
|
|
18855
19407
|
"installed or connected. Do not execute any interface or migration without explicit human confirmation."
|
|
18856
19408
|
);
|
|
18857
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
|
+
}
|
|
18858
19452
|
lines.push(
|
|
18859
19453
|
"",
|
|
18860
19454
|
"--- Implementation Plan ---",
|
|
@@ -18876,6 +19470,7 @@ function buildImplementationHandoff(dir, workItemId) {
|
|
|
18876
19470
|
lifecycle,
|
|
18877
19471
|
affectedModules,
|
|
18878
19472
|
resources: wiResources,
|
|
19473
|
+
deprecationTargets: wiDeprecationRefs,
|
|
18879
19474
|
domains,
|
|
18880
19475
|
scopeConfidence,
|
|
18881
19476
|
hasUnmaterializedDecisions: unmaterialized,
|
|
@@ -19127,7 +19722,7 @@ function detectFormat(raw) {
|
|
|
19127
19722
|
if (!trimmed) throw new ImportError("EMPTY_INPUT", "Import content is empty.");
|
|
19128
19723
|
if (trimmed.startsWith("---")) {
|
|
19129
19724
|
try {
|
|
19130
|
-
const { data } =
|
|
19725
|
+
const { data } = matter132(trimmed);
|
|
19131
19726
|
if (data && typeof data === "object" && Object.keys(data).length > 0) {
|
|
19132
19727
|
const d3 = data;
|
|
19133
19728
|
if (d3.type === "work-item" || d3.id && typeof d3.id === "string" && /^WI-\d+/.test(d3.id)) {
|
|
@@ -19149,7 +19744,7 @@ function parseContent(raw) {
|
|
|
19149
19744
|
let bodyText = trimmed;
|
|
19150
19745
|
if (format === "kaddo-frontmatter" || format === "markdown-frontmatter") {
|
|
19151
19746
|
try {
|
|
19152
|
-
const parsed =
|
|
19747
|
+
const parsed = matter132(trimmed);
|
|
19153
19748
|
frontmatterFields = parsed.data ?? {};
|
|
19154
19749
|
bodyText = parsed.content.trim();
|
|
19155
19750
|
} catch {
|
|
@@ -19246,7 +19841,7 @@ function findDuplicateByHash(dir, hash) {
|
|
|
19246
19841
|
const raw = readFile2(item.filePath);
|
|
19247
19842
|
if (!raw) continue;
|
|
19248
19843
|
try {
|
|
19249
|
-
const { data } =
|
|
19844
|
+
const { data } = matter132(raw);
|
|
19250
19845
|
const source22 = data?.source;
|
|
19251
19846
|
if (source22 && typeof source22 === "object" && !Array.isArray(source22)) {
|
|
19252
19847
|
const s = source22;
|
|
@@ -19755,8 +20350,8 @@ async function handleJiraResponse(res) {
|
|
|
19755
20350
|
}
|
|
19756
20351
|
return await res.json();
|
|
19757
20352
|
}
|
|
19758
|
-
async function jiraFetch(baseUrl,
|
|
19759
|
-
const url = new URL(
|
|
20353
|
+
async function jiraFetch(baseUrl, path62, auth, timeoutMs, params) {
|
|
20354
|
+
const url = new URL(path62, baseUrl + "/");
|
|
19760
20355
|
if (params) for (const [k2, v] of Object.entries(params)) url.searchParams.set(k2, v);
|
|
19761
20356
|
const controller = new AbortController();
|
|
19762
20357
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -19774,8 +20369,8 @@ async function jiraFetch(baseUrl, path52, auth, timeoutMs, params) {
|
|
|
19774
20369
|
clearTimeout(timer);
|
|
19775
20370
|
}
|
|
19776
20371
|
}
|
|
19777
|
-
async function jiraPost(baseUrl,
|
|
19778
|
-
const url = new URL(
|
|
20372
|
+
async function jiraPost(baseUrl, path62, auth, timeoutMs, body) {
|
|
20373
|
+
const url = new URL(path62, baseUrl + "/");
|
|
19779
20374
|
const controller = new AbortController();
|
|
19780
20375
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
19781
20376
|
try {
|
|
@@ -20141,7 +20736,7 @@ function buildInitiativeContent(id, input) {
|
|
|
20141
20736
|
if (input.priority) data.priority = input.priority;
|
|
20142
20737
|
if (input.source) data.source = input.source;
|
|
20143
20738
|
if (input.sourceId) data.source_id = input.sourceId;
|
|
20144
|
-
return
|
|
20739
|
+
return matter15.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
|
|
20145
20740
|
}
|
|
20146
20741
|
function createInitiative(dir, input) {
|
|
20147
20742
|
if (!input.title?.trim()) throw new InitiativeWriteError("Initiative title is required.");
|
|
@@ -20165,10 +20760,10 @@ function transitionInitiative(dir, id, to) {
|
|
|
20165
20760
|
throw new InitiativeWriteError(`Invalid transition ${ini.status} \u2192 ${to}.`);
|
|
20166
20761
|
}
|
|
20167
20762
|
const raw = readFile2(ini.filePath);
|
|
20168
|
-
const { data, content } =
|
|
20763
|
+
const { data, content } = matter15(raw, {});
|
|
20169
20764
|
data.status = to;
|
|
20170
20765
|
if (to === "completed") data.completed_at = today2();
|
|
20171
|
-
const newContent =
|
|
20766
|
+
const newContent = matter15.stringify(content, data);
|
|
20172
20767
|
writeFile2(ini.filePath, newContent);
|
|
20173
20768
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
20174
20769
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after transition.");
|
|
@@ -20178,7 +20773,7 @@ function addCandidate(dir, id, candidate) {
|
|
|
20178
20773
|
const ini = getInitiative(dir, id);
|
|
20179
20774
|
if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
|
|
20180
20775
|
const raw = readFile2(ini.filePath);
|
|
20181
|
-
const { data, content } =
|
|
20776
|
+
const { data, content } = matter15(raw, {});
|
|
20182
20777
|
const existing = Array.isArray(data.candidates) ? data.candidates : [];
|
|
20183
20778
|
const fallbackId = `WI-CANDIDATE-${String(existing.length + 1).padStart(3, "0")}`;
|
|
20184
20779
|
const normalized = normalizeCandidate(candidate, fallbackId);
|
|
@@ -20186,7 +20781,7 @@ function addCandidate(dir, id, candidate) {
|
|
|
20186
20781
|
throw new InitiativeWriteError(`Candidate ${normalized.id} already exists in ${id}.`);
|
|
20187
20782
|
}
|
|
20188
20783
|
data.candidates = [...existing, candidateToData(normalized)];
|
|
20189
|
-
const newContent =
|
|
20784
|
+
const newContent = matter15.stringify(content, data);
|
|
20190
20785
|
writeFile2(ini.filePath, newContent);
|
|
20191
20786
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
20192
20787
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding a candidate.");
|
|
@@ -20196,7 +20791,7 @@ function updateInitiative(dir, id, patch) {
|
|
|
20196
20791
|
const ini = getInitiative(dir, id);
|
|
20197
20792
|
if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
|
|
20198
20793
|
const raw = readFile2(ini.filePath);
|
|
20199
|
-
const { data, content } =
|
|
20794
|
+
const { data, content } = matter15(raw, {});
|
|
20200
20795
|
if (patch.title !== void 0) {
|
|
20201
20796
|
if (!patch.title.trim()) throw new InitiativeWriteError("Initiative title cannot be empty.");
|
|
20202
20797
|
data.title = patch.title.trim();
|
|
@@ -20206,7 +20801,7 @@ function updateInitiative(dir, id, patch) {
|
|
|
20206
20801
|
if (patch.knowledgeLevel !== void 0) data.knowledge_level = patch.knowledgeLevel;
|
|
20207
20802
|
if (patch.domains !== void 0) data.domains = patch.domains;
|
|
20208
20803
|
if (patch.relatedCapabilities !== void 0) data.related_capabilities = patch.relatedCapabilities;
|
|
20209
|
-
const newContent =
|
|
20804
|
+
const newContent = matter15.stringify(content, data);
|
|
20210
20805
|
writeFile2(ini.filePath, newContent);
|
|
20211
20806
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
20212
20807
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after update.");
|
|
@@ -20224,10 +20819,10 @@ function addExternalLink(dir, id, link) {
|
|
|
20224
20819
|
throw new InitiativeWriteError(`External link ${link.integration}:${link.externalId} already exists on ${id}.`);
|
|
20225
20820
|
}
|
|
20226
20821
|
const raw = readFile2(ini.filePath);
|
|
20227
|
-
const { data, content } =
|
|
20822
|
+
const { data, content } = matter15(raw, {});
|
|
20228
20823
|
const existing = Array.isArray(data.external_links) ? data.external_links : [];
|
|
20229
20824
|
data.external_links = [...existing, externalLinkToData(link)];
|
|
20230
|
-
const newContent =
|
|
20825
|
+
const newContent = matter15.stringify(content, data);
|
|
20231
20826
|
writeFile2(ini.filePath, newContent);
|
|
20232
20827
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
20233
20828
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding an external link.");
|
|
@@ -20296,7 +20891,7 @@ function buildWorkItemContent(wiId, iniId, candidate) {
|
|
|
20296
20891
|
"_What did we learn? Update after completion._",
|
|
20297
20892
|
""
|
|
20298
20893
|
].join("\n");
|
|
20299
|
-
return
|
|
20894
|
+
return matter15.stringify("\n" + body, data);
|
|
20300
20895
|
}
|
|
20301
20896
|
function materializeCandidate(dir, id, candidateId) {
|
|
20302
20897
|
const ini = getInitiative(dir, id);
|
|
@@ -20313,13 +20908,13 @@ function materializeCandidate(dir, id, candidateId) {
|
|
|
20313
20908
|
ensureDir2(join3(dir, DRAFT_DIR));
|
|
20314
20909
|
writeFile2(join3(dir, DRAFT_DIR, fileName), buildWorkItemContent(wiId, ini.id, candidate));
|
|
20315
20910
|
const raw = readFile2(ini.filePath);
|
|
20316
|
-
const { data, content } =
|
|
20911
|
+
const { data, content } = matter15(raw, {});
|
|
20317
20912
|
const cands = Array.isArray(data.candidates) ? data.candidates : [];
|
|
20318
20913
|
for (const c of cands) {
|
|
20319
20914
|
if (c && typeof c === "object" && String(c.id) === candidateId) c.materialized_as = wiId;
|
|
20320
20915
|
}
|
|
20321
20916
|
data.candidates = cands;
|
|
20322
|
-
const newContent =
|
|
20917
|
+
const newContent = matter15.stringify(content, data);
|
|
20323
20918
|
writeFile2(ini.filePath, newContent);
|
|
20324
20919
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
20325
20920
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after materialization.");
|
|
@@ -20433,7 +21028,7 @@ function evaluateInitiativeCompletion(dir, ini) {
|
|
|
20433
21028
|
// ../cli/src/core/resource-write.ts
|
|
20434
21029
|
import fs6 from "fs";
|
|
20435
21030
|
import path5 from "path";
|
|
20436
|
-
import
|
|
21031
|
+
import matter18 from "gray-matter";
|
|
20437
21032
|
var RESOURCES_DIR = "knowledge/tech/resources";
|
|
20438
21033
|
var ResourceWriteError = class extends Error {
|
|
20439
21034
|
constructor(code, message) {
|
|
@@ -20513,7 +21108,7 @@ function buildFrontmatter(id, input, base = {}) {
|
|
|
20513
21108
|
return data;
|
|
20514
21109
|
}
|
|
20515
21110
|
function serialize(data, body) {
|
|
20516
|
-
return
|
|
21111
|
+
return matter18.stringify(`
|
|
20517
21112
|
${body.trim()}
|
|
20518
21113
|
`, data);
|
|
20519
21114
|
}
|
|
@@ -20553,7 +21148,7 @@ function updateResource(dir, id, input) {
|
|
|
20553
21148
|
throw new ResourceWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
|
|
20554
21149
|
}
|
|
20555
21150
|
const raw = readFile(match.filePath);
|
|
20556
|
-
const parsed =
|
|
21151
|
+
const parsed = matter18(raw);
|
|
20557
21152
|
const base = parsed.data ?? {};
|
|
20558
21153
|
const data = buildFrontmatter(match.id || id, input, base);
|
|
20559
21154
|
let body = parsed.content.trim();
|
|
@@ -20588,6 +21183,355 @@ function deleteResource(dir, id, opts = {}) {
|
|
|
20588
21183
|
return { deleted: true, path: match.relPath };
|
|
20589
21184
|
}
|
|
20590
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
|
+
|
|
20591
21535
|
// src/tools.ts
|
|
20592
21536
|
var ok = (data) => ({ ok: true, data });
|
|
20593
21537
|
var fail = (message) => ({ ok: false, message });
|
|
@@ -20694,12 +21638,12 @@ function markWorkItemReady(root, id, confirm2) {
|
|
|
20694
21638
|
}
|
|
20695
21639
|
const raw = readText(root, item.path);
|
|
20696
21640
|
if (!raw) return fail(`Could not read Work Item file: ${item.path}`);
|
|
20697
|
-
const parsed =
|
|
21641
|
+
const parsed = matter20(raw);
|
|
20698
21642
|
const data = { ...parsed.data };
|
|
20699
21643
|
const today3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
20700
21644
|
data.status = "ready";
|
|
20701
21645
|
data.ready_at = today3;
|
|
20702
|
-
const newContent =
|
|
21646
|
+
const newContent = matter20.stringify(parsed.content, data);
|
|
20703
21647
|
const posixPath = item.path.replace(/\\/g, "/");
|
|
20704
21648
|
let newRelPath = item.path;
|
|
20705
21649
|
if (posixPath.includes("/work-items/draft/")) {
|
|
@@ -20923,11 +21867,78 @@ function deleteResourceTool(root, id, confirm2) {
|
|
|
20923
21867
|
return fail(e instanceof ResourceWriteError ? e.message : String(e));
|
|
20924
21868
|
}
|
|
20925
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
|
+
}
|
|
20926
21937
|
|
|
20927
21938
|
// src/multirepo.ts
|
|
20928
|
-
import
|
|
20929
|
-
import
|
|
20930
|
-
import
|
|
21939
|
+
import fs8 from "fs";
|
|
21940
|
+
import path7 from "path";
|
|
21941
|
+
import matter21 from "gray-matter";
|
|
20931
21942
|
import { parse as parseYaml11, stringify as stringifyYaml8 } from "yaml";
|
|
20932
21943
|
var ok2 = (data) => ({ ok: true, data });
|
|
20933
21944
|
var fail2 = (message) => ({ ok: false, message });
|
|
@@ -20947,24 +21958,24 @@ function isCore2(config) {
|
|
|
20947
21958
|
return projectRole2(config) === "core";
|
|
20948
21959
|
}
|
|
20949
21960
|
function readModuleFile(root, repoPath, relFile) {
|
|
20950
|
-
const moduleDir =
|
|
20951
|
-
const abs =
|
|
21961
|
+
const moduleDir = path7.resolve(root, repoPath);
|
|
21962
|
+
const abs = path7.resolve(moduleDir, relFile);
|
|
20952
21963
|
if (!abs.startsWith(moduleDir)) return null;
|
|
20953
|
-
if (!
|
|
20954
|
-
return
|
|
21964
|
+
if (!fs8.existsSync(abs) || !fs8.statSync(abs).isFile()) return null;
|
|
21965
|
+
return fs8.readFileSync(abs, "utf-8");
|
|
20955
21966
|
}
|
|
20956
21967
|
function detectModuleStatus2(root, repoPath) {
|
|
20957
|
-
const moduleDir =
|
|
20958
|
-
if (!
|
|
21968
|
+
const moduleDir = path7.resolve(root, repoPath);
|
|
21969
|
+
if (!fs8.existsSync(moduleDir)) {
|
|
20959
21970
|
return { status: "missing", configured: false, moduleContext: false, currentState: false, codebase: false, warning: `Directory \`${repoPath}\` does not exist.` };
|
|
20960
21971
|
}
|
|
20961
|
-
const configPath =
|
|
20962
|
-
if (!
|
|
21972
|
+
const configPath = path7.join(moduleDir, ".kaddo", "config.yml");
|
|
21973
|
+
if (!fs8.existsSync(configPath)) {
|
|
20963
21974
|
return { status: "not_configured", configured: false, moduleContext: false, currentState: false, codebase: false, warning: `Run \`kaddo init\` inside \`${repoPath}\` and select multirepo \u2192 module.` };
|
|
20964
21975
|
}
|
|
20965
21976
|
let parsed = null;
|
|
20966
21977
|
try {
|
|
20967
|
-
parsed = parseYaml11(
|
|
21978
|
+
parsed = parseYaml11(fs8.readFileSync(configPath, "utf-8"));
|
|
20968
21979
|
} catch {
|
|
20969
21980
|
}
|
|
20970
21981
|
const role = parsed?.project?.role ?? parsed?.multirepo?.role;
|
|
@@ -20975,14 +21986,14 @@ function detectModuleStatus2(root, repoPath) {
|
|
|
20975
21986
|
status: "configured",
|
|
20976
21987
|
configured: true,
|
|
20977
21988
|
role: "module",
|
|
20978
|
-
moduleContext:
|
|
20979
|
-
currentState:
|
|
20980
|
-
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"))
|
|
20981
21992
|
};
|
|
20982
21993
|
}
|
|
20983
21994
|
function resolveModuleContextPath(root, repoPath) {
|
|
20984
|
-
const moduleDir =
|
|
20985
|
-
if (
|
|
21995
|
+
const moduleDir = path7.resolve(root, repoPath);
|
|
21996
|
+
if (fs8.existsSync(path7.join(moduleDir, "knowledge", "tech", "module", "module-context.md"))) {
|
|
20986
21997
|
return "knowledge/tech/module/module-context.md";
|
|
20987
21998
|
}
|
|
20988
21999
|
return "knowledge/module/module-context.md";
|
|
@@ -21079,7 +22090,7 @@ function validateWorkItemModulesTool(root, args) {
|
|
|
21079
22090
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
21080
22091
|
const raw = readText(root, wi.path);
|
|
21081
22092
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
21082
|
-
const parsed =
|
|
22093
|
+
const parsed = matter21(raw);
|
|
21083
22094
|
const data = parsed.data;
|
|
21084
22095
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
21085
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) : [] })) : [];
|
|
@@ -21132,7 +22143,7 @@ function getWorkItemContextTool(root, args) {
|
|
|
21132
22143
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
21133
22144
|
const raw = readText(root, wi.path);
|
|
21134
22145
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
21135
|
-
const parsed =
|
|
22146
|
+
const parsed = matter21(raw);
|
|
21136
22147
|
const data = parsed.data;
|
|
21137
22148
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
21138
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) : [] })) : [];
|
|
@@ -21186,15 +22197,15 @@ function extractSection(content, heading) {
|
|
|
21186
22197
|
return out;
|
|
21187
22198
|
}
|
|
21188
22199
|
function buildBranchStrategy(wi, affectedModules, codeEntries, descriptor) {
|
|
21189
|
-
const
|
|
22200
|
+
const slug7 = slugify4(wi.title);
|
|
21190
22201
|
const wiLower = wi.id.toLowerCase();
|
|
21191
|
-
const coreBranch = `feature/${wiLower}-${
|
|
22202
|
+
const coreBranch = `feature/${wiLower}-${slug7}`;
|
|
21192
22203
|
const moduleBranches = {};
|
|
21193
22204
|
const commitMessages = {};
|
|
21194
22205
|
for (const modId of affectedModules) {
|
|
21195
22206
|
const mod = (descriptor.modules ?? []).find((m2) => m2.id === modId);
|
|
21196
22207
|
const repo = mod ? moduleRepoPath(mod) || modId : modId;
|
|
21197
|
-
moduleBranches[repo] = `feature/${wiLower}-${modId}-${
|
|
22208
|
+
moduleBranches[repo] = `feature/${wiLower}-${modId}-${slug7.slice(0, 20)}`;
|
|
21198
22209
|
commitMessages[repo] = `fix(${wiLower}): update ${modId}`;
|
|
21199
22210
|
}
|
|
21200
22211
|
commitMessages["core"] = `docs(${wiLower}): capture cross-repo implementation summary`;
|
|
@@ -21218,7 +22229,7 @@ function suggestBranchStrategyTool(root, args) {
|
|
|
21218
22229
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
21219
22230
|
const raw = readText(root, wi.path);
|
|
21220
22231
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
21221
|
-
const parsed =
|
|
22232
|
+
const parsed = matter21(raw);
|
|
21222
22233
|
const data = parsed.data;
|
|
21223
22234
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
21224
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) : [] })) : [];
|
|
@@ -21367,31 +22378,31 @@ function modulesDiscoverTool(root, args) {
|
|
|
21367
22378
|
const sysName = config.system?.name ?? config.project?.name ?? "";
|
|
21368
22379
|
const workspaceRoots2 = config.multirepo?.workspace_roots ?? [".."];
|
|
21369
22380
|
const IGNORED = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "coverage", "vendor", ".next", "out", "tmp", "temp", ".kaddo"]);
|
|
21370
|
-
const coreAbs =
|
|
22381
|
+
const coreAbs = path7.resolve(root);
|
|
21371
22382
|
const discovered = [];
|
|
21372
22383
|
for (const wr of workspaceRoots2) {
|
|
21373
|
-
const rootAbs =
|
|
21374
|
-
if (!
|
|
22384
|
+
const rootAbs = path7.resolve(root, wr);
|
|
22385
|
+
if (!fs8.existsSync(rootAbs) || !fs8.statSync(rootAbs).isDirectory()) continue;
|
|
21375
22386
|
let entries;
|
|
21376
22387
|
try {
|
|
21377
|
-
entries =
|
|
22388
|
+
entries = fs8.readdirSync(rootAbs);
|
|
21378
22389
|
} catch {
|
|
21379
22390
|
continue;
|
|
21380
22391
|
}
|
|
21381
22392
|
for (const entry of entries) {
|
|
21382
22393
|
if (IGNORED.has(entry)) continue;
|
|
21383
|
-
const candidateAbs =
|
|
21384
|
-
if (!
|
|
22394
|
+
const candidateAbs = path7.resolve(rootAbs, entry);
|
|
22395
|
+
if (!fs8.statSync(candidateAbs).isDirectory()) continue;
|
|
21385
22396
|
if (candidateAbs === coreAbs) continue;
|
|
21386
|
-
const relPath =
|
|
21387
|
-
const cfgPath =
|
|
21388
|
-
if (!
|
|
22397
|
+
const relPath = path7.relative(root, candidateAbs).replace(/\\/g, "/");
|
|
22398
|
+
const cfgPath = path7.join(candidateAbs, ".kaddo", "config.yml");
|
|
22399
|
+
if (!fs8.existsSync(cfgPath)) {
|
|
21389
22400
|
discovered.push({ id: entry, path: relPath, status: "not_configured", eligibleForMapping: false });
|
|
21390
22401
|
continue;
|
|
21391
22402
|
}
|
|
21392
22403
|
let parsed = null;
|
|
21393
22404
|
try {
|
|
21394
|
-
parsed = parseYaml11(
|
|
22405
|
+
parsed = parseYaml11(fs8.readFileSync(cfgPath, "utf-8"));
|
|
21395
22406
|
} catch {
|
|
21396
22407
|
}
|
|
21397
22408
|
const role = parsed?.project?.role ?? parsed?.multirepo?.role;
|
|
@@ -21465,8 +22476,8 @@ ${list3(data.owners)}
|
|
|
21465
22476
|
|
|
21466
22477
|
// ../cli/src/core/system-topology.ts
|
|
21467
22478
|
import { parse as parseYaml12, stringify as stringifyYaml9 } from "yaml";
|
|
21468
|
-
import
|
|
21469
|
-
import
|
|
22479
|
+
import fs9 from "fs";
|
|
22480
|
+
import path8 from "path";
|
|
21470
22481
|
import crypto from "crypto";
|
|
21471
22482
|
var TOPOLOGY_FILE2 = "knowledge/tech/system-topology.yml";
|
|
21472
22483
|
var SYSTEM_ENTITY_KINDS2 = /* @__PURE__ */ new Set([
|
|
@@ -21838,16 +22849,16 @@ function findSystemPaths(dir, source3, target, opts = {}) {
|
|
|
21838
22849
|
out.get(r2.source).push(r2.target);
|
|
21839
22850
|
}
|
|
21840
22851
|
const paths = [];
|
|
21841
|
-
const walk = (node,
|
|
22852
|
+
const walk = (node, path10) => {
|
|
21842
22853
|
if (paths.length >= 20) return;
|
|
21843
|
-
if (node === target &&
|
|
21844
|
-
paths.push([...
|
|
22854
|
+
if (node === target && path10.length > 1) {
|
|
22855
|
+
paths.push([...path10]);
|
|
21845
22856
|
return;
|
|
21846
22857
|
}
|
|
21847
|
-
if (
|
|
22858
|
+
if (path10.length > maxDepth) return;
|
|
21848
22859
|
for (const nxt of out.get(node) ?? []) {
|
|
21849
|
-
if (
|
|
21850
|
-
walk(nxt, [...
|
|
22860
|
+
if (path10.includes(nxt)) continue;
|
|
22861
|
+
walk(nxt, [...path10, nxt]);
|
|
21851
22862
|
}
|
|
21852
22863
|
};
|
|
21853
22864
|
walk(source3, [source3]);
|
|
@@ -21872,14 +22883,14 @@ function getImpactCandidates(dir, seedIds, opts = {}) {
|
|
|
21872
22883
|
existing.reason.push(reason);
|
|
21873
22884
|
continue;
|
|
21874
22885
|
}
|
|
21875
|
-
const
|
|
22886
|
+
const path10 = findSystemPaths(dir, seed, n.id, { maxDepth: opts.maxDepth ?? DEFAULT_MAX_DEPTH })[0];
|
|
21876
22887
|
candidates.set(n.id, {
|
|
21877
22888
|
nodeId: n.id,
|
|
21878
22889
|
label: n.label,
|
|
21879
22890
|
kind: n.type,
|
|
21880
22891
|
...n.moduleId ? { moduleId: n.moduleId } : {},
|
|
21881
22892
|
reason: [reason],
|
|
21882
|
-
...
|
|
22893
|
+
...path10 ? { graphPath: path10.map((id) => byId.get(id)?.label ?? id) } : {},
|
|
21883
22894
|
...n.implementationRefs?.length ? { implementationRefs: n.implementationRefs } : {},
|
|
21884
22895
|
...n.knowledgeRefs?.length ? { knowledgeRefs: n.knowledgeRefs } : {},
|
|
21885
22896
|
status: "candidate",
|
|
@@ -21978,14 +22989,14 @@ function systemImpactCandidatesTool(root, args) {
|
|
|
21978
22989
|
}
|
|
21979
22990
|
|
|
21980
22991
|
// ../cli/src/services/integrations.ts
|
|
21981
|
-
import
|
|
22992
|
+
import matter24 from "gray-matter";
|
|
21982
22993
|
import { parse as parseYaml13, stringify as stringifyYaml10 } from "yaml";
|
|
21983
22994
|
|
|
21984
22995
|
// ../cli/src/core/work-item-write.ts
|
|
21985
|
-
import
|
|
21986
|
-
import
|
|
22996
|
+
import fs10 from "fs";
|
|
22997
|
+
import path9 from "path";
|
|
21987
22998
|
import crypto4 from "crypto";
|
|
21988
|
-
import
|
|
22999
|
+
import matter23 from "gray-matter";
|
|
21989
23000
|
var SECTION_ORDER2 = [
|
|
21990
23001
|
"Actor",
|
|
21991
23002
|
"Outcome",
|
|
@@ -22653,8 +23664,8 @@ async function handleJiraResponse2(res) {
|
|
|
22653
23664
|
}
|
|
22654
23665
|
return await res.json();
|
|
22655
23666
|
}
|
|
22656
|
-
async function jiraFetch2(baseUrl,
|
|
22657
|
-
const url = new URL(
|
|
23667
|
+
async function jiraFetch2(baseUrl, path10, auth, timeoutMs, params) {
|
|
23668
|
+
const url = new URL(path10, baseUrl + "/");
|
|
22658
23669
|
if (params) for (const [k2, v] of Object.entries(params)) url.searchParams.set(k2, v);
|
|
22659
23670
|
const controller = new AbortController();
|
|
22660
23671
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -22672,8 +23683,8 @@ async function jiraFetch2(baseUrl, path9, auth, timeoutMs, params) {
|
|
|
22672
23683
|
clearTimeout(timer);
|
|
22673
23684
|
}
|
|
22674
23685
|
}
|
|
22675
|
-
async function jiraPost2(baseUrl,
|
|
22676
|
-
const url = new URL(
|
|
23686
|
+
async function jiraPost2(baseUrl, path10, auth, timeoutMs, body) {
|
|
23687
|
+
const url = new URL(path10, baseUrl + "/");
|
|
22677
23688
|
const controller = new AbortController();
|
|
22678
23689
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
22679
23690
|
try {
|
|
@@ -23033,7 +24044,7 @@ async function integrationsWorkItemTool(root, args) {
|
|
|
23033
24044
|
}
|
|
23034
24045
|
|
|
23035
24046
|
// ../cli/src/core/context-pack.ts
|
|
23036
|
-
import
|
|
24047
|
+
import matter25 from "gray-matter";
|
|
23037
24048
|
var CONTEXT_PACK_VERSION = "1";
|
|
23038
24049
|
var ARCH_DIR4 = "knowledge";
|
|
23039
24050
|
function readScanJson(dir) {
|
|
@@ -23046,7 +24057,7 @@ function readScanJson(dir) {
|
|
|
23046
24057
|
}
|
|
23047
24058
|
}
|
|
23048
24059
|
function firstParagraph4(markdown) {
|
|
23049
|
-
const body =
|
|
24060
|
+
const body = matter25(markdown).content.trim();
|
|
23050
24061
|
const para = body.split("\n\n").map((p) => p.trim()).find((p) => p && !p.startsWith("#"));
|
|
23051
24062
|
return para ?? "";
|
|
23052
24063
|
}
|
|
@@ -24038,7 +25049,7 @@ function requireKnowledge(root) {
|
|
|
24038
25049
|
throw new KaddoMcpError("Knowledge repository not found. Run `kaddo bootstrap` first.");
|
|
24039
25050
|
}
|
|
24040
25051
|
}
|
|
24041
|
-
function
|
|
25052
|
+
function slug6(s) {
|
|
24042
25053
|
return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
24043
25054
|
}
|
|
24044
25055
|
function generateContext(root) {
|
|
@@ -24171,7 +25182,7 @@ function generateCapsuleDraft(root) {
|
|
|
24171
25182
|
const config = requireConfig(root);
|
|
24172
25183
|
requireKnowledge(root);
|
|
24173
25184
|
const capsule = buildCapsule(root, config);
|
|
24174
|
-
const name =
|
|
25185
|
+
const name = slug6(config.project.name);
|
|
24175
25186
|
const md = `.kaddo/exports/${name}.capsule.md`;
|
|
24176
25187
|
const json = `.kaddo/exports/${name}.capsule.json`;
|
|
24177
25188
|
writeDerived(root, md, renderCapsuleMarkdown(capsule));
|
|
@@ -24534,6 +25545,74 @@ function createServer(root) {
|
|
|
24534
25545
|
},
|
|
24535
25546
|
async (args) => toolText(guarded(root, () => deleteResourceTool(root, args.id, args.confirm)))
|
|
24536
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
|
+
);
|
|
24537
25616
|
server.registerTool(
|
|
24538
25617
|
"kaddo_list_capsules",
|
|
24539
25618
|
{ title: "List Knowledge Capsules", description: "List registered external Knowledge Capsules.", inputSchema: {} },
|