@kaddo/cli 3.36.0 → 3.37.1
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 +269 -101
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -532,6 +532,8 @@ create --from roadmap → owners → guard → explain`.
|
|
|
532
532
|
| v3.34 | Pre-AI onboarding: `kaddo onboarding` (alias `onboard`) / `kaddo report onboarding` — read-only diagnosis of an existing project's readiness (scan/understand/knowledge/questions/roadmap/work-items/adapters) with a single recommended next step; `--json` |
|
|
533
533
|
| v3.35 | Folded onboarding into `kaddo explain`: removed `kaddo onboarding`/`onboard`/`report onboarding`; project readiness + single recommended next step now live in `kaddo explain` (human `## Project Readiness` + `readiness` in agent JSON) |
|
|
534
534
|
| v3.36 | State-aware bootstrap: `kaddo bootstrap` creates the full knowledge baseline for any `project.state` (new/pre-ai/legacy) with state-specific templates — no more new-only warning; idempotent, never overwrites, ensures `tech/decisions/` and `delivery/work-items/` |
|
|
535
|
+
| v3.37 | Placeholder-aware readiness: knowledge files are classified missing/placeholder/weak/useful; a bootstrap file isn't treated as ready knowledge. Layers downgrade to Placeholder/Weak, a new Knowledge Refinement phase recommends the right agent, and `create --from roadmap` is never suggested with 0 candidates |
|
|
536
|
+
| v3.37.1 | Unified next-step: one shared resolver (`core/next-step.ts`) powers `context`, `understand` and `explain` (Phase + Readiness) so they never diverge; agent JSON exposes `nextStepRecommendation`. Fixes duplicate `capabilities` line in explain |
|
|
535
537
|
|
|
536
538
|
**Optional modules (installed with `kaddo add`):**
|
|
537
539
|
|
package/dist/index.js
CHANGED
|
@@ -6041,14 +6041,23 @@ import { parse as parseYaml9 } from "yaml";
|
|
|
6041
6041
|
function layer(layers, name) {
|
|
6042
6042
|
return layers.find((l) => l.layer === name)?.status ?? "Missing";
|
|
6043
6043
|
}
|
|
6044
|
+
var NOT_READY_LAYER = ["Missing", "Placeholder", "Weak"];
|
|
6044
6045
|
function baseComplete(layers) {
|
|
6045
6046
|
return layer(layers, "Business") !== "Missing" && layer(layers, "Product") !== "Missing" && layer(layers, "Tech") !== "Missing";
|
|
6046
6047
|
}
|
|
6048
|
+
function baseUseful(layers) {
|
|
6049
|
+
return ["Business", "Product", "Tech"].every((n) => !NOT_READY_LAYER.includes(layer(layers, n)));
|
|
6050
|
+
}
|
|
6051
|
+
function roadmapHasCandidates(roadmap) {
|
|
6052
|
+
return roadmap.candidates > 0 || roadmap.remaining > 0;
|
|
6053
|
+
}
|
|
6047
6054
|
function determinePhase(input) {
|
|
6048
6055
|
const { roadmap, workItems } = input;
|
|
6049
6056
|
const active = workItems.byState.draft + workItems.byState.ready + workItems.byState["in-progress"] + workItems.byState.blocked;
|
|
6050
6057
|
if (!baseComplete(input.layers)) return "Discovery";
|
|
6058
|
+
if (!baseUseful(input.layers)) return "Knowledge Refinement";
|
|
6051
6059
|
if (!roadmap.present) return "Planning";
|
|
6060
|
+
if (!roadmapHasCandidates(roadmap) && workItems.total === 0) return "Planning";
|
|
6052
6061
|
if (workItems.total === 0) return "Delivery Preparation";
|
|
6053
6062
|
if (active > 0) return "Active Delivery";
|
|
6054
6063
|
return "Maintenance";
|
|
@@ -6078,6 +6087,14 @@ function firstMissingLayerAgent(layers) {
|
|
|
6078
6087
|
return { agent: "capability-agent", step: "Use capability-agent to create knowledge/product/capabilities.md" };
|
|
6079
6088
|
return { agent: "architecture-agent", step: "Use architecture-agent to create knowledge/tech/current-state.md" };
|
|
6080
6089
|
}
|
|
6090
|
+
function firstUnrefinedLayerAgent(layers) {
|
|
6091
|
+
const isThin = (n) => NOT_READY_LAYER.includes(layer(layers, n));
|
|
6092
|
+
if (isThin("Business"))
|
|
6093
|
+
return { agent: "business-agent", step: "Use business-agent to complete knowledge/business/business.md" };
|
|
6094
|
+
if (isThin("Product"))
|
|
6095
|
+
return { agent: "capability-agent", step: "Use capability-agent to complete knowledge/product/capabilities.md" };
|
|
6096
|
+
return { agent: "architecture-agent", step: "Use architecture-agent to complete knowledge/tech/current-state.md" };
|
|
6097
|
+
}
|
|
6081
6098
|
function assessPhase(input) {
|
|
6082
6099
|
const phase = determinePhase(input);
|
|
6083
6100
|
const reasons = buildReasons(input);
|
|
@@ -6095,6 +6112,17 @@ function assessPhase(input) {
|
|
|
6095
6112
|
llmInstructions = [`Use the ${m.agent} to fill the missing base knowledge.`, "Do not write code."];
|
|
6096
6113
|
break;
|
|
6097
6114
|
}
|
|
6115
|
+
case "Knowledge Refinement": {
|
|
6116
|
+
const m = firstUnrefinedLayerAgent(input.layers);
|
|
6117
|
+
recommendedAgents2 = [m.agent];
|
|
6118
|
+
nextStep = m.step;
|
|
6119
|
+
llmInstructions = [
|
|
6120
|
+
"The baseline files exist but still look like bootstrap placeholders.",
|
|
6121
|
+
`Use the ${m.agent} to replace the placeholders with real, project-specific knowledge.`,
|
|
6122
|
+
"Do not write code."
|
|
6123
|
+
];
|
|
6124
|
+
break;
|
|
6125
|
+
}
|
|
6098
6126
|
case "Planning": {
|
|
6099
6127
|
recommendedAgents2 = ["roadmap-agent"];
|
|
6100
6128
|
nextStep = "Use roadmap-agent to create knowledge/delivery/roadmap.md";
|
|
@@ -6957,9 +6985,97 @@ function statusFor(layer2, a) {
|
|
|
6957
6985
|
return "Missing";
|
|
6958
6986
|
}
|
|
6959
6987
|
|
|
6988
|
+
// src/core/artifact-quality.ts
|
|
6989
|
+
function isPlaceholderLine(line) {
|
|
6990
|
+
const t = line.trim();
|
|
6991
|
+
if (/^_.+_$/.test(t)) return true;
|
|
6992
|
+
if (/^[-*]\s+(\[[^\]]+\]\s*)?_.+_$/.test(t)) return true;
|
|
6993
|
+
if (/^[-*]\s+\[[^\]]+\]\s*$/.test(t)) return true;
|
|
6994
|
+
if (/^_(?:Describe|List|Document|What|Which|Who|Use|To be defined|No production code)\b/i.test(t)) return true;
|
|
6995
|
+
return false;
|
|
6996
|
+
}
|
|
6997
|
+
function isStructuralLine(line, inFrontMatter) {
|
|
6998
|
+
const t = line.trim();
|
|
6999
|
+
if (inFrontMatter) return true;
|
|
7000
|
+
if (t === "") return true;
|
|
7001
|
+
if (/^#{1,6}\s/.test(t)) return true;
|
|
7002
|
+
if (/^<!--/.test(t)) return true;
|
|
7003
|
+
if (/^>/.test(t)) return true;
|
|
7004
|
+
return false;
|
|
7005
|
+
}
|
|
7006
|
+
function analyzeContent(md) {
|
|
7007
|
+
const lines = md.split(/\r?\n/);
|
|
7008
|
+
let inFrontMatter = false;
|
|
7009
|
+
let seenFmFence = 0;
|
|
7010
|
+
let currentSectionHasUseful = false;
|
|
7011
|
+
const sectionsWithUseful = /* @__PURE__ */ new Set();
|
|
7012
|
+
let sectionIndex = 0;
|
|
7013
|
+
const contentLines = [];
|
|
7014
|
+
const usefulLines = [];
|
|
7015
|
+
for (const raw of lines) {
|
|
7016
|
+
const t = raw.trim();
|
|
7017
|
+
if (t === "---" && seenFmFence < 2) {
|
|
7018
|
+
seenFmFence += 1;
|
|
7019
|
+
inFrontMatter = seenFmFence === 1;
|
|
7020
|
+
continue;
|
|
7021
|
+
}
|
|
7022
|
+
if (seenFmFence === 1) continue;
|
|
7023
|
+
if (/^#{1,6}\s/.test(t)) {
|
|
7024
|
+
sectionIndex += 1;
|
|
7025
|
+
currentSectionHasUseful = false;
|
|
7026
|
+
continue;
|
|
7027
|
+
}
|
|
7028
|
+
if (isStructuralLine(raw, false)) continue;
|
|
7029
|
+
contentLines.push(t);
|
|
7030
|
+
if (!isPlaceholderLine(raw)) {
|
|
7031
|
+
usefulLines.push(t);
|
|
7032
|
+
if (!currentSectionHasUseful) {
|
|
7033
|
+
currentSectionHasUseful = true;
|
|
7034
|
+
sectionsWithUseful.add(sectionIndex);
|
|
7035
|
+
}
|
|
7036
|
+
}
|
|
7037
|
+
}
|
|
7038
|
+
if (usefulLines.length === 0) return "placeholder";
|
|
7039
|
+
const usefulWordCount = usefulLines.join(" ").split(/\s+/).filter(Boolean).length;
|
|
7040
|
+
const placeholderRatio = contentLines.length > 0 ? (contentLines.length - usefulLines.length) / contentLines.length : 1;
|
|
7041
|
+
if (usefulWordCount >= 80 && placeholderRatio < 0.25 && sectionsWithUseful.size >= 2) return "useful";
|
|
7042
|
+
return "weak";
|
|
7043
|
+
}
|
|
7044
|
+
function analyzeKnowledgeArtifact(dir, rel) {
|
|
7045
|
+
const p2 = join(dir, rel);
|
|
7046
|
+
if (!exists(p2)) return "missing";
|
|
7047
|
+
try {
|
|
7048
|
+
return analyzeContent(readFile(p2));
|
|
7049
|
+
} catch {
|
|
7050
|
+
return "missing";
|
|
7051
|
+
}
|
|
7052
|
+
}
|
|
7053
|
+
|
|
6960
7054
|
// src/core/layers.ts
|
|
7055
|
+
var LAYER_BASELINE = {
|
|
7056
|
+
Business: ["knowledge/business/business.md"],
|
|
7057
|
+
Product: ["knowledge/product/product.md", "knowledge/product/capabilities.md"],
|
|
7058
|
+
Tech: ["knowledge/tech/codebase.md", "knowledge/tech/current-state.md"],
|
|
7059
|
+
Delivery: ["knowledge/delivery/roadmap.md"]
|
|
7060
|
+
};
|
|
7061
|
+
function layerQuality(dir, layer2) {
|
|
7062
|
+
const order = ["missing", "placeholder", "weak", "useful"];
|
|
7063
|
+
let worst = null;
|
|
7064
|
+
for (const rel of LAYER_BASELINE[layer2]) {
|
|
7065
|
+
const q = analyzeKnowledgeArtifact(dir, rel);
|
|
7066
|
+
if (q === "missing") continue;
|
|
7067
|
+
if (worst === null || order.indexOf(q) < order.indexOf(worst)) worst = q;
|
|
7068
|
+
}
|
|
7069
|
+
return worst;
|
|
7070
|
+
}
|
|
6961
7071
|
function knowledgeLayers(dir) {
|
|
6962
|
-
return discoverLayers(dir)
|
|
7072
|
+
return discoverLayers(dir).map((l) => {
|
|
7073
|
+
if (l.status !== "Consolidated" && l.status !== "Structured") return l;
|
|
7074
|
+
const q = layerQuality(dir, l.layer);
|
|
7075
|
+
if (q === "placeholder") return { ...l, status: "Placeholder" };
|
|
7076
|
+
if (q === "weak") return { ...l, status: "Weak" };
|
|
7077
|
+
return l;
|
|
7078
|
+
});
|
|
6963
7079
|
}
|
|
6964
7080
|
function renderLayersMarkdown(layers) {
|
|
6965
7081
|
const lines = [];
|
|
@@ -7629,26 +7745,7 @@ function buildSharedFileStatuses(statuses) {
|
|
|
7629
7745
|
}));
|
|
7630
7746
|
}
|
|
7631
7747
|
|
|
7632
|
-
// src/core/
|
|
7633
|
-
var KNOWLEDGE_FILES = [
|
|
7634
|
-
{ key: "current_state", path: "knowledge/tech/current-state.md", label: "knowledge/tech/current-state.md" },
|
|
7635
|
-
{ key: "codebase", path: "knowledge/tech/codebase.md", label: "knowledge/tech/codebase.md" },
|
|
7636
|
-
{ key: "capabilities", path: "knowledge/product/capabilities.md", label: "knowledge/product/capabilities.md" },
|
|
7637
|
-
{ key: "product", path: "knowledge/product/product.md", label: "knowledge/product/product.md" },
|
|
7638
|
-
{ key: "business", path: "knowledge/business/business.md", label: "knowledge/business/business.md" }
|
|
7639
|
-
];
|
|
7640
|
-
function knowledgePresence(dir, rel) {
|
|
7641
|
-
const p2 = join(dir, rel);
|
|
7642
|
-
if (!exists(p2)) return "missing";
|
|
7643
|
-
let md;
|
|
7644
|
-
try {
|
|
7645
|
-
md = readFile(p2);
|
|
7646
|
-
} catch {
|
|
7647
|
-
return "missing";
|
|
7648
|
-
}
|
|
7649
|
-
const body = md.replace(/^---[\s\S]*?---/m, "").split(/\r?\n/).filter((l) => !/^\s*#{1,6}\s/.test(l) && !/^\s*$/.test(l) && !/^\s*<!--/.test(l)).join(" ").trim();
|
|
7650
|
-
return body.length >= 40 ? "present" : "weak";
|
|
7651
|
-
}
|
|
7748
|
+
// src/core/next-step.ts
|
|
7652
7749
|
function roadmapSignal(dir) {
|
|
7653
7750
|
const p2 = join(dir, "knowledge/delivery/roadmap.md");
|
|
7654
7751
|
if (!exists(p2)) return "missing";
|
|
@@ -7679,8 +7776,91 @@ function installedAdapters(dir) {
|
|
|
7679
7776
|
(s) => s.state === "injected" || s.state === "legacy-injected" || s.state === "full-generated" && (s.originAdapter === s.id || s.originAdapter === null)
|
|
7680
7777
|
).map((s) => s.id);
|
|
7681
7778
|
}
|
|
7779
|
+
var B = "knowledge/business/business.md";
|
|
7780
|
+
var P = "knowledge/product/product.md";
|
|
7781
|
+
var CAP = "knowledge/product/capabilities.md";
|
|
7782
|
+
var CS = "knowledge/tech/current-state.md";
|
|
7783
|
+
var CB = "knowledge/tech/codebase.md";
|
|
7784
|
+
function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
7785
|
+
const config = loadConfig(dir);
|
|
7786
|
+
if (!config) {
|
|
7787
|
+
return { id: "init", phase: "Setup", label: "Run `kaddo init` to initialize Kaddo.", command: "kaddo init", reason: "Kaddo is not initialized in this project." };
|
|
7788
|
+
}
|
|
7789
|
+
const state = config.project.state;
|
|
7790
|
+
const q = (rel) => analyzeKnowledgeArtifact(dir, rel);
|
|
7791
|
+
const qBusiness = q(B), qProduct = q(P), qCap = q(CAP), qCurrentState = q(CS), qCodebase = q(CB);
|
|
7792
|
+
if (qBusiness === "missing" || qProduct === "missing") {
|
|
7793
|
+
return { id: "bootstrap", phase: "Setup", label: "Run `kaddo bootstrap` to create the project knowledge baseline.", command: "kaddo bootstrap", reason: "The knowledge baseline is incomplete." };
|
|
7794
|
+
}
|
|
7795
|
+
const ctx = buildCodexAdapterContext(dir);
|
|
7796
|
+
if (!ctx.hasAgents) {
|
|
7797
|
+
return { id: "add-agents", phase: "Setup", label: "Run `kaddo add agents` to install the Kaddo agent prompt packs.", command: "kaddo add agents", reason: "No Kaddo agents are installed yet." };
|
|
7798
|
+
}
|
|
7799
|
+
if (!ctx.hasSkills) {
|
|
7800
|
+
return { id: "add-skills", phase: "Setup", label: "Run `kaddo add skills` to install reusable Kaddo skills.", command: "kaddo add skills", reason: "No Kaddo skills are installed yet." };
|
|
7801
|
+
}
|
|
7802
|
+
if ((state === "pre-ai" || state === "legacy") && !exists(join(dir, ".kaddo", "scan.json"))) {
|
|
7803
|
+
return { id: "scan", phase: "Discovery", label: "Run `kaddo scan` to capture deterministic signals from the existing code.", command: "kaddo scan", reason: "No scan baseline exists for this existing project yet." };
|
|
7804
|
+
}
|
|
7805
|
+
if (!exists(join(dir, ".kaddo", "context-pack.md"))) {
|
|
7806
|
+
return { id: "context", phase: "Discovery", label: "Run `kaddo context` to prepare the LLM context pack.", command: "kaddo context", reason: "No context pack has been generated yet." };
|
|
7807
|
+
}
|
|
7808
|
+
if (!exists(join(dir, ".kaddo", "understand.md"))) {
|
|
7809
|
+
return { id: "understand", phase: "Discovery", label: "Run `kaddo understand` to summarize the project context.", command: "kaddo understand", reason: "No understand handoff has been generated yet." };
|
|
7810
|
+
}
|
|
7811
|
+
const refine = (id, agent, target, quality) => ({
|
|
7812
|
+
id,
|
|
7813
|
+
phase: "Knowledge Refinement",
|
|
7814
|
+
label: `Use ${agent} to complete \`${target}\`.`,
|
|
7815
|
+
agent,
|
|
7816
|
+
target,
|
|
7817
|
+
reason: `${target} is ${quality === "missing" ? "missing" : "still a bootstrap placeholder or too thin"}.`,
|
|
7818
|
+
instructions: [
|
|
7819
|
+
"The baseline file exists but does not yet hold real, project-specific knowledge.",
|
|
7820
|
+
`Use the ${agent} to replace the placeholders with real knowledge.`,
|
|
7821
|
+
"Do not write code."
|
|
7822
|
+
]
|
|
7823
|
+
});
|
|
7824
|
+
if (qBusiness !== "useful") return refine("refine-business", "business-agent", B, qBusiness);
|
|
7825
|
+
if (qProduct !== "useful" || qCap !== "useful") {
|
|
7826
|
+
const target = qCap !== "useful" ? CAP : P;
|
|
7827
|
+
return refine("refine-product", "capability-agent", target, qCap !== "useful" ? qCap : qProduct);
|
|
7828
|
+
}
|
|
7829
|
+
if (qCurrentState !== "useful") return refine("refine-current-state", "architecture-agent", CS, qCurrentState);
|
|
7830
|
+
if (qCodebase !== "useful") {
|
|
7831
|
+
const agent = ctx.agents.includes("codebase-agent") ? "codebase-agent" : "architecture-agent";
|
|
7832
|
+
return refine("refine-codebase", agent, CB, qCodebase);
|
|
7833
|
+
}
|
|
7834
|
+
const oq = buildOpenQuestionsReport(dir, now);
|
|
7835
|
+
if (oq.summary.blocking_open > 0) {
|
|
7836
|
+
return { id: "questions", phase: "Knowledge Refinement", label: "Resolve, assume or defer the blocking open questions (`kaddo questions`).", command: "kaddo questions", reason: `${oq.summary.blocking_open} blocking open question(s) remain.` };
|
|
7837
|
+
}
|
|
7838
|
+
const roadmap = roadmapSignal(dir);
|
|
7839
|
+
if (roadmap !== "has-candidates") {
|
|
7840
|
+
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." };
|
|
7841
|
+
}
|
|
7842
|
+
const wi = workItemsSignal(dir);
|
|
7843
|
+
if (wi === "none" || wi === "none-ready") {
|
|
7844
|
+
return { id: "create-work-item", phase: "Delivery Preparation", label: "Run `kaddo create --from roadmap` to materialize the first Work Item.", command: "kaddo create --from roadmap", reason: "The roadmap has candidates but no Work Item is ready." };
|
|
7845
|
+
}
|
|
7846
|
+
const adapters = installedAdapters(dir);
|
|
7847
|
+
if (adapters.length === 0) {
|
|
7848
|
+
return { id: "install-adapter", phase: "Active Delivery", label: "Run `kaddo adapters list` and install the adapter for your preferred agent.", command: "kaddo adapters list", reason: "A Work Item is ready but no adapter is installed." };
|
|
7849
|
+
}
|
|
7850
|
+
return { id: "implement", phase: "Active Delivery", label: "Implement the ready Work Item and run `kaddo guard`.", reason: "A Work Item is ready and an adapter is installed." };
|
|
7851
|
+
}
|
|
7852
|
+
|
|
7853
|
+
// src/core/readiness.ts
|
|
7854
|
+
var KNOWLEDGE_FILES = [
|
|
7855
|
+
{ key: "current_state", path: "knowledge/tech/current-state.md", label: "knowledge/tech/current-state.md", agent: "architecture-agent" },
|
|
7856
|
+
{ key: "codebase", path: "knowledge/tech/codebase.md", label: "knowledge/tech/codebase.md", agent: "codebase-agent" },
|
|
7857
|
+
{ key: "capabilities", path: "knowledge/product/capabilities.md", label: "knowledge/product/capabilities.md", agent: "capability-agent" },
|
|
7858
|
+
{ key: "product", path: "knowledge/product/product.md", label: "knowledge/product/product.md", agent: "product-agent" },
|
|
7859
|
+
{ key: "business", path: "knowledge/business/business.md", label: "knowledge/business/business.md", agent: "business-agent" }
|
|
7860
|
+
];
|
|
7682
7861
|
function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
7683
7862
|
const config = loadConfig(dir);
|
|
7863
|
+
const rec = resolveNextStep(dir, now);
|
|
7684
7864
|
const stub = (overall2, label, command) => ({
|
|
7685
7865
|
project_name: config?.project.name ?? "unknown",
|
|
7686
7866
|
project_type: config?.project.state ?? "unknown",
|
|
@@ -7704,14 +7884,15 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
7704
7884
|
resolved_questions: 0,
|
|
7705
7885
|
deferred_questions: 0
|
|
7706
7886
|
},
|
|
7707
|
-
recommended_next_step: { label, ...command ? { command } : {} }
|
|
7887
|
+
recommended_next_step: { label, ...command ? { command } : {} },
|
|
7888
|
+
nextStepRecommendation: rec
|
|
7708
7889
|
});
|
|
7709
|
-
if (!config) return stub("not-initialized",
|
|
7890
|
+
if (!config) return stub("not-initialized", rec.label, rec.command);
|
|
7710
7891
|
if (config.project.state === "new") return stub("not-applicable", "This project uses the standard new-project Kaddo flow.");
|
|
7711
7892
|
if (config.project.state === "legacy") return stub("legacy-project", "Use the legacy project flow.");
|
|
7712
7893
|
const scan2 = exists(join(dir, ".kaddo", "scan.json")) ? "available" : "missing";
|
|
7713
7894
|
const understand = exists(join(dir, ".kaddo", "understand.md")) ? "available" : "missing";
|
|
7714
|
-
const presence = Object.fromEntries(KNOWLEDGE_FILES.map((f) => [f.key,
|
|
7895
|
+
const presence = Object.fromEntries(KNOWLEDGE_FILES.map((f) => [f.key, analyzeKnowledgeArtifact(dir, f.path)]));
|
|
7715
7896
|
const ctx = buildCodexAdapterContext(dir);
|
|
7716
7897
|
const agents = ctx.hasAgents ? "present" : "missing";
|
|
7717
7898
|
const skills = ctx.hasSkills ? "present" : "missing";
|
|
@@ -7739,41 +7920,26 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
7739
7920
|
resolved_questions: oq.summary.resolution.resolved,
|
|
7740
7921
|
deferred_questions: oq.summary.resolution.deferred
|
|
7741
7922
|
};
|
|
7923
|
+
const firstWeak = KNOWLEDGE_FILES.find((f) => presence[f.key] !== "useful");
|
|
7742
7924
|
let overall;
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
if (
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
|
|
7754
|
-
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
}
|
|
7761
|
-
overall = "knowledge-incomplete";
|
|
7762
|
-
next = { label: `Complete \`${firstWeak.label}\` (it is ${presence[firstWeak.key]}).` };
|
|
7763
|
-
} else if (oq.summary.blocking_open > 0) {
|
|
7764
|
-
overall = "needs-decisions";
|
|
7765
|
-
next = { label: "Resolve, assume or defer the blocking open questions (`kaddo questions`).", command: "kaddo questions" };
|
|
7766
|
-
} else if (roadmap !== "has-candidates") {
|
|
7767
|
-
overall = "ready-for-roadmap";
|
|
7768
|
-
next = { label: "Run `kaddo roadmap` to draft the roadmap from the current knowledge.", command: "kaddo roadmap" };
|
|
7769
|
-
} else if (work_items === "none" || work_items === "none-ready") {
|
|
7770
|
-
overall = "ready-for-work-item";
|
|
7771
|
-
next = { label: "Run `kaddo create --from roadmap` to materialize the first Work Item.", command: "kaddo create --from roadmap" };
|
|
7772
|
-
} else {
|
|
7773
|
-
overall = "ready-for-implementation";
|
|
7774
|
-
next = adapters.length > 0 ? { label: "Implement the ready Work Item and run `kaddo guard`." } : { label: "Run `kaddo adapters list` and install the adapter for your preferred agent.", command: "kaddo adapters list" };
|
|
7775
|
-
}
|
|
7776
|
-
return { project_name: config.project.name, project_type: config.project.state, overall, signals, recommended_next_step: next };
|
|
7925
|
+
if (scan2 === "missing") overall = "initialized";
|
|
7926
|
+
else if (bootstrapBaseline === "incomplete") overall = "bootstrap-incomplete";
|
|
7927
|
+
else if (agents === "missing") overall = "agents-missing";
|
|
7928
|
+
else if (skills === "missing") overall = "skills-missing";
|
|
7929
|
+
else if (understand === "missing") overall = "scanned";
|
|
7930
|
+
else if (firstWeak) overall = "knowledge-incomplete";
|
|
7931
|
+
else if (oq.summary.blocking_open > 0) overall = "needs-decisions";
|
|
7932
|
+
else if (roadmap !== "has-candidates") overall = "ready-for-roadmap";
|
|
7933
|
+
else if (work_items === "none" || work_items === "none-ready") overall = "ready-for-work-item";
|
|
7934
|
+
else overall = "ready-for-implementation";
|
|
7935
|
+
return {
|
|
7936
|
+
project_name: config.project.name,
|
|
7937
|
+
project_type: config.project.state,
|
|
7938
|
+
overall,
|
|
7939
|
+
signals,
|
|
7940
|
+
recommended_next_step: { label: rec.label, ...rec.command ? { command: rec.command } : {} },
|
|
7941
|
+
nextStepRecommendation: rec
|
|
7942
|
+
};
|
|
7777
7943
|
}
|
|
7778
7944
|
|
|
7779
7945
|
// src/core/project-explain.ts
|
|
@@ -7965,6 +8131,7 @@ function buildProjectExplanation(dir) {
|
|
|
7965
8131
|
"Run `kaddo owners suggest` for Work Items without code ownership."
|
|
7966
8132
|
);
|
|
7967
8133
|
}
|
|
8134
|
+
const readiness = buildReadinessReport(dir);
|
|
7968
8135
|
return {
|
|
7969
8136
|
project,
|
|
7970
8137
|
stack,
|
|
@@ -7985,7 +8152,8 @@ function buildProjectExplanation(dir) {
|
|
|
7985
8152
|
mappedModules,
|
|
7986
8153
|
missingKnowledge,
|
|
7987
8154
|
suggestedNextSteps,
|
|
7988
|
-
readiness
|
|
8155
|
+
readiness,
|
|
8156
|
+
nextStepRecommendation: readiness.nextStepRecommendation
|
|
7989
8157
|
};
|
|
7990
8158
|
}
|
|
7991
8159
|
function stateLabel(state) {
|
|
@@ -8166,7 +8334,7 @@ function renderExplanationHuman(exp) {
|
|
|
8166
8334
|
lines.push("- Reason:");
|
|
8167
8335
|
for (const r2 of assessment.reasons) lines.push(` - ${r2}`);
|
|
8168
8336
|
}
|
|
8169
|
-
|
|
8337
|
+
lines.push(`- Next step: ${exp.readiness.recommended_next_step.label}`);
|
|
8170
8338
|
lines.push("");
|
|
8171
8339
|
if (exp.suggestedNextSteps.length > 0) {
|
|
8172
8340
|
lines.push("## Suggested Next Steps");
|
|
@@ -8183,9 +8351,11 @@ function renderExplanationHuman(exp) {
|
|
|
8183
8351
|
lines.push(`- understand: ${s.understand}`);
|
|
8184
8352
|
lines.push(`- agents: ${s.agents}`);
|
|
8185
8353
|
lines.push(`- skills: ${s.skills}`);
|
|
8354
|
+
lines.push(`- business: ${s.business}`);
|
|
8355
|
+
lines.push(`- product: ${s.product}`);
|
|
8356
|
+
lines.push(`- capabilities: ${s.capabilities}`);
|
|
8186
8357
|
lines.push(`- current-state: ${s.current_state}`);
|
|
8187
8358
|
lines.push(`- codebase: ${s.codebase}`);
|
|
8188
|
-
lines.push(`- capabilities: ${s.capabilities}`);
|
|
8189
8359
|
lines.push(`- roadmap: ${s.roadmap}`);
|
|
8190
8360
|
lines.push(`- work-items: ${s.work_items}`);
|
|
8191
8361
|
lines.push(`- adapters: ${s.adapters.length > 0 ? s.adapters.join(", ") + " installed" : "none installed"}`);
|
|
@@ -8429,28 +8599,6 @@ function recommendedAgentsForState(state) {
|
|
|
8429
8599
|
return ["capability-agent", "architecture-agent", "roadmap-agent"];
|
|
8430
8600
|
}
|
|
8431
8601
|
}
|
|
8432
|
-
function nextStepsForState2(state) {
|
|
8433
|
-
switch (state) {
|
|
8434
|
-
case "new":
|
|
8435
|
-
return [
|
|
8436
|
-
"Use roadmap-agent to shape an initial roadmap.",
|
|
8437
|
-
"Use architecture-agent to outline the intended architecture."
|
|
8438
|
-
];
|
|
8439
|
-
case "legacy":
|
|
8440
|
-
return [
|
|
8441
|
-
"Use legacy-agent to surface risks and unknowns before changing code.",
|
|
8442
|
-
"Use architecture-agent to reconstruct the current architecture.",
|
|
8443
|
-
"Use capability-agent to map existing capabilities."
|
|
8444
|
-
];
|
|
8445
|
-
case "pre-ai":
|
|
8446
|
-
default:
|
|
8447
|
-
return [
|
|
8448
|
-
"Use capability-agent to extract system capabilities.",
|
|
8449
|
-
"Use architecture-agent to reconstruct the current architecture.",
|
|
8450
|
-
"Use roadmap-agent to propose roadmap candidates."
|
|
8451
|
-
];
|
|
8452
|
-
}
|
|
8453
|
-
}
|
|
8454
8602
|
var LLM_INSTRUCTIONS = [
|
|
8455
8603
|
"Use this context pack as the project baseline.",
|
|
8456
8604
|
"Do not write code yet.",
|
|
@@ -8522,6 +8670,24 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
8522
8670
|
}
|
|
8523
8671
|
const mappedModules = loadMappedModules(dir);
|
|
8524
8672
|
const layers = knowledgeLayers(dir);
|
|
8673
|
+
const qa = (rel) => analyzeKnowledgeArtifact(dir, rel);
|
|
8674
|
+
const qBusiness = qa("knowledge/business/business.md");
|
|
8675
|
+
const qProduct = qa("knowledge/product/product.md");
|
|
8676
|
+
const qCapabilities = qa("knowledge/product/capabilities.md");
|
|
8677
|
+
const qCodebase = qa("knowledge/tech/codebase.md");
|
|
8678
|
+
const qCurrentState = qa("knowledge/tech/current-state.md");
|
|
8679
|
+
const qRoadmap = qa("knowledge/delivery/roadmap.md");
|
|
8680
|
+
const layerStatusOf = (name) => layers.find((l) => l.layer === name)?.status ?? "Missing";
|
|
8681
|
+
const knowledgeQuality = {
|
|
8682
|
+
business: { status: layerStatusOf("Business"), artifacts: { "knowledge/business/business.md": qBusiness } },
|
|
8683
|
+
product: { status: layerStatusOf("Product"), artifacts: { "knowledge/product/product.md": qProduct, "knowledge/product/capabilities.md": qCapabilities } },
|
|
8684
|
+
tech: { status: layerStatusOf("Tech"), artifacts: { "knowledge/tech/codebase.md": qCodebase, "knowledge/tech/current-state.md": qCurrentState } },
|
|
8685
|
+
delivery: { status: layerStatusOf("Delivery"), artifacts: { "knowledge/delivery/roadmap.md": qRoadmap } }
|
|
8686
|
+
};
|
|
8687
|
+
if (qBusiness === "placeholder") missing.push("Business context exists but still looks like a bootstrap placeholder.");
|
|
8688
|
+
if (qProduct === "placeholder" || qCapabilities === "placeholder") missing.push("Product capabilities exist but still look like a bootstrap placeholder.");
|
|
8689
|
+
if (qCurrentState === "placeholder") missing.push("Current state exists but still looks like a bootstrap placeholder.");
|
|
8690
|
+
if (qCodebase === "placeholder") missing.push("Codebase map exists but still looks like a bootstrap placeholder.");
|
|
8525
8691
|
const allWorkItems = allArtifacts.filter((a) => a.isWorkItem);
|
|
8526
8692
|
const wiWithOwnership = allWorkItems.filter((a) => a.codeGlobs.length > 0).length;
|
|
8527
8693
|
const phase = assessPhase({
|
|
@@ -8544,6 +8710,12 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
8544
8710
|
workItemsMissingOwnership: allWorkItems.length - wiWithOwnership
|
|
8545
8711
|
}
|
|
8546
8712
|
});
|
|
8713
|
+
const nextStepRecommendation = resolveNextStep(dir, now);
|
|
8714
|
+
const unifiedPhase = {
|
|
8715
|
+
...phase,
|
|
8716
|
+
nextStep: nextStepRecommendation.label,
|
|
8717
|
+
recommendedAgents: nextStepRecommendation.agent ? [nextStepRecommendation.agent] : phase.recommendedAgents
|
|
8718
|
+
};
|
|
8547
8719
|
return {
|
|
8548
8720
|
version: CONTEXT_PACK_VERSION,
|
|
8549
8721
|
generatedAt: now.toISOString(),
|
|
@@ -8572,8 +8744,10 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
8572
8744
|
artifacts: allArtifacts.filter((a) => a.codeGlobs.length > 0).map(toContextArtifact)
|
|
8573
8745
|
},
|
|
8574
8746
|
layers,
|
|
8747
|
+
knowledgeQuality,
|
|
8575
8748
|
roadmap,
|
|
8576
|
-
phase,
|
|
8749
|
+
phase: unifiedPhase,
|
|
8750
|
+
nextStepRecommendation,
|
|
8577
8751
|
deliveryMix,
|
|
8578
8752
|
external: loadExternalCapsules(dir),
|
|
8579
8753
|
graph: loadGraphSummary(dir),
|
|
@@ -8581,12 +8755,12 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
8581
8755
|
skills: discoverInstalledSkills(dir).map((s) => s.id),
|
|
8582
8756
|
mappedModules,
|
|
8583
8757
|
missing,
|
|
8584
|
-
// VS-052: the handoff is driven by the
|
|
8585
|
-
//
|
|
8758
|
+
// VS-052/VS-073.2: the handoff is driven by the unified next step, so the pack never contradicts
|
|
8759
|
+
// the Current Phase block above it.
|
|
8586
8760
|
handoff: {
|
|
8587
|
-
recommendedAgents:
|
|
8588
|
-
nextSteps:
|
|
8589
|
-
instructions:
|
|
8761
|
+
recommendedAgents: unifiedPhase.recommendedAgents.length > 0 ? unifiedPhase.recommendedAgents : recommendedAgentsForState(state),
|
|
8762
|
+
nextSteps: [nextStepRecommendation.label],
|
|
8763
|
+
instructions: unifiedPhase.llmInstructions.length > 0 ? unifiedPhase.llmInstructions : LLM_INSTRUCTIONS,
|
|
8590
8764
|
operatingRules: OPERATING_RULES
|
|
8591
8765
|
}
|
|
8592
8766
|
};
|
|
@@ -9090,24 +9264,16 @@ function runUnderstand() {
|
|
|
9090
9264
|
console.log(`Project language: ${pack.project.language} (knowledge artifacts are written in this language)`);
|
|
9091
9265
|
const exp = buildProjectExplanation(dir);
|
|
9092
9266
|
const assessment = assessPhase(exp);
|
|
9267
|
+
const rec = exp.nextStepRecommendation;
|
|
9093
9268
|
console.log("");
|
|
9094
9269
|
console.log(`Current phase: ${assessment.phase}`);
|
|
9095
9270
|
if (assessment.reasons.length > 0) {
|
|
9096
9271
|
console.log("Reason:");
|
|
9097
9272
|
for (const r of assessment.reasons) console.log(` - ${r}`);
|
|
9098
9273
|
}
|
|
9099
|
-
if (
|
|
9100
|
-
|
|
9101
|
-
}
|
|
9102
|
-
if (assessment.nextStep) {
|
|
9103
|
-
console.log(`Next step: ${assessment.nextStep}`);
|
|
9104
|
-
}
|
|
9105
|
-
const readiness = exp.readiness;
|
|
9106
|
-
if (readiness.overall === "initialized" || readiness.overall === "bootstrap-incomplete") {
|
|
9107
|
-
console.log("");
|
|
9108
|
-
console.log(`Project readiness: ${readiness.overall}.`);
|
|
9109
|
-
console.log(` \u2192 ${readiness.recommended_next_step.label}`);
|
|
9110
|
-
}
|
|
9274
|
+
if (rec.agent) console.log(`Recommended: ${rec.agent}`);
|
|
9275
|
+
console.log(`Next step: ${rec.label}`);
|
|
9276
|
+
if (rec.reason) console.log(`Why: ${rec.reason}`);
|
|
9111
9277
|
const installedSkills = discoverInstalledSkills(dir);
|
|
9112
9278
|
if (installedSkills.length > 0 && assessment.recommendedAgents.length > 0) {
|
|
9113
9279
|
const recSkills = skillsForAgents(installedSkills, assessment.recommendedAgents);
|
|
@@ -11846,6 +12012,8 @@ function fm(type, state) {
|
|
|
11846
12012
|
return `---
|
|
11847
12013
|
type: ${type}
|
|
11848
12014
|
project_state: ${state}
|
|
12015
|
+
generated_by: kaddo-bootstrap
|
|
12016
|
+
template_version: 1
|
|
11849
12017
|
---
|
|
11850
12018
|
|
|
11851
12019
|
`;
|