@kaddo/cli 3.41.0 → 3.43.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 +404 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -540,6 +540,8 @@ create --from roadmap → owners → guard → explain`.
|
|
|
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
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` |
|
|
544
|
+
| v3.43 | Capability-grounded roadmap: each `RM-xxx` candidate is graded on related domain / capability / source signals; `roadmap_quality` surfaced in `explain`/`context`/`understand`; `create --from roadmap` preserves `source_roadmap_candidate` + related metadata into the Work Item; roadmap-agent emits grounded fields (never materializes Work Items); MCP `kaddo://roadmap-quality` |
|
|
543
545
|
|
|
544
546
|
**Optional modules (installed with `kaddo add`):**
|
|
545
547
|
|
package/dist/index.js
CHANGED
|
@@ -712,6 +712,14 @@ var COMMAND_HELP = {
|
|
|
712
712
|
"tech organize": {
|
|
713
713
|
question: "Is knowledge/tech/ cleanly separated (core vs discovery vs decisions)?",
|
|
714
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"
|
|
715
723
|
}
|
|
716
724
|
};
|
|
717
725
|
function commandFooterLines(name) {
|
|
@@ -1515,14 +1523,38 @@ var guardAdvancedModule = {
|
|
|
1515
1523
|
]
|
|
1516
1524
|
};
|
|
1517
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
|
+
|
|
1518
1549
|
// src/skills/skills.ts
|
|
1519
1550
|
function skill(id, title, group, appliesTo, body) {
|
|
1520
1551
|
const frontMatter2 = [
|
|
1521
1552
|
"---",
|
|
1522
1553
|
"type: skill",
|
|
1523
1554
|
`id: ${id}`,
|
|
1555
|
+
`name: ${id}`,
|
|
1524
1556
|
`title: ${title}`,
|
|
1525
|
-
|
|
1557
|
+
`version: ${KADDO_VERSION}`,
|
|
1526
1558
|
`group: ${group}`,
|
|
1527
1559
|
"applies_to:",
|
|
1528
1560
|
...appliesTo.map((a) => ` - ${a}`),
|
|
@@ -2613,6 +2645,24 @@ Adapt priorities to the project state from the context pack:
|
|
|
2613
2645
|
- **legacy** \u2014 prioritize risk reduction, unknowns and safe modernization before feature
|
|
2614
2646
|
delivery.
|
|
2615
2647
|
|
|
2648
|
+
## Grounding rules (VS-077)
|
|
2649
|
+
|
|
2650
|
+
Every candidate initiative must be **grounded** in the knowledge base \u2014 never a loose idea. For each
|
|
2651
|
+
\`### RM-xxx\` you must provide:
|
|
2652
|
+
|
|
2653
|
+
- **Related domain** \u2014 a domain from \`## Capability Domains\` in \`capabilities.md\` (or, if genuinely
|
|
2654
|
+
new, prefix it \`[new candidate domain] <name>\` \u2014 do not invent domains silently).
|
|
2655
|
+
- **Related capabilities** \u2014 one or more existing/partial capabilities.
|
|
2656
|
+
- **Source signals** \u2014 at least one traceable reason: Capability Gap, Roadmap Candidate Signal, Risk,
|
|
2657
|
+
Open Question, Assumption, Deferred Decision, Tech Decision Candidate, ADR, Business Goal,
|
|
2658
|
+
Operational Need or Legacy Modernization Signal.
|
|
2659
|
+
- **Expected value**, **Risks**, **Dependencies**, and **Suggested Work Items** (candidates only).
|
|
2660
|
+
|
|
2661
|
+
Do **not** emit a candidate with no source signal. Keep initiatives at initiative granularity (small
|
|
2662
|
+
tasks go under **Suggested Work Items**, not as their own RM). Every roadmap ends with a global
|
|
2663
|
+
**## Not Now** section. For pre-ai/legacy, prioritize stabilization, security, data, operations,
|
|
2664
|
+
architectural decisions and business-blocking gaps before expansive features.
|
|
2665
|
+
|
|
2616
2666
|
## Constraints
|
|
2617
2667
|
|
|
2618
2668
|
- Do not invent business priorities or business facts \u2014 mark them as assumptions when inferred.
|
|
@@ -2621,6 +2671,8 @@ Adapt priorities to the project state from the context pack:
|
|
|
2621
2671
|
implementation-agent, and only after Work Items are materialized. Your handoff is
|
|
2622
2672
|
\`kaddo create --from roadmap\` \u2192 work-item-agent.
|
|
2623
2673
|
- Do not create the work items themselves; only propose candidates.
|
|
2674
|
+
- **Never create files under \`knowledge/delivery/work-items/\`** \u2014 materialization is
|
|
2675
|
+
\`kaddo create --from roadmap\`, not the roadmap-agent.
|
|
2624
2676
|
- Make clear that initiatives and work items are **candidates**, not final decisions.
|
|
2625
2677
|
- Mark any uncertain information as an assumption or open question.
|
|
2626
2678
|
- Keep sequencing justified by dependencies and risk.
|
|
@@ -2654,31 +2706,38 @@ human review \u2014 not final commitments.
|
|
|
2654
2706
|
|
|
2655
2707
|
### RM-001: <Initiative Name>
|
|
2656
2708
|
|
|
2657
|
-
**
|
|
2709
|
+
**Status:** candidate <!-- candidate | selected | deferred | rejected -->
|
|
2658
2710
|
|
|
2659
|
-
**
|
|
2711
|
+
**Priority:** high / medium / low
|
|
2660
2712
|
|
|
2661
|
-
**
|
|
2713
|
+
**Suggested Knowledge Level:** K1 / K2 / K3 / K4
|
|
2662
2714
|
|
|
2663
|
-
**
|
|
2715
|
+
**Related domain:** <one of the ## Capability Domains from capabilities.md>
|
|
2664
2716
|
|
|
2665
|
-
**
|
|
2717
|
+
**Related capabilities:**
|
|
2718
|
+
- <existing or partial capability>
|
|
2666
2719
|
|
|
2667
|
-
**
|
|
2720
|
+
**Source signals:** <!-- REQUIRED: why this candidate exists (at least one) -->
|
|
2721
|
+
- Capability Gap: <...>
|
|
2722
|
+
- Roadmap Candidate Signal: <...>
|
|
2723
|
+
- Risk / Open Question / Assumption / Deferred Decision / Tech Decision Candidate / ADR / Business Goal: <...>
|
|
2668
2724
|
|
|
2669
|
-
**
|
|
2725
|
+
**Problem / opportunity:**
|
|
2726
|
+
|
|
2727
|
+
**Expected value:**
|
|
2670
2728
|
|
|
2671
|
-
**
|
|
2729
|
+
**Risks:**
|
|
2672
2730
|
|
|
2673
|
-
**
|
|
2731
|
+
**Dependencies:**
|
|
2674
2732
|
|
|
2733
|
+
**Suggested Work Items:**
|
|
2675
2734
|
- WI-CANDIDATE-001: <candidate work item>
|
|
2676
2735
|
- type:
|
|
2677
2736
|
- suggested knowledge level:
|
|
2678
2737
|
- expected value:
|
|
2679
2738
|
- notes:
|
|
2680
2739
|
|
|
2681
|
-
**
|
|
2740
|
+
**Not now:**
|
|
2682
2741
|
|
|
2683
2742
|
---
|
|
2684
2743
|
|
|
@@ -2973,6 +3032,14 @@ that it should be materialized first (\`kaddo adr\` + the adr-writing skill) and
|
|
|
2973
3032
|
without surfacing it. -->
|
|
2974
3033
|
\`\`\`
|
|
2975
3034
|
|
|
3035
|
+
### Preserve roadmap metadata (VS-077)
|
|
3036
|
+
|
|
3037
|
+
When a Work Item comes from \`kaddo create --from roadmap\`, the front matter already carries
|
|
3038
|
+
\`source_roadmap_candidate\`, \`related_domain\`, \`related_capability\` (+ \`related_capabilities\`),
|
|
3039
|
+
\`knowledge_level\`, \`expected_value\`, \`risks\` and \`dependencies\`. **Keep and refine** this metadata \u2014
|
|
3040
|
+
do not drop the trace back to the capability domain and source signals. Add \`related_decisions\` /
|
|
3041
|
+
\`decision_candidates\` when the work depends on a technical decision.
|
|
3042
|
+
|
|
2976
3043
|
## Where to Save the Result
|
|
2977
3044
|
|
|
2978
3045
|
Save new output as a draft under \`knowledge/delivery/work-items/draft/\` unless a human
|
|
@@ -4050,6 +4117,19 @@ function selectAgentFiles(opts) {
|
|
|
4050
4117
|
}
|
|
4051
4118
|
|
|
4052
4119
|
// src/modules/agents.ts
|
|
4120
|
+
function withAgentFrontMatter(fileName, content) {
|
|
4121
|
+
const name = fileName.replace(/\.md$/, "");
|
|
4122
|
+
const fm2 = [
|
|
4123
|
+
"---",
|
|
4124
|
+
"type: agent",
|
|
4125
|
+
`name: ${name}`,
|
|
4126
|
+
`version: ${KADDO_VERSION}`,
|
|
4127
|
+
`group: ${agentGroupOf(fileName)}`,
|
|
4128
|
+
"---",
|
|
4129
|
+
""
|
|
4130
|
+
].join("\n");
|
|
4131
|
+
return fm2 + content.replace(/^\s+/, "");
|
|
4132
|
+
}
|
|
4053
4133
|
var agentReadme = {
|
|
4054
4134
|
path: "knowledge/agents/README.md",
|
|
4055
4135
|
content: [
|
|
@@ -4121,7 +4201,7 @@ var agentReadme = {
|
|
|
4121
4201
|
};
|
|
4122
4202
|
var agentFiles = AGENT_PROMPTS.map((a) => ({
|
|
4123
4203
|
path: agentInstallPath(a.fileName),
|
|
4124
|
-
content: a.content
|
|
4204
|
+
content: withAgentFrontMatter(a.fileName, a.content)
|
|
4125
4205
|
}));
|
|
4126
4206
|
var agentsModule = {
|
|
4127
4207
|
name: "agents",
|
|
@@ -4934,6 +5014,16 @@ function buildRoadmapFrontMatter(id, type, level, title, candidate, answers) {
|
|
|
4934
5014
|
`source: roadmap`,
|
|
4935
5015
|
`source_id: ${candidate.id}`,
|
|
4936
5016
|
`source_initiative: ${candidate.initiative?.id ?? "unknown"}`,
|
|
5017
|
+
// Capability-grounded traceability (VS-077): carry the roadmap metadata into the Work Item.
|
|
5018
|
+
`source_roadmap_candidate: ${candidate.initiative?.id ?? candidate.id}`,
|
|
5019
|
+
...candidate.domain ? [`related_domain: "${candidate.domain.replace(/"/g, "'")}"`] : [],
|
|
5020
|
+
...candidate.relatedCapabilities && candidate.relatedCapabilities.length > 0 ? [
|
|
5021
|
+
`related_capability: "${candidate.relatedCapabilities[0].replace(/"/g, "'")}"`,
|
|
5022
|
+
`related_capabilities: [${candidate.relatedCapabilities.map((c) => `"${c.replace(/"/g, "'")}"`).join(", ")}]`
|
|
5023
|
+
] : [],
|
|
5024
|
+
...candidate.expectedValue ? [`expected_value: "${candidate.expectedValue.replace(/"/g, "'")}"`] : [],
|
|
5025
|
+
...candidate.risk ? [`risks: "${candidate.risk.replace(/"/g, "'")}"`] : [],
|
|
5026
|
+
...candidate.dependencies && candidate.dependencies.length > 0 ? [`dependencies: [${candidate.dependencies.map((d) => `"${d.replace(/"/g, "'")}"`).join(", ")}]`] : [],
|
|
4937
5027
|
`summary: "${summary.replace(/"/g, "'")}"`,
|
|
4938
5028
|
"---"
|
|
4939
5029
|
];
|
|
@@ -6202,7 +6292,7 @@ function runIgnoreRemove(artifactId) {
|
|
|
6202
6292
|
}
|
|
6203
6293
|
|
|
6204
6294
|
// src/commands/explain.ts
|
|
6205
|
-
import
|
|
6295
|
+
import matter5 from "gray-matter";
|
|
6206
6296
|
import { parse as parseYaml9 } from "yaml";
|
|
6207
6297
|
|
|
6208
6298
|
// src/core/delivery-phase.ts
|
|
@@ -8214,6 +8304,145 @@ function buildTechDecisions(dir) {
|
|
|
8214
8304
|
};
|
|
8215
8305
|
}
|
|
8216
8306
|
|
|
8307
|
+
// src/core/assets.ts
|
|
8308
|
+
import matter4 from "gray-matter";
|
|
8309
|
+
function canonicalAgents() {
|
|
8310
|
+
return AGENT_PROMPTS.map((a) => ({
|
|
8311
|
+
name: a.fileName.replace(/\.md$/, ""),
|
|
8312
|
+
path: agentInstallPath(a.fileName),
|
|
8313
|
+
content: withAgentFrontMatter(a.fileName, a.content)
|
|
8314
|
+
}));
|
|
8315
|
+
}
|
|
8316
|
+
function canonicalSkills() {
|
|
8317
|
+
return SKILLS.map((s) => ({ name: s.id, path: skillInstallPath(s.id), content: s.content }));
|
|
8318
|
+
}
|
|
8319
|
+
function versionOf(content) {
|
|
8320
|
+
try {
|
|
8321
|
+
const v = matter4(content).data.version;
|
|
8322
|
+
return v === void 0 || v === null ? null : String(v);
|
|
8323
|
+
} catch {
|
|
8324
|
+
return null;
|
|
8325
|
+
}
|
|
8326
|
+
}
|
|
8327
|
+
function cmpVersion(a, b) {
|
|
8328
|
+
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
8329
|
+
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
8330
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
8331
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
8332
|
+
if (d !== 0) return d < 0 ? -1 : 1;
|
|
8333
|
+
}
|
|
8334
|
+
return 0;
|
|
8335
|
+
}
|
|
8336
|
+
function classify(dir, asset) {
|
|
8337
|
+
const full = join(dir, asset.path);
|
|
8338
|
+
const base = { name: asset.name, path: asset.path, available: KADDO_VERSION };
|
|
8339
|
+
if (!exists(full)) return { ...base, installed: null, state: "missing" };
|
|
8340
|
+
let content = "";
|
|
8341
|
+
try {
|
|
8342
|
+
content = readFile(full);
|
|
8343
|
+
} catch {
|
|
8344
|
+
return { ...base, installed: null, state: "missing" };
|
|
8345
|
+
}
|
|
8346
|
+
const installed = versionOf(content);
|
|
8347
|
+
if (installed === null) return { ...base, installed: null, state: "unknown-version" };
|
|
8348
|
+
const cmp = cmpVersion(installed, KADDO_VERSION);
|
|
8349
|
+
if (cmp < 0) return { ...base, installed, state: "outdated" };
|
|
8350
|
+
if (content.trim() !== asset.content.trim()) return { ...base, installed, state: "modified" };
|
|
8351
|
+
return { ...base, installed, state: "up-to-date" };
|
|
8352
|
+
}
|
|
8353
|
+
function summarize(items) {
|
|
8354
|
+
const count = (s) => items.filter((i) => i.state === s).length;
|
|
8355
|
+
return {
|
|
8356
|
+
total: items.length,
|
|
8357
|
+
up_to_date: count("up-to-date"),
|
|
8358
|
+
outdated: count("outdated"),
|
|
8359
|
+
missing: count("missing"),
|
|
8360
|
+
unknown_version: count("unknown-version"),
|
|
8361
|
+
modified: count("modified"),
|
|
8362
|
+
items
|
|
8363
|
+
};
|
|
8364
|
+
}
|
|
8365
|
+
function assetStatus(dir, kind) {
|
|
8366
|
+
const catalog = kind === "agent" ? canonicalAgents() : canonicalSkills();
|
|
8367
|
+
return summarize(catalog.map((a) => classify(dir, a)));
|
|
8368
|
+
}
|
|
8369
|
+
function installedAssetsSummary(dir) {
|
|
8370
|
+
return { version: KADDO_VERSION, agents: assetStatus(dir, "agent"), skills: assetStatus(dir, "skill") };
|
|
8371
|
+
}
|
|
8372
|
+
|
|
8373
|
+
// src/core/roadmap-quality.ts
|
|
8374
|
+
var ROADMAP_PATH2 = "knowledge/delivery/roadmap.md";
|
|
8375
|
+
var INITIATIVE_RE2 = /^#{2,4}\s+(RM-[\w.-]+)\s*[:\-–]?\s*(.*)$/;
|
|
8376
|
+
function fieldPresent(lines, label) {
|
|
8377
|
+
for (let i = 0; i < lines.length; i++) {
|
|
8378
|
+
const m = lines[i].match(new RegExp(`^\\s*[-*]?\\s*(?:\\*\\*)?\\s*(${label.source})\\s*(?:\\*\\*)?\\s*:\\s*(.*)$`, "i"));
|
|
8379
|
+
if (!m) continue;
|
|
8380
|
+
const inline = m[2].trim();
|
|
8381
|
+
if (inline) return true;
|
|
8382
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
8383
|
+
if (/^\s{2,}[-*]\s+\S/.test(lines[j])) return true;
|
|
8384
|
+
if (/^\S/.test(lines[j]) || /^#{2,4}\s/.test(lines[j])) break;
|
|
8385
|
+
if (/^\s*[-*]\s+\S/.test(lines[j]) && !/^\s{2,}/.test(lines[j])) break;
|
|
8386
|
+
}
|
|
8387
|
+
}
|
|
8388
|
+
return false;
|
|
8389
|
+
}
|
|
8390
|
+
function parseRoadmapCandidateQuality(md) {
|
|
8391
|
+
const lines = md.split(/\r?\n/);
|
|
8392
|
+
const items = [];
|
|
8393
|
+
let cur = null;
|
|
8394
|
+
const flush = () => {
|
|
8395
|
+
if (!cur) return;
|
|
8396
|
+
const hasRelatedDomain = fieldPresent(cur.lines, /related domain/);
|
|
8397
|
+
const hasRelatedCapability = fieldPresent(cur.lines, /related capabilit(?:y|ies)/);
|
|
8398
|
+
const hasSourceSignals = fieldPresent(cur.lines, /source signals?/);
|
|
8399
|
+
items.push({
|
|
8400
|
+
id: cur.id,
|
|
8401
|
+
title: cur.title,
|
|
8402
|
+
hasRelatedDomain,
|
|
8403
|
+
hasRelatedCapability,
|
|
8404
|
+
hasSourceSignals,
|
|
8405
|
+
grounded: hasRelatedDomain && hasRelatedCapability && hasSourceSignals
|
|
8406
|
+
});
|
|
8407
|
+
cur = null;
|
|
8408
|
+
};
|
|
8409
|
+
for (const line of lines) {
|
|
8410
|
+
const m = line.match(INITIATIVE_RE2);
|
|
8411
|
+
if (m) {
|
|
8412
|
+
flush();
|
|
8413
|
+
cur = { id: m[1], title: m[2].trim(), lines: [] };
|
|
8414
|
+
continue;
|
|
8415
|
+
}
|
|
8416
|
+
if (cur) cur.lines.push(line);
|
|
8417
|
+
}
|
|
8418
|
+
flush();
|
|
8419
|
+
return items;
|
|
8420
|
+
}
|
|
8421
|
+
function buildRoadmapQuality(dir) {
|
|
8422
|
+
let md = "";
|
|
8423
|
+
const p2 = join(dir, ROADMAP_PATH2);
|
|
8424
|
+
if (exists(p2)) {
|
|
8425
|
+
try {
|
|
8426
|
+
md = readFile(p2);
|
|
8427
|
+
} catch {
|
|
8428
|
+
md = "";
|
|
8429
|
+
}
|
|
8430
|
+
}
|
|
8431
|
+
const items = parseRoadmapCandidateQuality(md);
|
|
8432
|
+
const count = (pred) => items.filter(pred).length;
|
|
8433
|
+
const grounded = count((i) => i.grounded);
|
|
8434
|
+
return {
|
|
8435
|
+
candidates: items.length,
|
|
8436
|
+
grounded,
|
|
8437
|
+
with_related_domain: count((i) => i.hasRelatedDomain),
|
|
8438
|
+
with_related_capability: count((i) => i.hasRelatedCapability),
|
|
8439
|
+
with_source_signals: count((i) => i.hasSourceSignals),
|
|
8440
|
+
// Only "needs refinement" when there are candidates and at least one isn't grounded.
|
|
8441
|
+
needs_refinement: items.length > 0 && grounded < items.length,
|
|
8442
|
+
items
|
|
8443
|
+
};
|
|
8444
|
+
}
|
|
8445
|
+
|
|
8217
8446
|
// src/core/project-explain.ts
|
|
8218
8447
|
var ARCH_DIR4 = "knowledge";
|
|
8219
8448
|
function normalizeTitle(t) {
|
|
@@ -8437,7 +8666,9 @@ function buildProjectExplanation(dir) {
|
|
|
8437
8666
|
decisionCandidates: exists(join(dir, "knowledge/tech/discovery/decision-candidates.md")) || exists(join(dir, "knowledge/tech/decision-candidates.md")),
|
|
8438
8667
|
legacyLocation: exists(join(dir, "knowledge/tech/architecture-notes.md")) || exists(join(dir, "knowledge/tech/decision-candidates.md"))
|
|
8439
8668
|
}
|
|
8440
|
-
}
|
|
8669
|
+
},
|
|
8670
|
+
installedAssets: installedAssetsSummary(dir),
|
|
8671
|
+
roadmapQuality: buildRoadmapQuality(dir)
|
|
8441
8672
|
};
|
|
8442
8673
|
}
|
|
8443
8674
|
function stateLabel(state) {
|
|
@@ -8672,6 +8903,35 @@ function renderExplanationHuman(exp) {
|
|
|
8672
8903
|
lines.push("Tech discovery files are in the legacy `knowledge/tech/` root. Suggested cleanup: run `kaddo tech organize`.");
|
|
8673
8904
|
lines.push("");
|
|
8674
8905
|
}
|
|
8906
|
+
const rq = exp.roadmapQuality;
|
|
8907
|
+
if (rq.candidates > 0) {
|
|
8908
|
+
lines.push("## Roadmap Quality");
|
|
8909
|
+
lines.push(`- Candidates: ${rq.candidates}`);
|
|
8910
|
+
lines.push(`- Grounded: ${rq.grounded}/${rq.candidates}`);
|
|
8911
|
+
lines.push(`- With related domain: ${rq.with_related_domain}/${rq.candidates}`);
|
|
8912
|
+
lines.push(`- With related capability: ${rq.with_related_capability}/${rq.candidates}`);
|
|
8913
|
+
lines.push(`- With source signals: ${rq.with_source_signals}/${rq.candidates}`);
|
|
8914
|
+
if (rq.needs_refinement) {
|
|
8915
|
+
lines.push("");
|
|
8916
|
+
lines.push("Roadmap quality: needs refinement. Suggested: use roadmap-agent to add domain / capability / source signals.");
|
|
8917
|
+
}
|
|
8918
|
+
lines.push("");
|
|
8919
|
+
}
|
|
8920
|
+
const ia = exp.installedAssets;
|
|
8921
|
+
const agentsInstalled = ia.agents.total - ia.agents.missing;
|
|
8922
|
+
const skillsInstalled = ia.skills.total - ia.skills.missing;
|
|
8923
|
+
if (agentsInstalled > 0 || skillsInstalled > 0) {
|
|
8924
|
+
const issues = (s2) => [s2.outdated ? `${s2.outdated} outdated` : "", s2.unknown_version ? `${s2.unknown_version} unknown-version` : "", s2.modified ? `${s2.modified} modified` : ""].filter(Boolean).join(", ");
|
|
8925
|
+
lines.push("## Installed Assets");
|
|
8926
|
+
lines.push(`- CLI version: ${ia.version}`);
|
|
8927
|
+
lines.push(`- Agents: ${agentsInstalled} installed${issues(ia.agents) ? ` (${issues(ia.agents)})` : ""}`);
|
|
8928
|
+
lines.push(`- Skills: ${skillsInstalled} installed${issues(ia.skills) ? ` (${issues(ia.skills)})` : ""}`);
|
|
8929
|
+
if (ia.agents.outdated + ia.agents.unknown_version + ia.skills.outdated + ia.skills.unknown_version > 0) {
|
|
8930
|
+
lines.push("");
|
|
8931
|
+
lines.push("Suggested: run `kaddo agents status` and `kaddo skills status`.");
|
|
8932
|
+
}
|
|
8933
|
+
lines.push("");
|
|
8934
|
+
}
|
|
8675
8935
|
return lines.join("\n").trimEnd() + "\n";
|
|
8676
8936
|
}
|
|
8677
8937
|
function renderExplanationAgent(exp) {
|
|
@@ -8687,7 +8947,7 @@ function readKnowledge(dir) {
|
|
|
8687
8947
|
if (!exists(knowledgePath)) return null;
|
|
8688
8948
|
try {
|
|
8689
8949
|
const raw = readFile(knowledgePath);
|
|
8690
|
-
const { data, content } =
|
|
8950
|
+
const { data, content } = matter5(raw);
|
|
8691
8951
|
return { content, data };
|
|
8692
8952
|
} catch {
|
|
8693
8953
|
return null;
|
|
@@ -8698,7 +8958,7 @@ function readRoadmap(dir) {
|
|
|
8698
8958
|
if (!exists(roadmapPath)) return null;
|
|
8699
8959
|
try {
|
|
8700
8960
|
const raw = readFile(roadmapPath);
|
|
8701
|
-
const { content } =
|
|
8961
|
+
const { content } = matter5(raw);
|
|
8702
8962
|
return content.trim();
|
|
8703
8963
|
} catch {
|
|
8704
8964
|
return null;
|
|
@@ -8867,7 +9127,7 @@ function runExplain(opts) {
|
|
|
8867
9127
|
}
|
|
8868
9128
|
|
|
8869
9129
|
// src/core/context-pack.ts
|
|
8870
|
-
import
|
|
9130
|
+
import matter6 from "gray-matter";
|
|
8871
9131
|
var CONTEXT_PACK_VERSION = "1";
|
|
8872
9132
|
var ARCH_DIR6 = "knowledge";
|
|
8873
9133
|
function readScanJson(dir) {
|
|
@@ -8880,7 +9140,7 @@ function readScanJson(dir) {
|
|
|
8880
9140
|
}
|
|
8881
9141
|
}
|
|
8882
9142
|
function firstParagraph2(markdown) {
|
|
8883
|
-
const body =
|
|
9143
|
+
const body = matter6(markdown).content.trim();
|
|
8884
9144
|
const para = body.split("\n\n").map((p2) => p2.trim()).find((p2) => p2 && !p2.startsWith("#"));
|
|
8885
9145
|
return para ?? "";
|
|
8886
9146
|
}
|
|
@@ -9075,6 +9335,12 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
9075
9335
|
nextStepRecommendation,
|
|
9076
9336
|
techDecisions,
|
|
9077
9337
|
techKnowledge,
|
|
9338
|
+
roadmapQuality: buildRoadmapQuality(dir),
|
|
9339
|
+
installedAssets: (() => {
|
|
9340
|
+
const s = installedAssetsSummary(dir);
|
|
9341
|
+
const compact = (a) => ({ total: a.total, installed: a.total - a.missing, outdated: a.outdated, unknown_version: a.unknown_version, modified: a.modified });
|
|
9342
|
+
return { version: s.version, agents: compact(s.agents), skills: compact(s.skills) };
|
|
9343
|
+
})(),
|
|
9078
9344
|
deliveryMix,
|
|
9079
9345
|
external: loadExternalCapsules(dir),
|
|
9080
9346
|
graph: loadGraphSummary(dir),
|
|
@@ -9184,6 +9450,24 @@ function renderContextPack(pack) {
|
|
|
9184
9450
|
}
|
|
9185
9451
|
}
|
|
9186
9452
|
parts.push((knowledge.roadmapSummary || "No roadmap baseline found.") + "\n");
|
|
9453
|
+
const rq = pack.roadmapQuality;
|
|
9454
|
+
if (rq.candidates > 0) {
|
|
9455
|
+
parts.push("## Roadmap Quality\n");
|
|
9456
|
+
parts.push(
|
|
9457
|
+
[
|
|
9458
|
+
`- Candidates: ${rq.candidates}`,
|
|
9459
|
+
`- Grounded: ${rq.grounded}/${rq.candidates}`,
|
|
9460
|
+
`- With related domain: ${rq.with_related_domain}/${rq.candidates}`,
|
|
9461
|
+
`- With related capability: ${rq.with_related_capability}/${rq.candidates}`,
|
|
9462
|
+
`- With source signals: ${rq.with_source_signals}/${rq.candidates}`
|
|
9463
|
+
].join("\n") + "\n"
|
|
9464
|
+
);
|
|
9465
|
+
if (rq.needs_refinement) {
|
|
9466
|
+
parts.push(
|
|
9467
|
+
"Roadmap quality: needs refinement. Use the roadmap-agent to add domain / capability / source signals.\n"
|
|
9468
|
+
);
|
|
9469
|
+
}
|
|
9470
|
+
}
|
|
9187
9471
|
parts.push("## Active Work Items\n");
|
|
9188
9472
|
if (knowledge.workItems.length > 0) {
|
|
9189
9473
|
const lines = knowledge.workItems.map((wi) => {
|
|
@@ -9616,6 +9900,27 @@ function runUnderstand() {
|
|
|
9616
9900
|
console.log(" \u2192 Use the adr-writing skill to create ADR drafts from `knowledge/tech/decision-candidates.md`");
|
|
9617
9901
|
console.log(" into `knowledge/tech/decisions/` before implementing affected technical Work Items (`kaddo adr`).");
|
|
9618
9902
|
}
|
|
9903
|
+
const rq = exp.roadmapQuality;
|
|
9904
|
+
if (rq.needs_refinement) {
|
|
9905
|
+
console.log("");
|
|
9906
|
+
console.log(`Roadmap quality: ${rq.grounded}/${rq.candidates} candidates grounded.`);
|
|
9907
|
+
console.log(" \u2192 Use roadmap-agent to ground roadmap candidates in capability domains, gaps and source signals");
|
|
9908
|
+
console.log(" before `kaddo create --from roadmap`.");
|
|
9909
|
+
} else if (rq.candidates > 0 && rq.grounded === rq.candidates) {
|
|
9910
|
+
console.log("");
|
|
9911
|
+
console.log("Roadmap candidates are grounded. \u2192 Run `kaddo create --from roadmap` to materialize the first Work Item.");
|
|
9912
|
+
}
|
|
9913
|
+
const ia = exp.installedAssets;
|
|
9914
|
+
const outdatedRecommended = ia.agents.items.filter(
|
|
9915
|
+
(a) => (a.state === "outdated" || a.state === "unknown-version") && assessment.recommendedAgents.includes(a.name)
|
|
9916
|
+
);
|
|
9917
|
+
if (outdatedRecommended.length > 0) {
|
|
9918
|
+
console.log("");
|
|
9919
|
+
for (const a of outdatedRecommended) {
|
|
9920
|
+
console.log(`Recommended agent ${a.name} is ${a.state} (installed ${a.installed ?? "unknown"}, available ${a.available}).`);
|
|
9921
|
+
}
|
|
9922
|
+
console.log(" \u2192 Run `kaddo agents update` or review `kaddo agents status`.");
|
|
9923
|
+
}
|
|
9619
9924
|
if (exp.externalCapsules.length > 0) {
|
|
9620
9925
|
console.log("");
|
|
9621
9926
|
console.log("External knowledge:");
|
|
@@ -9776,7 +10081,7 @@ var DECLARED_LEVEL = {
|
|
|
9776
10081
|
incident: "K2",
|
|
9777
10082
|
rfc: "K3"
|
|
9778
10083
|
};
|
|
9779
|
-
function
|
|
10084
|
+
function classify2(declaredType, declaredLevel, touchedFiles) {
|
|
9780
10085
|
const signals = detectSignals(touchedFiles);
|
|
9781
10086
|
const observedLevel = highestLevel(signals);
|
|
9782
10087
|
const expectedLevel = DECLARED_LEVEL[declaredType] ?? declaredLevel ?? "K2";
|
|
@@ -9856,7 +10161,7 @@ async function runClassify(opts = {}) {
|
|
|
9856
10161
|
return;
|
|
9857
10162
|
}
|
|
9858
10163
|
if (opts.type) {
|
|
9859
|
-
const result2 =
|
|
10164
|
+
const result2 = classify2(opts.type, opts.level ?? "", touchedFiles);
|
|
9860
10165
|
printResult2(result2);
|
|
9861
10166
|
return;
|
|
9862
10167
|
}
|
|
@@ -9867,7 +10172,7 @@ async function runClassify(opts = {}) {
|
|
|
9867
10172
|
console.log("Or create one with: kaddo create <type>");
|
|
9868
10173
|
return;
|
|
9869
10174
|
}
|
|
9870
|
-
const result =
|
|
10175
|
+
const result = classify2(activeItem.type, activeItem.knowledgeLevel, touchedFiles);
|
|
9871
10176
|
printResult2(result, activeItem.id || activeItem.title);
|
|
9872
10177
|
}
|
|
9873
10178
|
|
|
@@ -9936,7 +10241,7 @@ function runStatus() {
|
|
|
9936
10241
|
}
|
|
9937
10242
|
|
|
9938
10243
|
// src/commands/learn.ts
|
|
9939
|
-
import
|
|
10244
|
+
import matter7 from "gray-matter";
|
|
9940
10245
|
var ARCH_DIR9 = "knowledge";
|
|
9941
10246
|
var WORK_ITEMS_DIR2 = "knowledge/delivery/work-items";
|
|
9942
10247
|
function findWorkItemFile(dir, id) {
|
|
@@ -9948,7 +10253,7 @@ function findWorkItemFile(dir, id) {
|
|
|
9948
10253
|
}
|
|
9949
10254
|
function updateWorkItemFile(filePath, learning) {
|
|
9950
10255
|
const raw = readFile(filePath);
|
|
9951
|
-
const { data, content } =
|
|
10256
|
+
const { data, content } = matter7(raw);
|
|
9952
10257
|
data.status = "done";
|
|
9953
10258
|
data.completed_at = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
9954
10259
|
let updatedContent = content;
|
|
@@ -9973,7 +10278,7 @@ ${learning.trim()}
|
|
|
9973
10278
|
${learning.trim()}
|
|
9974
10279
|
`;
|
|
9975
10280
|
}
|
|
9976
|
-
const newRaw =
|
|
10281
|
+
const newRaw = matter7.stringify(updatedContent, data);
|
|
9977
10282
|
writeFile(filePath, newRaw);
|
|
9978
10283
|
}
|
|
9979
10284
|
async function runLearn(artifactId) {
|
|
@@ -10223,7 +10528,7 @@ function runAdd(moduleName, opts = {}, dir = cwd()) {
|
|
|
10223
10528
|
}
|
|
10224
10529
|
|
|
10225
10530
|
// src/core/ownership-suggest.ts
|
|
10226
|
-
import
|
|
10531
|
+
import matter8 from "gray-matter";
|
|
10227
10532
|
var SCAN_PATH = ".kaddo/scan.json";
|
|
10228
10533
|
function normalizeGlob(input) {
|
|
10229
10534
|
let g = input.trim().replace(/\\/g, "/");
|
|
@@ -10345,11 +10650,11 @@ function suggestGlobs(artifact, signals) {
|
|
|
10345
10650
|
return [...new Set(out)];
|
|
10346
10651
|
}
|
|
10347
10652
|
function applyOwnership(raw, globs, mode = "replace") {
|
|
10348
|
-
const parsed =
|
|
10653
|
+
const parsed = matter8(raw);
|
|
10349
10654
|
const existing = toStringArray3(parsed.data.code);
|
|
10350
10655
|
const next = mode === "append" ? [.../* @__PURE__ */ new Set([...existing, ...globs])] : [...new Set(globs)];
|
|
10351
10656
|
const data = { ...parsed.data, code: next };
|
|
10352
|
-
return
|
|
10657
|
+
return matter8.stringify(parsed.content, data);
|
|
10353
10658
|
}
|
|
10354
10659
|
|
|
10355
10660
|
// src/commands/owners.ts
|
|
@@ -12961,7 +13266,7 @@ function runGraphExport(opts = {}) {
|
|
|
12961
13266
|
}
|
|
12962
13267
|
|
|
12963
13268
|
// src/core/impact-report.ts
|
|
12964
|
-
import
|
|
13269
|
+
import matter9 from "gray-matter";
|
|
12965
13270
|
function isBroadGlob(glob) {
|
|
12966
13271
|
if (!glob.endsWith("/**")) return false;
|
|
12967
13272
|
const prefix = glob.slice(0, -3);
|
|
@@ -13030,7 +13335,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
13030
13335
|
}
|
|
13031
13336
|
}
|
|
13032
13337
|
try {
|
|
13033
|
-
const body =
|
|
13338
|
+
const body = matter9(readFile(wi.filePath)).content;
|
|
13034
13339
|
if (hasSection(body, /acceptance|criterios de aceptaci/i)) withAcceptance++;
|
|
13035
13340
|
else gaps.missing_acceptance_criteria.push(gapItem(wi, "Add an `## Acceptance Criteria` section."));
|
|
13036
13341
|
if (hasSection(body, /definition of done|^#{1,6}\s*dod\b|definici[oó]n de (terminado|hecho)/i)) withDoD++;
|
|
@@ -14141,6 +14446,70 @@ function runTechOrganize(dir = cwd()) {
|
|
|
14141
14446
|
outro2("Tech knowledge organized.");
|
|
14142
14447
|
}
|
|
14143
14448
|
|
|
14449
|
+
// src/commands/assets.ts
|
|
14450
|
+
var LABEL = { agent: "Agents", skill: "Skills" };
|
|
14451
|
+
function runAssetsStatus(kind, opts = {}) {
|
|
14452
|
+
const dir = cwd();
|
|
14453
|
+
requireConfig(dir);
|
|
14454
|
+
const summary = assetStatus(dir, kind);
|
|
14455
|
+
if (opts.json) {
|
|
14456
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
14457
|
+
return;
|
|
14458
|
+
}
|
|
14459
|
+
console.log("");
|
|
14460
|
+
console.log(LABEL[kind]);
|
|
14461
|
+
console.log("");
|
|
14462
|
+
const installed = summary.items.filter((i) => i.state !== "missing");
|
|
14463
|
+
for (const a of installed) {
|
|
14464
|
+
console.log(`${a.path}`);
|
|
14465
|
+
console.log(` Name: ${a.name}`);
|
|
14466
|
+
console.log(` Installed: ${a.installed ?? "unknown"}`);
|
|
14467
|
+
console.log(` Available: ${a.available}`);
|
|
14468
|
+
console.log(` Status: ${a.state}`);
|
|
14469
|
+
console.log("");
|
|
14470
|
+
}
|
|
14471
|
+
if (installed.length === 0) console.log(`No ${kind}s installed. Run \`kaddo add ${kind}s\`.`);
|
|
14472
|
+
console.log(
|
|
14473
|
+
`Summary: ${summary.up_to_date} up-to-date, ${summary.outdated} outdated, ${summary.unknown_version} unknown-version, ${summary.modified} modified, ${summary.missing} not installed.`
|
|
14474
|
+
);
|
|
14475
|
+
if (summary.outdated > 0 || summary.unknown_version > 0) {
|
|
14476
|
+
console.log(`Run \`kaddo ${kind}s update\` to refresh outdated ${kind}s (\`--force\` for unknown/modified).`);
|
|
14477
|
+
}
|
|
14478
|
+
printCommandFooter(`${kind}s status`);
|
|
14479
|
+
}
|
|
14480
|
+
function runAssetsUpdate(kind, opts = {}) {
|
|
14481
|
+
const dir = cwd();
|
|
14482
|
+
requireConfig(dir);
|
|
14483
|
+
intro2(`kaddo ${kind}s update`);
|
|
14484
|
+
const summary = assetStatus(dir, kind);
|
|
14485
|
+
const catalog = kind === "agent" ? canonicalAgents() : canonicalSkills();
|
|
14486
|
+
const byPath = new Map(catalog.map((a) => [a.path, a.content]));
|
|
14487
|
+
const updated = [];
|
|
14488
|
+
const skipped = [];
|
|
14489
|
+
for (const item of summary.items) {
|
|
14490
|
+
const canonical = byPath.get(item.path);
|
|
14491
|
+
if (!canonical) continue;
|
|
14492
|
+
const safe = item.state === "outdated";
|
|
14493
|
+
const needsForce = item.state === "unknown-version" || item.state === "modified";
|
|
14494
|
+
if (safe || needsForce && opts.force) {
|
|
14495
|
+
writeFile(join(dir, item.path), canonical);
|
|
14496
|
+
updated.push(`${item.path} (${item.installed ?? "unknown"} \u2192 ${item.available})`);
|
|
14497
|
+
} else if (needsForce) {
|
|
14498
|
+
skipped.push(`${item.path} \u2014 ${item.state} (use --force to overwrite local changes)`);
|
|
14499
|
+
}
|
|
14500
|
+
}
|
|
14501
|
+
if (updated.length > 0) {
|
|
14502
|
+
log2.success("Updated:");
|
|
14503
|
+
for (const u of updated) console.log(` - ${u}`);
|
|
14504
|
+
} else {
|
|
14505
|
+
log2.info(`No ${kind}s to update.`);
|
|
14506
|
+
}
|
|
14507
|
+
for (const s of skipped) log2.warn(`Skipped ${s}`);
|
|
14508
|
+
if (summary.missing > 0) log2.info(`${summary.missing} ${kind}(s) not installed \u2014 run \`kaddo add ${kind}s\` to add them.`);
|
|
14509
|
+
printCommandFooter(`${kind}s status`);
|
|
14510
|
+
outro2(`${LABEL[kind]} update complete.`);
|
|
14511
|
+
}
|
|
14512
|
+
|
|
14144
14513
|
// src/index.ts
|
|
14145
14514
|
var require2 = createRequire(import.meta.url);
|
|
14146
14515
|
var { version } = require2("../package.json");
|
|
@@ -14194,6 +14563,12 @@ program.command("drift").description("Drift Trend Report from recorded `kaddo gu
|
|
|
14194
14563
|
var questionsAction = (opts) => runQuestions(opts);
|
|
14195
14564
|
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);
|
|
14196
14565
|
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);
|
|
14566
|
+
var agentsCmd = program.command("agents").description("Inspect and update installed Kaddo agents (version status)");
|
|
14567
|
+
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));
|
|
14568
|
+
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));
|
|
14569
|
+
var skillsCmd = program.command("skills").description("Inspect and update installed Kaddo skills (version status)");
|
|
14570
|
+
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));
|
|
14571
|
+
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));
|
|
14197
14572
|
var techCmd = program.command("tech").description("Organize the knowledge/tech/ structure (core vs discovery vs decisions)");
|
|
14198
14573
|
techCmd.command("organize").description("Move discovery artifacts into knowledge/tech/discovery/ (never overwrites, no content change)").action(() => {
|
|
14199
14574
|
runTechOrganize();
|