@kaddo/cli 3.39.0 → 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.
Files changed (3) hide show
  1. package/README.md +2 -0
  2. package/dist/index.js +287 -55
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -536,6 +536,8 @@ create --from roadmap → owners → guard → explain`.
536
536
  | v3.37.1 | Unified next-step: one shared resolver (`core/next-step.ts`) powers `context`, `understand` and `explain` (Phase + Readiness) so they never diverge; agent JSON exposes `nextStepRecommendation`. Fixes duplicate `capabilities` line in 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
+ | 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 |
539
541
 
540
542
  **Optional modules (installed with `kaddo add`):**
541
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 containing: context, the decision, alternatives considered, consequences, the code
1550
- paths it governs (\`code:\` globs when known) and the decision status (proposed / accepted /
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
- # ADR-0007 \u2014 Use SQLite for local persistence
1571
- Status: accepted
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
- ## Decision
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
  );
@@ -2213,11 +2236,11 @@ Adapt to \`project.state\` (from \`.kaddo/config.yml\`):
2213
2236
 
2214
2237
  - **new \u2192 Planned Capability Definition.** Define the capabilities the product *should* have. Use
2215
2238
  \`[planned]\` items; evidence is not required yet.
2216
- - **pre-ai \u2192 Existing Capability Discovery.** Document the capabilities the system *already has*, as a
2217
- structured inventory with **evidence**, status and gaps \u2014 a photograph of what exists today, not a
2218
- wishlist.
2219
- - **legacy \u2192 Legacy Capability Discovery.** Same inventory plus **criticality**, **change risk**,
2220
- **operational dependency** and **modernization notes** per capability.
2239
+ - **pre-ai \u2192 Existing Capability Discovery (Domain-Oriented Capability Inventory).** Document the
2240
+ capabilities the system *already has*, **grouped by functional domain**, with **evidence**, status
2241
+ and gaps \u2014 a photograph of what exists today, not a wishlist.
2242
+ - **legacy \u2192 Legacy Capability Discovery.** Same domain-oriented inventory plus **criticality**,
2243
+ **change risk**, **operational dependency** and **modernization notes** per domain/capability.
2221
2244
 
2222
2245
  ## Capability status values
2223
2246
 
@@ -2256,10 +2279,12 @@ Analyze the context pack and identify:
2256
2279
  7. Suggested ownership.
2257
2280
  8. Candidate code globs if evident.
2258
2281
 
2259
- For **pre-ai** and **legacy**, produce the richer inventory (see Output Format): a
2260
- \`## Capability Inventory\` with status + evidence per capability, a \`## Capability Gaps\` section, and
2261
- \`## Roadmap Candidate Signals\` (signals only \u2014 never a formal roadmap). For **legacy**, add
2262
- \`Criticality\`, \`Change risk\`, \`Operational dependency\` and \`Modernization notes\` per capability.
2282
+ For **pre-ai** and **legacy**, produce the domain-oriented inventory (see Output Format): a
2283
+ \`## Capability Domains\` section where each \`### Domain:\` groups capabilities by functional
2284
+ responsibility (with Purpose + Evidence summary), each \`#### Capability:\` has status + evidence, plus
2285
+ \`## Capability Gaps\` and \`## Roadmap Candidate Signals\` (signals only \u2014 never a formal roadmap). Every
2286
+ gap and candidate names its \`Domain\` and \`Related capability\`. For **legacy**, add \`Criticality\`,
2287
+ \`Change risk\`, \`Operational dependency\` and \`Modernization notes\`.
2263
2288
 
2264
2289
  ## Constraints
2265
2290
 
@@ -2312,36 +2337,50 @@ Generated from Kaddo Context Pack.
2312
2337
  ## Suggested Next Step
2313
2338
  \`\`\`
2314
2339
 
2315
- ### Output Format \u2014 pre-ai / legacy (Existing Capability Discovery)
2340
+ ### Output Format \u2014 pre-ai / legacy (Domain-Oriented Capability Inventory)
2341
+
2342
+ Group capabilities by **functional domain**, not by technical folder. Infer domains from the system
2343
+ (e.g. Loyalty, Billing & Subscriptions, Communications, Operations & Automation) \u2014 do not use a rigid
2344
+ universal taxonomy and do not use folders like \`src/components\` or \`src/app/api\` as domains. A single
2345
+ capability may have evidence across layers (frontend hook + API route + table + webhook).
2316
2346
 
2317
2347
  \`\`\`markdown
2318
2348
  # Existing Capabilities
2319
2349
 
2320
- ## Capability Inventory
2350
+ ## Capability Domains
2321
2351
 
2322
- ### <Capability name>
2352
+ ### Domain: <Domain name>
2353
+
2354
+ **Purpose:** <functional responsibility of this domain>
2355
+
2356
+ **Evidence summary:**
2357
+ - \`<path>\` / \`<route>\` / \`<table>\` / \`<function>\`
2358
+ <!-- legacy only: -->
2359
+ **Criticality:** low | medium | high
2360
+ **Change risk:** low | medium | high
2361
+ **Operational dependency:** <...>
2362
+
2363
+ #### Capability: <Capability name>
2323
2364
 
2324
2365
  - Status: implemented | partial | inferred | risky | deprecated | unknown
2325
2366
  - Capability type: business | product | technical | integration | operational
2326
2367
  - User-facing: yes | no | internal
2327
2368
  - Evidence:
2328
- - \`<path/to/file>\`
2329
- - \`<route>\` / \`<table>\` / \`<function>\`
2330
- - Related flows / data / integrations:
2369
+ - \`<path/to/file>\` / \`<route>\` / \`<table>\` / \`<function>\`
2370
+ - Related flows:
2371
+ - Related data:
2372
+ - Related integrations:
2331
2373
  - Current behavior:
2332
2374
  - Known constraints:
2333
2375
  - Risks or uncertainty:
2334
2376
  - Open questions:
2335
2377
  - [open] ...
2336
- <!-- legacy only: -->
2337
- - Criticality: low | medium | high
2338
- - Change risk: low | medium | high
2339
- - Operational dependency:
2340
- - Modernization notes:
2378
+ <!-- legacy only, per capability: Modernization notes -->
2341
2379
 
2342
2380
  ## Capability Gaps
2343
2381
 
2344
2382
  - [gap] <Gap description>
2383
+ - Domain: <Domain name>
2345
2384
  - Related capability: <name>
2346
2385
  - Impact: low | medium | high
2347
2386
  - Possible roadmap candidate: yes | no
@@ -2349,10 +2388,20 @@ Generated from Kaddo Context Pack.
2349
2388
  ## Roadmap Candidate Signals
2350
2389
 
2351
2390
  - [candidate] <Potential roadmap candidate>
2352
- - Based on: partial capability | gap | risk | open question | business goal
2391
+ - Domain: <Domain name>
2353
2392
  - Related capability: <name>
2393
+ - Based on: partial capability | gap | risk | open question | business goal
2354
2394
  \`\`\`
2355
2395
 
2396
+ ### Domain grouping rules
2397
+
2398
+ - Group by **functional responsibility**, never by technical folder.
2399
+ - A capability may span multiple layers \u2014 list all its evidence.
2400
+ - Keep the VS-074 evidence rule: \`implemented\` needs concrete evidence; indirect \u2192 \`inferred\`; none \u2192
2401
+ \`unknown\`. Never invent domains, paths, routes, tables or functions.
2402
+ - Every \`[gap]\` names its \`Domain\` and \`Related capability\`; every \`[candidate]\` names \`Domain\`,
2403
+ \`Related capability\` and \`Based on\`.
2404
+
2356
2405
  ## Where to Save the Result
2357
2406
 
2358
2407
  Save the output as \`knowledge/product/capabilities.md\`.
@@ -2472,7 +2521,8 @@ Use this agent after capabilities and architecture are understood (or at least a
2472
2521
  Provide \`.kaddo/context-pack.md\` as the primary input, and treat
2473
2522
  \`knowledge/product/capabilities.md\` as the **primary source for roadmap candidates** (VS-074).
2474
2523
 
2475
- Derive roadmap candidates from the capability inventory, prioritizing:
2524
+ Read \`capabilities.md\` as a **map of functional domains** (\`## Capability Domains\`). Derive roadmap
2525
+ candidates from the inventory, prioritizing:
2476
2526
 
2477
2527
  - \`partial\` capabilities (finish what exists)
2478
2528
  - \`## Capability Gaps\` (\`[gap]\` items, especially Impact: high)
@@ -2481,6 +2531,15 @@ Derive roadmap candidates from the capability inventory, prioritizing:
2481
2531
  - resolved/assumed/deferred open questions and business goals
2482
2532
  - technical risks and decision candidates
2483
2533
 
2534
+ Each roadmap candidate should reference its \`Domain\` and \`Related capability\`, e.g.:
2535
+
2536
+ \`\`\`md
2537
+ - [candidate] Harden idempotent payment webhook processing.
2538
+ - Domain: Billing & Subscriptions
2539
+ - Related capability: Payment Webhook Processing
2540
+ - Based on: risk
2541
+ \`\`\`
2542
+
2484
2543
  **Do not** build a roadmap from general ideas when \`capabilities.md\` is still a placeholder or weak:
2485
2544
  if capabilities are not yet discovered, recommend running the \`capability-agent\` first.
2486
2545
 
@@ -2895,9 +2954,17 @@ A refined Work Item intended to be saved under the lifecycle workspace:
2895
2954
 
2896
2955
  **Suggested ownership (code globs):**
2897
2956
 
2898
- **Related capability:** <!-- recommended (VS-074): the capability from
2899
- knowledge/product/capabilities.md this Work Item advances, so work traces back to a real capability.
2900
- Add \`related_capability: <name>\` to the front matter when known. -->
2957
+ **Related domain / capability:** <!-- recommended (VS-074/074.1): the functional domain and the
2958
+ capability from knowledge/product/capabilities.md this Work Item advances, so work traces back to the
2959
+ system's functional map. Add \`related_domain: <domain>\` and \`related_capability: <name>\` to the front
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. -->
2901
2968
  \`\`\`
2902
2969
 
2903
2970
  ## Where to Save the Result
@@ -3474,6 +3541,12 @@ architecture, persistence, authentication or the Work Item's scope. Block only o
3474
3541
  mention the relevant assumptions instead of pausing. If any blocking question is still open, pause and
3475
3542
  ask the user to confirm assumptions or resolve them before writing code.
3476
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
+
3477
3550
  ## When to Use
3478
3551
 
3479
3552
  Use this agent after the work-item-agent has produced a clear, traceable Work Item under
@@ -7084,6 +7157,9 @@ function isPlaceholderLine(line) {
7084
7157
  if (/<[^>]+>/.test(t)) return true;
7085
7158
  if (/^[-*]\s+[^:]+:\s*$/.test(t)) return true;
7086
7159
  if (/^[-*]\s+[^:]+:\s*\S.*\s\|\s/.test(t)) return true;
7160
+ if (/^\*\*[^*]+:\*\*\s*$/.test(t)) return true;
7161
+ if (/^\*\*[^*]+:\*\*\s*_.*_$/.test(t)) return true;
7162
+ if (/^\*\*[^*]+:\*\*\s*\S.*\s\|\s/.test(t)) return true;
7087
7163
  return false;
7088
7164
  }
7089
7165
  function isStructuralLine(line, inFrontMatter) {
@@ -7926,7 +8002,7 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
7926
8002
  if (qBusiness !== "useful") return refine("refine-business", "business-agent", B, qBusiness);
7927
8003
  if (qProduct !== "useful" || qCap !== "useful") {
7928
8004
  if (qCap !== "useful") {
7929
- return refine("refine-product", "capability-agent", CAP, qCap, discovery ? "discover and document existing system capabilities in" : "complete");
8005
+ return refine("refine-product", "capability-agent", CAP, qCap, discovery ? "discover and document existing system capabilities grouped by functional domains in" : "complete");
7930
8006
  }
7931
8007
  return refine("refine-product", "capability-agent", P, qProduct);
7932
8008
  }
@@ -8046,6 +8122,76 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
8046
8122
  };
8047
8123
  }
8048
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
+
8049
8195
  // src/core/project-explain.ts
8050
8196
  var ARCH_DIR4 = "knowledge";
8051
8197
  function normalizeTitle(t) {
@@ -8257,7 +8403,8 @@ function buildProjectExplanation(dir) {
8257
8403
  missingKnowledge,
8258
8404
  suggestedNextSteps,
8259
8405
  readiness,
8260
- nextStepRecommendation: readiness.nextStepRecommendation
8406
+ nextStepRecommendation: readiness.nextStepRecommendation,
8407
+ techDecisions: buildTechDecisions(dir)
8261
8408
  };
8262
8409
  }
8263
8410
  function stateLabel(state) {
@@ -8471,6 +8618,16 @@ function renderExplanationHuman(exp) {
8471
8618
  lines.push("### Recommended next step");
8472
8619
  lines.push(r.recommended_next_step.label);
8473
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("");
8474
8631
  return lines.join("\n").trimEnd() + "\n";
8475
8632
  }
8476
8633
  function renderExplanationAgent(exp) {
@@ -8815,6 +8972,10 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
8815
8972
  }
8816
8973
  });
8817
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
+ }
8818
8979
  const unifiedPhase = {
8819
8980
  ...phase,
8820
8981
  nextStep: nextStepRecommendation.label,
@@ -8852,6 +9013,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
8852
9013
  roadmap,
8853
9014
  phase: unifiedPhase,
8854
9015
  nextStepRecommendation,
9016
+ techDecisions,
8855
9017
  deliveryMix,
8856
9018
  external: loadExternalCapsules(dir),
8857
9019
  graph: loadGraphSummary(dir),
@@ -9275,12 +9437,12 @@ function renderUnderstandTerminal(plan) {
9275
9437
 
9276
9438
  // src/core/delivery.ts
9277
9439
  import { parse as parseYaml10 } from "yaml";
9278
- function slugify2(s) {
9440
+ function slugify3(s) {
9279
9441
  return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
9280
9442
  }
9281
9443
  function toActive(a) {
9282
9444
  const id = a.id || a.title || "WI";
9283
- return { id, title: a.title || id, type: a.type, slug: slugify2(a.title || id) };
9445
+ return { id, title: a.title || id, type: a.type, slug: slugify3(a.title || id) };
9284
9446
  }
9285
9447
  function activeWorkItems(dir) {
9286
9448
  return discoverWorkItems(dir).filter((a) => a.lifecycle === "in-progress").map(toActive);
@@ -9386,6 +9548,13 @@ function runUnderstand() {
9386
9548
  for (const s of recSkills) console.log(` - ${s}`);
9387
9549
  }
9388
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
+ }
9389
9558
  if (exp.externalCapsules.length > 0) {
9390
9559
  console.log("");
9391
9560
  console.log("External knowledge:");
@@ -11900,7 +12069,7 @@ var MODULE_TYPES = [
11900
12069
  "data",
11901
12070
  "unknown"
11902
12071
  ];
11903
- function slugify3(name) {
12072
+ function slugify4(name) {
11904
12073
  return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
11905
12074
  }
11906
12075
  function readModulesDescriptor(dir) {
@@ -11920,7 +12089,7 @@ function moduleDir(id) {
11920
12089
  return `knowledge/tech/modules/${id}`;
11921
12090
  }
11922
12091
  function buildModule(input) {
11923
- const id = slugify3(input.name);
12092
+ const id = slugify4(input.name);
11924
12093
  const base = moduleDir(id);
11925
12094
  return {
11926
12095
  id,
@@ -12252,18 +12421,29 @@ _Document the product capabilities that should exist._
12252
12421
  `,
12253
12422
  "pre-ai": `# Existing Capabilities
12254
12423
 
12255
- > Discover the capabilities the system already has (evidence-backed). Use the capability-agent to fill this in \u2014 run \`kaddo add agents\`, then feed it the context pack.
12424
+ > Discover the capabilities the system already has, grouped by **functional domain** (not technical folders), evidence-backed. Use the capability-agent to fill this in \u2014 run \`kaddo add agents\`, then feed it the context pack.
12425
+
12426
+ ## Capability Domains
12427
+
12428
+ ### Domain: <Domain name>
12429
+
12430
+ **Purpose:** _What functional responsibility this domain covers._
12256
12431
 
12257
- ## Capability Inventory
12432
+ **Evidence summary:**
12433
+ - _\`path\`, route, table or function._
12258
12434
 
12259
- ### <Capability name>
12435
+ #### Capability: <Capability name>
12260
12436
 
12261
12437
  - Status: implemented | partial | inferred | risky | deprecated | unknown
12262
12438
  - Capability type: business | product | technical | integration | operational
12263
12439
  - User-facing: yes | no | internal
12264
12440
  - Evidence:
12265
12441
  - _\`path/to/file\`, route, table or function \u2014 or \`pending validation\`._
12266
- - Related flows / data / integrations:
12442
+ - Related flows:
12443
+ - _..._
12444
+ - Related data:
12445
+ - _..._
12446
+ - Related integrations:
12267
12447
  - _..._
12268
12448
  - Current behavior:
12269
12449
  - _..._
@@ -12277,6 +12457,7 @@ _Document the product capabilities that should exist._
12277
12457
  ## Capability Gaps
12278
12458
 
12279
12459
  - [gap] _Gap description._
12460
+ - Domain: _<Domain name>_
12280
12461
  - Related capability: _<name>_
12281
12462
  - Impact: low | medium | high
12282
12463
  - Possible roadmap candidate: yes | no
@@ -12284,24 +12465,32 @@ _Document the product capabilities that should exist._
12284
12465
  ## Roadmap Candidate Signals
12285
12466
 
12286
12467
  - [candidate] _Potential roadmap candidate._
12287
- - Based on: partial capability | gap | risk | open question | business goal
12468
+ - Domain: _<Domain name>_
12288
12469
  - Related capability: _<name>_
12470
+ - Based on: partial capability | gap | risk | open question | business goal
12289
12471
  `,
12290
12472
  legacy: `# Legacy Capabilities
12291
12473
 
12292
- > Discover existing capabilities plus their criticality, change risk and modernization signals. Use the capability-agent to fill this in.
12474
+ > Discover existing capabilities grouped by **functional domain** (not technical folders), plus criticality, change risk and modernization signals. Use the capability-agent to fill this in.
12475
+
12476
+ ## Capability Domains
12293
12477
 
12294
- ## Capability Inventory
12478
+ ### Domain: <Domain name>
12295
12479
 
12296
- ### <Capability name>
12480
+ **Purpose:** _What functional responsibility this domain covers._
12481
+
12482
+ **Criticality:** low | medium | high
12483
+ **Change risk:** low | medium | high
12484
+ **Operational dependency:** _..._
12485
+
12486
+ **Evidence summary:**
12487
+ - _\`path\`, route, table or function._
12488
+
12489
+ #### Capability: <Capability name>
12297
12490
 
12298
12491
  - Status: implemented | partial | inferred | risky | deprecated | unknown
12299
12492
  - Capability type: business | product | technical | integration | operational
12300
12493
  - User-facing: yes | no | internal
12301
- - Criticality: low | medium | high
12302
- - Change risk: low | medium | high
12303
- - Operational dependency:
12304
- - _..._
12305
12494
  - Evidence:
12306
12495
  - _\`path/to/file\`, route, table or function \u2014 or \`pending validation\`._
12307
12496
  - Current behavior:
@@ -12314,6 +12503,7 @@ _Document the product capabilities that should exist._
12314
12503
  ## Capability Gaps
12315
12504
 
12316
12505
  - [gap] _Gap description._
12506
+ - Domain: _<Domain name>_
12317
12507
  - Related capability: _<name>_
12318
12508
  - Impact: low | medium | high
12319
12509
  - Possible roadmap candidate: yes | no
@@ -12321,8 +12511,9 @@ _Document the product capabilities that should exist._
12321
12511
  ## Roadmap Candidate Signals
12322
12512
 
12323
12513
  - [candidate] _Potential modernization or roadmap candidate._
12324
- - Based on: partial capability | gap | risk | open question | business goal
12514
+ - Domain: _<Domain name>_
12325
12515
  - Related capability: _<name>_
12516
+ - Based on: partial capability | gap | risk | open question | business goal
12326
12517
  `
12327
12518
  },
12328
12519
  codebase: {
@@ -13807,6 +13998,44 @@ function runAdaptersStatus(opts = {}) {
13807
13998
  }
13808
13999
  }
13809
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
+
13810
14039
  // src/index.ts
13811
14040
  var require2 = createRequire(import.meta.url);
13812
14041
  var { version } = require2("../package.json");
@@ -13860,6 +14089,9 @@ program.command("drift").description("Drift Trend Report from recorded `kaddo gu
13860
14089
  var questionsAction = (opts) => runQuestions(opts);
13861
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);
13862
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
+ });
13863
14095
  var adaptersCmd = program.command("adapters").description("Generate adapters that project Kaddo knowledge for external coding agents");
13864
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) => {
13865
14097
  runAdaptersInstall(adapter, opts);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/cli",
3
- "version": "3.39.0",
3
+ "version": "3.40.0",
4
4
  "description": "Knowledge Driven Development toolkit",
5
5
  "license": "MIT",
6
6
  "repository": {