@kaddo/cli 3.40.1 → 3.42.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 +356 -39
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -539,6 +539,8 @@ 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` |
|
|
543
|
+
| v3.42 | Agent & skill version metadata: installed agents/skills carry a `version:`; `kaddo agents status` / `kaddo skills status` classify up-to-date/outdated/unknown-version/modified/missing; `agents update` / `skills update` refresh outdated safely (never overwrite edits without `--force`); MCP `kaddo://installed-assets` |
|
|
542
544
|
|
|
543
545
|
**Optional modules (installed with `kaddo add`):**
|
|
544
546
|
|
package/dist/index.js
CHANGED
|
@@ -708,6 +708,18 @@ 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/"
|
|
715
|
+
},
|
|
716
|
+
"agents status": {
|
|
717
|
+
question: "Are the installed agents aligned with the current Kaddo version?",
|
|
718
|
+
next: "Run `kaddo agents update` to refresh outdated agents"
|
|
719
|
+
},
|
|
720
|
+
"skills status": {
|
|
721
|
+
question: "Are the installed skills aligned with the current Kaddo version?",
|
|
722
|
+
next: "Run `kaddo skills update` to refresh outdated skills"
|
|
711
723
|
}
|
|
712
724
|
};
|
|
713
725
|
function commandFooterLines(name) {
|
|
@@ -1511,14 +1523,38 @@ var guardAdvancedModule = {
|
|
|
1511
1523
|
]
|
|
1512
1524
|
};
|
|
1513
1525
|
|
|
1526
|
+
// src/core/version.ts
|
|
1527
|
+
import { fileURLToPath } from "url";
|
|
1528
|
+
import { dirname, join as join2, parse } from "path";
|
|
1529
|
+
import { existsSync, readFileSync } from "fs";
|
|
1530
|
+
function resolveVersion() {
|
|
1531
|
+
try {
|
|
1532
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
1533
|
+
const root = parse(dir).root;
|
|
1534
|
+
for (; ; ) {
|
|
1535
|
+
const pkg = join2(dir, "package.json");
|
|
1536
|
+
if (existsSync(pkg)) {
|
|
1537
|
+
const v = JSON.parse(readFileSync(pkg, "utf-8")).version;
|
|
1538
|
+
if (v) return v;
|
|
1539
|
+
}
|
|
1540
|
+
if (dir === root) break;
|
|
1541
|
+
dir = dirname(dir);
|
|
1542
|
+
}
|
|
1543
|
+
} catch {
|
|
1544
|
+
}
|
|
1545
|
+
return "0.0.0";
|
|
1546
|
+
}
|
|
1547
|
+
var KADDO_VERSION = resolveVersion();
|
|
1548
|
+
|
|
1514
1549
|
// src/skills/skills.ts
|
|
1515
1550
|
function skill(id, title, group, appliesTo, body) {
|
|
1516
1551
|
const frontMatter2 = [
|
|
1517
1552
|
"---",
|
|
1518
1553
|
"type: skill",
|
|
1519
1554
|
`id: ${id}`,
|
|
1555
|
+
`name: ${id}`,
|
|
1520
1556
|
`title: ${title}`,
|
|
1521
|
-
|
|
1557
|
+
`version: ${KADDO_VERSION}`,
|
|
1522
1558
|
`group: ${group}`,
|
|
1523
1559
|
"applies_to:",
|
|
1524
1560
|
...appliesTo.map((a) => ` - ${a}`),
|
|
@@ -2439,9 +2475,9 @@ Optionally provide: existing diagrams, infra config, README, dependency manifest
|
|
|
2439
2475
|
|
|
2440
2476
|
Markdown artifacts intended to be saved as:
|
|
2441
2477
|
|
|
2442
|
-
- \`knowledge/tech/current-state.md\`
|
|
2443
|
-
- \`knowledge/tech/architecture-notes.md\`
|
|
2444
|
-
- \`knowledge/tech/decision-candidates.md\`
|
|
2478
|
+
- \`knowledge/tech/current-state.md\` (core artifact)
|
|
2479
|
+
- \`knowledge/tech/discovery/architecture-notes.md\` (discovery note)
|
|
2480
|
+
- \`knowledge/tech/discovery/decision-candidates.md\` (discovery input for ADRs)
|
|
2445
2481
|
|
|
2446
2482
|
## Instructions
|
|
2447
2483
|
|
|
@@ -2487,10 +2523,12 @@ Generated from Kaddo Context Pack.
|
|
|
2487
2523
|
|
|
2488
2524
|
## Where to Save the Result
|
|
2489
2525
|
|
|
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/
|
|
2526
|
+
Save the architecture overview as \`knowledge/tech/current-state.md\` (a **core** artifact). Save
|
|
2527
|
+
supporting notes as \`knowledge/tech/discovery/architecture-notes.md\` and decision candidates as
|
|
2528
|
+
\`knowledge/tech/discovery/decision-candidates.md\` \u2014 **discovery** inputs live under
|
|
2529
|
+
\`knowledge/tech/discovery/\`, not directly in \`knowledge/tech/\` (VS-075.2). Final ADRs always live
|
|
2530
|
+
under \`knowledge/tech/decisions/\`. Kaddo still reads the legacy root locations for backward
|
|
2531
|
+
compatibility, but new output should use \`discovery/\`.
|
|
2494
2532
|
|
|
2495
2533
|
## Quality Checklist
|
|
2496
2534
|
|
|
@@ -4044,6 +4082,19 @@ function selectAgentFiles(opts) {
|
|
|
4044
4082
|
}
|
|
4045
4083
|
|
|
4046
4084
|
// src/modules/agents.ts
|
|
4085
|
+
function withAgentFrontMatter(fileName, content) {
|
|
4086
|
+
const name = fileName.replace(/\.md$/, "");
|
|
4087
|
+
const fm2 = [
|
|
4088
|
+
"---",
|
|
4089
|
+
"type: agent",
|
|
4090
|
+
`name: ${name}`,
|
|
4091
|
+
`version: ${KADDO_VERSION}`,
|
|
4092
|
+
`group: ${agentGroupOf(fileName)}`,
|
|
4093
|
+
"---",
|
|
4094
|
+
""
|
|
4095
|
+
].join("\n");
|
|
4096
|
+
return fm2 + content.replace(/^\s+/, "");
|
|
4097
|
+
}
|
|
4047
4098
|
var agentReadme = {
|
|
4048
4099
|
path: "knowledge/agents/README.md",
|
|
4049
4100
|
content: [
|
|
@@ -4115,7 +4166,7 @@ var agentReadme = {
|
|
|
4115
4166
|
};
|
|
4116
4167
|
var agentFiles = AGENT_PROMPTS.map((a) => ({
|
|
4117
4168
|
path: agentInstallPath(a.fileName),
|
|
4118
|
-
content: a.content
|
|
4169
|
+
content: withAgentFrontMatter(a.fileName, a.content)
|
|
4119
4170
|
}));
|
|
4120
4171
|
var agentsModule = {
|
|
4121
4172
|
name: "agents",
|
|
@@ -6196,7 +6247,7 @@ function runIgnoreRemove(artifactId) {
|
|
|
6196
6247
|
}
|
|
6197
6248
|
|
|
6198
6249
|
// src/commands/explain.ts
|
|
6199
|
-
import
|
|
6250
|
+
import matter5 from "gray-matter";
|
|
6200
6251
|
import { parse as parseYaml9 } from "yaml";
|
|
6201
6252
|
|
|
6202
6253
|
// src/core/delivery-phase.ts
|
|
@@ -8123,8 +8174,16 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
8123
8174
|
}
|
|
8124
8175
|
|
|
8125
8176
|
// src/core/decisions.ts
|
|
8126
|
-
var
|
|
8177
|
+
var CANDIDATES_DISCOVERY = "knowledge/tech/discovery/decision-candidates.md";
|
|
8178
|
+
var CANDIDATES_LEGACY = "knowledge/tech/decision-candidates.md";
|
|
8127
8179
|
var DECISIONS_DIR = "knowledge/tech/decisions";
|
|
8180
|
+
function resolveCandidatesPath(dir) {
|
|
8181
|
+
const discovery = exists(join(dir, CANDIDATES_DISCOVERY));
|
|
8182
|
+
const legacy = exists(join(dir, CANDIDATES_LEGACY));
|
|
8183
|
+
if (discovery) return { path: CANDIDATES_DISCOVERY, legacy: false, bothExist: legacy };
|
|
8184
|
+
if (legacy) return { path: CANDIDATES_LEGACY, legacy: true, bothExist: false };
|
|
8185
|
+
return { path: null, legacy: false, bothExist: false };
|
|
8186
|
+
}
|
|
8128
8187
|
function cleanCandidateTitle(title) {
|
|
8129
8188
|
return title.replace(/^\s*#{1,6}\s+/, "").replace(/^\s*[-*]\s+/, "").replace(/^\s*\(?\d+\)?[.):]\s+/, "").trim();
|
|
8130
8189
|
}
|
|
@@ -8168,11 +8227,11 @@ function countAdrs(dir) {
|
|
|
8168
8227
|
return { total, draft, accepted };
|
|
8169
8228
|
}
|
|
8170
8229
|
function buildTechDecisions(dir) {
|
|
8171
|
-
const
|
|
8230
|
+
const resolved = resolveCandidatesPath(dir);
|
|
8172
8231
|
let titles = [];
|
|
8173
|
-
if (
|
|
8232
|
+
if (resolved.path) {
|
|
8174
8233
|
try {
|
|
8175
|
-
titles = parseDecisionCandidates(readFile(
|
|
8234
|
+
titles = parseDecisionCandidates(readFile(join(dir, resolved.path)));
|
|
8176
8235
|
} catch {
|
|
8177
8236
|
titles = [];
|
|
8178
8237
|
}
|
|
@@ -8180,7 +8239,7 @@ function buildTechDecisions(dir) {
|
|
|
8180
8239
|
const { total, draft, accepted } = countAdrs(dir);
|
|
8181
8240
|
const candidate_list = titles.map((title, i) => {
|
|
8182
8241
|
const n = String(total + i + 1).padStart(3, "0");
|
|
8183
|
-
return { title, source:
|
|
8242
|
+
return { title, source: resolved.path, suggestedAdrFile: `${DECISIONS_DIR}/ADR-${n}-${slugify2(title)}.md` };
|
|
8184
8243
|
});
|
|
8185
8244
|
let status;
|
|
8186
8245
|
if (accepted > 0) status = "accepted-adrs";
|
|
@@ -8193,10 +8252,79 @@ function buildTechDecisions(dir) {
|
|
|
8193
8252
|
adrs: total,
|
|
8194
8253
|
draft_adrs: draft,
|
|
8195
8254
|
accepted_adrs: accepted,
|
|
8196
|
-
candidate_list
|
|
8255
|
+
candidate_list,
|
|
8256
|
+
candidates_source: resolved.path,
|
|
8257
|
+
candidates_legacy_location: resolved.legacy,
|
|
8258
|
+
candidates_both_exist: resolved.bothExist
|
|
8197
8259
|
};
|
|
8198
8260
|
}
|
|
8199
8261
|
|
|
8262
|
+
// src/core/assets.ts
|
|
8263
|
+
import matter4 from "gray-matter";
|
|
8264
|
+
function canonicalAgents() {
|
|
8265
|
+
return AGENT_PROMPTS.map((a) => ({
|
|
8266
|
+
name: a.fileName.replace(/\.md$/, ""),
|
|
8267
|
+
path: agentInstallPath(a.fileName),
|
|
8268
|
+
content: withAgentFrontMatter(a.fileName, a.content)
|
|
8269
|
+
}));
|
|
8270
|
+
}
|
|
8271
|
+
function canonicalSkills() {
|
|
8272
|
+
return SKILLS.map((s) => ({ name: s.id, path: skillInstallPath(s.id), content: s.content }));
|
|
8273
|
+
}
|
|
8274
|
+
function versionOf(content) {
|
|
8275
|
+
try {
|
|
8276
|
+
const v = matter4(content).data.version;
|
|
8277
|
+
return v === void 0 || v === null ? null : String(v);
|
|
8278
|
+
} catch {
|
|
8279
|
+
return null;
|
|
8280
|
+
}
|
|
8281
|
+
}
|
|
8282
|
+
function cmpVersion(a, b) {
|
|
8283
|
+
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
8284
|
+
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
8285
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
8286
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
8287
|
+
if (d !== 0) return d < 0 ? -1 : 1;
|
|
8288
|
+
}
|
|
8289
|
+
return 0;
|
|
8290
|
+
}
|
|
8291
|
+
function classify(dir, asset) {
|
|
8292
|
+
const full = join(dir, asset.path);
|
|
8293
|
+
const base = { name: asset.name, path: asset.path, available: KADDO_VERSION };
|
|
8294
|
+
if (!exists(full)) return { ...base, installed: null, state: "missing" };
|
|
8295
|
+
let content = "";
|
|
8296
|
+
try {
|
|
8297
|
+
content = readFile(full);
|
|
8298
|
+
} catch {
|
|
8299
|
+
return { ...base, installed: null, state: "missing" };
|
|
8300
|
+
}
|
|
8301
|
+
const installed = versionOf(content);
|
|
8302
|
+
if (installed === null) return { ...base, installed: null, state: "unknown-version" };
|
|
8303
|
+
const cmp = cmpVersion(installed, KADDO_VERSION);
|
|
8304
|
+
if (cmp < 0) return { ...base, installed, state: "outdated" };
|
|
8305
|
+
if (content.trim() !== asset.content.trim()) return { ...base, installed, state: "modified" };
|
|
8306
|
+
return { ...base, installed, state: "up-to-date" };
|
|
8307
|
+
}
|
|
8308
|
+
function summarize(items) {
|
|
8309
|
+
const count = (s) => items.filter((i) => i.state === s).length;
|
|
8310
|
+
return {
|
|
8311
|
+
total: items.length,
|
|
8312
|
+
up_to_date: count("up-to-date"),
|
|
8313
|
+
outdated: count("outdated"),
|
|
8314
|
+
missing: count("missing"),
|
|
8315
|
+
unknown_version: count("unknown-version"),
|
|
8316
|
+
modified: count("modified"),
|
|
8317
|
+
items
|
|
8318
|
+
};
|
|
8319
|
+
}
|
|
8320
|
+
function assetStatus(dir, kind) {
|
|
8321
|
+
const catalog = kind === "agent" ? canonicalAgents() : canonicalSkills();
|
|
8322
|
+
return summarize(catalog.map((a) => classify(dir, a)));
|
|
8323
|
+
}
|
|
8324
|
+
function installedAssetsSummary(dir) {
|
|
8325
|
+
return { version: KADDO_VERSION, agents: assetStatus(dir, "agent"), skills: assetStatus(dir, "skill") };
|
|
8326
|
+
}
|
|
8327
|
+
|
|
8200
8328
|
// src/core/project-explain.ts
|
|
8201
8329
|
var ARCH_DIR4 = "knowledge";
|
|
8202
8330
|
function normalizeTitle(t) {
|
|
@@ -8409,7 +8537,19 @@ function buildProjectExplanation(dir) {
|
|
|
8409
8537
|
suggestedNextSteps,
|
|
8410
8538
|
readiness,
|
|
8411
8539
|
nextStepRecommendation: readiness.nextStepRecommendation,
|
|
8412
|
-
techDecisions: buildTechDecisions(dir)
|
|
8540
|
+
techDecisions: buildTechDecisions(dir),
|
|
8541
|
+
techKnowledge: {
|
|
8542
|
+
core: {
|
|
8543
|
+
currentState: exists(join(dir, "knowledge/tech/current-state.md")),
|
|
8544
|
+
codebase: exists(join(dir, "knowledge/tech/codebase.md"))
|
|
8545
|
+
},
|
|
8546
|
+
discovery: {
|
|
8547
|
+
architectureNotes: exists(join(dir, "knowledge/tech/discovery/architecture-notes.md")) || exists(join(dir, "knowledge/tech/architecture-notes.md")),
|
|
8548
|
+
decisionCandidates: exists(join(dir, "knowledge/tech/discovery/decision-candidates.md")) || exists(join(dir, "knowledge/tech/decision-candidates.md")),
|
|
8549
|
+
legacyLocation: exists(join(dir, "knowledge/tech/architecture-notes.md")) || exists(join(dir, "knowledge/tech/decision-candidates.md"))
|
|
8550
|
+
}
|
|
8551
|
+
},
|
|
8552
|
+
installedAssets: installedAssetsSummary(dir)
|
|
8413
8553
|
};
|
|
8414
8554
|
}
|
|
8415
8555
|
function stateLabel(state) {
|
|
@@ -8624,15 +8764,41 @@ function renderExplanationHuman(exp) {
|
|
|
8624
8764
|
lines.push(r.recommended_next_step.label);
|
|
8625
8765
|
lines.push("");
|
|
8626
8766
|
const td = exp.techDecisions;
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
lines.push(
|
|
8630
|
-
lines.push(
|
|
8767
|
+
const tk = exp.techKnowledge;
|
|
8768
|
+
const mark = (b) => b ? "\u2713" : "\u2717";
|
|
8769
|
+
lines.push("## Tech Knowledge");
|
|
8770
|
+
lines.push("Core:");
|
|
8771
|
+
lines.push(`- ${mark(tk.core.currentState)} current-state.md`);
|
|
8772
|
+
lines.push(`- ${mark(tk.core.codebase)} codebase.md`);
|
|
8773
|
+
lines.push("Decisions:");
|
|
8774
|
+
lines.push(`- ADRs: ${td.adrs} (draft: ${td.draft_adrs}, accepted: ${td.accepted_adrs}) \xB7 status: ${td.status}`);
|
|
8775
|
+
lines.push("Discovery:");
|
|
8776
|
+
lines.push(`- ${mark(tk.discovery.architectureNotes)} architecture-notes.md`);
|
|
8777
|
+
lines.push(`- ${mark(tk.discovery.decisionCandidates)} decision-candidates.md`);
|
|
8778
|
+
lines.push("");
|
|
8631
8779
|
if (td.candidates > 0 && td.adrs === 0) {
|
|
8632
|
-
lines.push("");
|
|
8633
8780
|
lines.push("Use the adr-writing skill to materialize decision candidates into ADRs (`kaddo adr`) before implementing related technical Work Items.");
|
|
8781
|
+
lines.push("");
|
|
8782
|
+
}
|
|
8783
|
+
if (tk.discovery.legacyLocation) {
|
|
8784
|
+
lines.push("Tech discovery files are in the legacy `knowledge/tech/` root. Suggested cleanup: run `kaddo tech organize`.");
|
|
8785
|
+
lines.push("");
|
|
8786
|
+
}
|
|
8787
|
+
const ia = exp.installedAssets;
|
|
8788
|
+
const agentsInstalled = ia.agents.total - ia.agents.missing;
|
|
8789
|
+
const skillsInstalled = ia.skills.total - ia.skills.missing;
|
|
8790
|
+
if (agentsInstalled > 0 || skillsInstalled > 0) {
|
|
8791
|
+
const issues = (s2) => [s2.outdated ? `${s2.outdated} outdated` : "", s2.unknown_version ? `${s2.unknown_version} unknown-version` : "", s2.modified ? `${s2.modified} modified` : ""].filter(Boolean).join(", ");
|
|
8792
|
+
lines.push("## Installed Assets");
|
|
8793
|
+
lines.push(`- CLI version: ${ia.version}`);
|
|
8794
|
+
lines.push(`- Agents: ${agentsInstalled} installed${issues(ia.agents) ? ` (${issues(ia.agents)})` : ""}`);
|
|
8795
|
+
lines.push(`- Skills: ${skillsInstalled} installed${issues(ia.skills) ? ` (${issues(ia.skills)})` : ""}`);
|
|
8796
|
+
if (ia.agents.outdated + ia.agents.unknown_version + ia.skills.outdated + ia.skills.unknown_version > 0) {
|
|
8797
|
+
lines.push("");
|
|
8798
|
+
lines.push("Suggested: run `kaddo agents status` and `kaddo skills status`.");
|
|
8799
|
+
}
|
|
8800
|
+
lines.push("");
|
|
8634
8801
|
}
|
|
8635
|
-
lines.push("");
|
|
8636
8802
|
return lines.join("\n").trimEnd() + "\n";
|
|
8637
8803
|
}
|
|
8638
8804
|
function renderExplanationAgent(exp) {
|
|
@@ -8648,7 +8814,7 @@ function readKnowledge(dir) {
|
|
|
8648
8814
|
if (!exists(knowledgePath)) return null;
|
|
8649
8815
|
try {
|
|
8650
8816
|
const raw = readFile(knowledgePath);
|
|
8651
|
-
const { data, content } =
|
|
8817
|
+
const { data, content } = matter5(raw);
|
|
8652
8818
|
return { content, data };
|
|
8653
8819
|
} catch {
|
|
8654
8820
|
return null;
|
|
@@ -8659,7 +8825,7 @@ function readRoadmap(dir) {
|
|
|
8659
8825
|
if (!exists(roadmapPath)) return null;
|
|
8660
8826
|
try {
|
|
8661
8827
|
const raw = readFile(roadmapPath);
|
|
8662
|
-
const { content } =
|
|
8828
|
+
const { content } = matter5(raw);
|
|
8663
8829
|
return content.trim();
|
|
8664
8830
|
} catch {
|
|
8665
8831
|
return null;
|
|
@@ -8828,7 +8994,7 @@ function runExplain(opts) {
|
|
|
8828
8994
|
}
|
|
8829
8995
|
|
|
8830
8996
|
// src/core/context-pack.ts
|
|
8831
|
-
import
|
|
8997
|
+
import matter6 from "gray-matter";
|
|
8832
8998
|
var CONTEXT_PACK_VERSION = "1";
|
|
8833
8999
|
var ARCH_DIR6 = "knowledge";
|
|
8834
9000
|
function readScanJson(dir) {
|
|
@@ -8841,7 +9007,7 @@ function readScanJson(dir) {
|
|
|
8841
9007
|
}
|
|
8842
9008
|
}
|
|
8843
9009
|
function firstParagraph2(markdown) {
|
|
8844
|
-
const body =
|
|
9010
|
+
const body = matter6(markdown).content.trim();
|
|
8845
9011
|
const para = body.split("\n\n").map((p2) => p2.trim()).find((p2) => p2 && !p2.startsWith("#"));
|
|
8846
9012
|
return para ?? "";
|
|
8847
9013
|
}
|
|
@@ -8981,6 +9147,22 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
8981
9147
|
if (techDecisions.candidates > 0 && techDecisions.adrs === 0) {
|
|
8982
9148
|
missing.push(`${techDecisions.candidates} technical decision candidate(s) not yet materialized as ADRs (run \`kaddo adr\`).`);
|
|
8983
9149
|
}
|
|
9150
|
+
const techFile = (rel) => exists(join(dir, ARCH_DIR6, rel));
|
|
9151
|
+
const techKnowledge = {
|
|
9152
|
+
core: {
|
|
9153
|
+
"current-state.md": techFile("tech/current-state.md"),
|
|
9154
|
+
"codebase.md": techFile("tech/codebase.md")
|
|
9155
|
+
},
|
|
9156
|
+
decisions: { adrs: techDecisions.adrs, dir: exists(join(dir, ARCH_DIR6, "tech/decisions")) },
|
|
9157
|
+
discovery: {
|
|
9158
|
+
"architecture-notes.md": techFile("tech/discovery/architecture-notes.md") || techFile("tech/architecture-notes.md"),
|
|
9159
|
+
"decision-candidates.md": techFile("tech/discovery/decision-candidates.md") || techFile("tech/decision-candidates.md"),
|
|
9160
|
+
legacyLocation: techFile("tech/architecture-notes.md") || techFile("tech/decision-candidates.md")
|
|
9161
|
+
}
|
|
9162
|
+
};
|
|
9163
|
+
if (techKnowledge.discovery.legacyLocation) {
|
|
9164
|
+
missing.push("Tech discovery files are in the legacy `knowledge/tech/` root. Run `kaddo tech organize` to move them to `knowledge/tech/discovery/`.");
|
|
9165
|
+
}
|
|
8984
9166
|
const unifiedPhase = {
|
|
8985
9167
|
...phase,
|
|
8986
9168
|
nextStep: nextStepRecommendation.label,
|
|
@@ -9019,6 +9201,12 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
9019
9201
|
phase: unifiedPhase,
|
|
9020
9202
|
nextStepRecommendation,
|
|
9021
9203
|
techDecisions,
|
|
9204
|
+
techKnowledge,
|
|
9205
|
+
installedAssets: (() => {
|
|
9206
|
+
const s = installedAssetsSummary(dir);
|
|
9207
|
+
const compact = (a) => ({ total: a.total, installed: a.total - a.missing, outdated: a.outdated, unknown_version: a.unknown_version, modified: a.modified });
|
|
9208
|
+
return { version: s.version, agents: compact(s.agents), skills: compact(s.skills) };
|
|
9209
|
+
})(),
|
|
9022
9210
|
deliveryMix,
|
|
9023
9211
|
external: loadExternalCapsules(dir),
|
|
9024
9212
|
graph: loadGraphSummary(dir),
|
|
@@ -9560,6 +9748,17 @@ function runUnderstand() {
|
|
|
9560
9748
|
console.log(" \u2192 Use the adr-writing skill to create ADR drafts from `knowledge/tech/decision-candidates.md`");
|
|
9561
9749
|
console.log(" into `knowledge/tech/decisions/` before implementing affected technical Work Items (`kaddo adr`).");
|
|
9562
9750
|
}
|
|
9751
|
+
const ia = exp.installedAssets;
|
|
9752
|
+
const outdatedRecommended = ia.agents.items.filter(
|
|
9753
|
+
(a) => (a.state === "outdated" || a.state === "unknown-version") && assessment.recommendedAgents.includes(a.name)
|
|
9754
|
+
);
|
|
9755
|
+
if (outdatedRecommended.length > 0) {
|
|
9756
|
+
console.log("");
|
|
9757
|
+
for (const a of outdatedRecommended) {
|
|
9758
|
+
console.log(`Recommended agent ${a.name} is ${a.state} (installed ${a.installed ?? "unknown"}, available ${a.available}).`);
|
|
9759
|
+
}
|
|
9760
|
+
console.log(" \u2192 Run `kaddo agents update` or review `kaddo agents status`.");
|
|
9761
|
+
}
|
|
9563
9762
|
if (exp.externalCapsules.length > 0) {
|
|
9564
9763
|
console.log("");
|
|
9565
9764
|
console.log("External knowledge:");
|
|
@@ -9720,7 +9919,7 @@ var DECLARED_LEVEL = {
|
|
|
9720
9919
|
incident: "K2",
|
|
9721
9920
|
rfc: "K3"
|
|
9722
9921
|
};
|
|
9723
|
-
function
|
|
9922
|
+
function classify2(declaredType, declaredLevel, touchedFiles) {
|
|
9724
9923
|
const signals = detectSignals(touchedFiles);
|
|
9725
9924
|
const observedLevel = highestLevel(signals);
|
|
9726
9925
|
const expectedLevel = DECLARED_LEVEL[declaredType] ?? declaredLevel ?? "K2";
|
|
@@ -9800,7 +9999,7 @@ async function runClassify(opts = {}) {
|
|
|
9800
9999
|
return;
|
|
9801
10000
|
}
|
|
9802
10001
|
if (opts.type) {
|
|
9803
|
-
const result2 =
|
|
10002
|
+
const result2 = classify2(opts.type, opts.level ?? "", touchedFiles);
|
|
9804
10003
|
printResult2(result2);
|
|
9805
10004
|
return;
|
|
9806
10005
|
}
|
|
@@ -9811,7 +10010,7 @@ async function runClassify(opts = {}) {
|
|
|
9811
10010
|
console.log("Or create one with: kaddo create <type>");
|
|
9812
10011
|
return;
|
|
9813
10012
|
}
|
|
9814
|
-
const result =
|
|
10013
|
+
const result = classify2(activeItem.type, activeItem.knowledgeLevel, touchedFiles);
|
|
9815
10014
|
printResult2(result, activeItem.id || activeItem.title);
|
|
9816
10015
|
}
|
|
9817
10016
|
|
|
@@ -9880,7 +10079,7 @@ function runStatus() {
|
|
|
9880
10079
|
}
|
|
9881
10080
|
|
|
9882
10081
|
// src/commands/learn.ts
|
|
9883
|
-
import
|
|
10082
|
+
import matter7 from "gray-matter";
|
|
9884
10083
|
var ARCH_DIR9 = "knowledge";
|
|
9885
10084
|
var WORK_ITEMS_DIR2 = "knowledge/delivery/work-items";
|
|
9886
10085
|
function findWorkItemFile(dir, id) {
|
|
@@ -9892,7 +10091,7 @@ function findWorkItemFile(dir, id) {
|
|
|
9892
10091
|
}
|
|
9893
10092
|
function updateWorkItemFile(filePath, learning) {
|
|
9894
10093
|
const raw = readFile(filePath);
|
|
9895
|
-
const { data, content } =
|
|
10094
|
+
const { data, content } = matter7(raw);
|
|
9896
10095
|
data.status = "done";
|
|
9897
10096
|
data.completed_at = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
9898
10097
|
let updatedContent = content;
|
|
@@ -9917,7 +10116,7 @@ ${learning.trim()}
|
|
|
9917
10116
|
${learning.trim()}
|
|
9918
10117
|
`;
|
|
9919
10118
|
}
|
|
9920
|
-
const newRaw =
|
|
10119
|
+
const newRaw = matter7.stringify(updatedContent, data);
|
|
9921
10120
|
writeFile(filePath, newRaw);
|
|
9922
10121
|
}
|
|
9923
10122
|
async function runLearn(artifactId) {
|
|
@@ -10167,7 +10366,7 @@ function runAdd(moduleName, opts = {}, dir = cwd()) {
|
|
|
10167
10366
|
}
|
|
10168
10367
|
|
|
10169
10368
|
// src/core/ownership-suggest.ts
|
|
10170
|
-
import
|
|
10369
|
+
import matter8 from "gray-matter";
|
|
10171
10370
|
var SCAN_PATH = ".kaddo/scan.json";
|
|
10172
10371
|
function normalizeGlob(input) {
|
|
10173
10372
|
let g = input.trim().replace(/\\/g, "/");
|
|
@@ -10289,11 +10488,11 @@ function suggestGlobs(artifact, signals) {
|
|
|
10289
10488
|
return [...new Set(out)];
|
|
10290
10489
|
}
|
|
10291
10490
|
function applyOwnership(raw, globs, mode = "replace") {
|
|
10292
|
-
const parsed =
|
|
10491
|
+
const parsed = matter8(raw);
|
|
10293
10492
|
const existing = toStringArray3(parsed.data.code);
|
|
10294
10493
|
const next = mode === "append" ? [.../* @__PURE__ */ new Set([...existing, ...globs])] : [...new Set(globs)];
|
|
10295
10494
|
const data = { ...parsed.data, code: next };
|
|
10296
|
-
return
|
|
10495
|
+
return matter8.stringify(parsed.content, data);
|
|
10297
10496
|
}
|
|
10298
10497
|
|
|
10299
10498
|
// src/commands/owners.ts
|
|
@@ -12725,7 +12924,7 @@ var FILE_TARGETS = [
|
|
|
12725
12924
|
{ path: "knowledge/tech/current-state.md", kind: "current-state" },
|
|
12726
12925
|
{ path: "knowledge/delivery/roadmap.md", kind: "roadmap" }
|
|
12727
12926
|
];
|
|
12728
|
-
var DIR_TARGETS = ["knowledge/tech/decisions", "knowledge/delivery/work-items"];
|
|
12927
|
+
var DIR_TARGETS = ["knowledge/tech/decisions", "knowledge/tech/discovery", "knowledge/delivery/work-items"];
|
|
12729
12928
|
function withLanguageDirective(content, language) {
|
|
12730
12929
|
if (language !== "es") return content;
|
|
12731
12930
|
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";
|
|
@@ -12905,7 +13104,7 @@ function runGraphExport(opts = {}) {
|
|
|
12905
13104
|
}
|
|
12906
13105
|
|
|
12907
13106
|
// src/core/impact-report.ts
|
|
12908
|
-
import
|
|
13107
|
+
import matter9 from "gray-matter";
|
|
12909
13108
|
function isBroadGlob(glob) {
|
|
12910
13109
|
if (!glob.endsWith("/**")) return false;
|
|
12911
13110
|
const prefix = glob.slice(0, -3);
|
|
@@ -12974,7 +13173,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
12974
13173
|
}
|
|
12975
13174
|
}
|
|
12976
13175
|
try {
|
|
12977
|
-
const body =
|
|
13176
|
+
const body = matter9(readFile(wi.filePath)).content;
|
|
12978
13177
|
if (hasSection(body, /acceptance|criterios de aceptaci/i)) withAcceptance++;
|
|
12979
13178
|
else gaps.missing_acceptance_criteria.push(gapItem(wi, "Add an `## Acceptance Criteria` section."));
|
|
12980
13179
|
if (hasSection(body, /definition of done|^#{1,6}\s*dod\b|definici[oó]n de (terminado|hecho)/i)) withDoD++;
|
|
@@ -14017,6 +14216,15 @@ function runAdr(opts = {}) {
|
|
|
14017
14216
|
console.log(` Decision candidates: ${td.candidates}`);
|
|
14018
14217
|
console.log(` ADRs: ${td.adrs} (draft: ${td.draft_adrs}, accepted: ${td.accepted_adrs})`);
|
|
14019
14218
|
console.log(` Status: ${td.status}`);
|
|
14219
|
+
if (td.candidates_source) console.log(` Source: ${td.candidates_source}`);
|
|
14220
|
+
if (td.candidates_both_exist) {
|
|
14221
|
+
console.log("");
|
|
14222
|
+
console.log(" Note: both decision-candidate files exist.");
|
|
14223
|
+
console.log(" Using: knowledge/tech/discovery/decision-candidates.md");
|
|
14224
|
+
console.log(" Legacy file also found: knowledge/tech/decision-candidates.md");
|
|
14225
|
+
} else if (td.candidates_legacy_location) {
|
|
14226
|
+
console.log(" (legacy location \u2014 consider `kaddo tech organize` to move it to knowledge/tech/discovery/)");
|
|
14227
|
+
}
|
|
14020
14228
|
if (td.candidate_list.length > 0 && td.adrs === 0) {
|
|
14021
14229
|
console.log("");
|
|
14022
14230
|
console.log("ADR candidates found:");
|
|
@@ -14041,6 +14249,105 @@ function runAdr(opts = {}) {
|
|
|
14041
14249
|
printCommandFooter("adr");
|
|
14042
14250
|
}
|
|
14043
14251
|
|
|
14252
|
+
// src/commands/tech.ts
|
|
14253
|
+
import fs3 from "fs";
|
|
14254
|
+
var DISCOVERY_FILES = ["architecture-notes.md", "decision-candidates.md"];
|
|
14255
|
+
var DISCOVERY_DIR = "knowledge/tech/discovery";
|
|
14256
|
+
function runTechOrganize(dir = cwd()) {
|
|
14257
|
+
requireConfig(dir);
|
|
14258
|
+
intro2("kaddo tech organize");
|
|
14259
|
+
const moved = [];
|
|
14260
|
+
const skipped = [];
|
|
14261
|
+
for (const file of DISCOVERY_FILES) {
|
|
14262
|
+
const from = join(dir, "knowledge/tech", file);
|
|
14263
|
+
const to = join(dir, DISCOVERY_DIR, file);
|
|
14264
|
+
if (!exists(from)) continue;
|
|
14265
|
+
if (exists(to)) {
|
|
14266
|
+
skipped.push(file);
|
|
14267
|
+
continue;
|
|
14268
|
+
}
|
|
14269
|
+
ensureDir(join(dir, DISCOVERY_DIR));
|
|
14270
|
+
fs3.renameSync(from, to);
|
|
14271
|
+
moved.push(`knowledge/tech/${file} \u2192 ${DISCOVERY_DIR}/${file}`);
|
|
14272
|
+
}
|
|
14273
|
+
if (moved.length > 0) {
|
|
14274
|
+
log2.success("Moved:");
|
|
14275
|
+
for (const m of moved) console.log(` - ${m}`);
|
|
14276
|
+
}
|
|
14277
|
+
for (const file of skipped) {
|
|
14278
|
+
log2.warn(`Cannot move ${file} because knowledge/tech/discovery/${file} already exists. Review both files manually.`);
|
|
14279
|
+
}
|
|
14280
|
+
if (moved.length === 0 && skipped.length === 0) {
|
|
14281
|
+
log2.info("Nothing to organize \u2014 no discovery files in the legacy `knowledge/tech/` root.");
|
|
14282
|
+
}
|
|
14283
|
+
printCommandFooter("tech organize");
|
|
14284
|
+
outro2("Tech knowledge organized.");
|
|
14285
|
+
}
|
|
14286
|
+
|
|
14287
|
+
// src/commands/assets.ts
|
|
14288
|
+
var LABEL = { agent: "Agents", skill: "Skills" };
|
|
14289
|
+
function runAssetsStatus(kind, opts = {}) {
|
|
14290
|
+
const dir = cwd();
|
|
14291
|
+
requireConfig(dir);
|
|
14292
|
+
const summary = assetStatus(dir, kind);
|
|
14293
|
+
if (opts.json) {
|
|
14294
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
14295
|
+
return;
|
|
14296
|
+
}
|
|
14297
|
+
console.log("");
|
|
14298
|
+
console.log(LABEL[kind]);
|
|
14299
|
+
console.log("");
|
|
14300
|
+
const installed = summary.items.filter((i) => i.state !== "missing");
|
|
14301
|
+
for (const a of installed) {
|
|
14302
|
+
console.log(`${a.path}`);
|
|
14303
|
+
console.log(` Name: ${a.name}`);
|
|
14304
|
+
console.log(` Installed: ${a.installed ?? "unknown"}`);
|
|
14305
|
+
console.log(` Available: ${a.available}`);
|
|
14306
|
+
console.log(` Status: ${a.state}`);
|
|
14307
|
+
console.log("");
|
|
14308
|
+
}
|
|
14309
|
+
if (installed.length === 0) console.log(`No ${kind}s installed. Run \`kaddo add ${kind}s\`.`);
|
|
14310
|
+
console.log(
|
|
14311
|
+
`Summary: ${summary.up_to_date} up-to-date, ${summary.outdated} outdated, ${summary.unknown_version} unknown-version, ${summary.modified} modified, ${summary.missing} not installed.`
|
|
14312
|
+
);
|
|
14313
|
+
if (summary.outdated > 0 || summary.unknown_version > 0) {
|
|
14314
|
+
console.log(`Run \`kaddo ${kind}s update\` to refresh outdated ${kind}s (\`--force\` for unknown/modified).`);
|
|
14315
|
+
}
|
|
14316
|
+
printCommandFooter(`${kind}s status`);
|
|
14317
|
+
}
|
|
14318
|
+
function runAssetsUpdate(kind, opts = {}) {
|
|
14319
|
+
const dir = cwd();
|
|
14320
|
+
requireConfig(dir);
|
|
14321
|
+
intro2(`kaddo ${kind}s update`);
|
|
14322
|
+
const summary = assetStatus(dir, kind);
|
|
14323
|
+
const catalog = kind === "agent" ? canonicalAgents() : canonicalSkills();
|
|
14324
|
+
const byPath = new Map(catalog.map((a) => [a.path, a.content]));
|
|
14325
|
+
const updated = [];
|
|
14326
|
+
const skipped = [];
|
|
14327
|
+
for (const item of summary.items) {
|
|
14328
|
+
const canonical = byPath.get(item.path);
|
|
14329
|
+
if (!canonical) continue;
|
|
14330
|
+
const safe = item.state === "outdated";
|
|
14331
|
+
const needsForce = item.state === "unknown-version" || item.state === "modified";
|
|
14332
|
+
if (safe || needsForce && opts.force) {
|
|
14333
|
+
writeFile(join(dir, item.path), canonical);
|
|
14334
|
+
updated.push(`${item.path} (${item.installed ?? "unknown"} \u2192 ${item.available})`);
|
|
14335
|
+
} else if (needsForce) {
|
|
14336
|
+
skipped.push(`${item.path} \u2014 ${item.state} (use --force to overwrite local changes)`);
|
|
14337
|
+
}
|
|
14338
|
+
}
|
|
14339
|
+
if (updated.length > 0) {
|
|
14340
|
+
log2.success("Updated:");
|
|
14341
|
+
for (const u of updated) console.log(` - ${u}`);
|
|
14342
|
+
} else {
|
|
14343
|
+
log2.info(`No ${kind}s to update.`);
|
|
14344
|
+
}
|
|
14345
|
+
for (const s of skipped) log2.warn(`Skipped ${s}`);
|
|
14346
|
+
if (summary.missing > 0) log2.info(`${summary.missing} ${kind}(s) not installed \u2014 run \`kaddo add ${kind}s\` to add them.`);
|
|
14347
|
+
printCommandFooter(`${kind}s status`);
|
|
14348
|
+
outro2(`${LABEL[kind]} update complete.`);
|
|
14349
|
+
}
|
|
14350
|
+
|
|
14044
14351
|
// src/index.ts
|
|
14045
14352
|
var require2 = createRequire(import.meta.url);
|
|
14046
14353
|
var { version } = require2("../package.json");
|
|
@@ -14094,6 +14401,16 @@ program.command("drift").description("Drift Trend Report from recorded `kaddo gu
|
|
|
14094
14401
|
var questionsAction = (opts) => runQuestions(opts);
|
|
14095
14402
|
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
14403
|
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);
|
|
14404
|
+
var agentsCmd = program.command("agents").description("Inspect and update installed Kaddo agents (version status)");
|
|
14405
|
+
agentsCmd.command("status").description("Show installed agents, their version and whether they are up to date").option("--json", "Output JSON").action((opts) => runAssetsStatus("agent", opts));
|
|
14406
|
+
agentsCmd.command("update").description("Refresh outdated agents (never overwrites modified files without --force)").option("--force", "Overwrite unknown-version / locally-modified agents").action((opts) => runAssetsUpdate("agent", opts));
|
|
14407
|
+
var skillsCmd = program.command("skills").description("Inspect and update installed Kaddo skills (version status)");
|
|
14408
|
+
skillsCmd.command("status").description("Show installed skills, their version and whether they are up to date").option("--json", "Output JSON").action((opts) => runAssetsStatus("skill", opts));
|
|
14409
|
+
skillsCmd.command("update").description("Refresh outdated skills (never overwrites modified files without --force)").option("--force", "Overwrite unknown-version / locally-modified skills").action((opts) => runAssetsUpdate("skill", opts));
|
|
14410
|
+
var techCmd = program.command("tech").description("Organize the knowledge/tech/ structure (core vs discovery vs decisions)");
|
|
14411
|
+
techCmd.command("organize").description("Move discovery artifacts into knowledge/tech/discovery/ (never overwrites, no content change)").action(() => {
|
|
14412
|
+
runTechOrganize();
|
|
14413
|
+
});
|
|
14097
14414
|
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
14415
|
runAdr(opts);
|
|
14099
14416
|
});
|