@kaddo/cli 3.23.0 → 3.23.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/README.md +2 -0
- package/dist/index.js +151 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -511,6 +511,8 @@ create --from roadmap → owners → guard → explain`.
|
|
|
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
513
|
| v3.23 | Knowledge Impact Report: `kaddo report impact` (Markdown/JSON, `--output`); evidence-first health/coverage/traceability/readiness/signals; MCP resource + tool |
|
|
514
|
+
| v3.23.1 | Impact report Actionable Gaps: per-Work-Item missing source/initiative/ownership/level/acceptance/DoD/validation, broad globs, overlaps; Work-Item-specific Suggested Actions + Score Breakdown |
|
|
515
|
+
| v3.23.2 | Impact report defaults to `all` scope (accumulated impact), graph built in memory; `--scope active`; `scope_source`/`default_scope` in JSON |
|
|
514
516
|
|
|
515
517
|
**Optional modules (installed with `kaddo add`):**
|
|
516
518
|
|
package/dist/index.js
CHANGED
|
@@ -10918,7 +10918,11 @@ function runGraphExport(opts = {}) {
|
|
|
10918
10918
|
|
|
10919
10919
|
// src/core/impact-report.ts
|
|
10920
10920
|
import matter8 from "gray-matter";
|
|
10921
|
-
|
|
10921
|
+
function isBroadGlob(glob) {
|
|
10922
|
+
if (!glob.endsWith("/**")) return false;
|
|
10923
|
+
const prefix = glob.slice(0, -3);
|
|
10924
|
+
return prefix.length > 0 && prefix.split("/").length <= 2;
|
|
10925
|
+
}
|
|
10922
10926
|
function levelFromRatio(r) {
|
|
10923
10927
|
if (r >= 0.9) return "Very High";
|
|
10924
10928
|
if (r >= 0.7) return "High";
|
|
@@ -10929,6 +10933,8 @@ function hasSection(body, re) {
|
|
|
10929
10933
|
return body.split(/\r?\n/).some((l) => /^#{1,6}\s+/.test(l) && re.test(l));
|
|
10930
10934
|
}
|
|
10931
10935
|
function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
10936
|
+
const resolvedScope = opts.scope ?? "all";
|
|
10937
|
+
const scopeSource = opts.scopeSource ?? (opts.scope ? "explicit" : "default");
|
|
10932
10938
|
const exp = buildProjectExplanation(dir);
|
|
10933
10939
|
const wis = discoverWorkItems(dir);
|
|
10934
10940
|
const total = wis.length;
|
|
@@ -10938,40 +10944,74 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
10938
10944
|
let withLevel = 0;
|
|
10939
10945
|
let withAcceptance = 0;
|
|
10940
10946
|
let withDoD = 0;
|
|
10947
|
+
let withValidation = 0;
|
|
10941
10948
|
let connectedRoadmap = 0;
|
|
10942
10949
|
const globCounts = /* @__PURE__ */ new Map();
|
|
10950
|
+
const globOwners = /* @__PURE__ */ new Map();
|
|
10951
|
+
const gaps = {
|
|
10952
|
+
missing_source: [],
|
|
10953
|
+
missing_initiative: [],
|
|
10954
|
+
missing_code_ownership: [],
|
|
10955
|
+
missing_knowledge_level: [],
|
|
10956
|
+
missing_acceptance_criteria: [],
|
|
10957
|
+
missing_definition_of_done: [],
|
|
10958
|
+
missing_validation: [],
|
|
10959
|
+
broad_ownership_globs: [],
|
|
10960
|
+
ownership_overlaps: []
|
|
10961
|
+
};
|
|
10962
|
+
const gapItem = (wi, action) => ({
|
|
10963
|
+
id: wi.id || wi.title,
|
|
10964
|
+
title: wi.title,
|
|
10965
|
+
status: wi.lifecycle ?? wi.status,
|
|
10966
|
+
path: wi.relPath,
|
|
10967
|
+
suggested_action: action
|
|
10968
|
+
});
|
|
10943
10969
|
for (const wi of wis) {
|
|
10970
|
+
const id = wi.id || wi.title;
|
|
10944
10971
|
if (wi.codeGlobs.length > 0) withOwnership++;
|
|
10972
|
+
else gaps.missing_code_ownership.push(gapItem(wi, "Add `code:` globs to connect this Work Item to source paths (`kaddo owners suggest`)."));
|
|
10945
10973
|
const hasSource = Boolean(wi.sourceId) || wi.source === "roadmap";
|
|
10946
10974
|
if (hasSource) withSource++;
|
|
10975
|
+
else gaps.missing_source.push(gapItem(wi, "Add `source` or `source_id` if this Work Item came from the roadmap."));
|
|
10947
10976
|
if (hasSource || wi.initiative) connectedRoadmap++;
|
|
10948
10977
|
if (wi.initiative) withInitiative++;
|
|
10978
|
+
else gaps.missing_initiative.push(gapItem(wi, "Add `initiative` to connect this Work Item to a delivery initiative."));
|
|
10949
10979
|
if (wi.knowledgeLevel) withLevel++;
|
|
10950
|
-
|
|
10980
|
+
else gaps.missing_knowledge_level.push(gapItem(wi, "Add `knowledge_level` (K0\u2013K4) to the front matter."));
|
|
10981
|
+
for (const g2 of wi.codeGlobs) {
|
|
10982
|
+
globCounts.set(g2, (globCounts.get(g2) ?? 0) + 1);
|
|
10983
|
+
globOwners.set(g2, [...globOwners.get(g2) ?? [], id]);
|
|
10984
|
+
if (isBroadGlob(g2)) {
|
|
10985
|
+
gaps.broad_ownership_globs.push({ id, title: wi.title, glob: g2, suggested_action: "Replace with specific files or narrower module paths." });
|
|
10986
|
+
}
|
|
10987
|
+
}
|
|
10951
10988
|
try {
|
|
10952
10989
|
const body = matter8(readFile(wi.filePath)).content;
|
|
10953
|
-
if (hasSection(body, /acceptance/i)) withAcceptance++;
|
|
10954
|
-
|
|
10990
|
+
if (hasSection(body, /acceptance|criterios de aceptaci/i)) withAcceptance++;
|
|
10991
|
+
else gaps.missing_acceptance_criteria.push(gapItem(wi, "Add an `## Acceptance Criteria` section."));
|
|
10992
|
+
if (hasSection(body, /definition of done|^#{1,6}\s*dod\b|definici[oó]n de (terminado|hecho)/i)) withDoD++;
|
|
10993
|
+
else gaps.missing_definition_of_done.push(gapItem(wi, "Add a `## Definition of Done` section."));
|
|
10994
|
+
if (hasSection(body, /how to test|validation|validaci|c[oó]mo probarlo/i)) withValidation++;
|
|
10995
|
+
else gaps.missing_validation.push(gapItem(wi, "Add a `## How to test it` (validation) section."));
|
|
10955
10996
|
} catch {
|
|
10956
10997
|
}
|
|
10957
10998
|
}
|
|
10958
10999
|
const ownedCodePaths = [...globCounts.values()].reduce((a, b) => a + b, 0);
|
|
10959
|
-
const broadGlobs = [...globCounts.keys()].filter((g2) =>
|
|
10960
|
-
const
|
|
10961
|
-
|
|
10962
|
-
|
|
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: "" };
|
|
11000
|
+
const broadGlobs = [...globCounts.keys()].filter((g2) => isBroadGlob(g2)).length;
|
|
11001
|
+
for (const [glob, owners] of globOwners) {
|
|
11002
|
+
if (owners.length > 1) {
|
|
11003
|
+
gaps.ownership_overlaps.push({ code_path: glob, work_items: [...new Set(owners)], suggested_action: "Review whether the overlap is expected or should be narrowed." });
|
|
10970
11004
|
}
|
|
10971
|
-
}
|
|
10972
|
-
|
|
11005
|
+
}
|
|
11006
|
+
const ownershipOverlaps = gaps.ownership_overlaps.length;
|
|
11007
|
+
let g;
|
|
11008
|
+
const config = loadConfig(dir);
|
|
11009
|
+
if (config) {
|
|
11010
|
+
const graph = buildGraph(dir, config, { scope: resolvedScope }, now);
|
|
11011
|
+
const hints = buildGraphHints(dir, graph, now);
|
|
11012
|
+
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 };
|
|
10973
11013
|
} else {
|
|
10974
|
-
g = { available: false, scope:
|
|
11014
|
+
g = { available: false, scope: resolvedScope, scopeReason: "", nodes: 0, edges: 0, quality: "unknown", hints: 0, generatedAt: "" };
|
|
10975
11015
|
}
|
|
10976
11016
|
const layerStatus = (name) => exp.layers.find((l) => l.layer === name)?.status ?? "Missing";
|
|
10977
11017
|
const byState = exp.workItems.byState;
|
|
@@ -11040,6 +11080,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
11040
11080
|
maintenance_readiness: levelFromRatio((coverageRatio(withOwnership) + (deliveryTraceable ? 1 : 0)) / 2)
|
|
11041
11081
|
};
|
|
11042
11082
|
let score = null;
|
|
11083
|
+
let score_breakdown = null;
|
|
11043
11084
|
if (total > 0) {
|
|
11044
11085
|
const healthPts = [layerStatus("Business"), layerStatus("Product"), layerStatus("Tech"), layerStatus("Delivery")].filter((s) => s !== "Missing").length / 4 * 20;
|
|
11045
11086
|
const covAvg = knowledge_coverage.reduce((a, c) => a + coverageRatio(c.have), 0) / knowledge_coverage.length;
|
|
@@ -11051,13 +11092,34 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
11051
11092
|
const graphPts = (g.available ? qualityMap[g.quality] ?? 0 : 0) * 15;
|
|
11052
11093
|
const ctxPts = { Low: 0.25, Medium: 0.5, High: 0.8, "Very High": 1 }[ctxLevel] * 10;
|
|
11053
11094
|
score = Math.round(healthPts + coveragePts + ownPts + tracePts + graphPts + ctxPts);
|
|
11095
|
+
score_breakdown = {
|
|
11096
|
+
knowledge_health: { points: Math.round(healthPts), max: 20 },
|
|
11097
|
+
knowledge_coverage: { points: Math.round(coveragePts), max: 20 },
|
|
11098
|
+
ownership_coverage: { points: Math.round(ownPts), max: 15 },
|
|
11099
|
+
traceability: { points: Math.round(tracePts), max: 20 },
|
|
11100
|
+
graph_quality: { points: Math.round(graphPts), max: 15 },
|
|
11101
|
+
context_readiness: { points: Math.round(ctxPts), max: 10 }
|
|
11102
|
+
};
|
|
11054
11103
|
}
|
|
11104
|
+
const idsOf = (items) => items.map((i) => i.id);
|
|
11105
|
+
const groupedAction = (items, verb) => {
|
|
11106
|
+
if (items.length === 0) return null;
|
|
11107
|
+
const ids = idsOf(items);
|
|
11108
|
+
return ids.length <= 3 ? `${verb} ${ids.join(", ")}.` : `${verb} ${ids.length} Work Items: ${ids.slice(0, 3).join(", ")}, \u2026`;
|
|
11109
|
+
};
|
|
11055
11110
|
const actions = [];
|
|
11056
11111
|
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
|
|
11112
|
+
else if (g.scope === "active" && g.quality === "empty") actions.push("Run `kaddo impact --scope all` to measure accumulated knowledge impact.");
|
|
11058
11113
|
if (exp.roadmap.remaining > 0) actions.push(`Materialize the ${exp.roadmap.remaining} remaining roadmap candidate(s) with \`kaddo create --from roadmap\`.`);
|
|
11059
|
-
|
|
11060
|
-
|
|
11114
|
+
for (const a of [
|
|
11115
|
+
groupedAction(gaps.missing_code_ownership, "Add `code:` ownership to"),
|
|
11116
|
+
groupedAction(gaps.missing_source, "Add `source`/`source_id` to"),
|
|
11117
|
+
groupedAction(gaps.missing_initiative, "Add an `initiative` to"),
|
|
11118
|
+
groupedAction(gaps.missing_acceptance_criteria, "Add Acceptance Criteria to"),
|
|
11119
|
+
groupedAction(gaps.missing_definition_of_done, "Add a Definition of Done to"),
|
|
11120
|
+
groupedAction(gaps.missing_validation, "Add a validation (How to test it) section to")
|
|
11121
|
+
]) if (a) actions.push(a);
|
|
11122
|
+
if (gaps.broad_ownership_globs.length > 0) actions.push(`Narrow ${gaps.broad_ownership_globs.length} broad ownership glob(s) into specific paths.`);
|
|
11061
11123
|
if (g.available && g.hints > 0) actions.push("Review graph hints (`kaddo://graph-hints` or `.kaddo/graph-hints.md`).");
|
|
11062
11124
|
if (skills.length === 0) actions.push("Install reusable skills with `kaddo add skills`.");
|
|
11063
11125
|
actions.push("Run `kaddo guard` before committing to catch knowledge drift.");
|
|
@@ -11072,6 +11134,8 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
11072
11134
|
generated_at: now.toISOString(),
|
|
11073
11135
|
project: exp.project.name,
|
|
11074
11136
|
scope: g.scope,
|
|
11137
|
+
default_scope: "all",
|
|
11138
|
+
scope_source: scopeSource,
|
|
11075
11139
|
executive_summary: summary,
|
|
11076
11140
|
knowledge_health,
|
|
11077
11141
|
knowledge_coverage,
|
|
@@ -11109,7 +11173,9 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
11109
11173
|
note: "Guard history is not persisted. Run `kaddo guard` to check drift; future versions may store runs for trend analysis."
|
|
11110
11174
|
},
|
|
11111
11175
|
impact_signals,
|
|
11176
|
+
actionable_gaps: gaps,
|
|
11112
11177
|
score,
|
|
11178
|
+
score_breakdown,
|
|
11113
11179
|
suggested_actions: actions
|
|
11114
11180
|
};
|
|
11115
11181
|
}
|
|
@@ -11124,6 +11190,11 @@ function renderImpactMarkdown(r) {
|
|
|
11124
11190
|
L.push(`Scope: ${r.scope}`);
|
|
11125
11191
|
if (r.score !== null) L.push(`Knowledge Impact Score: ${r.score}/100`);
|
|
11126
11192
|
else L.push("Knowledge Impact Score: not available");
|
|
11193
|
+
if (r.scope_source === "default") {
|
|
11194
|
+
L.push("Scope note: Impact reports use `all` by default to measure accumulated knowledge impact.");
|
|
11195
|
+
} else if (r.scope === "active" && r.graph_quality.available === false) {
|
|
11196
|
+
L.push("Tip: Run `kaddo impact --scope all` to inspect accumulated knowledge impact.");
|
|
11197
|
+
}
|
|
11127
11198
|
L.push("");
|
|
11128
11199
|
L.push("## Executive Summary", "");
|
|
11129
11200
|
for (const s2 of r.executive_summary) L.push(`- ${s2}`);
|
|
@@ -11180,6 +11251,9 @@ function renderImpactMarkdown(r) {
|
|
|
11180
11251
|
L.push(`- Hints: ${gq.hints}`);
|
|
11181
11252
|
if (gq.reason) L.push(`- Reason: ${gq.reason}`);
|
|
11182
11253
|
if (gq.last_exported) L.push(`- Last exported: ${gq.last_exported}`);
|
|
11254
|
+
if (gq.scope === "active" && gq.quality === "empty") {
|
|
11255
|
+
L.push("- Tip: Run `kaddo impact --scope all` to inspect accumulated knowledge impact.");
|
|
11256
|
+
}
|
|
11183
11257
|
} else {
|
|
11184
11258
|
L.push("- Graph data not available.");
|
|
11185
11259
|
L.push(`- Tip: ${r.graph_quality.suggestion}`);
|
|
@@ -11198,6 +11272,58 @@ function renderImpactMarkdown(r) {
|
|
|
11198
11272
|
L.push(`- AI context readiness: ${s.ai_context_readiness}`);
|
|
11199
11273
|
L.push(`- Maintenance readiness: ${s.maintenance_readiness}`);
|
|
11200
11274
|
L.push("");
|
|
11275
|
+
L.push("## Actionable Gaps", "");
|
|
11276
|
+
const gp = r.actionable_gaps;
|
|
11277
|
+
const gapSection = (title, items) => {
|
|
11278
|
+
if (items.length === 0) return;
|
|
11279
|
+
L.push(`### ${title}`, "");
|
|
11280
|
+
for (const it of items) {
|
|
11281
|
+
L.push(`- ${it.id} \u2014 ${it.title}`);
|
|
11282
|
+
L.push(` - Path: ${it.path}`);
|
|
11283
|
+
L.push(` - Suggested action: ${it.suggested_action}`);
|
|
11284
|
+
}
|
|
11285
|
+
L.push("");
|
|
11286
|
+
};
|
|
11287
|
+
gapSection("Work Items missing source", gp.missing_source);
|
|
11288
|
+
gapSection("Work Items missing initiative", gp.missing_initiative);
|
|
11289
|
+
gapSection("Work Items missing code ownership", gp.missing_code_ownership);
|
|
11290
|
+
gapSection("Work Items missing knowledge level", gp.missing_knowledge_level);
|
|
11291
|
+
gapSection("Work Items missing acceptance criteria", gp.missing_acceptance_criteria);
|
|
11292
|
+
gapSection("Work Items missing Definition of Done", gp.missing_definition_of_done);
|
|
11293
|
+
gapSection("Work Items missing validation", gp.missing_validation);
|
|
11294
|
+
if (gp.broad_ownership_globs.length > 0) {
|
|
11295
|
+
L.push("### Broad ownership globs", "");
|
|
11296
|
+
for (const b of gp.broad_ownership_globs) {
|
|
11297
|
+
L.push(`- ${b.id} \u2014 ${b.title}`);
|
|
11298
|
+
L.push(` - Glob: \`${b.glob}\``);
|
|
11299
|
+
L.push(` - Suggested action: ${b.suggested_action}`);
|
|
11300
|
+
}
|
|
11301
|
+
L.push("");
|
|
11302
|
+
}
|
|
11303
|
+
if (gp.ownership_overlaps.length > 0) {
|
|
11304
|
+
L.push("### Ownership overlaps", "");
|
|
11305
|
+
for (const o of gp.ownership_overlaps) {
|
|
11306
|
+
L.push(`- \`${o.code_path}\``);
|
|
11307
|
+
L.push(` - Owned by: ${o.work_items.join(", ")}`);
|
|
11308
|
+
L.push(` - Suggested action: ${o.suggested_action}`);
|
|
11309
|
+
}
|
|
11310
|
+
L.push("");
|
|
11311
|
+
}
|
|
11312
|
+
const anyGaps = gp.missing_source.length + gp.missing_initiative.length + gp.missing_code_ownership.length + gp.missing_knowledge_level.length + gp.missing_acceptance_criteria.length + gp.missing_definition_of_done.length + gp.missing_validation.length + gp.broad_ownership_globs.length + gp.ownership_overlaps.length;
|
|
11313
|
+
if (anyGaps === 0) {
|
|
11314
|
+
L.push("No actionable knowledge gaps detected. \u{1F389}", "");
|
|
11315
|
+
}
|
|
11316
|
+
if (r.score_breakdown) {
|
|
11317
|
+
L.push("## Score Breakdown", "");
|
|
11318
|
+
const b = r.score_breakdown;
|
|
11319
|
+
L.push(`- Knowledge Health: ${b.knowledge_health.points}/${b.knowledge_health.max}`);
|
|
11320
|
+
L.push(`- Knowledge Coverage: ${b.knowledge_coverage.points}/${b.knowledge_coverage.max}`);
|
|
11321
|
+
L.push(`- Ownership Coverage: ${b.ownership_coverage.points}/${b.ownership_coverage.max}`);
|
|
11322
|
+
L.push(`- Traceability: ${b.traceability.points}/${b.traceability.max}`);
|
|
11323
|
+
L.push(`- Graph Quality: ${b.graph_quality.points}/${b.graph_quality.max}`);
|
|
11324
|
+
L.push(`- Context Readiness: ${b.context_readiness.points}/${b.context_readiness.max}`);
|
|
11325
|
+
L.push("");
|
|
11326
|
+
}
|
|
11201
11327
|
L.push("## Suggested Actions", "");
|
|
11202
11328
|
r.suggested_actions.forEach((a, i) => L.push(`${i + 1}. ${a}`));
|
|
11203
11329
|
L.push("");
|
|
@@ -11211,7 +11337,8 @@ function serializeImpactJson(r) {
|
|
|
11211
11337
|
function runReportImpact(opts = {}) {
|
|
11212
11338
|
const dir = cwd();
|
|
11213
11339
|
requireConfig(dir);
|
|
11214
|
-
const
|
|
11340
|
+
const scope = opts.scope === "active" ? "active" : opts.scope === "all" ? "all" : void 0;
|
|
11341
|
+
const report = buildImpactReport(dir, { scope });
|
|
11215
11342
|
const content = opts.json ? serializeImpactJson(report) : renderImpactMarkdown(report);
|
|
11216
11343
|
if (opts.output) {
|
|
11217
11344
|
intro2("kaddo report impact");
|
|
@@ -11254,10 +11381,10 @@ graphCmd.command("export").description("Write the knowledge graph to .kaddo/grap
|
|
|
11254
11381
|
runGraphExport(opts);
|
|
11255
11382
|
});
|
|
11256
11383
|
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) => {
|
|
11384
|
+
reportCmd.command("impact").description("Knowledge Impact Report: knowledge health, coverage, traceability, readiness (deterministic, no LLM)").option("--json", "Output JSON instead of Markdown").option("--scope <scope>", "Scope: all (default \u2014 accumulated impact) or active").option("--output <path>", "Write the report to a file (e.g. .kaddo/reports/impact-report.md)").action((opts) => {
|
|
11258
11385
|
runReportImpact(opts);
|
|
11259
11386
|
});
|
|
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) => {
|
|
11387
|
+
program.command("impact").description("Alias for `kaddo report impact`").option("--json", "Output JSON instead of Markdown").option("--scope <scope>", "Scope: all (default \u2014 accumulated impact) or active").option("--output <path>", "Write the report to a file").action((opts) => {
|
|
11261
11388
|
runReportImpact(opts);
|
|
11262
11389
|
});
|
|
11263
11390
|
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) => {
|