@kaddo/cli 3.40.1 → 3.41.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 +1 -0
  2. package/dist/index.js +125 -21
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -539,6 +539,7 @@ create --from roadmap → owners → guard → explain`.
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
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 |
541
541
  | v3.40.1 | ADR slug cleanup + MCP: suggested ADR filenames strip list/heading prefixes (no `ADR-001-1-…`) and normalize acronyms; new read-only MCP resource `kaddo://tech-decisions` sharing `buildTechDecisions` with `kaddo adr` |
542
+ | v3.41 | Tech knowledge structure: `knowledge/tech/discovery/` for architecture-notes/decision-candidates (core vs decisions vs discovery); `kaddo adr` reads discovery-first with legacy fallback; `kaddo tech organize` migrates safely; `explain` shows `## Tech Knowledge` |
542
543
 
543
544
  **Optional modules (installed with `kaddo add`):**
544
545
 
package/dist/index.js CHANGED
@@ -708,6 +708,10 @@ var COMMAND_HELP = {
708
708
  adr: {
709
709
  question: "Which technical decisions still need to become ADRs?",
710
710
  next: "Use the adr-writing skill to draft ADRs, then mark them accepted when confirmed"
711
+ },
712
+ "tech organize": {
713
+ question: "Is knowledge/tech/ cleanly separated (core vs discovery vs decisions)?",
714
+ next: "Discovery notes now live under knowledge/tech/discovery/"
711
715
  }
712
716
  };
713
717
  function commandFooterLines(name) {
@@ -2439,9 +2443,9 @@ Optionally provide: existing diagrams, infra config, README, dependency manifest
2439
2443
 
2440
2444
  Markdown artifacts intended to be saved as:
2441
2445
 
2442
- - \`knowledge/tech/current-state.md\`
2443
- - \`knowledge/tech/architecture-notes.md\`
2444
- - \`knowledge/tech/decision-candidates.md\`
2446
+ - \`knowledge/tech/current-state.md\` (core artifact)
2447
+ - \`knowledge/tech/discovery/architecture-notes.md\` (discovery note)
2448
+ - \`knowledge/tech/discovery/decision-candidates.md\` (discovery input for ADRs)
2445
2449
 
2446
2450
  ## Instructions
2447
2451
 
@@ -2487,10 +2491,12 @@ Generated from Kaddo Context Pack.
2487
2491
 
2488
2492
  ## Where to Save the Result
2489
2493
 
2490
- Save the architecture overview as \`knowledge/tech/current-state.md\`, supporting notes as
2491
- \`knowledge/tech/architecture-notes.md\`, and decision candidates as
2492
- \`knowledge/tech/decision-candidates.md\`. Final ADRs always live under
2493
- \`knowledge/tech/decisions/\` \u2014 never directly in \`knowledge/tech/\`.
2494
+ Save the architecture overview as \`knowledge/tech/current-state.md\` (a **core** artifact). Save
2495
+ supporting notes as \`knowledge/tech/discovery/architecture-notes.md\` and decision candidates as
2496
+ \`knowledge/tech/discovery/decision-candidates.md\` \u2014 **discovery** inputs live under
2497
+ \`knowledge/tech/discovery/\`, not directly in \`knowledge/tech/\` (VS-075.2). Final ADRs always live
2498
+ under \`knowledge/tech/decisions/\`. Kaddo still reads the legacy root locations for backward
2499
+ compatibility, but new output should use \`discovery/\`.
2494
2500
 
2495
2501
  ## Quality Checklist
2496
2502
 
@@ -8123,8 +8129,16 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
8123
8129
  }
8124
8130
 
8125
8131
  // src/core/decisions.ts
8126
- var CANDIDATES_PATH = "knowledge/tech/decision-candidates.md";
8132
+ var CANDIDATES_DISCOVERY = "knowledge/tech/discovery/decision-candidates.md";
8133
+ var CANDIDATES_LEGACY = "knowledge/tech/decision-candidates.md";
8127
8134
  var DECISIONS_DIR = "knowledge/tech/decisions";
8135
+ function resolveCandidatesPath(dir) {
8136
+ const discovery = exists(join(dir, CANDIDATES_DISCOVERY));
8137
+ const legacy = exists(join(dir, CANDIDATES_LEGACY));
8138
+ if (discovery) return { path: CANDIDATES_DISCOVERY, legacy: false, bothExist: legacy };
8139
+ if (legacy) return { path: CANDIDATES_LEGACY, legacy: true, bothExist: false };
8140
+ return { path: null, legacy: false, bothExist: false };
8141
+ }
8128
8142
  function cleanCandidateTitle(title) {
8129
8143
  return title.replace(/^\s*#{1,6}\s+/, "").replace(/^\s*[-*]\s+/, "").replace(/^\s*\(?\d+\)?[.):]\s+/, "").trim();
8130
8144
  }
@@ -8168,11 +8182,11 @@ function countAdrs(dir) {
8168
8182
  return { total, draft, accepted };
8169
8183
  }
8170
8184
  function buildTechDecisions(dir) {
8171
- const candFile = join(dir, CANDIDATES_PATH);
8185
+ const resolved = resolveCandidatesPath(dir);
8172
8186
  let titles = [];
8173
- if (exists(candFile)) {
8187
+ if (resolved.path) {
8174
8188
  try {
8175
- titles = parseDecisionCandidates(readFile(candFile));
8189
+ titles = parseDecisionCandidates(readFile(join(dir, resolved.path)));
8176
8190
  } catch {
8177
8191
  titles = [];
8178
8192
  }
@@ -8180,7 +8194,7 @@ function buildTechDecisions(dir) {
8180
8194
  const { total, draft, accepted } = countAdrs(dir);
8181
8195
  const candidate_list = titles.map((title, i) => {
8182
8196
  const n = String(total + i + 1).padStart(3, "0");
8183
- return { title, source: CANDIDATES_PATH, suggestedAdrFile: `${DECISIONS_DIR}/ADR-${n}-${slugify2(title)}.md` };
8197
+ return { title, source: resolved.path, suggestedAdrFile: `${DECISIONS_DIR}/ADR-${n}-${slugify2(title)}.md` };
8184
8198
  });
8185
8199
  let status;
8186
8200
  if (accepted > 0) status = "accepted-adrs";
@@ -8193,7 +8207,10 @@ function buildTechDecisions(dir) {
8193
8207
  adrs: total,
8194
8208
  draft_adrs: draft,
8195
8209
  accepted_adrs: accepted,
8196
- candidate_list
8210
+ candidate_list,
8211
+ candidates_source: resolved.path,
8212
+ candidates_legacy_location: resolved.legacy,
8213
+ candidates_both_exist: resolved.bothExist
8197
8214
  };
8198
8215
  }
8199
8216
 
@@ -8409,7 +8426,18 @@ function buildProjectExplanation(dir) {
8409
8426
  suggestedNextSteps,
8410
8427
  readiness,
8411
8428
  nextStepRecommendation: readiness.nextStepRecommendation,
8412
- techDecisions: buildTechDecisions(dir)
8429
+ techDecisions: buildTechDecisions(dir),
8430
+ techKnowledge: {
8431
+ core: {
8432
+ currentState: exists(join(dir, "knowledge/tech/current-state.md")),
8433
+ codebase: exists(join(dir, "knowledge/tech/codebase.md"))
8434
+ },
8435
+ discovery: {
8436
+ architectureNotes: exists(join(dir, "knowledge/tech/discovery/architecture-notes.md")) || exists(join(dir, "knowledge/tech/architecture-notes.md")),
8437
+ decisionCandidates: exists(join(dir, "knowledge/tech/discovery/decision-candidates.md")) || exists(join(dir, "knowledge/tech/decision-candidates.md")),
8438
+ legacyLocation: exists(join(dir, "knowledge/tech/architecture-notes.md")) || exists(join(dir, "knowledge/tech/decision-candidates.md"))
8439
+ }
8440
+ }
8413
8441
  };
8414
8442
  }
8415
8443
  function stateLabel(state) {
@@ -8624,15 +8652,26 @@ function renderExplanationHuman(exp) {
8624
8652
  lines.push(r.recommended_next_step.label);
8625
8653
  lines.push("");
8626
8654
  const td = exp.techDecisions;
8627
- lines.push("## Tech Decisions");
8628
- lines.push(`- Decision candidates: ${td.candidates}`);
8629
- lines.push(`- ADRs: ${td.adrs} (draft: ${td.draft_adrs}, accepted: ${td.accepted_adrs})`);
8630
- lines.push(`- Status: ${td.status}`);
8655
+ const tk = exp.techKnowledge;
8656
+ const mark = (b) => b ? "\u2713" : "\u2717";
8657
+ lines.push("## Tech Knowledge");
8658
+ lines.push("Core:");
8659
+ lines.push(`- ${mark(tk.core.currentState)} current-state.md`);
8660
+ lines.push(`- ${mark(tk.core.codebase)} codebase.md`);
8661
+ lines.push("Decisions:");
8662
+ lines.push(`- ADRs: ${td.adrs} (draft: ${td.draft_adrs}, accepted: ${td.accepted_adrs}) \xB7 status: ${td.status}`);
8663
+ lines.push("Discovery:");
8664
+ lines.push(`- ${mark(tk.discovery.architectureNotes)} architecture-notes.md`);
8665
+ lines.push(`- ${mark(tk.discovery.decisionCandidates)} decision-candidates.md`);
8666
+ lines.push("");
8631
8667
  if (td.candidates > 0 && td.adrs === 0) {
8632
- lines.push("");
8633
8668
  lines.push("Use the adr-writing skill to materialize decision candidates into ADRs (`kaddo adr`) before implementing related technical Work Items.");
8669
+ lines.push("");
8670
+ }
8671
+ if (tk.discovery.legacyLocation) {
8672
+ lines.push("Tech discovery files are in the legacy `knowledge/tech/` root. Suggested cleanup: run `kaddo tech organize`.");
8673
+ lines.push("");
8634
8674
  }
8635
- lines.push("");
8636
8675
  return lines.join("\n").trimEnd() + "\n";
8637
8676
  }
8638
8677
  function renderExplanationAgent(exp) {
@@ -8981,6 +9020,22 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
8981
9020
  if (techDecisions.candidates > 0 && techDecisions.adrs === 0) {
8982
9021
  missing.push(`${techDecisions.candidates} technical decision candidate(s) not yet materialized as ADRs (run \`kaddo adr\`).`);
8983
9022
  }
9023
+ const techFile = (rel) => exists(join(dir, ARCH_DIR6, rel));
9024
+ const techKnowledge = {
9025
+ core: {
9026
+ "current-state.md": techFile("tech/current-state.md"),
9027
+ "codebase.md": techFile("tech/codebase.md")
9028
+ },
9029
+ decisions: { adrs: techDecisions.adrs, dir: exists(join(dir, ARCH_DIR6, "tech/decisions")) },
9030
+ discovery: {
9031
+ "architecture-notes.md": techFile("tech/discovery/architecture-notes.md") || techFile("tech/architecture-notes.md"),
9032
+ "decision-candidates.md": techFile("tech/discovery/decision-candidates.md") || techFile("tech/decision-candidates.md"),
9033
+ legacyLocation: techFile("tech/architecture-notes.md") || techFile("tech/decision-candidates.md")
9034
+ }
9035
+ };
9036
+ if (techKnowledge.discovery.legacyLocation) {
9037
+ missing.push("Tech discovery files are in the legacy `knowledge/tech/` root. Run `kaddo tech organize` to move them to `knowledge/tech/discovery/`.");
9038
+ }
8984
9039
  const unifiedPhase = {
8985
9040
  ...phase,
8986
9041
  nextStep: nextStepRecommendation.label,
@@ -9019,6 +9074,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
9019
9074
  phase: unifiedPhase,
9020
9075
  nextStepRecommendation,
9021
9076
  techDecisions,
9077
+ techKnowledge,
9022
9078
  deliveryMix,
9023
9079
  external: loadExternalCapsules(dir),
9024
9080
  graph: loadGraphSummary(dir),
@@ -12725,7 +12781,7 @@ var FILE_TARGETS = [
12725
12781
  { path: "knowledge/tech/current-state.md", kind: "current-state" },
12726
12782
  { path: "knowledge/delivery/roadmap.md", kind: "roadmap" }
12727
12783
  ];
12728
- var DIR_TARGETS = ["knowledge/tech/decisions", "knowledge/delivery/work-items"];
12784
+ var DIR_TARGETS = ["knowledge/tech/decisions", "knowledge/tech/discovery", "knowledge/delivery/work-items"];
12729
12785
  function withLanguageDirective(content, language) {
12730
12786
  if (language !== "es") return content;
12731
12787
  const note = "> Idioma del proyecto: **espa\xF1ol**. Escribe este conocimiento en espa\xF1ol. Mant\xE9n en ingl\xE9s el c\xF3digo, los nombres de archivo, los comandos y las claves de configuraci\xF3n.\n";
@@ -14017,6 +14073,15 @@ function runAdr(opts = {}) {
14017
14073
  console.log(` Decision candidates: ${td.candidates}`);
14018
14074
  console.log(` ADRs: ${td.adrs} (draft: ${td.draft_adrs}, accepted: ${td.accepted_adrs})`);
14019
14075
  console.log(` Status: ${td.status}`);
14076
+ if (td.candidates_source) console.log(` Source: ${td.candidates_source}`);
14077
+ if (td.candidates_both_exist) {
14078
+ console.log("");
14079
+ console.log(" Note: both decision-candidate files exist.");
14080
+ console.log(" Using: knowledge/tech/discovery/decision-candidates.md");
14081
+ console.log(" Legacy file also found: knowledge/tech/decision-candidates.md");
14082
+ } else if (td.candidates_legacy_location) {
14083
+ console.log(" (legacy location \u2014 consider `kaddo tech organize` to move it to knowledge/tech/discovery/)");
14084
+ }
14020
14085
  if (td.candidate_list.length > 0 && td.adrs === 0) {
14021
14086
  console.log("");
14022
14087
  console.log("ADR candidates found:");
@@ -14041,6 +14106,41 @@ function runAdr(opts = {}) {
14041
14106
  printCommandFooter("adr");
14042
14107
  }
14043
14108
 
14109
+ // src/commands/tech.ts
14110
+ import fs3 from "fs";
14111
+ var DISCOVERY_FILES = ["architecture-notes.md", "decision-candidates.md"];
14112
+ var DISCOVERY_DIR = "knowledge/tech/discovery";
14113
+ function runTechOrganize(dir = cwd()) {
14114
+ requireConfig(dir);
14115
+ intro2("kaddo tech organize");
14116
+ const moved = [];
14117
+ const skipped = [];
14118
+ for (const file of DISCOVERY_FILES) {
14119
+ const from = join(dir, "knowledge/tech", file);
14120
+ const to = join(dir, DISCOVERY_DIR, file);
14121
+ if (!exists(from)) continue;
14122
+ if (exists(to)) {
14123
+ skipped.push(file);
14124
+ continue;
14125
+ }
14126
+ ensureDir(join(dir, DISCOVERY_DIR));
14127
+ fs3.renameSync(from, to);
14128
+ moved.push(`knowledge/tech/${file} \u2192 ${DISCOVERY_DIR}/${file}`);
14129
+ }
14130
+ if (moved.length > 0) {
14131
+ log2.success("Moved:");
14132
+ for (const m of moved) console.log(` - ${m}`);
14133
+ }
14134
+ for (const file of skipped) {
14135
+ log2.warn(`Cannot move ${file} because knowledge/tech/discovery/${file} already exists. Review both files manually.`);
14136
+ }
14137
+ if (moved.length === 0 && skipped.length === 0) {
14138
+ log2.info("Nothing to organize \u2014 no discovery files in the legacy `knowledge/tech/` root.");
14139
+ }
14140
+ printCommandFooter("tech organize");
14141
+ outro2("Tech knowledge organized.");
14142
+ }
14143
+
14044
14144
  // src/index.ts
14045
14145
  var require2 = createRequire(import.meta.url);
14046
14146
  var { version } = require2("../package.json");
@@ -14094,6 +14194,10 @@ program.command("drift").description("Drift Trend Report from recorded `kaddo gu
14094
14194
  var questionsAction = (opts) => runQuestions(opts);
14095
14195
  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);
14096
14196
  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);
14197
+ var techCmd = program.command("tech").description("Organize the knowledge/tech/ structure (core vs discovery vs decisions)");
14198
+ techCmd.command("organize").description("Move discovery artifacts into knowledge/tech/discovery/ (never overwrites, no content change)").action(() => {
14199
+ runTechOrganize();
14200
+ });
14097
14201
  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) => {
14098
14202
  runAdr(opts);
14099
14203
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/cli",
3
- "version": "3.40.1",
3
+ "version": "3.41.0",
4
4
  "description": "Knowledge Driven Development toolkit",
5
5
  "license": "MIT",
6
6
  "repository": {