@kaddo/cli 3.44.0 → 3.46.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 +1 -0
- package/dist/index.js +457 -122
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -543,6 +543,7 @@ create --from roadmap → owners → guard → explain`.
|
|
|
543
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
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` |
|
|
545
545
|
| v3.44 | Roadmap counting alignment + materialization quality: `explain`/`context` separate **Roadmap initiatives** from **Work Item candidates** (`## Roadmap Status`); two-level `roadmapQuality` (initiatives + work_item_candidates); `create --from roadmap` normalizes metadata — fills `domains` from `related_domain`, splits comma-joined `related_capabilities` into a real list, carries `source_roadmap_initiative`/`source_work_item_candidate`/`source_signals`/`decision_candidates`, improved Source + Context-From-Roadmap body + ADR warning; MCP `kaddo://work-item-candidates` |
|
|
546
|
+
| v3.45 | State-aware next step: `resolveNextStep` decides from the real delivery state (draft/ready/in-progress, ownership, ADRs, adapters) instead of always suggesting `create --from roadmap` — draft → work-item-agent, ready → adapter/implementation-agent, in-progress → guard; parallel **secondary** recommendations (ownership, ADRs, remaining candidates); `deliveryState` + recommendation in `explain`/`context`; MCP `kaddo://next-step` |
|
|
546
547
|
|
|
547
548
|
**Optional modules (installed with `kaddo add`):**
|
|
548
549
|
|
package/dist/index.js
CHANGED
|
@@ -8113,7 +8113,124 @@ function buildSharedFileStatuses(statuses) {
|
|
|
8113
8113
|
}));
|
|
8114
8114
|
}
|
|
8115
8115
|
|
|
8116
|
+
// src/core/decisions.ts
|
|
8117
|
+
var CANDIDATES_DISCOVERY = "knowledge/tech/discovery/decision-candidates.md";
|
|
8118
|
+
var CANDIDATES_LEGACY = "knowledge/tech/decision-candidates.md";
|
|
8119
|
+
var DECISIONS_DIR = "knowledge/tech/decisions";
|
|
8120
|
+
function resolveCandidatesPath(dir) {
|
|
8121
|
+
const discovery = exists(join(dir, CANDIDATES_DISCOVERY));
|
|
8122
|
+
const legacy = exists(join(dir, CANDIDATES_LEGACY));
|
|
8123
|
+
if (discovery) return { path: CANDIDATES_DISCOVERY, legacy: false, bothExist: legacy };
|
|
8124
|
+
if (legacy) return { path: CANDIDATES_LEGACY, legacy: true, bothExist: false };
|
|
8125
|
+
return { path: null, legacy: false, bothExist: false };
|
|
8126
|
+
}
|
|
8127
|
+
function cleanCandidateTitle(title) {
|
|
8128
|
+
return title.replace(/^\s*#{1,6}\s+/, "").replace(/^\s*[-*]\s+/, "").replace(/^\s*\(?\d+\)?[.):]\s+/, "").trim();
|
|
8129
|
+
}
|
|
8130
|
+
function slugify2(s) {
|
|
8131
|
+
return cleanCandidateTitle(s).toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 70).replace(/-+$/g, "");
|
|
8132
|
+
}
|
|
8133
|
+
function parseDecisionCandidates(md) {
|
|
8134
|
+
const out = [];
|
|
8135
|
+
for (const line of md.split(/\r?\n/)) {
|
|
8136
|
+
const m = line.match(/^##\s+(.+?)\s*$/);
|
|
8137
|
+
if (m) {
|
|
8138
|
+
const raw = m[1].trim();
|
|
8139
|
+
if (!raw || /^_.*_$/.test(raw)) continue;
|
|
8140
|
+
const t = cleanCandidateTitle(raw);
|
|
8141
|
+
if (t) out.push(t);
|
|
8142
|
+
}
|
|
8143
|
+
}
|
|
8144
|
+
return out;
|
|
8145
|
+
}
|
|
8146
|
+
function countAdrs(dir) {
|
|
8147
|
+
const base = join(dir, DECISIONS_DIR);
|
|
8148
|
+
if (!exists(base)) return { total: 0, draft: 0, accepted: 0 };
|
|
8149
|
+
let total = 0;
|
|
8150
|
+
let draft = 0;
|
|
8151
|
+
let accepted = 0;
|
|
8152
|
+
for (const entry of readDir(base)) {
|
|
8153
|
+
if (!entry.endsWith(".md") || entry === ".gitkeep") continue;
|
|
8154
|
+
const full = join(base, entry);
|
|
8155
|
+
if (!isFile(full)) continue;
|
|
8156
|
+
total += 1;
|
|
8157
|
+
let content = "";
|
|
8158
|
+
try {
|
|
8159
|
+
content = readFile(full);
|
|
8160
|
+
} catch {
|
|
8161
|
+
continue;
|
|
8162
|
+
}
|
|
8163
|
+
const status = content.match(/^\s*status:\s*([a-z-]+)/im)?.[1]?.toLowerCase();
|
|
8164
|
+
if (status === "accepted") accepted += 1;
|
|
8165
|
+
else draft += 1;
|
|
8166
|
+
}
|
|
8167
|
+
return { total, draft, accepted };
|
|
8168
|
+
}
|
|
8169
|
+
function buildTechDecisions(dir) {
|
|
8170
|
+
const resolved = resolveCandidatesPath(dir);
|
|
8171
|
+
let titles = [];
|
|
8172
|
+
if (resolved.path) {
|
|
8173
|
+
try {
|
|
8174
|
+
titles = parseDecisionCandidates(readFile(join(dir, resolved.path)));
|
|
8175
|
+
} catch {
|
|
8176
|
+
titles = [];
|
|
8177
|
+
}
|
|
8178
|
+
}
|
|
8179
|
+
const { total, draft, accepted } = countAdrs(dir);
|
|
8180
|
+
const candidate_list = titles.map((title, i) => {
|
|
8181
|
+
const n = String(total + i + 1).padStart(3, "0");
|
|
8182
|
+
return { title, source: resolved.path, suggestedAdrFile: `${DECISIONS_DIR}/ADR-${n}-${slugify2(title)}.md` };
|
|
8183
|
+
});
|
|
8184
|
+
let status;
|
|
8185
|
+
if (accepted > 0) status = "accepted-adrs";
|
|
8186
|
+
else if (total > 0) status = "draft-adrs";
|
|
8187
|
+
else if (titles.length > 0) status = "candidates";
|
|
8188
|
+
else status = "none";
|
|
8189
|
+
return {
|
|
8190
|
+
status,
|
|
8191
|
+
candidates: titles.length,
|
|
8192
|
+
adrs: total,
|
|
8193
|
+
draft_adrs: draft,
|
|
8194
|
+
accepted_adrs: accepted,
|
|
8195
|
+
candidate_list,
|
|
8196
|
+
candidates_source: resolved.path,
|
|
8197
|
+
candidates_legacy_location: resolved.legacy,
|
|
8198
|
+
candidates_both_exist: resolved.bothExist
|
|
8199
|
+
};
|
|
8200
|
+
}
|
|
8201
|
+
|
|
8116
8202
|
// src/core/next-step.ts
|
|
8203
|
+
function buildDeliveryState(dir) {
|
|
8204
|
+
const wis = discoverWorkItems(dir);
|
|
8205
|
+
const byState = (s) => wis.filter((w) => w.lifecycle === s).length;
|
|
8206
|
+
const total = wis.length;
|
|
8207
|
+
const withOwnership = wis.filter((w) => w.codeGlobs.length > 0).length;
|
|
8208
|
+
const td = buildTechDecisions(dir);
|
|
8209
|
+
const roadmapPath = join(dir, "knowledge/delivery/roadmap.md");
|
|
8210
|
+
const roadmapMd = exists(roadmapPath) ? safeRead(roadmapPath) : null;
|
|
8211
|
+
const stats = roadmapStats(roadmapMd, total);
|
|
8212
|
+
const adapters = installedAdapters(dir);
|
|
8213
|
+
return {
|
|
8214
|
+
phase: "",
|
|
8215
|
+
draft_work_items: byState("draft"),
|
|
8216
|
+
ready_work_items: byState("ready"),
|
|
8217
|
+
in_progress_work_items: byState("in-progress"),
|
|
8218
|
+
blocked_work_items: byState("blocked"),
|
|
8219
|
+
total_work_items: total,
|
|
8220
|
+
ownership_coverage: `${withOwnership}/${total}`,
|
|
8221
|
+
remaining_work_item_candidates: stats.remaining_work_item_candidates,
|
|
8222
|
+
decision_candidates: td.candidates,
|
|
8223
|
+
accepted_adrs: td.accepted_adrs,
|
|
8224
|
+
adapters_installed: adapters.length
|
|
8225
|
+
};
|
|
8226
|
+
}
|
|
8227
|
+
function safeRead(p2) {
|
|
8228
|
+
try {
|
|
8229
|
+
return readFile(p2);
|
|
8230
|
+
} catch {
|
|
8231
|
+
return null;
|
|
8232
|
+
}
|
|
8233
|
+
}
|
|
8117
8234
|
function roadmapSignal(dir) {
|
|
8118
8235
|
const p2 = join(dir, "knowledge/delivery/roadmap.md");
|
|
8119
8236
|
if (!exists(p2)) return "missing";
|
|
@@ -8210,15 +8327,117 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
8210
8327
|
if (roadmap !== "has-candidates") {
|
|
8211
8328
|
return { id: "roadmap", phase: "Planning", label: "Use roadmap-agent to define roadmap candidates (`kaddo roadmap`).", command: "kaddo roadmap", agent: "roadmap-agent", target: "knowledge/delivery/roadmap.md", reason: "The roadmap has no candidates yet." };
|
|
8212
8329
|
}
|
|
8213
|
-
const
|
|
8214
|
-
|
|
8215
|
-
|
|
8330
|
+
const st = buildDeliveryState(dir);
|
|
8331
|
+
const secondary = buildSecondaryRecommendations(st);
|
|
8332
|
+
if (st.total_work_items === 0) {
|
|
8333
|
+
return {
|
|
8334
|
+
id: "create-work-item",
|
|
8335
|
+
phase: "Delivery Preparation",
|
|
8336
|
+
label: "Run `kaddo create --from roadmap` to materialize the first Work Item.",
|
|
8337
|
+
command: "kaddo create --from roadmap",
|
|
8338
|
+
reason: "The roadmap has candidates but no Work Item exists yet.",
|
|
8339
|
+
...secondary.length ? { secondary } : {}
|
|
8340
|
+
};
|
|
8216
8341
|
}
|
|
8217
|
-
const adapters =
|
|
8218
|
-
if (
|
|
8219
|
-
|
|
8342
|
+
const adapters = st.adapters_installed;
|
|
8343
|
+
if (st.ready_work_items > 0) {
|
|
8344
|
+
if (adapters === 0) {
|
|
8345
|
+
return {
|
|
8346
|
+
id: "install-adapter",
|
|
8347
|
+
phase: "Active Delivery",
|
|
8348
|
+
label: "Install or configure an adapter before implementation (`kaddo adapters list`).",
|
|
8349
|
+
command: "kaddo adapters list",
|
|
8350
|
+
reason: `${st.ready_work_items} Work Item(s) are ready but no adapter is installed.`,
|
|
8351
|
+
...secondary.length ? { secondary } : {}
|
|
8352
|
+
};
|
|
8353
|
+
}
|
|
8354
|
+
return {
|
|
8355
|
+
id: "implement",
|
|
8356
|
+
phase: "Active Delivery",
|
|
8357
|
+
label: "Use the implementation-agent or your installed adapter to plan implementation.",
|
|
8358
|
+
agent: "implementation-agent",
|
|
8359
|
+
reason: `${st.ready_work_items} Work Item(s) are ready and an adapter is installed.`,
|
|
8360
|
+
...secondary.length ? { secondary } : {}
|
|
8361
|
+
};
|
|
8362
|
+
}
|
|
8363
|
+
if (st.in_progress_work_items > 0) {
|
|
8364
|
+
return {
|
|
8365
|
+
id: "guard",
|
|
8366
|
+
phase: "Active Delivery",
|
|
8367
|
+
label: "Run `kaddo guard` and update affected knowledge after significant changes.",
|
|
8368
|
+
command: "kaddo guard",
|
|
8369
|
+
reason: `${st.in_progress_work_items} Work Item(s) are in progress.`,
|
|
8370
|
+
...secondary.length ? { secondary } : {}
|
|
8371
|
+
};
|
|
8372
|
+
}
|
|
8373
|
+
if (st.draft_work_items > 0) {
|
|
8374
|
+
return {
|
|
8375
|
+
id: "refine-work-item",
|
|
8376
|
+
phase: "Active Delivery",
|
|
8377
|
+
label: "Refine the existing draft Work Item with the work-item-agent.",
|
|
8378
|
+
agent: "work-item-agent",
|
|
8379
|
+
skill: "work-item-refinement",
|
|
8380
|
+
reason: `There ${st.draft_work_items === 1 ? "is" : "are"} ${st.draft_work_items} draft Work Item${st.draft_work_items === 1 ? "" : "s"} and no Work Item is ready.`,
|
|
8381
|
+
...secondary.length ? { secondary } : {}
|
|
8382
|
+
};
|
|
8383
|
+
}
|
|
8384
|
+
if (st.blocked_work_items > 0) {
|
|
8385
|
+
return {
|
|
8386
|
+
id: "resolve-blocker",
|
|
8387
|
+
phase: "Active Delivery",
|
|
8388
|
+
label: "Resolve the blocker on the blocked Work Item with the work-item-agent.",
|
|
8389
|
+
agent: "work-item-agent",
|
|
8390
|
+
reason: `${st.blocked_work_items} Work Item(s) are blocked.`,
|
|
8391
|
+
...secondary.length ? { secondary } : {}
|
|
8392
|
+
};
|
|
8393
|
+
}
|
|
8394
|
+
if (st.remaining_work_item_candidates > 0) {
|
|
8395
|
+
return {
|
|
8396
|
+
id: "materialize-more-work-items",
|
|
8397
|
+
phase: "Maintenance",
|
|
8398
|
+
label: `Materialize the remaining ${st.remaining_work_item_candidates} Work Item candidate(s) with \`kaddo create --from roadmap\`.`,
|
|
8399
|
+
command: "kaddo create --from roadmap",
|
|
8400
|
+
reason: "No active Work Items remain; roadmap candidates are still pending.",
|
|
8401
|
+
...secondary.length ? { secondary } : {}
|
|
8402
|
+
};
|
|
8403
|
+
}
|
|
8404
|
+
return {
|
|
8405
|
+
id: "plan-next",
|
|
8406
|
+
phase: "Maintenance",
|
|
8407
|
+
label: "Use the roadmap-agent to plan the next initiative.",
|
|
8408
|
+
agent: "roadmap-agent",
|
|
8409
|
+
reason: "No active Work Items and no remaining roadmap candidates."
|
|
8410
|
+
};
|
|
8411
|
+
}
|
|
8412
|
+
function buildSecondaryRecommendations(st) {
|
|
8413
|
+
const out = [];
|
|
8414
|
+
const [withOwnership] = st.ownership_coverage.split("/").map(Number);
|
|
8415
|
+
if (st.total_work_items > 0 && withOwnership < st.total_work_items) {
|
|
8416
|
+
out.push({
|
|
8417
|
+
id: "suggest-ownership",
|
|
8418
|
+
label: "Run `kaddo owners suggest` for Work Items without code ownership.",
|
|
8419
|
+
command: "kaddo owners suggest",
|
|
8420
|
+
reason: `Ownership coverage is ${st.ownership_coverage}.`
|
|
8421
|
+
});
|
|
8220
8422
|
}
|
|
8221
|
-
|
|
8423
|
+
if (st.total_work_items > 0 && st.decision_candidates > 0 && st.accepted_adrs === 0) {
|
|
8424
|
+
out.push({
|
|
8425
|
+
id: "materialize-adrs",
|
|
8426
|
+
label: "Use the adr-writing skill (`kaddo adr`) to materialize decision candidates into ADRs before implementing related technical Work Items.",
|
|
8427
|
+
command: "kaddo adr",
|
|
8428
|
+
skill: "adr-writing",
|
|
8429
|
+
reason: `There are ${st.decision_candidates} technical decision candidate(s) and ${st.accepted_adrs} accepted ADR(s).`
|
|
8430
|
+
});
|
|
8431
|
+
}
|
|
8432
|
+
if (st.total_work_items > 0 && st.remaining_work_item_candidates > 0) {
|
|
8433
|
+
out.push({
|
|
8434
|
+
id: "materialize-more-work-items",
|
|
8435
|
+
label: `Later, materialize the remaining ${st.remaining_work_item_candidates} Work Item candidate(s) with \`kaddo create --from roadmap\`.`,
|
|
8436
|
+
command: "kaddo create --from roadmap",
|
|
8437
|
+
reason: `There are ${st.remaining_work_item_candidates} remaining Work Item candidate(s).`
|
|
8438
|
+
});
|
|
8439
|
+
}
|
|
8440
|
+
return out;
|
|
8222
8441
|
}
|
|
8223
8442
|
|
|
8224
8443
|
// src/core/readiness.ts
|
|
@@ -8313,92 +8532,6 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
8313
8532
|
};
|
|
8314
8533
|
}
|
|
8315
8534
|
|
|
8316
|
-
// src/core/decisions.ts
|
|
8317
|
-
var CANDIDATES_DISCOVERY = "knowledge/tech/discovery/decision-candidates.md";
|
|
8318
|
-
var CANDIDATES_LEGACY = "knowledge/tech/decision-candidates.md";
|
|
8319
|
-
var DECISIONS_DIR = "knowledge/tech/decisions";
|
|
8320
|
-
function resolveCandidatesPath(dir) {
|
|
8321
|
-
const discovery = exists(join(dir, CANDIDATES_DISCOVERY));
|
|
8322
|
-
const legacy = exists(join(dir, CANDIDATES_LEGACY));
|
|
8323
|
-
if (discovery) return { path: CANDIDATES_DISCOVERY, legacy: false, bothExist: legacy };
|
|
8324
|
-
if (legacy) return { path: CANDIDATES_LEGACY, legacy: true, bothExist: false };
|
|
8325
|
-
return { path: null, legacy: false, bothExist: false };
|
|
8326
|
-
}
|
|
8327
|
-
function cleanCandidateTitle(title) {
|
|
8328
|
-
return title.replace(/^\s*#{1,6}\s+/, "").replace(/^\s*[-*]\s+/, "").replace(/^\s*\(?\d+\)?[.):]\s+/, "").trim();
|
|
8329
|
-
}
|
|
8330
|
-
function slugify2(s) {
|
|
8331
|
-
return cleanCandidateTitle(s).toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 70).replace(/-+$/g, "");
|
|
8332
|
-
}
|
|
8333
|
-
function parseDecisionCandidates(md) {
|
|
8334
|
-
const out = [];
|
|
8335
|
-
for (const line of md.split(/\r?\n/)) {
|
|
8336
|
-
const m = line.match(/^##\s+(.+?)\s*$/);
|
|
8337
|
-
if (m) {
|
|
8338
|
-
const raw = m[1].trim();
|
|
8339
|
-
if (!raw || /^_.*_$/.test(raw)) continue;
|
|
8340
|
-
const t = cleanCandidateTitle(raw);
|
|
8341
|
-
if (t) out.push(t);
|
|
8342
|
-
}
|
|
8343
|
-
}
|
|
8344
|
-
return out;
|
|
8345
|
-
}
|
|
8346
|
-
function countAdrs(dir) {
|
|
8347
|
-
const base = join(dir, DECISIONS_DIR);
|
|
8348
|
-
if (!exists(base)) return { total: 0, draft: 0, accepted: 0 };
|
|
8349
|
-
let total = 0;
|
|
8350
|
-
let draft = 0;
|
|
8351
|
-
let accepted = 0;
|
|
8352
|
-
for (const entry of readDir(base)) {
|
|
8353
|
-
if (!entry.endsWith(".md") || entry === ".gitkeep") continue;
|
|
8354
|
-
const full = join(base, entry);
|
|
8355
|
-
if (!isFile(full)) continue;
|
|
8356
|
-
total += 1;
|
|
8357
|
-
let content = "";
|
|
8358
|
-
try {
|
|
8359
|
-
content = readFile(full);
|
|
8360
|
-
} catch {
|
|
8361
|
-
continue;
|
|
8362
|
-
}
|
|
8363
|
-
const status = content.match(/^\s*status:\s*([a-z-]+)/im)?.[1]?.toLowerCase();
|
|
8364
|
-
if (status === "accepted") accepted += 1;
|
|
8365
|
-
else draft += 1;
|
|
8366
|
-
}
|
|
8367
|
-
return { total, draft, accepted };
|
|
8368
|
-
}
|
|
8369
|
-
function buildTechDecisions(dir) {
|
|
8370
|
-
const resolved = resolveCandidatesPath(dir);
|
|
8371
|
-
let titles = [];
|
|
8372
|
-
if (resolved.path) {
|
|
8373
|
-
try {
|
|
8374
|
-
titles = parseDecisionCandidates(readFile(join(dir, resolved.path)));
|
|
8375
|
-
} catch {
|
|
8376
|
-
titles = [];
|
|
8377
|
-
}
|
|
8378
|
-
}
|
|
8379
|
-
const { total, draft, accepted } = countAdrs(dir);
|
|
8380
|
-
const candidate_list = titles.map((title, i) => {
|
|
8381
|
-
const n = String(total + i + 1).padStart(3, "0");
|
|
8382
|
-
return { title, source: resolved.path, suggestedAdrFile: `${DECISIONS_DIR}/ADR-${n}-${slugify2(title)}.md` };
|
|
8383
|
-
});
|
|
8384
|
-
let status;
|
|
8385
|
-
if (accepted > 0) status = "accepted-adrs";
|
|
8386
|
-
else if (total > 0) status = "draft-adrs";
|
|
8387
|
-
else if (titles.length > 0) status = "candidates";
|
|
8388
|
-
else status = "none";
|
|
8389
|
-
return {
|
|
8390
|
-
status,
|
|
8391
|
-
candidates: titles.length,
|
|
8392
|
-
adrs: total,
|
|
8393
|
-
draft_adrs: draft,
|
|
8394
|
-
accepted_adrs: accepted,
|
|
8395
|
-
candidate_list,
|
|
8396
|
-
candidates_source: resolved.path,
|
|
8397
|
-
candidates_legacy_location: resolved.legacy,
|
|
8398
|
-
candidates_both_exist: resolved.bothExist
|
|
8399
|
-
};
|
|
8400
|
-
}
|
|
8401
|
-
|
|
8402
8535
|
// src/core/assets.ts
|
|
8403
8536
|
import matter4 from "gray-matter";
|
|
8404
8537
|
function canonicalAgents() {
|
|
@@ -8956,9 +9089,12 @@ function renderExplanationHuman(exp) {
|
|
|
8956
9089
|
}
|
|
8957
9090
|
lines.push(`- Next step: ${exp.readiness.recommended_next_step.label}`);
|
|
8958
9091
|
lines.push("");
|
|
8959
|
-
|
|
9092
|
+
const rec = exp.nextStepRecommendation;
|
|
9093
|
+
const secondary = rec.secondary ?? [];
|
|
9094
|
+
const steps = secondary.length > 0 || /Delivery|Active|Maintenance/.test(rec.phase) ? [rec.label, ...secondary.map((s2) => s2.label)] : exp.suggestedNextSteps;
|
|
9095
|
+
if (steps.length > 0) {
|
|
8960
9096
|
lines.push("## Suggested Next Steps");
|
|
8961
|
-
|
|
9097
|
+
steps.forEach((s2, i) => lines.push(`${i + 1}. ${s2}`));
|
|
8962
9098
|
lines.push("");
|
|
8963
9099
|
}
|
|
8964
9100
|
const r = exp.readiness;
|
|
@@ -9431,6 +9567,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
9431
9567
|
nextStep: nextStepRecommendation.label,
|
|
9432
9568
|
recommendedAgents: nextStepRecommendation.agent ? [nextStepRecommendation.agent] : phase.recommendedAgents
|
|
9433
9569
|
};
|
|
9570
|
+
const deliveryState = { ...buildDeliveryState(dir), phase: nextStepRecommendation.phase };
|
|
9434
9571
|
return {
|
|
9435
9572
|
version: CONTEXT_PACK_VERSION,
|
|
9436
9573
|
generatedAt: now.toISOString(),
|
|
@@ -9463,6 +9600,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
9463
9600
|
roadmap,
|
|
9464
9601
|
phase: unifiedPhase,
|
|
9465
9602
|
nextStepRecommendation,
|
|
9603
|
+
deliveryState,
|
|
9466
9604
|
techDecisions,
|
|
9467
9605
|
techKnowledge,
|
|
9468
9606
|
roadmapQuality: buildRoadmapQuality(dir),
|
|
@@ -9527,6 +9665,31 @@ function renderContextPack(pack) {
|
|
|
9527
9665
|
parts.push(`Next step: ${pack.phase.nextStep}
|
|
9528
9666
|
`);
|
|
9529
9667
|
}
|
|
9668
|
+
const ds = pack.deliveryState;
|
|
9669
|
+
const rec = pack.nextStepRecommendation;
|
|
9670
|
+
if (ds.total_work_items > 0 || rec.phase !== "Setup") {
|
|
9671
|
+
parts.push("## Delivery State\n");
|
|
9672
|
+
parts.push(
|
|
9673
|
+
[
|
|
9674
|
+
`- Phase: ${ds.phase}`,
|
|
9675
|
+
`- Draft Work Items: ${ds.draft_work_items}`,
|
|
9676
|
+
`- Ready Work Items: ${ds.ready_work_items}`,
|
|
9677
|
+
`- In-progress Work Items: ${ds.in_progress_work_items}`,
|
|
9678
|
+
`- Ownership coverage: ${ds.ownership_coverage}`,
|
|
9679
|
+
`- Remaining Work Item candidates: ${ds.remaining_work_item_candidates}`
|
|
9680
|
+
].join("\n") + "\n"
|
|
9681
|
+
);
|
|
9682
|
+
parts.push("## Next Step Recommendation\n");
|
|
9683
|
+
const recLines = [`- ${rec.label}`, ` - id: ${rec.id}`, ` - reason: ${rec.reason}`];
|
|
9684
|
+
if (rec.agent) recLines.push(` - agent: ${rec.agent}`);
|
|
9685
|
+
if (rec.skill) recLines.push(` - skill: ${rec.skill}`);
|
|
9686
|
+
if (rec.command) recLines.push(` - command: \`${rec.command}\``);
|
|
9687
|
+
parts.push(recLines.join("\n") + "\n");
|
|
9688
|
+
if (rec.secondary && rec.secondary.length > 0) {
|
|
9689
|
+
parts.push("Also (secondary):\n");
|
|
9690
|
+
parts.push(rec.secondary.map((s) => `- ${s.label}`).join("\n") + "\n");
|
|
9691
|
+
}
|
|
9692
|
+
}
|
|
9530
9693
|
parts.push("## Knowledge Layers\n");
|
|
9531
9694
|
parts.push(
|
|
9532
9695
|
"Project knowledge is organized in four layers: **Business \u2192 Product \u2192 Tech \u2192 Delivery**.\n"
|
|
@@ -9812,7 +9975,8 @@ function buildUnderstandPlan(dir, config) {
|
|
|
9812
9975
|
name: config.project.name,
|
|
9813
9976
|
state,
|
|
9814
9977
|
teamSize: config.team.size,
|
|
9815
|
-
structure: config.project.structure
|
|
9978
|
+
structure: config.project.structure,
|
|
9979
|
+
language: "English"
|
|
9816
9980
|
},
|
|
9817
9981
|
scanAvailable: exists(join(dir, ".kaddo", "scan.json")),
|
|
9818
9982
|
contextPackPath: ".kaddo/context-pack.md",
|
|
@@ -9821,6 +9985,18 @@ function buildUnderstandPlan(dir, config) {
|
|
|
9821
9985
|
steps
|
|
9822
9986
|
};
|
|
9823
9987
|
}
|
|
9988
|
+
function enrichUnderstandPlan(plan, opts) {
|
|
9989
|
+
return {
|
|
9990
|
+
...plan,
|
|
9991
|
+
project: { ...plan.project, language: opts.language },
|
|
9992
|
+
phase: opts.phase,
|
|
9993
|
+
nextStepRecommendation: opts.nextStepRecommendation,
|
|
9994
|
+
deliveryState: opts.deliveryState,
|
|
9995
|
+
activeWorkItems: opts.activeWorkItems,
|
|
9996
|
+
recommendedPaths: opts.recommendedPaths,
|
|
9997
|
+
recommendedSkillPaths: opts.recommendedSkillPaths
|
|
9998
|
+
};
|
|
9999
|
+
}
|
|
9824
10000
|
|
|
9825
10001
|
// src/templates/understand-template.ts
|
|
9826
10002
|
function stateLabel2(state) {
|
|
@@ -9831,42 +10007,140 @@ function agentName(file) {
|
|
|
9831
10007
|
}
|
|
9832
10008
|
function renderUnderstand(plan) {
|
|
9833
10009
|
const { project, steps } = plan;
|
|
10010
|
+
const rec = plan.nextStepRecommendation;
|
|
10011
|
+
const ds = plan.deliveryState;
|
|
9834
10012
|
const parts = [];
|
|
9835
10013
|
parts.push("# Kaddo Understand Handoff\n");
|
|
9836
10014
|
parts.push(
|
|
9837
10015
|
"> Generated by `kaddo understand`. Kaddo does not call an LLM \u2014 you stay in control of the interpretation.\n"
|
|
9838
10016
|
);
|
|
9839
10017
|
parts.push("## Project\n");
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
);
|
|
9848
|
-
|
|
9849
|
-
|
|
10018
|
+
const projectLines = [
|
|
10019
|
+
`- Name: ${project.name}`,
|
|
10020
|
+
`- State: ${project.state}`,
|
|
10021
|
+
`- Team: ${project.teamSize}`,
|
|
10022
|
+
`- Structure: ${project.structure}`
|
|
10023
|
+
];
|
|
10024
|
+
if (project.language) projectLines.push(`- Language: ${project.language}`);
|
|
10025
|
+
parts.push(projectLines.join("\n") + "\n");
|
|
10026
|
+
if (plan.phase && rec) {
|
|
10027
|
+
parts.push("## Current Phase\n");
|
|
10028
|
+
const phaseLines = [`- Phase: ${plan.phase}`];
|
|
10029
|
+
if (rec.agent) phaseLines.push(`- Recommended agent: ${agentName(rec.agent)}`);
|
|
10030
|
+
if (rec.skill) phaseLines.push(`- Recommended skill: ${rec.skill}`);
|
|
10031
|
+
phaseLines.push(`- Next step: ${rec.label}`);
|
|
10032
|
+
if (rec.reason) phaseLines.push(`- Reason: ${rec.reason}`);
|
|
10033
|
+
parts.push(phaseLines.join("\n") + "\n");
|
|
10034
|
+
}
|
|
10035
|
+
if (ds && ds.total_work_items > 0) {
|
|
10036
|
+
parts.push("## Delivery State\n");
|
|
10037
|
+
parts.push(
|
|
10038
|
+
[
|
|
10039
|
+
`- Draft Work Items: ${ds.draft_work_items}`,
|
|
10040
|
+
`- Ready Work Items: ${ds.ready_work_items}`,
|
|
10041
|
+
`- In-progress Work Items: ${ds.in_progress_work_items}`,
|
|
10042
|
+
`- Blocked Work Items: ${ds.blocked_work_items}`,
|
|
10043
|
+
`- Ownership coverage: ${ds.ownership_coverage}`,
|
|
10044
|
+
`- Remaining Work Item candidates: ${ds.remaining_work_item_candidates}`,
|
|
10045
|
+
`- Technical decision candidates: ${ds.decision_candidates}`,
|
|
10046
|
+
`- Accepted ADRs: ${ds.accepted_adrs}`,
|
|
10047
|
+
`- Installed adapters: ${ds.adapters_installed}`
|
|
10048
|
+
].join("\n") + "\n"
|
|
10049
|
+
);
|
|
10050
|
+
}
|
|
10051
|
+
if (steps.length > 0) {
|
|
10052
|
+
parts.push("## Recommended Agent Flow\n");
|
|
10053
|
+
parts.push(`Recommended order for a ${stateLabel2(project.state)} project:
|
|
9850
10054
|
`);
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
10055
|
+
parts.push(
|
|
10056
|
+
steps.map((s, i) => {
|
|
10057
|
+
const flag = s.installed ? "" : " _(not installed \u2014 run `kaddo add agents`)_";
|
|
10058
|
+
return `${i + 1}. ${agentName(s.agent)} \u2192 \`${s.output}\`${flag}`;
|
|
10059
|
+
}).join("\n") + "\n"
|
|
10060
|
+
);
|
|
10061
|
+
} else if (rec) {
|
|
10062
|
+
parts.push("## Recommended Agent Flow\n");
|
|
10063
|
+
const flowSteps = [];
|
|
10064
|
+
if (rec.agent) flowSteps.push(agentName(rec.agent));
|
|
10065
|
+
if (rec.secondary) {
|
|
10066
|
+
for (const s of rec.secondary) {
|
|
10067
|
+
if (s.agent) flowSteps.push(agentName(s.agent));
|
|
10068
|
+
else if (s.skill) flowSteps.push(`${s.skill} skill`);
|
|
10069
|
+
else if (s.command) flowSteps.push(`\`${s.command}\``);
|
|
10070
|
+
}
|
|
10071
|
+
}
|
|
10072
|
+
if (flowSteps.length > 0) {
|
|
10073
|
+
parts.push(flowSteps.map((f, i) => `${i + 1}. ${f}`).join("\n") + "\n");
|
|
10074
|
+
}
|
|
10075
|
+
}
|
|
10076
|
+
if (rec) {
|
|
10077
|
+
parts.push("## Primary Recommendation\n");
|
|
10078
|
+
const recLines = [`- id: ${rec.id}`];
|
|
10079
|
+
if (rec.agent) recLines.push(`- agent: ${agentName(rec.agent)}`);
|
|
10080
|
+
if (rec.skill) recLines.push(`- skill: ${rec.skill}`);
|
|
10081
|
+
if (rec.command) recLines.push(`- command: \`${rec.command}\``);
|
|
10082
|
+
recLines.push(`- reason: ${rec.reason}`);
|
|
10083
|
+
parts.push(recLines.join("\n") + "\n");
|
|
10084
|
+
}
|
|
10085
|
+
if (rec?.secondary && rec.secondary.length > 0) {
|
|
10086
|
+
parts.push("## Secondary Recommendations\n");
|
|
10087
|
+
parts.push(rec.secondary.map((s, i) => `${i + 1}. ${s.label}`).join("\n") + "\n");
|
|
10088
|
+
}
|
|
10089
|
+
if (plan.activeWorkItems && plan.activeWorkItems.length > 0) {
|
|
10090
|
+
parts.push("## Active Work Items\n");
|
|
10091
|
+
parts.push(
|
|
10092
|
+
plan.activeWorkItems.map((w) => `- ${w.id} [${w.type}] ${w.lifecycle} \u2014 ${w.title}`).join("\n") + "\n"
|
|
10093
|
+
);
|
|
10094
|
+
}
|
|
9857
10095
|
parts.push("## Context Pack\n");
|
|
9858
10096
|
const scanNote = plan.scanAvailable ? "" : " (incomplete \u2014 run `kaddo scan` first for a richer baseline)";
|
|
9859
10097
|
parts.push(`Use \`${plan.contextPackPath}\` as the primary input${scanNote}.
|
|
9860
10098
|
`);
|
|
10099
|
+
const agentPaths = [];
|
|
10100
|
+
if (plan.recommendedPaths && plan.recommendedPaths.length > 0) {
|
|
10101
|
+
agentPaths.push(...plan.recommendedPaths);
|
|
10102
|
+
} else if (steps.length > 0) {
|
|
10103
|
+
agentPaths.push(...steps.map((s) => agentInstallPath(s.agent)));
|
|
10104
|
+
}
|
|
10105
|
+
if (plan.recommendedSkillPaths && plan.recommendedSkillPaths.length > 0) {
|
|
10106
|
+
agentPaths.push(...plan.recommendedSkillPaths);
|
|
10107
|
+
}
|
|
10108
|
+
agentPaths.push(plan.contextPackPath);
|
|
9861
10109
|
parts.push("## Agent Prompts\n");
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
|
|
9865
|
-
|
|
9866
|
-
|
|
10110
|
+
if (agentPaths.length > 1) {
|
|
10111
|
+
parts.push("Use:\n");
|
|
10112
|
+
parts.push(agentPaths.map((p2) => `- \`${p2}\``).join("\n") + "\n");
|
|
10113
|
+
} else {
|
|
10114
|
+
parts.push(`- \`${plan.contextPackPath}\`
|
|
10115
|
+
`);
|
|
10116
|
+
}
|
|
10117
|
+
if (steps.length > 0) {
|
|
10118
|
+
parts.push("## Expected Outputs\n");
|
|
10119
|
+
parts.push(steps.map((s) => `- \`${s.output}\``).join("\n") + "\n");
|
|
10120
|
+
} else if (rec) {
|
|
10121
|
+
parts.push("## Expected Outputs\n");
|
|
10122
|
+
const outputs = [];
|
|
10123
|
+
if (rec.target) outputs.push(`- \`${rec.target}\``);
|
|
10124
|
+
if (rec.id === "refine-work-item" || rec.id === "resolve-blocker")
|
|
10125
|
+
outputs.push("- Refined Work Item content.");
|
|
10126
|
+
if (rec.id === "create-work-item")
|
|
10127
|
+
outputs.push("- A new Work Item under `knowledge/delivery/work-items/`.");
|
|
10128
|
+
if (rec.id === "implement")
|
|
10129
|
+
outputs.push("- Implementation plan or code changes guided by the Work Item.");
|
|
10130
|
+
if (rec.id === "guard")
|
|
10131
|
+
outputs.push("- Updated knowledge artifacts reflecting recent code changes.");
|
|
10132
|
+
if (rec.id === "install-adapter")
|
|
10133
|
+
outputs.push("- An adapter configured and injected (`kaddo adapters list`).");
|
|
10134
|
+
if (outputs.length > 0) {
|
|
10135
|
+
parts.push("The LLM should produce:\n");
|
|
10136
|
+
parts.push(outputs.join("\n") + "\n");
|
|
10137
|
+
} else {
|
|
10138
|
+
parts.push("_See the primary recommendation above._\n");
|
|
10139
|
+
}
|
|
10140
|
+
}
|
|
9867
10141
|
parts.push("## Copy/Paste Instructions\n");
|
|
9868
|
-
|
|
9869
|
-
|
|
10142
|
+
if (steps.length > 0) {
|
|
10143
|
+
const first2 = steps[0];
|
|
9870
10144
|
parts.push(
|
|
9871
10145
|
[
|
|
9872
10146
|
`Start with **${agentName(first2.agent)}**:`,
|
|
@@ -9878,9 +10152,31 @@ function renderUnderstand(plan) {
|
|
|
9878
10152
|
`5. Save the result in: \`${first2.output}\``
|
|
9879
10153
|
].join("\n") + "\n"
|
|
9880
10154
|
);
|
|
10155
|
+
} else if (agentPaths.length > 1) {
|
|
10156
|
+
parts.push("Paste the following into your LLM chat:\n");
|
|
10157
|
+
parts.push(agentPaths.map((p2, i) => `${i + 1}. \`${p2}\``).join("\n") + "\n");
|
|
10158
|
+
if (rec?.agent) {
|
|
10159
|
+
parts.push(`Ask the LLM to follow the ${agentName(rec.agent)} instructions.
|
|
10160
|
+
`);
|
|
10161
|
+
}
|
|
10162
|
+
} else {
|
|
10163
|
+
parts.push(`Paste \`${plan.contextPackPath}\` into your LLM chat.
|
|
10164
|
+
`);
|
|
9881
10165
|
}
|
|
9882
10166
|
parts.push("## Next Steps\n");
|
|
9883
|
-
if (
|
|
10167
|
+
if (rec) {
|
|
10168
|
+
const nextLines = [];
|
|
10169
|
+
nextLines.push(`1. ${rec.label}`);
|
|
10170
|
+
let n = 2;
|
|
10171
|
+
if (rec.secondary) {
|
|
10172
|
+
for (const s of rec.secondary) {
|
|
10173
|
+
nextLines.push(`${n}. ${s.label}`);
|
|
10174
|
+
n++;
|
|
10175
|
+
}
|
|
10176
|
+
}
|
|
10177
|
+
nextLines.push(`${n}. Re-run \`kaddo explain\`.`);
|
|
10178
|
+
parts.push(nextLines.join("\n") + "\n");
|
|
10179
|
+
} else if (steps.length > 1) {
|
|
9884
10180
|
parts.push(
|
|
9885
10181
|
`After saving \`${steps[0].output}\`, continue with **${agentName(steps[1].agent)}** (feed it the context pack plus the artifacts you already produced). Re-run \`kaddo understand\` any time to see this plan again.
|
|
9886
10182
|
`
|
|
@@ -10030,8 +10326,13 @@ function runUnderstand() {
|
|
|
10030
10326
|
for (const r of assessment.reasons) console.log(` - ${r}`);
|
|
10031
10327
|
}
|
|
10032
10328
|
if (rec.agent) console.log(`Recommended: ${rec.agent}`);
|
|
10329
|
+
if (rec.skill) console.log(`Recommended skill: ${rec.skill}`);
|
|
10033
10330
|
console.log(`Next step: ${rec.label}`);
|
|
10034
10331
|
if (rec.reason) console.log(`Why: ${rec.reason}`);
|
|
10332
|
+
if (rec.secondary && rec.secondary.length > 0) {
|
|
10333
|
+
console.log("Also:");
|
|
10334
|
+
for (const s of rec.secondary) console.log(` - ${s.label}`);
|
|
10335
|
+
}
|
|
10035
10336
|
const installedSkills = discoverInstalledSkills(dir);
|
|
10036
10337
|
if (installedSkills.length > 0 && assessment.recommendedAgents.length > 0) {
|
|
10037
10338
|
const recSkills = skillsForAgents(installedSkills, assessment.recommendedAgents);
|
|
@@ -10053,7 +10354,7 @@ function runUnderstand() {
|
|
|
10053
10354
|
console.log(`Roadmap quality: ${rqi.grounded}/${rqi.total} initiatives grounded.`);
|
|
10054
10355
|
console.log(" \u2192 Use roadmap-agent to ground roadmap initiatives in capability domains, gaps and source signals");
|
|
10055
10356
|
console.log(" before `kaddo create --from roadmap`.");
|
|
10056
|
-
} else if (rqi.total > 0 && rqi.grounded === rqi.total) {
|
|
10357
|
+
} else if (rqi.total > 0 && rqi.grounded === rqi.total && exp.roadmap.materialized_work_items === 0) {
|
|
10057
10358
|
console.log("");
|
|
10058
10359
|
console.log("Roadmap initiatives are grounded. \u2192 Run `kaddo create --from roadmap` to materialize the first Work Item.");
|
|
10059
10360
|
}
|
|
@@ -10100,7 +10401,41 @@ function runUnderstand() {
|
|
|
10100
10401
|
console.log(`Other active work items: ${active.slice(1).map((w) => w.id).join(", ")}`);
|
|
10101
10402
|
}
|
|
10102
10403
|
}
|
|
10103
|
-
|
|
10404
|
+
const ds = buildDeliveryState(dir);
|
|
10405
|
+
ds.phase = assessment.phase;
|
|
10406
|
+
const wis = discoverWorkItems(dir);
|
|
10407
|
+
const activeWis = wis.filter((w) => w.lifecycle && ["draft", "ready", "in-progress", "blocked"].includes(w.lifecycle)).map((w) => ({
|
|
10408
|
+
id: w.id,
|
|
10409
|
+
title: w.title,
|
|
10410
|
+
type: w.type,
|
|
10411
|
+
lifecycle: w.lifecycle ?? "draft",
|
|
10412
|
+
knowledgeLevel: w.knowledgeLevel,
|
|
10413
|
+
hasOwnership: w.codeGlobs.length > 0
|
|
10414
|
+
}));
|
|
10415
|
+
const recommendedPaths = [];
|
|
10416
|
+
if (rec.agent) {
|
|
10417
|
+
const agentFile = rec.agent.endsWith(".md") ? rec.agent : `${rec.agent}.md`;
|
|
10418
|
+
if (agentIsInstalled(dir, agentFile)) {
|
|
10419
|
+
recommendedPaths.push(agentInstallPath(agentFile));
|
|
10420
|
+
}
|
|
10421
|
+
}
|
|
10422
|
+
const recommendedSkillPaths = [];
|
|
10423
|
+
if (rec.skill) {
|
|
10424
|
+
const sp = skillInstallPath(rec.skill);
|
|
10425
|
+
if (exists(join(dir, sp))) {
|
|
10426
|
+
recommendedSkillPaths.push(sp);
|
|
10427
|
+
}
|
|
10428
|
+
}
|
|
10429
|
+
const enrichedPlan = enrichUnderstandPlan(plan, {
|
|
10430
|
+
phase: assessment.phase,
|
|
10431
|
+
nextStepRecommendation: rec,
|
|
10432
|
+
deliveryState: ds,
|
|
10433
|
+
activeWorkItems: activeWis,
|
|
10434
|
+
recommendedPaths,
|
|
10435
|
+
recommendedSkillPaths,
|
|
10436
|
+
language: languageLabel(projectLanguage(config))
|
|
10437
|
+
});
|
|
10438
|
+
writeFile(join(dir, ".kaddo", "understand.md"), renderUnderstand(enrichedPlan));
|
|
10104
10439
|
log2.success("Wrote .kaddo/understand.md");
|
|
10105
10440
|
printCommandFooter("understand");
|
|
10106
10441
|
outro2("Handoff ready. CLI prepares context \u2014 your LLM creates the understanding.");
|