@kaddo/mcp 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/dist/index.js +418 -120
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2282,7 +2282,124 @@ function buildAdapterStatuses(dir) {
|
|
|
2282
2282
|
});
|
|
2283
2283
|
}
|
|
2284
2284
|
|
|
2285
|
+
// ../cli/src/core/decisions.ts
|
|
2286
|
+
var CANDIDATES_DISCOVERY = "knowledge/tech/discovery/decision-candidates.md";
|
|
2287
|
+
var CANDIDATES_LEGACY = "knowledge/tech/decision-candidates.md";
|
|
2288
|
+
var DECISIONS_DIR = "knowledge/tech/decisions";
|
|
2289
|
+
function resolveCandidatesPath(dir) {
|
|
2290
|
+
const discovery = exists(join(dir, CANDIDATES_DISCOVERY));
|
|
2291
|
+
const legacy = exists(join(dir, CANDIDATES_LEGACY));
|
|
2292
|
+
if (discovery) return { path: CANDIDATES_DISCOVERY, legacy: false, bothExist: legacy };
|
|
2293
|
+
if (legacy) return { path: CANDIDATES_LEGACY, legacy: true, bothExist: false };
|
|
2294
|
+
return { path: null, legacy: false, bothExist: false };
|
|
2295
|
+
}
|
|
2296
|
+
function cleanCandidateTitle(title) {
|
|
2297
|
+
return title.replace(/^\s*#{1,6}\s+/, "").replace(/^\s*[-*]\s+/, "").replace(/^\s*\(?\d+\)?[.):]\s+/, "").trim();
|
|
2298
|
+
}
|
|
2299
|
+
function slugify(s) {
|
|
2300
|
+
return cleanCandidateTitle(s).toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 70).replace(/-+$/g, "");
|
|
2301
|
+
}
|
|
2302
|
+
function parseDecisionCandidates(md) {
|
|
2303
|
+
const out = [];
|
|
2304
|
+
for (const line of md.split(/\r?\n/)) {
|
|
2305
|
+
const m = line.match(/^##\s+(.+?)\s*$/);
|
|
2306
|
+
if (m) {
|
|
2307
|
+
const raw = m[1].trim();
|
|
2308
|
+
if (!raw || /^_.*_$/.test(raw)) continue;
|
|
2309
|
+
const t = cleanCandidateTitle(raw);
|
|
2310
|
+
if (t) out.push(t);
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
return out;
|
|
2314
|
+
}
|
|
2315
|
+
function countAdrs(dir) {
|
|
2316
|
+
const base = join(dir, DECISIONS_DIR);
|
|
2317
|
+
if (!exists(base)) return { total: 0, draft: 0, accepted: 0 };
|
|
2318
|
+
let total = 0;
|
|
2319
|
+
let draft = 0;
|
|
2320
|
+
let accepted = 0;
|
|
2321
|
+
for (const entry of readDir(base)) {
|
|
2322
|
+
if (!entry.endsWith(".md") || entry === ".gitkeep") continue;
|
|
2323
|
+
const full = join(base, entry);
|
|
2324
|
+
if (!isFile(full)) continue;
|
|
2325
|
+
total += 1;
|
|
2326
|
+
let content = "";
|
|
2327
|
+
try {
|
|
2328
|
+
content = readFile(full);
|
|
2329
|
+
} catch {
|
|
2330
|
+
continue;
|
|
2331
|
+
}
|
|
2332
|
+
const status = content.match(/^\s*status:\s*([a-z-]+)/im)?.[1]?.toLowerCase();
|
|
2333
|
+
if (status === "accepted") accepted += 1;
|
|
2334
|
+
else draft += 1;
|
|
2335
|
+
}
|
|
2336
|
+
return { total, draft, accepted };
|
|
2337
|
+
}
|
|
2338
|
+
function buildTechDecisions(dir) {
|
|
2339
|
+
const resolved = resolveCandidatesPath(dir);
|
|
2340
|
+
let titles = [];
|
|
2341
|
+
if (resolved.path) {
|
|
2342
|
+
try {
|
|
2343
|
+
titles = parseDecisionCandidates(readFile(join(dir, resolved.path)));
|
|
2344
|
+
} catch {
|
|
2345
|
+
titles = [];
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
const { total, draft, accepted } = countAdrs(dir);
|
|
2349
|
+
const candidate_list = titles.map((title, i) => {
|
|
2350
|
+
const n = String(total + i + 1).padStart(3, "0");
|
|
2351
|
+
return { title, source: resolved.path, suggestedAdrFile: `${DECISIONS_DIR}/ADR-${n}-${slugify(title)}.md` };
|
|
2352
|
+
});
|
|
2353
|
+
let status;
|
|
2354
|
+
if (accepted > 0) status = "accepted-adrs";
|
|
2355
|
+
else if (total > 0) status = "draft-adrs";
|
|
2356
|
+
else if (titles.length > 0) status = "candidates";
|
|
2357
|
+
else status = "none";
|
|
2358
|
+
return {
|
|
2359
|
+
status,
|
|
2360
|
+
candidates: titles.length,
|
|
2361
|
+
adrs: total,
|
|
2362
|
+
draft_adrs: draft,
|
|
2363
|
+
accepted_adrs: accepted,
|
|
2364
|
+
candidate_list,
|
|
2365
|
+
candidates_source: resolved.path,
|
|
2366
|
+
candidates_legacy_location: resolved.legacy,
|
|
2367
|
+
candidates_both_exist: resolved.bothExist
|
|
2368
|
+
};
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2285
2371
|
// ../cli/src/core/next-step.ts
|
|
2372
|
+
function buildDeliveryState(dir) {
|
|
2373
|
+
const wis = discoverWorkItems(dir);
|
|
2374
|
+
const byState = (s) => wis.filter((w) => w.lifecycle === s).length;
|
|
2375
|
+
const total = wis.length;
|
|
2376
|
+
const withOwnership = wis.filter((w) => w.codeGlobs.length > 0).length;
|
|
2377
|
+
const td = buildTechDecisions(dir);
|
|
2378
|
+
const roadmapPath = join(dir, "knowledge/delivery/roadmap.md");
|
|
2379
|
+
const roadmapMd = exists(roadmapPath) ? safeRead(roadmapPath) : null;
|
|
2380
|
+
const stats = roadmapStats(roadmapMd, total);
|
|
2381
|
+
const adapters = installedAdapters(dir);
|
|
2382
|
+
return {
|
|
2383
|
+
phase: "",
|
|
2384
|
+
draft_work_items: byState("draft"),
|
|
2385
|
+
ready_work_items: byState("ready"),
|
|
2386
|
+
in_progress_work_items: byState("in-progress"),
|
|
2387
|
+
blocked_work_items: byState("blocked"),
|
|
2388
|
+
total_work_items: total,
|
|
2389
|
+
ownership_coverage: `${withOwnership}/${total}`,
|
|
2390
|
+
remaining_work_item_candidates: stats.remaining_work_item_candidates,
|
|
2391
|
+
decision_candidates: td.candidates,
|
|
2392
|
+
accepted_adrs: td.accepted_adrs,
|
|
2393
|
+
adapters_installed: adapters.length
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
function safeRead(p) {
|
|
2397
|
+
try {
|
|
2398
|
+
return readFile(p);
|
|
2399
|
+
} catch {
|
|
2400
|
+
return null;
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2286
2403
|
function roadmapSignal(dir) {
|
|
2287
2404
|
const p = join(dir, "knowledge/delivery/roadmap.md");
|
|
2288
2405
|
if (!exists(p)) return "missing";
|
|
@@ -2379,15 +2496,117 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
2379
2496
|
if (roadmap !== "has-candidates") {
|
|
2380
2497
|
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." };
|
|
2381
2498
|
}
|
|
2382
|
-
const
|
|
2383
|
-
|
|
2384
|
-
|
|
2499
|
+
const st = buildDeliveryState(dir);
|
|
2500
|
+
const secondary = buildSecondaryRecommendations(st);
|
|
2501
|
+
if (st.total_work_items === 0) {
|
|
2502
|
+
return {
|
|
2503
|
+
id: "create-work-item",
|
|
2504
|
+
phase: "Delivery Preparation",
|
|
2505
|
+
label: "Run `kaddo create --from roadmap` to materialize the first Work Item.",
|
|
2506
|
+
command: "kaddo create --from roadmap",
|
|
2507
|
+
reason: "The roadmap has candidates but no Work Item exists yet.",
|
|
2508
|
+
...secondary.length ? { secondary } : {}
|
|
2509
|
+
};
|
|
2385
2510
|
}
|
|
2386
|
-
const adapters =
|
|
2387
|
-
if (
|
|
2388
|
-
|
|
2511
|
+
const adapters = st.adapters_installed;
|
|
2512
|
+
if (st.ready_work_items > 0) {
|
|
2513
|
+
if (adapters === 0) {
|
|
2514
|
+
return {
|
|
2515
|
+
id: "install-adapter",
|
|
2516
|
+
phase: "Active Delivery",
|
|
2517
|
+
label: "Install or configure an adapter before implementation (`kaddo adapters list`).",
|
|
2518
|
+
command: "kaddo adapters list",
|
|
2519
|
+
reason: `${st.ready_work_items} Work Item(s) are ready but no adapter is installed.`,
|
|
2520
|
+
...secondary.length ? { secondary } : {}
|
|
2521
|
+
};
|
|
2522
|
+
}
|
|
2523
|
+
return {
|
|
2524
|
+
id: "implement",
|
|
2525
|
+
phase: "Active Delivery",
|
|
2526
|
+
label: "Use the implementation-agent or your installed adapter to plan implementation.",
|
|
2527
|
+
agent: "implementation-agent",
|
|
2528
|
+
reason: `${st.ready_work_items} Work Item(s) are ready and an adapter is installed.`,
|
|
2529
|
+
...secondary.length ? { secondary } : {}
|
|
2530
|
+
};
|
|
2389
2531
|
}
|
|
2390
|
-
|
|
2532
|
+
if (st.in_progress_work_items > 0) {
|
|
2533
|
+
return {
|
|
2534
|
+
id: "guard",
|
|
2535
|
+
phase: "Active Delivery",
|
|
2536
|
+
label: "Run `kaddo guard` and update affected knowledge after significant changes.",
|
|
2537
|
+
command: "kaddo guard",
|
|
2538
|
+
reason: `${st.in_progress_work_items} Work Item(s) are in progress.`,
|
|
2539
|
+
...secondary.length ? { secondary } : {}
|
|
2540
|
+
};
|
|
2541
|
+
}
|
|
2542
|
+
if (st.draft_work_items > 0) {
|
|
2543
|
+
return {
|
|
2544
|
+
id: "refine-work-item",
|
|
2545
|
+
phase: "Active Delivery",
|
|
2546
|
+
label: "Refine the existing draft Work Item with the work-item-agent.",
|
|
2547
|
+
agent: "work-item-agent",
|
|
2548
|
+
skill: "work-item-refinement",
|
|
2549
|
+
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.`,
|
|
2550
|
+
...secondary.length ? { secondary } : {}
|
|
2551
|
+
};
|
|
2552
|
+
}
|
|
2553
|
+
if (st.blocked_work_items > 0) {
|
|
2554
|
+
return {
|
|
2555
|
+
id: "resolve-blocker",
|
|
2556
|
+
phase: "Active Delivery",
|
|
2557
|
+
label: "Resolve the blocker on the blocked Work Item with the work-item-agent.",
|
|
2558
|
+
agent: "work-item-agent",
|
|
2559
|
+
reason: `${st.blocked_work_items} Work Item(s) are blocked.`,
|
|
2560
|
+
...secondary.length ? { secondary } : {}
|
|
2561
|
+
};
|
|
2562
|
+
}
|
|
2563
|
+
if (st.remaining_work_item_candidates > 0) {
|
|
2564
|
+
return {
|
|
2565
|
+
id: "materialize-more-work-items",
|
|
2566
|
+
phase: "Maintenance",
|
|
2567
|
+
label: `Materialize the remaining ${st.remaining_work_item_candidates} Work Item candidate(s) with \`kaddo create --from roadmap\`.`,
|
|
2568
|
+
command: "kaddo create --from roadmap",
|
|
2569
|
+
reason: "No active Work Items remain; roadmap candidates are still pending.",
|
|
2570
|
+
...secondary.length ? { secondary } : {}
|
|
2571
|
+
};
|
|
2572
|
+
}
|
|
2573
|
+
return {
|
|
2574
|
+
id: "plan-next",
|
|
2575
|
+
phase: "Maintenance",
|
|
2576
|
+
label: "Use the roadmap-agent to plan the next initiative.",
|
|
2577
|
+
agent: "roadmap-agent",
|
|
2578
|
+
reason: "No active Work Items and no remaining roadmap candidates."
|
|
2579
|
+
};
|
|
2580
|
+
}
|
|
2581
|
+
function buildSecondaryRecommendations(st) {
|
|
2582
|
+
const out = [];
|
|
2583
|
+
const [withOwnership] = st.ownership_coverage.split("/").map(Number);
|
|
2584
|
+
if (st.total_work_items > 0 && withOwnership < st.total_work_items) {
|
|
2585
|
+
out.push({
|
|
2586
|
+
id: "suggest-ownership",
|
|
2587
|
+
label: "Run `kaddo owners suggest` for Work Items without code ownership.",
|
|
2588
|
+
command: "kaddo owners suggest",
|
|
2589
|
+
reason: `Ownership coverage is ${st.ownership_coverage}.`
|
|
2590
|
+
});
|
|
2591
|
+
}
|
|
2592
|
+
if (st.total_work_items > 0 && st.decision_candidates > 0 && st.accepted_adrs === 0) {
|
|
2593
|
+
out.push({
|
|
2594
|
+
id: "materialize-adrs",
|
|
2595
|
+
label: "Use the adr-writing skill (`kaddo adr`) to materialize decision candidates into ADRs before implementing related technical Work Items.",
|
|
2596
|
+
command: "kaddo adr",
|
|
2597
|
+
skill: "adr-writing",
|
|
2598
|
+
reason: `There are ${st.decision_candidates} technical decision candidate(s) and ${st.accepted_adrs} accepted ADR(s).`
|
|
2599
|
+
});
|
|
2600
|
+
}
|
|
2601
|
+
if (st.total_work_items > 0 && st.remaining_work_item_candidates > 0) {
|
|
2602
|
+
out.push({
|
|
2603
|
+
id: "materialize-more-work-items",
|
|
2604
|
+
label: `Later, materialize the remaining ${st.remaining_work_item_candidates} Work Item candidate(s) with \`kaddo create --from roadmap\`.`,
|
|
2605
|
+
command: "kaddo create --from roadmap",
|
|
2606
|
+
reason: `There are ${st.remaining_work_item_candidates} remaining Work Item candidate(s).`
|
|
2607
|
+
});
|
|
2608
|
+
}
|
|
2609
|
+
return out;
|
|
2391
2610
|
}
|
|
2392
2611
|
|
|
2393
2612
|
// ../cli/src/core/readiness.ts
|
|
@@ -2482,92 +2701,6 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
2482
2701
|
};
|
|
2483
2702
|
}
|
|
2484
2703
|
|
|
2485
|
-
// ../cli/src/core/decisions.ts
|
|
2486
|
-
var CANDIDATES_DISCOVERY = "knowledge/tech/discovery/decision-candidates.md";
|
|
2487
|
-
var CANDIDATES_LEGACY = "knowledge/tech/decision-candidates.md";
|
|
2488
|
-
var DECISIONS_DIR = "knowledge/tech/decisions";
|
|
2489
|
-
function resolveCandidatesPath(dir) {
|
|
2490
|
-
const discovery = exists(join(dir, CANDIDATES_DISCOVERY));
|
|
2491
|
-
const legacy = exists(join(dir, CANDIDATES_LEGACY));
|
|
2492
|
-
if (discovery) return { path: CANDIDATES_DISCOVERY, legacy: false, bothExist: legacy };
|
|
2493
|
-
if (legacy) return { path: CANDIDATES_LEGACY, legacy: true, bothExist: false };
|
|
2494
|
-
return { path: null, legacy: false, bothExist: false };
|
|
2495
|
-
}
|
|
2496
|
-
function cleanCandidateTitle(title) {
|
|
2497
|
-
return title.replace(/^\s*#{1,6}\s+/, "").replace(/^\s*[-*]\s+/, "").replace(/^\s*\(?\d+\)?[.):]\s+/, "").trim();
|
|
2498
|
-
}
|
|
2499
|
-
function slugify(s) {
|
|
2500
|
-
return cleanCandidateTitle(s).toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 70).replace(/-+$/g, "");
|
|
2501
|
-
}
|
|
2502
|
-
function parseDecisionCandidates(md) {
|
|
2503
|
-
const out = [];
|
|
2504
|
-
for (const line of md.split(/\r?\n/)) {
|
|
2505
|
-
const m = line.match(/^##\s+(.+?)\s*$/);
|
|
2506
|
-
if (m) {
|
|
2507
|
-
const raw = m[1].trim();
|
|
2508
|
-
if (!raw || /^_.*_$/.test(raw)) continue;
|
|
2509
|
-
const t = cleanCandidateTitle(raw);
|
|
2510
|
-
if (t) out.push(t);
|
|
2511
|
-
}
|
|
2512
|
-
}
|
|
2513
|
-
return out;
|
|
2514
|
-
}
|
|
2515
|
-
function countAdrs(dir) {
|
|
2516
|
-
const base = join(dir, DECISIONS_DIR);
|
|
2517
|
-
if (!exists(base)) return { total: 0, draft: 0, accepted: 0 };
|
|
2518
|
-
let total = 0;
|
|
2519
|
-
let draft = 0;
|
|
2520
|
-
let accepted = 0;
|
|
2521
|
-
for (const entry of readDir(base)) {
|
|
2522
|
-
if (!entry.endsWith(".md") || entry === ".gitkeep") continue;
|
|
2523
|
-
const full = join(base, entry);
|
|
2524
|
-
if (!isFile(full)) continue;
|
|
2525
|
-
total += 1;
|
|
2526
|
-
let content = "";
|
|
2527
|
-
try {
|
|
2528
|
-
content = readFile(full);
|
|
2529
|
-
} catch {
|
|
2530
|
-
continue;
|
|
2531
|
-
}
|
|
2532
|
-
const status = content.match(/^\s*status:\s*([a-z-]+)/im)?.[1]?.toLowerCase();
|
|
2533
|
-
if (status === "accepted") accepted += 1;
|
|
2534
|
-
else draft += 1;
|
|
2535
|
-
}
|
|
2536
|
-
return { total, draft, accepted };
|
|
2537
|
-
}
|
|
2538
|
-
function buildTechDecisions(dir) {
|
|
2539
|
-
const resolved = resolveCandidatesPath(dir);
|
|
2540
|
-
let titles = [];
|
|
2541
|
-
if (resolved.path) {
|
|
2542
|
-
try {
|
|
2543
|
-
titles = parseDecisionCandidates(readFile(join(dir, resolved.path)));
|
|
2544
|
-
} catch {
|
|
2545
|
-
titles = [];
|
|
2546
|
-
}
|
|
2547
|
-
}
|
|
2548
|
-
const { total, draft, accepted } = countAdrs(dir);
|
|
2549
|
-
const candidate_list = titles.map((title, i) => {
|
|
2550
|
-
const n = String(total + i + 1).padStart(3, "0");
|
|
2551
|
-
return { title, source: resolved.path, suggestedAdrFile: `${DECISIONS_DIR}/ADR-${n}-${slugify(title)}.md` };
|
|
2552
|
-
});
|
|
2553
|
-
let status;
|
|
2554
|
-
if (accepted > 0) status = "accepted-adrs";
|
|
2555
|
-
else if (total > 0) status = "draft-adrs";
|
|
2556
|
-
else if (titles.length > 0) status = "candidates";
|
|
2557
|
-
else status = "none";
|
|
2558
|
-
return {
|
|
2559
|
-
status,
|
|
2560
|
-
candidates: titles.length,
|
|
2561
|
-
adrs: total,
|
|
2562
|
-
draft_adrs: draft,
|
|
2563
|
-
accepted_adrs: accepted,
|
|
2564
|
-
candidate_list,
|
|
2565
|
-
candidates_source: resolved.path,
|
|
2566
|
-
candidates_legacy_location: resolved.legacy,
|
|
2567
|
-
candidates_both_exist: resolved.bothExist
|
|
2568
|
-
};
|
|
2569
|
-
}
|
|
2570
|
-
|
|
2571
2704
|
// ../cli/src/core/assets.ts
|
|
2572
2705
|
import matter6 from "gray-matter";
|
|
2573
2706
|
|
|
@@ -5792,9 +5925,12 @@ function renderExplanationHuman(exp) {
|
|
|
5792
5925
|
}
|
|
5793
5926
|
lines.push(`- Next step: ${exp.readiness.recommended_next_step.label}`);
|
|
5794
5927
|
lines.push("");
|
|
5795
|
-
|
|
5928
|
+
const rec = exp.nextStepRecommendation;
|
|
5929
|
+
const secondary = rec.secondary ?? [];
|
|
5930
|
+
const steps = secondary.length > 0 || /Delivery|Active|Maintenance/.test(rec.phase) ? [rec.label, ...secondary.map((s2) => s2.label)] : exp.suggestedNextSteps;
|
|
5931
|
+
if (steps.length > 0) {
|
|
5796
5932
|
lines.push("## Suggested Next Steps");
|
|
5797
|
-
|
|
5933
|
+
steps.forEach((s2, i) => lines.push(`${i + 1}. ${s2}`));
|
|
5798
5934
|
lines.push("");
|
|
5799
5935
|
}
|
|
5800
5936
|
const r = exp.readiness;
|
|
@@ -7134,6 +7270,20 @@ var RESOURCES = [
|
|
|
7134
7270
|
const candidates = md ? parseRoadmapCandidates(md) : [];
|
|
7135
7271
|
return [{ uri: "kaddo://work-item-candidates", text: JSON.stringify({ candidates }, null, 2), mimeType: "application/json" }];
|
|
7136
7272
|
}
|
|
7273
|
+
},
|
|
7274
|
+
{
|
|
7275
|
+
uri: "kaddo://next-step",
|
|
7276
|
+
name: "Kaddo next step",
|
|
7277
|
+
description: "State-aware next-step recommendation for the current delivery state, with parallel (secondary) recommendations and the delivery-state counts behind it (VS-079). Read-only.",
|
|
7278
|
+
mimeType: "application/json",
|
|
7279
|
+
read: (root) => {
|
|
7280
|
+
if (!hasKnowledge(root)) {
|
|
7281
|
+
return text("kaddo://next-step", "Knowledge repository not found. Run `kaddo bootstrap` first.", "text/plain");
|
|
7282
|
+
}
|
|
7283
|
+
const nextStepRecommendation = resolveNextStep(root);
|
|
7284
|
+
const deliveryState = { ...buildDeliveryState(root), phase: nextStepRecommendation.phase };
|
|
7285
|
+
return [{ uri: "kaddo://next-step", text: JSON.stringify({ nextStepRecommendation, deliveryState }, null, 2), mimeType: "application/json" }];
|
|
7286
|
+
}
|
|
7137
7287
|
}
|
|
7138
7288
|
];
|
|
7139
7289
|
|
|
@@ -7428,6 +7578,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
7428
7578
|
nextStep: nextStepRecommendation.label,
|
|
7429
7579
|
recommendedAgents: nextStepRecommendation.agent ? [nextStepRecommendation.agent] : phase.recommendedAgents
|
|
7430
7580
|
};
|
|
7581
|
+
const deliveryState = { ...buildDeliveryState(dir), phase: nextStepRecommendation.phase };
|
|
7431
7582
|
return {
|
|
7432
7583
|
version: CONTEXT_PACK_VERSION,
|
|
7433
7584
|
generatedAt: now.toISOString(),
|
|
@@ -7460,6 +7611,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
7460
7611
|
roadmap,
|
|
7461
7612
|
phase: unifiedPhase,
|
|
7462
7613
|
nextStepRecommendation,
|
|
7614
|
+
deliveryState,
|
|
7463
7615
|
techDecisions,
|
|
7464
7616
|
techKnowledge,
|
|
7465
7617
|
roadmapQuality: buildRoadmapQuality(dir),
|
|
@@ -7524,6 +7676,31 @@ function renderContextPack(pack) {
|
|
|
7524
7676
|
parts.push(`Next step: ${pack.phase.nextStep}
|
|
7525
7677
|
`);
|
|
7526
7678
|
}
|
|
7679
|
+
const ds = pack.deliveryState;
|
|
7680
|
+
const rec = pack.nextStepRecommendation;
|
|
7681
|
+
if (ds.total_work_items > 0 || rec.phase !== "Setup") {
|
|
7682
|
+
parts.push("## Delivery State\n");
|
|
7683
|
+
parts.push(
|
|
7684
|
+
[
|
|
7685
|
+
`- Phase: ${ds.phase}`,
|
|
7686
|
+
`- Draft Work Items: ${ds.draft_work_items}`,
|
|
7687
|
+
`- Ready Work Items: ${ds.ready_work_items}`,
|
|
7688
|
+
`- In-progress Work Items: ${ds.in_progress_work_items}`,
|
|
7689
|
+
`- Ownership coverage: ${ds.ownership_coverage}`,
|
|
7690
|
+
`- Remaining Work Item candidates: ${ds.remaining_work_item_candidates}`
|
|
7691
|
+
].join("\n") + "\n"
|
|
7692
|
+
);
|
|
7693
|
+
parts.push("## Next Step Recommendation\n");
|
|
7694
|
+
const recLines = [`- ${rec.label}`, ` - id: ${rec.id}`, ` - reason: ${rec.reason}`];
|
|
7695
|
+
if (rec.agent) recLines.push(` - agent: ${rec.agent}`);
|
|
7696
|
+
if (rec.skill) recLines.push(` - skill: ${rec.skill}`);
|
|
7697
|
+
if (rec.command) recLines.push(` - command: \`${rec.command}\``);
|
|
7698
|
+
parts.push(recLines.join("\n") + "\n");
|
|
7699
|
+
if (rec.secondary && rec.secondary.length > 0) {
|
|
7700
|
+
parts.push("Also (secondary):\n");
|
|
7701
|
+
parts.push(rec.secondary.map((s) => `- ${s.label}`).join("\n") + "\n");
|
|
7702
|
+
}
|
|
7703
|
+
}
|
|
7527
7704
|
parts.push("## Knowledge Layers\n");
|
|
7528
7705
|
parts.push(
|
|
7529
7706
|
"Project knowledge is organized in four layers: **Business \u2192 Product \u2192 Tech \u2192 Delivery**.\n"
|
|
@@ -7770,7 +7947,8 @@ function buildUnderstandPlan(dir, config) {
|
|
|
7770
7947
|
name: config.project.name,
|
|
7771
7948
|
state,
|
|
7772
7949
|
teamSize: config.team.size,
|
|
7773
|
-
structure: config.project.structure
|
|
7950
|
+
structure: config.project.structure,
|
|
7951
|
+
language: "English"
|
|
7774
7952
|
},
|
|
7775
7953
|
scanAvailable: exists(join(dir, ".kaddo", "scan.json")),
|
|
7776
7954
|
contextPackPath: ".kaddo/context-pack.md",
|
|
@@ -7789,42 +7967,140 @@ function agentName(file) {
|
|
|
7789
7967
|
}
|
|
7790
7968
|
function renderUnderstand(plan) {
|
|
7791
7969
|
const { project, steps } = plan;
|
|
7970
|
+
const rec = plan.nextStepRecommendation;
|
|
7971
|
+
const ds = plan.deliveryState;
|
|
7792
7972
|
const parts = [];
|
|
7793
7973
|
parts.push("# Kaddo Understand Handoff\n");
|
|
7794
7974
|
parts.push(
|
|
7795
7975
|
"> Generated by `kaddo understand`. Kaddo does not call an LLM \u2014 you stay in control of the interpretation.\n"
|
|
7796
7976
|
);
|
|
7797
7977
|
parts.push("## Project\n");
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
);
|
|
7806
|
-
|
|
7807
|
-
|
|
7978
|
+
const projectLines = [
|
|
7979
|
+
`- Name: ${project.name}`,
|
|
7980
|
+
`- State: ${project.state}`,
|
|
7981
|
+
`- Team: ${project.teamSize}`,
|
|
7982
|
+
`- Structure: ${project.structure}`
|
|
7983
|
+
];
|
|
7984
|
+
if (project.language) projectLines.push(`- Language: ${project.language}`);
|
|
7985
|
+
parts.push(projectLines.join("\n") + "\n");
|
|
7986
|
+
if (plan.phase && rec) {
|
|
7987
|
+
parts.push("## Current Phase\n");
|
|
7988
|
+
const phaseLines = [`- Phase: ${plan.phase}`];
|
|
7989
|
+
if (rec.agent) phaseLines.push(`- Recommended agent: ${agentName(rec.agent)}`);
|
|
7990
|
+
if (rec.skill) phaseLines.push(`- Recommended skill: ${rec.skill}`);
|
|
7991
|
+
phaseLines.push(`- Next step: ${rec.label}`);
|
|
7992
|
+
if (rec.reason) phaseLines.push(`- Reason: ${rec.reason}`);
|
|
7993
|
+
parts.push(phaseLines.join("\n") + "\n");
|
|
7994
|
+
}
|
|
7995
|
+
if (ds && ds.total_work_items > 0) {
|
|
7996
|
+
parts.push("## Delivery State\n");
|
|
7997
|
+
parts.push(
|
|
7998
|
+
[
|
|
7999
|
+
`- Draft Work Items: ${ds.draft_work_items}`,
|
|
8000
|
+
`- Ready Work Items: ${ds.ready_work_items}`,
|
|
8001
|
+
`- In-progress Work Items: ${ds.in_progress_work_items}`,
|
|
8002
|
+
`- Blocked Work Items: ${ds.blocked_work_items}`,
|
|
8003
|
+
`- Ownership coverage: ${ds.ownership_coverage}`,
|
|
8004
|
+
`- Remaining Work Item candidates: ${ds.remaining_work_item_candidates}`,
|
|
8005
|
+
`- Technical decision candidates: ${ds.decision_candidates}`,
|
|
8006
|
+
`- Accepted ADRs: ${ds.accepted_adrs}`,
|
|
8007
|
+
`- Installed adapters: ${ds.adapters_installed}`
|
|
8008
|
+
].join("\n") + "\n"
|
|
8009
|
+
);
|
|
8010
|
+
}
|
|
8011
|
+
if (steps.length > 0) {
|
|
8012
|
+
parts.push("## Recommended Agent Flow\n");
|
|
8013
|
+
parts.push(`Recommended order for a ${stateLabel2(project.state)} project:
|
|
7808
8014
|
`);
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
8015
|
+
parts.push(
|
|
8016
|
+
steps.map((s, i) => {
|
|
8017
|
+
const flag = s.installed ? "" : " _(not installed \u2014 run `kaddo add agents`)_";
|
|
8018
|
+
return `${i + 1}. ${agentName(s.agent)} \u2192 \`${s.output}\`${flag}`;
|
|
8019
|
+
}).join("\n") + "\n"
|
|
8020
|
+
);
|
|
8021
|
+
} else if (rec) {
|
|
8022
|
+
parts.push("## Recommended Agent Flow\n");
|
|
8023
|
+
const flowSteps = [];
|
|
8024
|
+
if (rec.agent) flowSteps.push(agentName(rec.agent));
|
|
8025
|
+
if (rec.secondary) {
|
|
8026
|
+
for (const s of rec.secondary) {
|
|
8027
|
+
if (s.agent) flowSteps.push(agentName(s.agent));
|
|
8028
|
+
else if (s.skill) flowSteps.push(`${s.skill} skill`);
|
|
8029
|
+
else if (s.command) flowSteps.push(`\`${s.command}\``);
|
|
8030
|
+
}
|
|
8031
|
+
}
|
|
8032
|
+
if (flowSteps.length > 0) {
|
|
8033
|
+
parts.push(flowSteps.map((f, i) => `${i + 1}. ${f}`).join("\n") + "\n");
|
|
8034
|
+
}
|
|
8035
|
+
}
|
|
8036
|
+
if (rec) {
|
|
8037
|
+
parts.push("## Primary Recommendation\n");
|
|
8038
|
+
const recLines = [`- id: ${rec.id}`];
|
|
8039
|
+
if (rec.agent) recLines.push(`- agent: ${agentName(rec.agent)}`);
|
|
8040
|
+
if (rec.skill) recLines.push(`- skill: ${rec.skill}`);
|
|
8041
|
+
if (rec.command) recLines.push(`- command: \`${rec.command}\``);
|
|
8042
|
+
recLines.push(`- reason: ${rec.reason}`);
|
|
8043
|
+
parts.push(recLines.join("\n") + "\n");
|
|
8044
|
+
}
|
|
8045
|
+
if (rec?.secondary && rec.secondary.length > 0) {
|
|
8046
|
+
parts.push("## Secondary Recommendations\n");
|
|
8047
|
+
parts.push(rec.secondary.map((s, i) => `${i + 1}. ${s.label}`).join("\n") + "\n");
|
|
8048
|
+
}
|
|
8049
|
+
if (plan.activeWorkItems && plan.activeWorkItems.length > 0) {
|
|
8050
|
+
parts.push("## Active Work Items\n");
|
|
8051
|
+
parts.push(
|
|
8052
|
+
plan.activeWorkItems.map((w) => `- ${w.id} [${w.type}] ${w.lifecycle} \u2014 ${w.title}`).join("\n") + "\n"
|
|
8053
|
+
);
|
|
8054
|
+
}
|
|
7815
8055
|
parts.push("## Context Pack\n");
|
|
7816
8056
|
const scanNote = plan.scanAvailable ? "" : " (incomplete \u2014 run `kaddo scan` first for a richer baseline)";
|
|
7817
8057
|
parts.push(`Use \`${plan.contextPackPath}\` as the primary input${scanNote}.
|
|
7818
8058
|
`);
|
|
8059
|
+
const agentPaths = [];
|
|
8060
|
+
if (plan.recommendedPaths && plan.recommendedPaths.length > 0) {
|
|
8061
|
+
agentPaths.push(...plan.recommendedPaths);
|
|
8062
|
+
} else if (steps.length > 0) {
|
|
8063
|
+
agentPaths.push(...steps.map((s) => agentInstallPath(s.agent)));
|
|
8064
|
+
}
|
|
8065
|
+
if (plan.recommendedSkillPaths && plan.recommendedSkillPaths.length > 0) {
|
|
8066
|
+
agentPaths.push(...plan.recommendedSkillPaths);
|
|
8067
|
+
}
|
|
8068
|
+
agentPaths.push(plan.contextPackPath);
|
|
7819
8069
|
parts.push("## Agent Prompts\n");
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
8070
|
+
if (agentPaths.length > 1) {
|
|
8071
|
+
parts.push("Use:\n");
|
|
8072
|
+
parts.push(agentPaths.map((p) => `- \`${p}\``).join("\n") + "\n");
|
|
8073
|
+
} else {
|
|
8074
|
+
parts.push(`- \`${plan.contextPackPath}\`
|
|
8075
|
+
`);
|
|
8076
|
+
}
|
|
8077
|
+
if (steps.length > 0) {
|
|
8078
|
+
parts.push("## Expected Outputs\n");
|
|
8079
|
+
parts.push(steps.map((s) => `- \`${s.output}\``).join("\n") + "\n");
|
|
8080
|
+
} else if (rec) {
|
|
8081
|
+
parts.push("## Expected Outputs\n");
|
|
8082
|
+
const outputs = [];
|
|
8083
|
+
if (rec.target) outputs.push(`- \`${rec.target}\``);
|
|
8084
|
+
if (rec.id === "refine-work-item" || rec.id === "resolve-blocker")
|
|
8085
|
+
outputs.push("- Refined Work Item content.");
|
|
8086
|
+
if (rec.id === "create-work-item")
|
|
8087
|
+
outputs.push("- A new Work Item under `knowledge/delivery/work-items/`.");
|
|
8088
|
+
if (rec.id === "implement")
|
|
8089
|
+
outputs.push("- Implementation plan or code changes guided by the Work Item.");
|
|
8090
|
+
if (rec.id === "guard")
|
|
8091
|
+
outputs.push("- Updated knowledge artifacts reflecting recent code changes.");
|
|
8092
|
+
if (rec.id === "install-adapter")
|
|
8093
|
+
outputs.push("- An adapter configured and injected (`kaddo adapters list`).");
|
|
8094
|
+
if (outputs.length > 0) {
|
|
8095
|
+
parts.push("The LLM should produce:\n");
|
|
8096
|
+
parts.push(outputs.join("\n") + "\n");
|
|
8097
|
+
} else {
|
|
8098
|
+
parts.push("_See the primary recommendation above._\n");
|
|
8099
|
+
}
|
|
8100
|
+
}
|
|
7825
8101
|
parts.push("## Copy/Paste Instructions\n");
|
|
7826
|
-
|
|
7827
|
-
|
|
8102
|
+
if (steps.length > 0) {
|
|
8103
|
+
const first2 = steps[0];
|
|
7828
8104
|
parts.push(
|
|
7829
8105
|
[
|
|
7830
8106
|
`Start with **${agentName(first2.agent)}**:`,
|
|
@@ -7836,9 +8112,31 @@ function renderUnderstand(plan) {
|
|
|
7836
8112
|
`5. Save the result in: \`${first2.output}\``
|
|
7837
8113
|
].join("\n") + "\n"
|
|
7838
8114
|
);
|
|
8115
|
+
} else if (agentPaths.length > 1) {
|
|
8116
|
+
parts.push("Paste the following into your LLM chat:\n");
|
|
8117
|
+
parts.push(agentPaths.map((p, i) => `${i + 1}. \`${p}\``).join("\n") + "\n");
|
|
8118
|
+
if (rec?.agent) {
|
|
8119
|
+
parts.push(`Ask the LLM to follow the ${agentName(rec.agent)} instructions.
|
|
8120
|
+
`);
|
|
8121
|
+
}
|
|
8122
|
+
} else {
|
|
8123
|
+
parts.push(`Paste \`${plan.contextPackPath}\` into your LLM chat.
|
|
8124
|
+
`);
|
|
7839
8125
|
}
|
|
7840
8126
|
parts.push("## Next Steps\n");
|
|
7841
|
-
if (
|
|
8127
|
+
if (rec) {
|
|
8128
|
+
const nextLines = [];
|
|
8129
|
+
nextLines.push(`1. ${rec.label}`);
|
|
8130
|
+
let n = 2;
|
|
8131
|
+
if (rec.secondary) {
|
|
8132
|
+
for (const s of rec.secondary) {
|
|
8133
|
+
nextLines.push(`${n}. ${s.label}`);
|
|
8134
|
+
n++;
|
|
8135
|
+
}
|
|
8136
|
+
}
|
|
8137
|
+
nextLines.push(`${n}. Re-run \`kaddo explain\`.`);
|
|
8138
|
+
parts.push(nextLines.join("\n") + "\n");
|
|
8139
|
+
} else if (steps.length > 1) {
|
|
7842
8140
|
parts.push(
|
|
7843
8141
|
`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.
|
|
7844
8142
|
`
|
package/package.json
CHANGED