@kaddo/cli 3.39.1 → 3.40.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 +186 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -537,6 +537,7 @@ create --from roadmap → owners → guard → explain`.
|
|
|
537
537
|
| v3.38 | Open-question source locations: `kaddo questions` shows each question's `Source` (path:line), `Status`, `Severity`, `Note` + a copy/paste resolution example and localized how-to-resolve guide; JSON/report/MCP carry `sourcePath`, `line`, `raw`, `note` |
|
|
538
538
|
| v3.39 | Existing capability discovery: state-aware `capabilities.md` (pre-ai/legacy = evidence-backed Capability Inventory + Gaps + Roadmap Candidate Signals; legacy adds criticality/change-risk/modernization); capability-agent discovers with evidence, roadmap-agent treats capabilities as primary source |
|
|
539
539
|
| v3.39.1 | Domain-oriented capability inventory: pre-ai/legacy `capabilities.md` groups capabilities under `## Capability Domains` (functional domains, not technical folders); gaps/candidates name their `Domain`; roadmap-agent reads it as a domain map; work-item-agent recommends `related_domain` + `related_capability` |
|
|
540
|
+
| v3.40 | ADR materialization: `kaddo adr` (alias `decisions`) detects decision candidates + ADRs and hands off the ADR files to create; `tech_decisions` status (none/candidates/draft-adrs/accepted-adrs) surfaced in `explain`/`context`/`understand`; adr-writing skill formalized |
|
|
540
541
|
|
|
541
542
|
**Optional modules (installed with `kaddo add`):**
|
|
542
543
|
|
package/dist/index.js
CHANGED
|
@@ -704,6 +704,10 @@ var COMMAND_HELP = {
|
|
|
704
704
|
"adapters install codex": {
|
|
705
705
|
question: "How does Codex work with this Kaddo project?",
|
|
706
706
|
next: "Open AGENTS.md, or regenerate it after knowledge changes"
|
|
707
|
+
},
|
|
708
|
+
adr: {
|
|
709
|
+
question: "Which technical decisions still need to become ADRs?",
|
|
710
|
+
next: "Use the adr-writing skill to draft ADRs, then mark them accepted when confirmed"
|
|
707
711
|
}
|
|
708
712
|
};
|
|
709
713
|
function commandFooterLines(name) {
|
|
@@ -1546,37 +1550,56 @@ The context pack, the relevant Work Item or architecture note, and the decision
|
|
|
1546
1550
|
|
|
1547
1551
|
## Output
|
|
1548
1552
|
|
|
1549
|
-
A single ADR
|
|
1550
|
-
|
|
1551
|
-
superseded
|
|
1553
|
+
A single ADR (saved under \`knowledge/tech/decisions/\`) with front matter and the standard sections:
|
|
1554
|
+
context, options considered, the decision, consequences, related capabilities and related Work Items.
|
|
1555
|
+
Status is one of \`draft\`, \`accepted\`, \`superseded\`, \`deprecated\`.
|
|
1556
|
+
|
|
1557
|
+
## Materializing from decision candidates (VS-075)
|
|
1558
|
+
|
|
1559
|
+
When \`knowledge/tech/decisions/\` is empty but \`knowledge/tech/decision-candidates.md\` holds
|
|
1560
|
+
candidates, materialize each candidate as an ADR **draft** \u2014 copy its context and options, leave the
|
|
1561
|
+
decision and consequences as \`[open]\` for human confirmation, and record its origin with
|
|
1562
|
+
\`created_from: knowledge/tech/decision-candidates.md\`. Never mark a materialized draft \`accepted\`;
|
|
1563
|
+
acceptance is a human decision.
|
|
1552
1564
|
|
|
1553
1565
|
## Rules
|
|
1554
1566
|
|
|
1555
1567
|
- One ADR = one decision. Never mix unrelated decisions.
|
|
1556
1568
|
- Never invent decisions; never write an ADR without a clear reason.
|
|
1557
|
-
- Record alternatives honestly, including the one chosen and why.
|
|
1569
|
+
- Record alternatives honestly, including the one chosen and why (or \`[open]\` when undecided).
|
|
1558
1570
|
- Prefer narrow governed \`code:\` globs over broad ones.
|
|
1559
1571
|
|
|
1560
1572
|
## Quality checklist
|
|
1561
1573
|
|
|
1562
1574
|
- Context explains why the decision was needed.
|
|
1563
|
-
- The decision and its alternatives are explicit.
|
|
1575
|
+
- The decision and its alternatives are explicit (or explicitly \`[open]\`).
|
|
1564
1576
|
- Consequences (positive and negative) are stated.
|
|
1565
1577
|
- Status and governed paths are present.
|
|
1566
1578
|
|
|
1567
1579
|
## Example output
|
|
1568
1580
|
|
|
1569
1581
|
\`\`\`md
|
|
1570
|
-
|
|
1571
|
-
|
|
1582
|
+
---
|
|
1583
|
+
type: adr
|
|
1584
|
+
status: draft | accepted | superseded | deprecated
|
|
1585
|
+
date: YYYY-MM-DD
|
|
1586
|
+
created_from: knowledge/tech/decision-candidates.md
|
|
1587
|
+
---
|
|
1588
|
+
|
|
1589
|
+
# ADR-001 \u2014 Use INTERNAL_CRON_SECRET for internal endpoint protection
|
|
1590
|
+
|
|
1572
1591
|
## Context
|
|
1573
1592
|
...
|
|
1574
|
-
##
|
|
1575
|
-
...
|
|
1576
|
-
## Alternatives considered
|
|
1593
|
+
## Options Considered
|
|
1577
1594
|
...
|
|
1595
|
+
## Decision
|
|
1596
|
+
[open]
|
|
1578
1597
|
## Consequences
|
|
1579
|
-
|
|
1598
|
+
[open]
|
|
1599
|
+
## Related Capabilities
|
|
1600
|
+
- <domain / capability>
|
|
1601
|
+
## Related Work Items
|
|
1602
|
+
- <WI id>
|
|
1580
1603
|
\`\`\`
|
|
1581
1604
|
`
|
|
1582
1605
|
);
|
|
@@ -2935,6 +2958,13 @@ A refined Work Item intended to be saved under the lifecycle workspace:
|
|
|
2935
2958
|
capability from knowledge/product/capabilities.md this Work Item advances, so work traces back to the
|
|
2936
2959
|
system's functional map. Add \`related_domain: <domain>\` and \`related_capability: <name>\` to the front
|
|
2937
2960
|
matter when known. -->
|
|
2961
|
+
|
|
2962
|
+
**Related decisions:** <!-- recommended (VS-075): if this Work Item is affected by a technical
|
|
2963
|
+
decision, reference the ADR under knowledge/tech/decisions/ as \`related_decisions: [ADR-001-...]\`. If
|
|
2964
|
+
the decision is still a candidate in knowledge/tech/decision-candidates.md with no ADR yet, **warn**
|
|
2965
|
+
that it should be materialized first (\`kaddo adr\` + the adr-writing skill) and record
|
|
2966
|
+
\`decision_candidates: [<title>]\` \u2014 do not implement work that depends on an unformalized decision
|
|
2967
|
+
without surfacing it. -->
|
|
2938
2968
|
\`\`\`
|
|
2939
2969
|
|
|
2940
2970
|
## Where to Save the Result
|
|
@@ -3511,6 +3541,12 @@ architecture, persistence, authentication or the Work Item's scope. Block only o
|
|
|
3511
3541
|
mention the relevant assumptions instead of pausing. If any blocking question is still open, pause and
|
|
3512
3542
|
ask the user to confirm assumptions or resolve them before writing code.
|
|
3513
3543
|
|
|
3544
|
+
Also check **technical decisions** (VS-075): if the Work Item touches a decision that is still a
|
|
3545
|
+
candidate in \`knowledge/tech/decision-candidates.md\` with no ADR under \`knowledge/tech/decisions/\`
|
|
3546
|
+
(run \`kaddo adr\`), **warn** and recommend materializing it as an ADR (adr-writing skill) before
|
|
3547
|
+
implementing \u2014 do not silently implement work that depends on an unformalized architectural,
|
|
3548
|
+
security, data, integration or infrastructure decision.
|
|
3549
|
+
|
|
3514
3550
|
## When to Use
|
|
3515
3551
|
|
|
3516
3552
|
Use this agent after the work-item-agent has produced a clear, traceable Work Item under
|
|
@@ -8086,6 +8122,76 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
8086
8122
|
};
|
|
8087
8123
|
}
|
|
8088
8124
|
|
|
8125
|
+
// src/core/decisions.ts
|
|
8126
|
+
var CANDIDATES_PATH = "knowledge/tech/decision-candidates.md";
|
|
8127
|
+
var DECISIONS_DIR = "knowledge/tech/decisions";
|
|
8128
|
+
function slugify2(s) {
|
|
8129
|
+
return s.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
|
|
8130
|
+
}
|
|
8131
|
+
function parseDecisionCandidates(md) {
|
|
8132
|
+
const out = [];
|
|
8133
|
+
for (const line of md.split(/\r?\n/)) {
|
|
8134
|
+
const m = line.match(/^##\s+(.+?)\s*$/);
|
|
8135
|
+
if (m) {
|
|
8136
|
+
const t = m[1].trim();
|
|
8137
|
+
if (t && !/^_.*_$/.test(t)) out.push(t);
|
|
8138
|
+
}
|
|
8139
|
+
}
|
|
8140
|
+
return out;
|
|
8141
|
+
}
|
|
8142
|
+
function countAdrs(dir) {
|
|
8143
|
+
const base = join(dir, DECISIONS_DIR);
|
|
8144
|
+
if (!exists(base)) return { total: 0, draft: 0, accepted: 0 };
|
|
8145
|
+
let total = 0;
|
|
8146
|
+
let draft = 0;
|
|
8147
|
+
let accepted = 0;
|
|
8148
|
+
for (const entry of readDir(base)) {
|
|
8149
|
+
if (!entry.endsWith(".md") || entry === ".gitkeep") continue;
|
|
8150
|
+
const full = join(base, entry);
|
|
8151
|
+
if (!isFile(full)) continue;
|
|
8152
|
+
total += 1;
|
|
8153
|
+
let content = "";
|
|
8154
|
+
try {
|
|
8155
|
+
content = readFile(full);
|
|
8156
|
+
} catch {
|
|
8157
|
+
continue;
|
|
8158
|
+
}
|
|
8159
|
+
const status = content.match(/^\s*status:\s*([a-z-]+)/im)?.[1]?.toLowerCase();
|
|
8160
|
+
if (status === "accepted") accepted += 1;
|
|
8161
|
+
else draft += 1;
|
|
8162
|
+
}
|
|
8163
|
+
return { total, draft, accepted };
|
|
8164
|
+
}
|
|
8165
|
+
function buildTechDecisions(dir) {
|
|
8166
|
+
const candFile = join(dir, CANDIDATES_PATH);
|
|
8167
|
+
let titles = [];
|
|
8168
|
+
if (exists(candFile)) {
|
|
8169
|
+
try {
|
|
8170
|
+
titles = parseDecisionCandidates(readFile(candFile));
|
|
8171
|
+
} catch {
|
|
8172
|
+
titles = [];
|
|
8173
|
+
}
|
|
8174
|
+
}
|
|
8175
|
+
const { total, draft, accepted } = countAdrs(dir);
|
|
8176
|
+
const candidate_list = titles.map((title, i) => {
|
|
8177
|
+
const n = String(total + i + 1).padStart(3, "0");
|
|
8178
|
+
return { title, source: CANDIDATES_PATH, suggestedAdrFile: `${DECISIONS_DIR}/ADR-${n}-${slugify2(title)}.md` };
|
|
8179
|
+
});
|
|
8180
|
+
let status;
|
|
8181
|
+
if (accepted > 0) status = "accepted-adrs";
|
|
8182
|
+
else if (total > 0) status = "draft-adrs";
|
|
8183
|
+
else if (titles.length > 0) status = "candidates";
|
|
8184
|
+
else status = "none";
|
|
8185
|
+
return {
|
|
8186
|
+
status,
|
|
8187
|
+
candidates: titles.length,
|
|
8188
|
+
adrs: total,
|
|
8189
|
+
draft_adrs: draft,
|
|
8190
|
+
accepted_adrs: accepted,
|
|
8191
|
+
candidate_list
|
|
8192
|
+
};
|
|
8193
|
+
}
|
|
8194
|
+
|
|
8089
8195
|
// src/core/project-explain.ts
|
|
8090
8196
|
var ARCH_DIR4 = "knowledge";
|
|
8091
8197
|
function normalizeTitle(t) {
|
|
@@ -8297,7 +8403,8 @@ function buildProjectExplanation(dir) {
|
|
|
8297
8403
|
missingKnowledge,
|
|
8298
8404
|
suggestedNextSteps,
|
|
8299
8405
|
readiness,
|
|
8300
|
-
nextStepRecommendation: readiness.nextStepRecommendation
|
|
8406
|
+
nextStepRecommendation: readiness.nextStepRecommendation,
|
|
8407
|
+
techDecisions: buildTechDecisions(dir)
|
|
8301
8408
|
};
|
|
8302
8409
|
}
|
|
8303
8410
|
function stateLabel(state) {
|
|
@@ -8511,6 +8618,16 @@ function renderExplanationHuman(exp) {
|
|
|
8511
8618
|
lines.push("### Recommended next step");
|
|
8512
8619
|
lines.push(r.recommended_next_step.label);
|
|
8513
8620
|
lines.push("");
|
|
8621
|
+
const td = exp.techDecisions;
|
|
8622
|
+
lines.push("## Tech Decisions");
|
|
8623
|
+
lines.push(`- Decision candidates: ${td.candidates}`);
|
|
8624
|
+
lines.push(`- ADRs: ${td.adrs} (draft: ${td.draft_adrs}, accepted: ${td.accepted_adrs})`);
|
|
8625
|
+
lines.push(`- Status: ${td.status}`);
|
|
8626
|
+
if (td.candidates > 0 && td.adrs === 0) {
|
|
8627
|
+
lines.push("");
|
|
8628
|
+
lines.push("Use the adr-writing skill to materialize decision candidates into ADRs (`kaddo adr`) before implementing related technical Work Items.");
|
|
8629
|
+
}
|
|
8630
|
+
lines.push("");
|
|
8514
8631
|
return lines.join("\n").trimEnd() + "\n";
|
|
8515
8632
|
}
|
|
8516
8633
|
function renderExplanationAgent(exp) {
|
|
@@ -8855,6 +8972,10 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
8855
8972
|
}
|
|
8856
8973
|
});
|
|
8857
8974
|
const nextStepRecommendation = resolveNextStep(dir, now);
|
|
8975
|
+
const techDecisions = buildTechDecisions(dir);
|
|
8976
|
+
if (techDecisions.candidates > 0 && techDecisions.adrs === 0) {
|
|
8977
|
+
missing.push(`${techDecisions.candidates} technical decision candidate(s) not yet materialized as ADRs (run \`kaddo adr\`).`);
|
|
8978
|
+
}
|
|
8858
8979
|
const unifiedPhase = {
|
|
8859
8980
|
...phase,
|
|
8860
8981
|
nextStep: nextStepRecommendation.label,
|
|
@@ -8892,6 +9013,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
8892
9013
|
roadmap,
|
|
8893
9014
|
phase: unifiedPhase,
|
|
8894
9015
|
nextStepRecommendation,
|
|
9016
|
+
techDecisions,
|
|
8895
9017
|
deliveryMix,
|
|
8896
9018
|
external: loadExternalCapsules(dir),
|
|
8897
9019
|
graph: loadGraphSummary(dir),
|
|
@@ -9315,12 +9437,12 @@ function renderUnderstandTerminal(plan) {
|
|
|
9315
9437
|
|
|
9316
9438
|
// src/core/delivery.ts
|
|
9317
9439
|
import { parse as parseYaml10 } from "yaml";
|
|
9318
|
-
function
|
|
9440
|
+
function slugify3(s) {
|
|
9319
9441
|
return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
9320
9442
|
}
|
|
9321
9443
|
function toActive(a) {
|
|
9322
9444
|
const id = a.id || a.title || "WI";
|
|
9323
|
-
return { id, title: a.title || id, type: a.type, slug:
|
|
9445
|
+
return { id, title: a.title || id, type: a.type, slug: slugify3(a.title || id) };
|
|
9324
9446
|
}
|
|
9325
9447
|
function activeWorkItems(dir) {
|
|
9326
9448
|
return discoverWorkItems(dir).filter((a) => a.lifecycle === "in-progress").map(toActive);
|
|
@@ -9426,6 +9548,13 @@ function runUnderstand() {
|
|
|
9426
9548
|
for (const s of recSkills) console.log(` - ${s}`);
|
|
9427
9549
|
}
|
|
9428
9550
|
}
|
|
9551
|
+
const td = exp.techDecisions;
|
|
9552
|
+
if (td.candidates > 0 && td.adrs === 0) {
|
|
9553
|
+
console.log("");
|
|
9554
|
+
console.log(`Tech decisions: ${td.candidates} decision candidate(s) not yet materialized as ADRs.`);
|
|
9555
|
+
console.log(" \u2192 Use the adr-writing skill to create ADR drafts from `knowledge/tech/decision-candidates.md`");
|
|
9556
|
+
console.log(" into `knowledge/tech/decisions/` before implementing affected technical Work Items (`kaddo adr`).");
|
|
9557
|
+
}
|
|
9429
9558
|
if (exp.externalCapsules.length > 0) {
|
|
9430
9559
|
console.log("");
|
|
9431
9560
|
console.log("External knowledge:");
|
|
@@ -11940,7 +12069,7 @@ var MODULE_TYPES = [
|
|
|
11940
12069
|
"data",
|
|
11941
12070
|
"unknown"
|
|
11942
12071
|
];
|
|
11943
|
-
function
|
|
12072
|
+
function slugify4(name) {
|
|
11944
12073
|
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
11945
12074
|
}
|
|
11946
12075
|
function readModulesDescriptor(dir) {
|
|
@@ -11960,7 +12089,7 @@ function moduleDir(id) {
|
|
|
11960
12089
|
return `knowledge/tech/modules/${id}`;
|
|
11961
12090
|
}
|
|
11962
12091
|
function buildModule(input) {
|
|
11963
|
-
const id =
|
|
12092
|
+
const id = slugify4(input.name);
|
|
11964
12093
|
const base = moduleDir(id);
|
|
11965
12094
|
return {
|
|
11966
12095
|
id,
|
|
@@ -13869,6 +13998,44 @@ function runAdaptersStatus(opts = {}) {
|
|
|
13869
13998
|
}
|
|
13870
13999
|
}
|
|
13871
14000
|
|
|
14001
|
+
// src/commands/adr.ts
|
|
14002
|
+
function runAdr(opts = {}) {
|
|
14003
|
+
const dir = cwd();
|
|
14004
|
+
requireConfig(dir);
|
|
14005
|
+
const td = buildTechDecisions(dir);
|
|
14006
|
+
if (opts.json) {
|
|
14007
|
+
console.log(JSON.stringify(td, null, 2));
|
|
14008
|
+
return;
|
|
14009
|
+
}
|
|
14010
|
+
console.log("");
|
|
14011
|
+
console.log("Tech Decisions");
|
|
14012
|
+
console.log(` Decision candidates: ${td.candidates}`);
|
|
14013
|
+
console.log(` ADRs: ${td.adrs} (draft: ${td.draft_adrs}, accepted: ${td.accepted_adrs})`);
|
|
14014
|
+
console.log(` Status: ${td.status}`);
|
|
14015
|
+
if (td.candidate_list.length > 0 && td.adrs === 0) {
|
|
14016
|
+
console.log("");
|
|
14017
|
+
console.log("ADR candidates found:");
|
|
14018
|
+
td.candidate_list.forEach((c, i) => {
|
|
14019
|
+
console.log("");
|
|
14020
|
+
console.log(`${i + 1}. ${c.title}`);
|
|
14021
|
+
console.log(` Source: ${c.source}`);
|
|
14022
|
+
console.log(` Suggested ADR: ${c.suggestedAdrFile}`);
|
|
14023
|
+
});
|
|
14024
|
+
console.log("");
|
|
14025
|
+
console.log("Next:");
|
|
14026
|
+
console.log(" Use the adr-writing skill to create ADR drafts from these candidates");
|
|
14027
|
+
console.log(" (copy context + options; leave the decision/consequences as [open] for human review).");
|
|
14028
|
+
} else if (td.candidates === 0) {
|
|
14029
|
+
console.log("");
|
|
14030
|
+
console.log("No decision candidates found. Run the architecture-agent to produce");
|
|
14031
|
+
console.log("`knowledge/tech/decision-candidates.md`, or add ADRs under `knowledge/tech/decisions/`.");
|
|
14032
|
+
} else {
|
|
14033
|
+
console.log("");
|
|
14034
|
+
console.log("ADRs already exist. Review drafts and mark them `accepted` when confirmed.");
|
|
14035
|
+
}
|
|
14036
|
+
printCommandFooter("adr");
|
|
14037
|
+
}
|
|
14038
|
+
|
|
13872
14039
|
// src/index.ts
|
|
13873
14040
|
var require2 = createRequire(import.meta.url);
|
|
13874
14041
|
var { version } = require2("../package.json");
|
|
@@ -13922,6 +14089,9 @@ program.command("drift").description("Drift Trend Report from recorded `kaddo gu
|
|
|
13922
14089
|
var questionsAction = (opts) => runQuestions(opts);
|
|
13923
14090
|
program.command("questions").description("Open-questions readiness gate: blocking/important/deferred decisions before the roadmap").option("--json", "Output JSON instead of a summary").option("--output <path>", "Write the report to a file (e.g. .kaddo/reports/questions-report.md)").action(questionsAction);
|
|
13924
14091
|
program.command("readiness").description("Alias for `kaddo questions`").option("--json", "Output JSON instead of a summary").option("--output <path>", "Write the report to a file").action(questionsAction);
|
|
14092
|
+
program.command("adr").alias("decisions").description("List technical decision candidates and the ADR files to create from them (read-only)").option("--json", "Output JSON").action((opts) => {
|
|
14093
|
+
runAdr(opts);
|
|
14094
|
+
});
|
|
13925
14095
|
var adaptersCmd = program.command("adapters").description("Generate adapters that project Kaddo knowledge for external coding agents");
|
|
13926
14096
|
adaptersCmd.command("install <adapter>").description("Generate an adapter file (codex/opencode/antigravity/kiro \u2192 AGENTS.md, claude \u2192 CLAUDE.md) from Kaddo knowledge").option("--force", "Overwrite an existing output file").option("--inject", "Add or update only the Kaddo block in an existing file, preserving the rest").option("--dry-run", "Print the content without writing files").action((adapter, opts) => {
|
|
13927
14097
|
runAdaptersInstall(adapter, opts);
|