@kaddo/cli 3.23.2 → 3.24.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 +263 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -513,6 +513,7 @@ create --from roadmap → owners → guard → explain`.
|
|
|
513
513
|
| v3.23 | Knowledge Impact Report: `kaddo report impact` (Markdown/JSON, `--output`); evidence-first health/coverage/traceability/readiness/signals; MCP resource + tool |
|
|
514
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
515
|
| v3.23.2 | Impact report defaults to `all` scope (accumulated impact), graph built in memory; `--scope active`; `scope_source`/`default_scope` in JSON |
|
|
516
|
+
| v3.24 | Estimated Savings Model: `kaddo savings` (+ `savings init`, `.kaddo/savings.yml`); evidence-based time/value estimates, confidence; MCP resource + tool |
|
|
516
517
|
|
|
517
518
|
**Optional modules (installed with `kaddo add`):**
|
|
518
519
|
|
package/dist/index.js
CHANGED
|
@@ -688,6 +688,10 @@ var COMMAND_HELP = {
|
|
|
688
688
|
"report impact": {
|
|
689
689
|
question: "What impact is Kaddo having on this project?",
|
|
690
690
|
next: "Act on the Suggested Actions section"
|
|
691
|
+
},
|
|
692
|
+
savings: {
|
|
693
|
+
question: "How much time/value might Kaddo be saving (estimate)?",
|
|
694
|
+
next: "Run `kaddo savings init` to calibrate assumptions"
|
|
691
695
|
}
|
|
692
696
|
};
|
|
693
697
|
function commandFooterLines(name) {
|
|
@@ -11352,6 +11356,256 @@ function runReportImpact(opts = {}) {
|
|
|
11352
11356
|
console.log(content);
|
|
11353
11357
|
}
|
|
11354
11358
|
|
|
11359
|
+
// src/core/savings.ts
|
|
11360
|
+
import { parse as parseYaml15 } from "yaml";
|
|
11361
|
+
var DEFAULT_ASSUMPTIONS = {
|
|
11362
|
+
currency: "USD",
|
|
11363
|
+
hourly_cost: 40,
|
|
11364
|
+
context_preparation_minutes_saved_per_work_item: 30,
|
|
11365
|
+
rework_hours_avoided_per_resolved_drift: 2,
|
|
11366
|
+
onboarding_hours_saved_per_new_contributor: 4,
|
|
11367
|
+
review_minutes_saved_per_work_item_with_ownership: 20,
|
|
11368
|
+
clarification_minutes_saved_per_ready_work_item: 25,
|
|
11369
|
+
architecture_discovery_hours_saved_when_graph_good: 3,
|
|
11370
|
+
expected_new_contributors_per_month: 1,
|
|
11371
|
+
expected_work_items_per_month: 8
|
|
11372
|
+
};
|
|
11373
|
+
var SAVINGS_PATH = ".kaddo/savings.yml";
|
|
11374
|
+
function loadAssumptions(dir) {
|
|
11375
|
+
const p2 = join(dir, SAVINGS_PATH);
|
|
11376
|
+
if (!exists(p2)) return { assumptions: { ...DEFAULT_ASSUMPTIONS }, source: "default" };
|
|
11377
|
+
try {
|
|
11378
|
+
const raw = parseYaml15(readFile(p2));
|
|
11379
|
+
const a = raw?.assumptions ?? {};
|
|
11380
|
+
const t = raw?.team ?? {};
|
|
11381
|
+
const num = (v, d) => typeof v === "number" && Number.isFinite(v) ? v : d;
|
|
11382
|
+
return {
|
|
11383
|
+
source: "file",
|
|
11384
|
+
assumptions: {
|
|
11385
|
+
currency: raw?.currency ?? DEFAULT_ASSUMPTIONS.currency,
|
|
11386
|
+
hourly_cost: num(raw?.hourly_cost, DEFAULT_ASSUMPTIONS.hourly_cost),
|
|
11387
|
+
context_preparation_minutes_saved_per_work_item: num(a.context_preparation_minutes_saved_per_work_item, DEFAULT_ASSUMPTIONS.context_preparation_minutes_saved_per_work_item),
|
|
11388
|
+
rework_hours_avoided_per_resolved_drift: num(a.rework_hours_avoided_per_resolved_drift, DEFAULT_ASSUMPTIONS.rework_hours_avoided_per_resolved_drift),
|
|
11389
|
+
onboarding_hours_saved_per_new_contributor: num(a.onboarding_hours_saved_per_new_contributor, DEFAULT_ASSUMPTIONS.onboarding_hours_saved_per_new_contributor),
|
|
11390
|
+
review_minutes_saved_per_work_item_with_ownership: num(a.review_minutes_saved_per_work_item_with_ownership, DEFAULT_ASSUMPTIONS.review_minutes_saved_per_work_item_with_ownership),
|
|
11391
|
+
clarification_minutes_saved_per_ready_work_item: num(a.clarification_minutes_saved_per_ready_work_item, DEFAULT_ASSUMPTIONS.clarification_minutes_saved_per_ready_work_item),
|
|
11392
|
+
architecture_discovery_hours_saved_when_graph_good: num(a.architecture_discovery_hours_saved_when_graph_good, DEFAULT_ASSUMPTIONS.architecture_discovery_hours_saved_when_graph_good),
|
|
11393
|
+
expected_new_contributors_per_month: num(t.expected_new_contributors_per_month, DEFAULT_ASSUMPTIONS.expected_new_contributors_per_month),
|
|
11394
|
+
expected_work_items_per_month: num(t.expected_work_items_per_month, DEFAULT_ASSUMPTIONS.expected_work_items_per_month)
|
|
11395
|
+
}
|
|
11396
|
+
};
|
|
11397
|
+
} catch {
|
|
11398
|
+
return { assumptions: { ...DEFAULT_ASSUMPTIONS }, source: "default" };
|
|
11399
|
+
}
|
|
11400
|
+
}
|
|
11401
|
+
function savingsTemplate() {
|
|
11402
|
+
return [
|
|
11403
|
+
"# Kaddo savings assumptions (VS-062). Estimates are evidence-based, not exact ROI.",
|
|
11404
|
+
"# Edit these to match your team, then run `kaddo savings`.",
|
|
11405
|
+
"currency: USD",
|
|
11406
|
+
"",
|
|
11407
|
+
"hourly_cost: 40",
|
|
11408
|
+
"",
|
|
11409
|
+
"assumptions:",
|
|
11410
|
+
" context_preparation_minutes_saved_per_work_item: 30",
|
|
11411
|
+
" rework_hours_avoided_per_resolved_drift: 2",
|
|
11412
|
+
" onboarding_hours_saved_per_new_contributor: 4",
|
|
11413
|
+
" review_minutes_saved_per_work_item_with_ownership: 20",
|
|
11414
|
+
" clarification_minutes_saved_per_ready_work_item: 25",
|
|
11415
|
+
" architecture_discovery_hours_saved_when_graph_good: 3",
|
|
11416
|
+
"",
|
|
11417
|
+
"team:",
|
|
11418
|
+
" expected_new_contributors_per_month: 1",
|
|
11419
|
+
" expected_work_items_per_month: 8",
|
|
11420
|
+
""
|
|
11421
|
+
].join("\n");
|
|
11422
|
+
}
|
|
11423
|
+
var READINESS_MULT = { Low: 0.25, Medium: 0.5, High: 0.75, "Very High": 1 };
|
|
11424
|
+
var GRAPH_MULT = { empty: 0, sparse: 0.25, partial: 0.6, good: 1, unknown: 0 };
|
|
11425
|
+
var round2 = (n) => Math.round(n * 100) / 100;
|
|
11426
|
+
function parseRatioNumerator(s) {
|
|
11427
|
+
const m = s.match(/^(\d+)/);
|
|
11428
|
+
return m ? Number(m[1]) : 0;
|
|
11429
|
+
}
|
|
11430
|
+
var SAVINGS_DISCLAIMER = "These are evidence-based estimates, not exact ROI. Adjust assumptions with real team data.";
|
|
11431
|
+
function buildSavingsReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
11432
|
+
const { assumptions: a, source } = loadAssumptions(dir);
|
|
11433
|
+
const impact = buildImpactReport(dir, { scope: opts.scope, scopeSource: opts.scopeSource }, now);
|
|
11434
|
+
const completed = impact.traceability.completed_work_items;
|
|
11435
|
+
const wiWithOwnership = parseRatioNumerator(impact.traceability.work_items_connected_to_code);
|
|
11436
|
+
const acceptanceCov = impact.knowledge_coverage.find((c) => c.label.toLowerCase().includes("acceptance"));
|
|
11437
|
+
const wiWithAcceptance = acceptanceCov?.have ?? 0;
|
|
11438
|
+
const graphQuality = impact.graph_quality.available ? impact.graph_quality.quality : "unknown";
|
|
11439
|
+
const readiness = impact.context_readiness.level;
|
|
11440
|
+
const ctxPrepH = round2(completed * a.context_preparation_minutes_saved_per_work_item / 60);
|
|
11441
|
+
const reviewH = round2(wiWithOwnership * a.review_minutes_saved_per_work_item_with_ownership / 60);
|
|
11442
|
+
const clarH = round2(wiWithAcceptance * a.clarification_minutes_saved_per_ready_work_item / 60);
|
|
11443
|
+
const readinessMult = READINESS_MULT[readiness] ?? 0.5;
|
|
11444
|
+
const onboardH = round2(a.expected_new_contributors_per_month * a.onboarding_hours_saved_per_new_contributor * readinessMult);
|
|
11445
|
+
const graphMult = GRAPH_MULT[graphQuality] ?? 0;
|
|
11446
|
+
const archH = round2(a.architecture_discovery_hours_saved_when_graph_good * graphMult);
|
|
11447
|
+
const totalHours = round2(ctxPrepH + reviewH + clarH + onboardH + archH);
|
|
11448
|
+
const totalValue = Math.round(totalHours * a.hourly_cost);
|
|
11449
|
+
const reasons = [];
|
|
11450
|
+
let level;
|
|
11451
|
+
if (impact.score === null || impact.score < 60 || graphQuality === "empty") {
|
|
11452
|
+
level = "Low";
|
|
11453
|
+
reasons.push("Impact score is low or the knowledge graph is empty.");
|
|
11454
|
+
} else {
|
|
11455
|
+
level = "Medium";
|
|
11456
|
+
reasons.push("Strong knowledge and graph evidence.");
|
|
11457
|
+
}
|
|
11458
|
+
reasons.push("Guard history is not persisted yet \u2014 avoided rework is not estimated.");
|
|
11459
|
+
reasons.push(source === "file" ? "Assumptions come from `.kaddo/savings.yml`." : "Savings assumptions are defaults and should be calibrated.");
|
|
11460
|
+
const actions = [];
|
|
11461
|
+
if (source === "default") actions.push("Run `kaddo savings init` to customize assumptions.");
|
|
11462
|
+
actions.push("Calibrate hourly cost and team assumptions with real team data.");
|
|
11463
|
+
actions.push("Run `kaddo impact` to verify the underlying evidence quality.");
|
|
11464
|
+
actions.push("Use this report as directional evidence, not accounting data.");
|
|
11465
|
+
actions.push("Future: persist Guard history to estimate avoided rework.");
|
|
11466
|
+
return {
|
|
11467
|
+
generated_at: now.toISOString(),
|
|
11468
|
+
project: impact.project,
|
|
11469
|
+
scope: impact.scope,
|
|
11470
|
+
currency: a.currency,
|
|
11471
|
+
disclaimer: SAVINGS_DISCLAIMER,
|
|
11472
|
+
assumptions_source: source,
|
|
11473
|
+
assumptions: {
|
|
11474
|
+
hourly_cost: a.hourly_cost,
|
|
11475
|
+
context_preparation_minutes_saved_per_work_item: a.context_preparation_minutes_saved_per_work_item,
|
|
11476
|
+
review_minutes_saved_per_work_item_with_ownership: a.review_minutes_saved_per_work_item_with_ownership,
|
|
11477
|
+
clarification_minutes_saved_per_ready_work_item: a.clarification_minutes_saved_per_ready_work_item,
|
|
11478
|
+
onboarding_hours_saved_per_new_contributor: a.onboarding_hours_saved_per_new_contributor,
|
|
11479
|
+
expected_new_contributors_per_month: a.expected_new_contributors_per_month,
|
|
11480
|
+
architecture_discovery_hours_saved_when_graph_good: a.architecture_discovery_hours_saved_when_graph_good
|
|
11481
|
+
},
|
|
11482
|
+
evidence: {
|
|
11483
|
+
knowledge_impact_score: impact.score,
|
|
11484
|
+
completed_work_items: completed,
|
|
11485
|
+
ownership_coverage_percent: impact.ownership_coverage.coverage_percent,
|
|
11486
|
+
work_items_with_acceptance_criteria: acceptanceCov ? `${acceptanceCov.have}/${acceptanceCov.total}` : "0/0",
|
|
11487
|
+
graph_quality: graphQuality,
|
|
11488
|
+
graph_nodes: impact.graph_quality.available ? impact.graph_quality.nodes : null,
|
|
11489
|
+
graph_edges: impact.graph_quality.available ? impact.graph_quality.edges : null,
|
|
11490
|
+
context_readiness: readiness,
|
|
11491
|
+
guard_history_available: false
|
|
11492
|
+
},
|
|
11493
|
+
estimated_savings: {
|
|
11494
|
+
context_preparation: { hours: ctxPrepH, formula: `${completed} Work Items \xD7 ${a.context_preparation_minutes_saved_per_work_item} min` },
|
|
11495
|
+
review_effort: { hours: reviewH, formula: `${wiWithOwnership} Work Items with ownership \xD7 ${a.review_minutes_saved_per_work_item_with_ownership} min` },
|
|
11496
|
+
clarification_reduction: { hours: clarH, formula: `${wiWithAcceptance} Work Items with Acceptance Criteria \xD7 ${a.clarification_minutes_saved_per_ready_work_item} min` },
|
|
11497
|
+
onboarding: { hours: onboardH, formula: `${a.expected_new_contributors_per_month} contributor \xD7 ${a.onboarding_hours_saved_per_new_contributor} h \xD7 ${readinessMult.toFixed(2)} readiness multiplier` },
|
|
11498
|
+
architecture_discovery: { hours: archH, formula: `${a.architecture_discovery_hours_saved_when_graph_good} h \xD7 ${graphMult.toFixed(2)} graph multiplier` },
|
|
11499
|
+
drift_prevention: { available: false, hours: 0, reason: "Guard history is not persisted yet." }
|
|
11500
|
+
},
|
|
11501
|
+
total: { estimated_hours_saved: totalHours, estimated_value: totalValue, currency: a.currency },
|
|
11502
|
+
confidence: { level, reasons },
|
|
11503
|
+
suggested_actions: actions
|
|
11504
|
+
};
|
|
11505
|
+
}
|
|
11506
|
+
function renderSavingsMarkdown(r) {
|
|
11507
|
+
const L = [];
|
|
11508
|
+
L.push("# Kaddo Estimated Savings Report", "");
|
|
11509
|
+
L.push(`Generated at: ${r.generated_at}`);
|
|
11510
|
+
L.push(`Project: ${r.project}`);
|
|
11511
|
+
L.push(`Scope: ${r.scope}`);
|
|
11512
|
+
L.push(`Currency: ${r.currency}`);
|
|
11513
|
+
L.push("");
|
|
11514
|
+
L.push("## Disclaimer", "");
|
|
11515
|
+
L.push(r.disclaimer, "");
|
|
11516
|
+
L.push("## Executive Summary", "");
|
|
11517
|
+
L.push(`- Estimated time saved: ${r.total.estimated_hours_saved} hours`);
|
|
11518
|
+
L.push(`- Estimated value: ${r.total.estimated_value} ${r.currency}`);
|
|
11519
|
+
L.push(`- Confidence: ${r.confidence.level}`);
|
|
11520
|
+
if (r.assumptions_source === "default") L.push("- Using default assumptions. Run `kaddo savings init` to customize them.");
|
|
11521
|
+
L.push("");
|
|
11522
|
+
L.push("## Assumptions", "");
|
|
11523
|
+
L.push(`- Hourly cost: ${r.assumptions.hourly_cost} ${r.currency}`);
|
|
11524
|
+
L.push(`- Context preparation saved per Work Item: ${r.assumptions.context_preparation_minutes_saved_per_work_item} min`);
|
|
11525
|
+
L.push(`- Review saved per Work Item with ownership: ${r.assumptions.review_minutes_saved_per_work_item_with_ownership} min`);
|
|
11526
|
+
L.push(`- Clarification saved per Work Item with acceptance criteria: ${r.assumptions.clarification_minutes_saved_per_ready_work_item} min`);
|
|
11527
|
+
L.push(`- Onboarding saved per new contributor: ${r.assumptions.onboarding_hours_saved_per_new_contributor} h`);
|
|
11528
|
+
L.push(`- Expected new contributors per month: ${r.assumptions.expected_new_contributors_per_month}`);
|
|
11529
|
+
L.push(`- Architecture discovery saved when graph is good: ${r.assumptions.architecture_discovery_hours_saved_when_graph_good} h`);
|
|
11530
|
+
L.push("");
|
|
11531
|
+
L.push("## Evidence Used", "");
|
|
11532
|
+
const e = r.evidence;
|
|
11533
|
+
L.push(`- Knowledge Impact Score: ${e.knowledge_impact_score ?? "not available"}${e.knowledge_impact_score !== null ? "/100" : ""}`);
|
|
11534
|
+
L.push(`- Completed Work Items: ${e.completed_work_items}`);
|
|
11535
|
+
L.push(`- Ownership coverage: ${e.ownership_coverage_percent}%`);
|
|
11536
|
+
L.push(`- Work Items with Acceptance Criteria: ${e.work_items_with_acceptance_criteria}`);
|
|
11537
|
+
L.push(`- Graph quality: ${e.graph_quality}`);
|
|
11538
|
+
if (e.graph_nodes !== null) L.push(`- Graph nodes: ${e.graph_nodes}`);
|
|
11539
|
+
if (e.graph_edges !== null) L.push(`- Graph edges: ${e.graph_edges}`);
|
|
11540
|
+
L.push(`- Context readiness: ${e.context_readiness}`);
|
|
11541
|
+
L.push("- Guard history: not available");
|
|
11542
|
+
L.push("");
|
|
11543
|
+
L.push("## Estimated Savings", "");
|
|
11544
|
+
const s = r.estimated_savings;
|
|
11545
|
+
const line = (title, l) => {
|
|
11546
|
+
L.push(`### ${title}`, "");
|
|
11547
|
+
L.push(`- Formula: ${l.formula}`);
|
|
11548
|
+
L.push(`- Estimated: ${l.hours} h`, "");
|
|
11549
|
+
};
|
|
11550
|
+
line("Context Preparation", s.context_preparation);
|
|
11551
|
+
line("Review Effort", s.review_effort);
|
|
11552
|
+
line("Clarification Reduction", s.clarification_reduction);
|
|
11553
|
+
line("Onboarding", s.onboarding);
|
|
11554
|
+
line("Architecture Discovery", s.architecture_discovery);
|
|
11555
|
+
L.push("### Drift Prevention", "");
|
|
11556
|
+
L.push("- Not available yet.");
|
|
11557
|
+
L.push(`- Reason: ${s.drift_prevention.reason}`, "");
|
|
11558
|
+
L.push("## Total", "");
|
|
11559
|
+
L.push(`- Estimated time saved: ${r.total.estimated_hours_saved} h`);
|
|
11560
|
+
L.push(`- Estimated value: ${r.total.estimated_value} ${r.currency}`);
|
|
11561
|
+
L.push("");
|
|
11562
|
+
L.push("## Confidence", "");
|
|
11563
|
+
L.push(`Confidence: ${r.confidence.level}`, "", "Reason:");
|
|
11564
|
+
for (const reason of r.confidence.reasons) L.push(`- ${reason}`);
|
|
11565
|
+
L.push("");
|
|
11566
|
+
L.push("## Suggested Actions", "");
|
|
11567
|
+
r.suggested_actions.forEach((act, i) => L.push(`${i + 1}. ${act}`));
|
|
11568
|
+
L.push("");
|
|
11569
|
+
return L.join("\n");
|
|
11570
|
+
}
|
|
11571
|
+
function serializeSavingsJson(r) {
|
|
11572
|
+
return JSON.stringify(r, null, 2) + "\n";
|
|
11573
|
+
}
|
|
11574
|
+
|
|
11575
|
+
// src/commands/savings.ts
|
|
11576
|
+
function runSavings(opts = {}) {
|
|
11577
|
+
const dir = cwd();
|
|
11578
|
+
requireConfig(dir);
|
|
11579
|
+
const scope = opts.scope === "active" ? "active" : opts.scope === "all" ? "all" : void 0;
|
|
11580
|
+
const report = buildSavingsReport(dir, { scope });
|
|
11581
|
+
const content = opts.json ? serializeSavingsJson(report) : renderSavingsMarkdown(report);
|
|
11582
|
+
if (opts.output) {
|
|
11583
|
+
intro2("kaddo savings");
|
|
11584
|
+
writeFile(join(dir, opts.output), content);
|
|
11585
|
+
log2.success(`Wrote ${opts.output.replace(/\\/g, "/")}`);
|
|
11586
|
+
printCommandFooter("savings");
|
|
11587
|
+
outro2("Savings report written.");
|
|
11588
|
+
return;
|
|
11589
|
+
}
|
|
11590
|
+
console.log(content);
|
|
11591
|
+
}
|
|
11592
|
+
function runSavingsInit(opts = {}) {
|
|
11593
|
+
const dir = cwd();
|
|
11594
|
+
requireConfig(dir);
|
|
11595
|
+
intro2("kaddo savings init");
|
|
11596
|
+
const rel = ".kaddo/savings.yml";
|
|
11597
|
+
const full = join(dir, rel);
|
|
11598
|
+
if (exists(full) && !opts.force) {
|
|
11599
|
+
log2.warn(`${rel} already exists. Use --force to overwrite.`);
|
|
11600
|
+
outro2("Nothing changed.");
|
|
11601
|
+
return;
|
|
11602
|
+
}
|
|
11603
|
+
writeFile(full, savingsTemplate());
|
|
11604
|
+
log2.success(`Wrote ${rel}`);
|
|
11605
|
+
log2.info("Edit the assumptions, then run `kaddo savings`.");
|
|
11606
|
+
outro2("Savings assumptions ready.");
|
|
11607
|
+
}
|
|
11608
|
+
|
|
11355
11609
|
// src/index.ts
|
|
11356
11610
|
var require2 = createRequire(import.meta.url);
|
|
11357
11611
|
var { version } = require2("../package.json");
|
|
@@ -11387,6 +11641,15 @@ reportCmd.command("impact").description("Knowledge Impact Report: knowledge heal
|
|
|
11387
11641
|
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) => {
|
|
11388
11642
|
runReportImpact(opts);
|
|
11389
11643
|
});
|
|
11644
|
+
reportCmd.command("savings").description("Estimated Savings Report: evidence-based time/effort/value estimates (deterministic, no LLM)").option("--json", "Output JSON instead of Markdown").option("--scope <scope>", "Scope: all (default) or active").option("--output <path>", "Write the report to a file (e.g. .kaddo/reports/savings-report.md)").action((opts) => {
|
|
11645
|
+
runSavings(opts);
|
|
11646
|
+
});
|
|
11647
|
+
var savingsCmd = program.command("savings").description("Estimated Savings Report from impact evidence + configurable assumptions").option("--json", "Output JSON instead of Markdown").option("--scope <scope>", "Scope: all (default) or active").option("--output <path>", "Write the report to a file").action((opts) => {
|
|
11648
|
+
runSavings(opts);
|
|
11649
|
+
});
|
|
11650
|
+
savingsCmd.command("init").description("Create an editable `.kaddo/savings.yml` with savings assumptions").option("--force", "Overwrite an existing .kaddo/savings.yml").action((opts) => {
|
|
11651
|
+
runSavingsInit(opts);
|
|
11652
|
+
});
|
|
11390
11653
|
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) => {
|
|
11391
11654
|
await runGuard(opts);
|
|
11392
11655
|
});
|