@kaddo/cli 3.40.0 → 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.
- package/README.md +2 -0
- package/dist/index.js +133 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -538,6 +538,8 @@ create --from roadmap → owners → guard → explain`.
|
|
|
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
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
|
+
| 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` |
|
|
541
543
|
|
|
542
544
|
**Optional modules (installed with `kaddo add`):**
|
|
543
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
|
|
2491
|
-
\`knowledge/tech/architecture-notes.md
|
|
2492
|
-
\`knowledge/tech/decision-candidates.md
|
|
2493
|
-
\`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,18 +8129,31 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
8123
8129
|
}
|
|
8124
8130
|
|
|
8125
8131
|
// src/core/decisions.ts
|
|
8126
|
-
var
|
|
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
|
+
}
|
|
8142
|
+
function cleanCandidateTitle(title) {
|
|
8143
|
+
return title.replace(/^\s*#{1,6}\s+/, "").replace(/^\s*[-*]\s+/, "").replace(/^\s*\(?\d+\)?[.):]\s+/, "").trim();
|
|
8144
|
+
}
|
|
8128
8145
|
function slugify2(s) {
|
|
8129
|
-
return s.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0,
|
|
8146
|
+
return cleanCandidateTitle(s).toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 70).replace(/-+$/g, "");
|
|
8130
8147
|
}
|
|
8131
8148
|
function parseDecisionCandidates(md) {
|
|
8132
8149
|
const out = [];
|
|
8133
8150
|
for (const line of md.split(/\r?\n/)) {
|
|
8134
8151
|
const m = line.match(/^##\s+(.+?)\s*$/);
|
|
8135
8152
|
if (m) {
|
|
8136
|
-
const
|
|
8137
|
-
if (
|
|
8153
|
+
const raw = m[1].trim();
|
|
8154
|
+
if (!raw || /^_.*_$/.test(raw)) continue;
|
|
8155
|
+
const t = cleanCandidateTitle(raw);
|
|
8156
|
+
if (t) out.push(t);
|
|
8138
8157
|
}
|
|
8139
8158
|
}
|
|
8140
8159
|
return out;
|
|
@@ -8163,11 +8182,11 @@ function countAdrs(dir) {
|
|
|
8163
8182
|
return { total, draft, accepted };
|
|
8164
8183
|
}
|
|
8165
8184
|
function buildTechDecisions(dir) {
|
|
8166
|
-
const
|
|
8185
|
+
const resolved = resolveCandidatesPath(dir);
|
|
8167
8186
|
let titles = [];
|
|
8168
|
-
if (
|
|
8187
|
+
if (resolved.path) {
|
|
8169
8188
|
try {
|
|
8170
|
-
titles = parseDecisionCandidates(readFile(
|
|
8189
|
+
titles = parseDecisionCandidates(readFile(join(dir, resolved.path)));
|
|
8171
8190
|
} catch {
|
|
8172
8191
|
titles = [];
|
|
8173
8192
|
}
|
|
@@ -8175,7 +8194,7 @@ function buildTechDecisions(dir) {
|
|
|
8175
8194
|
const { total, draft, accepted } = countAdrs(dir);
|
|
8176
8195
|
const candidate_list = titles.map((title, i) => {
|
|
8177
8196
|
const n = String(total + i + 1).padStart(3, "0");
|
|
8178
|
-
return { title, source:
|
|
8197
|
+
return { title, source: resolved.path, suggestedAdrFile: `${DECISIONS_DIR}/ADR-${n}-${slugify2(title)}.md` };
|
|
8179
8198
|
});
|
|
8180
8199
|
let status;
|
|
8181
8200
|
if (accepted > 0) status = "accepted-adrs";
|
|
@@ -8188,7 +8207,10 @@ function buildTechDecisions(dir) {
|
|
|
8188
8207
|
adrs: total,
|
|
8189
8208
|
draft_adrs: draft,
|
|
8190
8209
|
accepted_adrs: accepted,
|
|
8191
|
-
candidate_list
|
|
8210
|
+
candidate_list,
|
|
8211
|
+
candidates_source: resolved.path,
|
|
8212
|
+
candidates_legacy_location: resolved.legacy,
|
|
8213
|
+
candidates_both_exist: resolved.bothExist
|
|
8192
8214
|
};
|
|
8193
8215
|
}
|
|
8194
8216
|
|
|
@@ -8404,7 +8426,18 @@ function buildProjectExplanation(dir) {
|
|
|
8404
8426
|
suggestedNextSteps,
|
|
8405
8427
|
readiness,
|
|
8406
8428
|
nextStepRecommendation: readiness.nextStepRecommendation,
|
|
8407
|
-
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
|
+
}
|
|
8408
8441
|
};
|
|
8409
8442
|
}
|
|
8410
8443
|
function stateLabel(state) {
|
|
@@ -8619,15 +8652,26 @@ function renderExplanationHuman(exp) {
|
|
|
8619
8652
|
lines.push(r.recommended_next_step.label);
|
|
8620
8653
|
lines.push("");
|
|
8621
8654
|
const td = exp.techDecisions;
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
lines.push(
|
|
8625
|
-
lines.push(
|
|
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("");
|
|
8626
8667
|
if (td.candidates > 0 && td.adrs === 0) {
|
|
8627
|
-
lines.push("");
|
|
8628
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("");
|
|
8629
8674
|
}
|
|
8630
|
-
lines.push("");
|
|
8631
8675
|
return lines.join("\n").trimEnd() + "\n";
|
|
8632
8676
|
}
|
|
8633
8677
|
function renderExplanationAgent(exp) {
|
|
@@ -8976,6 +9020,22 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
8976
9020
|
if (techDecisions.candidates > 0 && techDecisions.adrs === 0) {
|
|
8977
9021
|
missing.push(`${techDecisions.candidates} technical decision candidate(s) not yet materialized as ADRs (run \`kaddo adr\`).`);
|
|
8978
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
|
+
}
|
|
8979
9039
|
const unifiedPhase = {
|
|
8980
9040
|
...phase,
|
|
8981
9041
|
nextStep: nextStepRecommendation.label,
|
|
@@ -9014,6 +9074,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
9014
9074
|
phase: unifiedPhase,
|
|
9015
9075
|
nextStepRecommendation,
|
|
9016
9076
|
techDecisions,
|
|
9077
|
+
techKnowledge,
|
|
9017
9078
|
deliveryMix,
|
|
9018
9079
|
external: loadExternalCapsules(dir),
|
|
9019
9080
|
graph: loadGraphSummary(dir),
|
|
@@ -12720,7 +12781,7 @@ var FILE_TARGETS = [
|
|
|
12720
12781
|
{ path: "knowledge/tech/current-state.md", kind: "current-state" },
|
|
12721
12782
|
{ path: "knowledge/delivery/roadmap.md", kind: "roadmap" }
|
|
12722
12783
|
];
|
|
12723
|
-
var DIR_TARGETS = ["knowledge/tech/decisions", "knowledge/delivery/work-items"];
|
|
12784
|
+
var DIR_TARGETS = ["knowledge/tech/decisions", "knowledge/tech/discovery", "knowledge/delivery/work-items"];
|
|
12724
12785
|
function withLanguageDirective(content, language) {
|
|
12725
12786
|
if (language !== "es") return content;
|
|
12726
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";
|
|
@@ -14012,6 +14073,15 @@ function runAdr(opts = {}) {
|
|
|
14012
14073
|
console.log(` Decision candidates: ${td.candidates}`);
|
|
14013
14074
|
console.log(` ADRs: ${td.adrs} (draft: ${td.draft_adrs}, accepted: ${td.accepted_adrs})`);
|
|
14014
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
|
+
}
|
|
14015
14085
|
if (td.candidate_list.length > 0 && td.adrs === 0) {
|
|
14016
14086
|
console.log("");
|
|
14017
14087
|
console.log("ADR candidates found:");
|
|
@@ -14036,6 +14106,41 @@ function runAdr(opts = {}) {
|
|
|
14036
14106
|
printCommandFooter("adr");
|
|
14037
14107
|
}
|
|
14038
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
|
+
|
|
14039
14144
|
// src/index.ts
|
|
14040
14145
|
var require2 = createRequire(import.meta.url);
|
|
14041
14146
|
var { version } = require2("../package.json");
|
|
@@ -14089,6 +14194,10 @@ program.command("drift").description("Drift Trend Report from recorded `kaddo gu
|
|
|
14089
14194
|
var questionsAction = (opts) => runQuestions(opts);
|
|
14090
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);
|
|
14091
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
|
+
});
|
|
14092
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) => {
|
|
14093
14202
|
runAdr(opts);
|
|
14094
14203
|
});
|