@kaddo/cli 3.22.1 → 3.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/index.js +320 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -510,6 +510,7 @@ create --from roadmap → owners → guard → explain`.
|
|
|
510
510
|
| v3.21 | Reusable Skills layer (`kaddo add skills`); skills in context/explain/understand, agent prompts and MCP |
|
|
511
511
|
| v3.22 | Guard & graph scope semantics: graph scope metadata, contextual-empty messaging, Guard ownership scope (active + completed; `--include-archived`) |
|
|
512
512
|
| v3.22.1 | Guard project-root path normalization: matches `code:` globs when the project is a subfolder of the Git root |
|
|
513
|
+
| v3.23 | Knowledge Impact Report: `kaddo report impact` (Markdown/JSON, `--output`); evidence-first health/coverage/traceability/readiness/signals; MCP resource + tool |
|
|
513
514
|
|
|
514
515
|
**Optional modules (installed with `kaddo add`):**
|
|
515
516
|
|
package/dist/index.js
CHANGED
|
@@ -684,6 +684,10 @@ var COMMAND_HELP = {
|
|
|
684
684
|
"graph export": {
|
|
685
685
|
question: "How is my project knowledge connected?",
|
|
686
686
|
next: "Open .kaddo/graph.mmd, or run kaddo explain for a summary"
|
|
687
|
+
},
|
|
688
|
+
"report impact": {
|
|
689
|
+
question: "What impact is Kaddo having on this project?",
|
|
690
|
+
next: "Act on the Suggested Actions section"
|
|
687
691
|
}
|
|
688
692
|
};
|
|
689
693
|
function commandFooterLines(name) {
|
|
@@ -10912,6 +10916,315 @@ function runGraphExport(opts = {}) {
|
|
|
10912
10916
|
outro2("Knowledge graph ready.");
|
|
10913
10917
|
}
|
|
10914
10918
|
|
|
10919
|
+
// src/core/impact-report.ts
|
|
10920
|
+
import matter8 from "gray-matter";
|
|
10921
|
+
var BROAD_GLOB = /^[^/*]+\/\*\*$/;
|
|
10922
|
+
function levelFromRatio(r) {
|
|
10923
|
+
if (r >= 0.9) return "Very High";
|
|
10924
|
+
if (r >= 0.7) return "High";
|
|
10925
|
+
if (r >= 0.4) return "Medium";
|
|
10926
|
+
return "Low";
|
|
10927
|
+
}
|
|
10928
|
+
function hasSection(body, re) {
|
|
10929
|
+
return body.split(/\r?\n/).some((l) => /^#{1,6}\s+/.test(l) && re.test(l));
|
|
10930
|
+
}
|
|
10931
|
+
function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
10932
|
+
const exp = buildProjectExplanation(dir);
|
|
10933
|
+
const wis = discoverWorkItems(dir);
|
|
10934
|
+
const total = wis.length;
|
|
10935
|
+
let withOwnership = 0;
|
|
10936
|
+
let withSource = 0;
|
|
10937
|
+
let withInitiative = 0;
|
|
10938
|
+
let withLevel = 0;
|
|
10939
|
+
let withAcceptance = 0;
|
|
10940
|
+
let withDoD = 0;
|
|
10941
|
+
let connectedRoadmap = 0;
|
|
10942
|
+
const globCounts = /* @__PURE__ */ new Map();
|
|
10943
|
+
for (const wi of wis) {
|
|
10944
|
+
if (wi.codeGlobs.length > 0) withOwnership++;
|
|
10945
|
+
const hasSource = Boolean(wi.sourceId) || wi.source === "roadmap";
|
|
10946
|
+
if (hasSource) withSource++;
|
|
10947
|
+
if (hasSource || wi.initiative) connectedRoadmap++;
|
|
10948
|
+
if (wi.initiative) withInitiative++;
|
|
10949
|
+
if (wi.knowledgeLevel) withLevel++;
|
|
10950
|
+
for (const g2 of wi.codeGlobs) globCounts.set(g2, (globCounts.get(g2) ?? 0) + 1);
|
|
10951
|
+
try {
|
|
10952
|
+
const body = matter8(readFile(wi.filePath)).content;
|
|
10953
|
+
if (hasSection(body, /acceptance/i)) withAcceptance++;
|
|
10954
|
+
if (hasSection(body, /definition of done|^#{1,6}\s*done\b/i)) withDoD++;
|
|
10955
|
+
} catch {
|
|
10956
|
+
}
|
|
10957
|
+
}
|
|
10958
|
+
const ownedCodePaths = [...globCounts.values()].reduce((a, b) => a + b, 0);
|
|
10959
|
+
const broadGlobs = [...globCounts.keys()].filter((g2) => BROAD_GLOB.test(g2)).length;
|
|
10960
|
+
const ownershipOverlaps = [...globCounts.values()].filter((n) => n > 1).length;
|
|
10961
|
+
let g;
|
|
10962
|
+
if (opts.scope) {
|
|
10963
|
+
const config = loadConfig(dir);
|
|
10964
|
+
if (config) {
|
|
10965
|
+
const graph = buildGraph(dir, config, { scope: opts.scope }, now);
|
|
10966
|
+
const hints = buildGraphHints(dir, graph, now);
|
|
10967
|
+
g = { available: true, scope: graph.scope, scopeReason: graph.scope_reason, nodes: graph.nodes.length, edges: graph.edges.length, quality: hints.quality, hints: hints.summary.hints, generatedAt: graph.generated_at };
|
|
10968
|
+
} else {
|
|
10969
|
+
g = { available: false, scope: opts.scope, scopeReason: "", nodes: 0, edges: 0, quality: "unknown", hints: 0, generatedAt: "" };
|
|
10970
|
+
}
|
|
10971
|
+
} else if (exp.graph) {
|
|
10972
|
+
g = { available: true, scope: exp.graph.scope, scopeReason: exp.graph.scopeReason, nodes: exp.graph.nodes, edges: exp.graph.edges, quality: exp.graphHints?.quality ?? "unknown", hints: exp.graphHints?.totalHints ?? 0, generatedAt: exp.graph.generatedAt };
|
|
10973
|
+
} else {
|
|
10974
|
+
g = { available: false, scope: "n/a", scopeReason: "", nodes: 0, edges: 0, quality: "unknown", hints: 0, generatedAt: "" };
|
|
10975
|
+
}
|
|
10976
|
+
const layerStatus = (name) => exp.layers.find((l) => l.layer === name)?.status ?? "Missing";
|
|
10977
|
+
const byState = exp.workItems.byState;
|
|
10978
|
+
const skills = discoverInstalledSkills(dir);
|
|
10979
|
+
const knowledge_health = {
|
|
10980
|
+
Business: layerStatus("Business"),
|
|
10981
|
+
Product: layerStatus("Product"),
|
|
10982
|
+
Tech: layerStatus("Tech"),
|
|
10983
|
+
Delivery: layerStatus("Delivery"),
|
|
10984
|
+
Inventory: exp.knowledge.hasInventory ? "available" : "missing",
|
|
10985
|
+
"Context pack": exp.knowledge.hasContextPack ? "available" : "missing",
|
|
10986
|
+
Agents: exp.knowledge.hasAgents ? "available" : "missing",
|
|
10987
|
+
Skills: skills.length > 0 ? "available" : "missing"
|
|
10988
|
+
};
|
|
10989
|
+
const knowledge_coverage = [
|
|
10990
|
+
{ label: "Work Items with ownership", have: withOwnership, total },
|
|
10991
|
+
{ label: "Work Items with source", have: withSource, total },
|
|
10992
|
+
{ label: "Work Items with initiative", have: withInitiative, total },
|
|
10993
|
+
{ label: "Work Items with acceptance criteria", have: withAcceptance, total },
|
|
10994
|
+
{ label: "Work Items with Definition of Done", have: withDoD, total },
|
|
10995
|
+
{ label: "Work Items with knowledge level", have: withLevel, total }
|
|
10996
|
+
];
|
|
10997
|
+
const coverageRatio = (have) => total > 0 ? have / total : 0;
|
|
10998
|
+
const deliveryTraceable = ["Traceable", "Structured", "Consolidated"].includes(layerStatus("Delivery"));
|
|
10999
|
+
const ctxReasons = [];
|
|
11000
|
+
let ctxScore = 0;
|
|
11001
|
+
if (["Consolidated", "Structured"].includes(layerStatus("Business"))) {
|
|
11002
|
+
ctxScore++;
|
|
11003
|
+
ctxReasons.push("Business knowledge is present.");
|
|
11004
|
+
}
|
|
11005
|
+
if (["Consolidated", "Structured"].includes(layerStatus("Product"))) {
|
|
11006
|
+
ctxScore++;
|
|
11007
|
+
ctxReasons.push("Product knowledge is present.");
|
|
11008
|
+
}
|
|
11009
|
+
if (layerStatus("Tech") !== "Missing") {
|
|
11010
|
+
ctxScore++;
|
|
11011
|
+
ctxReasons.push("Tech knowledge is structured.");
|
|
11012
|
+
}
|
|
11013
|
+
if (deliveryTraceable) {
|
|
11014
|
+
ctxScore++;
|
|
11015
|
+
ctxReasons.push("Delivery is traceable.");
|
|
11016
|
+
}
|
|
11017
|
+
if (exp.knowledge.hasContextPack) {
|
|
11018
|
+
ctxScore++;
|
|
11019
|
+
ctxReasons.push("Context pack is available.");
|
|
11020
|
+
}
|
|
11021
|
+
if (g.available && g.quality !== "empty") {
|
|
11022
|
+
ctxScore++;
|
|
11023
|
+
ctxReasons.push(`Graph quality is ${g.quality}.`);
|
|
11024
|
+
}
|
|
11025
|
+
const ctxLevel = ctxScore >= 6 ? "Very High" : ctxScore >= 4 ? "High" : ctxScore >= 2 ? "Medium" : "Low";
|
|
11026
|
+
const ambiguityRatio = total > 0 ? (withAcceptance + withDoD + withSource) / (3 * total) : 0;
|
|
11027
|
+
const aiReady = exp.knowledge.hasContextPack && g.available && g.quality !== "empty" && skills.length > 0 && deliveryTraceable ? "High" : exp.knowledge.hasContextPack && g.available && g.quality !== "empty" ? "Medium" : "Low";
|
|
11028
|
+
const onboarding = levelFromRatio(
|
|
11029
|
+
[layerStatus("Business"), layerStatus("Product"), layerStatus("Tech"), layerStatus("Delivery")].filter((s) => s !== "Missing").length / 4 * (exp.knowledge.hasContextPack ? 1 : 0.6)
|
|
11030
|
+
);
|
|
11031
|
+
const deliveryTrace = levelFromRatio(
|
|
11032
|
+
coverageRatio(withOwnership) * 0.5 + (g.available && g.edges > 0 ? 1 : 0) * 0.25 + (exp.roadmap.present ? 1 : 0) * 0.25
|
|
11033
|
+
);
|
|
11034
|
+
const impact_signals = {
|
|
11035
|
+
ambiguity_reduction: levelFromRatio(ambiguityRatio),
|
|
11036
|
+
drift_prevention: withOwnership > 0 ? "Active" : "Limited",
|
|
11037
|
+
onboarding_support: onboarding,
|
|
11038
|
+
delivery_traceability: deliveryTrace,
|
|
11039
|
+
ai_context_readiness: aiReady,
|
|
11040
|
+
maintenance_readiness: levelFromRatio((coverageRatio(withOwnership) + (deliveryTraceable ? 1 : 0)) / 2)
|
|
11041
|
+
};
|
|
11042
|
+
let score = null;
|
|
11043
|
+
if (total > 0) {
|
|
11044
|
+
const healthPts = [layerStatus("Business"), layerStatus("Product"), layerStatus("Tech"), layerStatus("Delivery")].filter((s) => s !== "Missing").length / 4 * 20;
|
|
11045
|
+
const covAvg = knowledge_coverage.reduce((a, c) => a + coverageRatio(c.have), 0) / knowledge_coverage.length;
|
|
11046
|
+
const coveragePts = covAvg * 20;
|
|
11047
|
+
const ownPts = coverageRatio(withOwnership) * 15;
|
|
11048
|
+
const traceBlend = (exp.roadmap.candidates > 0 ? exp.roadmap.materialized / exp.roadmap.candidates : 1) * 0.5 + coverageRatio(withOwnership) * 0.5;
|
|
11049
|
+
const tracePts = traceBlend * 20;
|
|
11050
|
+
const qualityMap = { good: 1, partial: 0.6, sparse: 0.3, empty: 0, unknown: 0 };
|
|
11051
|
+
const graphPts = (g.available ? qualityMap[g.quality] ?? 0 : 0) * 15;
|
|
11052
|
+
const ctxPts = { Low: 0.25, Medium: 0.5, High: 0.8, "Very High": 1 }[ctxLevel] * 10;
|
|
11053
|
+
score = Math.round(healthPts + coveragePts + ownPts + tracePts + graphPts + ctxPts);
|
|
11054
|
+
}
|
|
11055
|
+
const actions = [];
|
|
11056
|
+
if (!g.available) actions.push("Run `kaddo graph export --scope all` to inspect full traceability.");
|
|
11057
|
+
else if (g.scope === "active" && g.quality === "empty") actions.push("Run `kaddo graph export --scope all` to include completed Work Items.");
|
|
11058
|
+
if (exp.roadmap.remaining > 0) actions.push(`Materialize the ${exp.roadmap.remaining} remaining roadmap candidate(s) with \`kaddo create --from roadmap\`.`);
|
|
11059
|
+
if (total > 0 && withOwnership < total) actions.push("Add code ownership to Work Items without `code:` globs (`kaddo owners suggest`).");
|
|
11060
|
+
if (total > 0 && withInitiative < total) actions.push("Add an initiative to Work Items missing one.");
|
|
11061
|
+
if (g.available && g.hints > 0) actions.push("Review graph hints (`kaddo://graph-hints` or `.kaddo/graph-hints.md`).");
|
|
11062
|
+
if (skills.length === 0) actions.push("Install reusable skills with `kaddo add skills`.");
|
|
11063
|
+
actions.push("Run `kaddo guard` before committing to catch knowledge drift.");
|
|
11064
|
+
const summary = [];
|
|
11065
|
+
if (total > 0) summary.push(`${withOwnership}/${total} Work Items include code ownership.`);
|
|
11066
|
+
if (g.available) summary.push(`The knowledge graph is ${g.quality} with ${g.nodes} nodes and ${g.edges} edges (${g.scope} scope).`);
|
|
11067
|
+
else summary.push("The knowledge graph has not been exported yet.");
|
|
11068
|
+
if (g.available) summary.push(g.hints === 0 ? "No graph hints are currently open." : `${g.hints} graph hint(s) are open.`);
|
|
11069
|
+
if (exp.roadmap.present) summary.push(`Delivery: ${exp.roadmap.materialized} materialized, ${exp.roadmap.remaining} roadmap candidate(s) remaining.`);
|
|
11070
|
+
summary.push(`Context readiness: ${ctxLevel}.`);
|
|
11071
|
+
return {
|
|
11072
|
+
generated_at: now.toISOString(),
|
|
11073
|
+
project: exp.project.name,
|
|
11074
|
+
scope: g.scope,
|
|
11075
|
+
executive_summary: summary,
|
|
11076
|
+
knowledge_health,
|
|
11077
|
+
knowledge_coverage,
|
|
11078
|
+
ownership_coverage: {
|
|
11079
|
+
coverage_percent: total > 0 ? Math.round(withOwnership / total * 100) : 0,
|
|
11080
|
+
work_items_with_ownership: `${withOwnership}/${total}`,
|
|
11081
|
+
owned_code_paths: ownedCodePaths,
|
|
11082
|
+
broad_globs: broadGlobs,
|
|
11083
|
+
ownership_overlaps: ownershipOverlaps
|
|
11084
|
+
},
|
|
11085
|
+
traceability: {
|
|
11086
|
+
roadmap_candidates: exp.roadmap.candidates,
|
|
11087
|
+
materialized_work_items: exp.roadmap.materialized,
|
|
11088
|
+
remaining_candidates: exp.roadmap.remaining,
|
|
11089
|
+
completed_work_items: byState.completed,
|
|
11090
|
+
work_items_connected_to_roadmap: `${connectedRoadmap}/${total}`,
|
|
11091
|
+
work_items_connected_to_code: `${withOwnership}/${total}`,
|
|
11092
|
+
graph_quality: g.available ? g.quality : "not available",
|
|
11093
|
+
graph_nodes: g.available ? g.nodes : null,
|
|
11094
|
+
graph_edges: g.available ? g.edges : null,
|
|
11095
|
+
graph_hints: g.available ? g.hints : null
|
|
11096
|
+
},
|
|
11097
|
+
context_readiness: { level: ctxLevel, reasons: ctxReasons },
|
|
11098
|
+
work_item_readiness: {
|
|
11099
|
+
draft: byState.draft,
|
|
11100
|
+
ready: byState.ready,
|
|
11101
|
+
in_progress: byState["in-progress"],
|
|
11102
|
+
blocked: byState.blocked,
|
|
11103
|
+
completed: byState.completed,
|
|
11104
|
+
ready_quality: byState.ready > 0 ? `${byState.ready} ready` : "not applicable"
|
|
11105
|
+
},
|
|
11106
|
+
graph_quality: g.available ? { available: true, scope: g.scope, quality: g.quality, nodes: g.nodes, edges: g.edges, hints: g.hints, reason: g.scopeReason, last_exported: g.generatedAt } : { available: false, suggestion: "Run `kaddo graph export --scope all`." },
|
|
11107
|
+
guard_activity: {
|
|
11108
|
+
available: false,
|
|
11109
|
+
note: "Guard history is not persisted. Run `kaddo guard` to check drift; future versions may store runs for trend analysis."
|
|
11110
|
+
},
|
|
11111
|
+
impact_signals,
|
|
11112
|
+
score,
|
|
11113
|
+
suggested_actions: actions
|
|
11114
|
+
};
|
|
11115
|
+
}
|
|
11116
|
+
function ratio(have, total) {
|
|
11117
|
+
return `${have}/${total}`;
|
|
11118
|
+
}
|
|
11119
|
+
function renderImpactMarkdown(r) {
|
|
11120
|
+
const L = [];
|
|
11121
|
+
L.push("# Kaddo Knowledge Impact Report", "");
|
|
11122
|
+
L.push(`Generated at: ${r.generated_at}`);
|
|
11123
|
+
L.push(`Project: ${r.project}`);
|
|
11124
|
+
L.push(`Scope: ${r.scope}`);
|
|
11125
|
+
if (r.score !== null) L.push(`Knowledge Impact Score: ${r.score}/100`);
|
|
11126
|
+
else L.push("Knowledge Impact Score: not available");
|
|
11127
|
+
L.push("");
|
|
11128
|
+
L.push("## Executive Summary", "");
|
|
11129
|
+
for (const s2 of r.executive_summary) L.push(`- ${s2}`);
|
|
11130
|
+
L.push("");
|
|
11131
|
+
L.push("## Knowledge Health", "");
|
|
11132
|
+
for (const [k, v] of Object.entries(r.knowledge_health)) L.push(`- ${k}: ${v}`);
|
|
11133
|
+
L.push("");
|
|
11134
|
+
L.push("## Knowledge Coverage", "");
|
|
11135
|
+
for (const c of r.knowledge_coverage) L.push(`- ${c.label}: ${ratio(c.have, c.total)}`);
|
|
11136
|
+
L.push("");
|
|
11137
|
+
L.push("## Ownership Coverage", "");
|
|
11138
|
+
L.push(`- Coverage: ${r.ownership_coverage.coverage_percent}%`);
|
|
11139
|
+
L.push(`- Work Items with code ownership: ${r.ownership_coverage.work_items_with_ownership}`);
|
|
11140
|
+
L.push(`- Owned code paths: ${r.ownership_coverage.owned_code_paths}`);
|
|
11141
|
+
L.push(`- Broad globs detected: ${r.ownership_coverage.broad_globs}`);
|
|
11142
|
+
L.push(`- Ownership overlaps: ${r.ownership_coverage.ownership_overlaps}`);
|
|
11143
|
+
L.push("");
|
|
11144
|
+
L.push("## Traceability", "");
|
|
11145
|
+
const t = r.traceability;
|
|
11146
|
+
L.push(`- Roadmap candidates: ${t.roadmap_candidates}`);
|
|
11147
|
+
L.push(`- Materialized Work Items: ${t.materialized_work_items}`);
|
|
11148
|
+
L.push(`- Remaining candidates: ${t.remaining_candidates}`);
|
|
11149
|
+
L.push(`- Completed Work Items: ${t.completed_work_items}`);
|
|
11150
|
+
L.push(`- Work Items connected to roadmap: ${t.work_items_connected_to_roadmap}`);
|
|
11151
|
+
L.push(`- Work Items connected to code: ${t.work_items_connected_to_code}`);
|
|
11152
|
+
L.push(`- Graph quality: ${t.graph_quality}`);
|
|
11153
|
+
if (t.graph_nodes !== null) L.push(`- Graph nodes: ${t.graph_nodes}`);
|
|
11154
|
+
if (t.graph_edges !== null) L.push(`- Graph edges: ${t.graph_edges}`);
|
|
11155
|
+
if (t.graph_hints !== null) L.push(`- Graph hints: ${t.graph_hints}`);
|
|
11156
|
+
L.push("");
|
|
11157
|
+
L.push("## Context Readiness", "");
|
|
11158
|
+
L.push(`Context Readiness: ${r.context_readiness.level}`);
|
|
11159
|
+
if (r.context_readiness.reasons.length > 0) {
|
|
11160
|
+
L.push("", "Reason:");
|
|
11161
|
+
for (const reason of r.context_readiness.reasons) L.push(`- ${reason}`);
|
|
11162
|
+
}
|
|
11163
|
+
L.push("");
|
|
11164
|
+
L.push("## Work Item Readiness", "");
|
|
11165
|
+
const w = r.work_item_readiness;
|
|
11166
|
+
L.push(`- Draft: ${w.draft}`);
|
|
11167
|
+
L.push(`- Ready: ${w.ready}`);
|
|
11168
|
+
L.push(`- In Progress: ${w.in_progress}`);
|
|
11169
|
+
L.push(`- Blocked: ${w.blocked}`);
|
|
11170
|
+
L.push(`- Completed: ${w.completed}`);
|
|
11171
|
+
L.push(`- Ready quality: ${w.ready_quality}`);
|
|
11172
|
+
L.push("");
|
|
11173
|
+
L.push("## Graph Quality", "");
|
|
11174
|
+
if (r.graph_quality.available) {
|
|
11175
|
+
const gq = r.graph_quality;
|
|
11176
|
+
L.push(`- Scope: ${gq.scope}`);
|
|
11177
|
+
L.push(`- Quality: ${gq.quality}`);
|
|
11178
|
+
L.push(`- Nodes: ${gq.nodes}`);
|
|
11179
|
+
L.push(`- Edges: ${gq.edges}`);
|
|
11180
|
+
L.push(`- Hints: ${gq.hints}`);
|
|
11181
|
+
if (gq.reason) L.push(`- Reason: ${gq.reason}`);
|
|
11182
|
+
if (gq.last_exported) L.push(`- Last exported: ${gq.last_exported}`);
|
|
11183
|
+
} else {
|
|
11184
|
+
L.push("- Graph data not available.");
|
|
11185
|
+
L.push(`- Tip: ${r.graph_quality.suggestion}`);
|
|
11186
|
+
}
|
|
11187
|
+
L.push("");
|
|
11188
|
+
L.push("## Guard Activity", "");
|
|
11189
|
+
L.push("- Guard history: not available");
|
|
11190
|
+
L.push(`- Note: ${r.guard_activity.note}`);
|
|
11191
|
+
L.push("");
|
|
11192
|
+
L.push("## Impact Signals", "");
|
|
11193
|
+
const s = r.impact_signals;
|
|
11194
|
+
L.push(`- Ambiguity reduction: ${s.ambiguity_reduction}`);
|
|
11195
|
+
L.push(`- Drift prevention: ${s.drift_prevention}`);
|
|
11196
|
+
L.push(`- Onboarding support: ${s.onboarding_support}`);
|
|
11197
|
+
L.push(`- Delivery traceability: ${s.delivery_traceability}`);
|
|
11198
|
+
L.push(`- AI context readiness: ${s.ai_context_readiness}`);
|
|
11199
|
+
L.push(`- Maintenance readiness: ${s.maintenance_readiness}`);
|
|
11200
|
+
L.push("");
|
|
11201
|
+
L.push("## Suggested Actions", "");
|
|
11202
|
+
r.suggested_actions.forEach((a, i) => L.push(`${i + 1}. ${a}`));
|
|
11203
|
+
L.push("");
|
|
11204
|
+
return L.join("\n");
|
|
11205
|
+
}
|
|
11206
|
+
function serializeImpactJson(r) {
|
|
11207
|
+
return JSON.stringify(r, null, 2) + "\n";
|
|
11208
|
+
}
|
|
11209
|
+
|
|
11210
|
+
// src/commands/report.ts
|
|
11211
|
+
function runReportImpact(opts = {}) {
|
|
11212
|
+
const dir = cwd();
|
|
11213
|
+
requireConfig(dir);
|
|
11214
|
+
const report = buildImpactReport(dir);
|
|
11215
|
+
const content = opts.json ? serializeImpactJson(report) : renderImpactMarkdown(report);
|
|
11216
|
+
if (opts.output) {
|
|
11217
|
+
intro2("kaddo report impact");
|
|
11218
|
+
const rel = opts.output;
|
|
11219
|
+
writeFile(join(dir, rel), content);
|
|
11220
|
+
log2.success(`Wrote ${rel.replace(/\\/g, "/")}`);
|
|
11221
|
+
printCommandFooter("report impact");
|
|
11222
|
+
outro2("Impact report written.");
|
|
11223
|
+
return;
|
|
11224
|
+
}
|
|
11225
|
+
console.log(content);
|
|
11226
|
+
}
|
|
11227
|
+
|
|
10915
11228
|
// src/index.ts
|
|
10916
11229
|
var require2 = createRequire(import.meta.url);
|
|
10917
11230
|
var { version } = require2("../package.json");
|
|
@@ -10940,6 +11253,13 @@ var graphCmd = program.command("graph").description("Export the lightweight, fil
|
|
|
10940
11253
|
graphCmd.command("export").description("Write the knowledge graph to .kaddo/graph.json and .kaddo/graph.mmd").option("--scope <scope>", "Graph scope: active (default) or all").option("--format <format>", "Output format: json, mermaid (default: both)").action((opts) => {
|
|
10941
11254
|
runGraphExport(opts);
|
|
10942
11255
|
});
|
|
11256
|
+
var reportCmd = program.command("report").description("Generate Kaddo reports");
|
|
11257
|
+
reportCmd.command("impact").description("Knowledge Impact Report: knowledge health, coverage, traceability, readiness (deterministic, no LLM)").option("--json", "Output JSON instead of Markdown").option("--output <path>", "Write the report to a file (e.g. .kaddo/reports/impact-report.md)").action((opts) => {
|
|
11258
|
+
runReportImpact(opts);
|
|
11259
|
+
});
|
|
11260
|
+
program.command("impact").description("Alias for `kaddo report impact`").option("--json", "Output JSON instead of Markdown").option("--output <path>", "Write the report to a file").action((opts) => {
|
|
11261
|
+
runReportImpact(opts);
|
|
11262
|
+
});
|
|
10943
11263
|
program.command("guard").description("Check if modified code has related artifacts that were not updated").option("--staged", "Check only staged files").option("--no-interactive", "Disable interactive ignore prompts").option("--ci", "CI mode: output JSON, no prompts, non-blocking").option("--json", "Output JSON (alias for --ci)").option("--workspace", "Also check local mapped module repos from .kaddo/modules.yml (opt-in)").option("--include-archived", "Include archived Work Items in ownership matching (excluded by default)").action(async (opts) => {
|
|
10944
11264
|
await runGuard(opts);
|
|
10945
11265
|
});
|