@kaddo/cli 3.36.0 → 3.37.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 +149 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -532,6 +532,7 @@ 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 |
|
|
535
536
|
|
|
536
537
|
**Optional modules (installed with `kaddo add`):**
|
|
537
538
|
|
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 = [];
|
|
@@ -7631,24 +7747,12 @@ function buildSharedFileStatuses(statuses) {
|
|
|
7631
7747
|
|
|
7632
7748
|
// src/core/readiness.ts
|
|
7633
7749
|
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" }
|
|
7750
|
+
{ key: "current_state", path: "knowledge/tech/current-state.md", label: "knowledge/tech/current-state.md", agent: "architecture-agent" },
|
|
7751
|
+
{ key: "codebase", path: "knowledge/tech/codebase.md", label: "knowledge/tech/codebase.md", agent: "codebase-agent" },
|
|
7752
|
+
{ key: "capabilities", path: "knowledge/product/capabilities.md", label: "knowledge/product/capabilities.md", agent: "capability-agent" },
|
|
7753
|
+
{ key: "product", path: "knowledge/product/product.md", label: "knowledge/product/product.md", agent: "product-agent" },
|
|
7754
|
+
{ key: "business", path: "knowledge/business/business.md", label: "knowledge/business/business.md", agent: "business-agent" }
|
|
7639
7755
|
];
|
|
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
|
-
}
|
|
7652
7756
|
function roadmapSignal(dir) {
|
|
7653
7757
|
const p2 = join(dir, "knowledge/delivery/roadmap.md");
|
|
7654
7758
|
if (!exists(p2)) return "missing";
|
|
@@ -7711,7 +7815,7 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
7711
7815
|
if (config.project.state === "legacy") return stub("legacy-project", "Use the legacy project flow.");
|
|
7712
7816
|
const scan2 = exists(join(dir, ".kaddo", "scan.json")) ? "available" : "missing";
|
|
7713
7817
|
const understand = exists(join(dir, ".kaddo", "understand.md")) ? "available" : "missing";
|
|
7714
|
-
const presence = Object.fromEntries(KNOWLEDGE_FILES.map((f) => [f.key,
|
|
7818
|
+
const presence = Object.fromEntries(KNOWLEDGE_FILES.map((f) => [f.key, analyzeKnowledgeArtifact(dir, f.path)]));
|
|
7715
7819
|
const ctx = buildCodexAdapterContext(dir);
|
|
7716
7820
|
const agents = ctx.hasAgents ? "present" : "missing";
|
|
7717
7821
|
const skills = ctx.hasSkills ? "present" : "missing";
|
|
@@ -7741,7 +7845,7 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
7741
7845
|
};
|
|
7742
7846
|
let overall;
|
|
7743
7847
|
let next;
|
|
7744
|
-
const firstWeak = KNOWLEDGE_FILES.find((f) => presence[f.key] !== "
|
|
7848
|
+
const firstWeak = KNOWLEDGE_FILES.find((f) => presence[f.key] !== "useful");
|
|
7745
7849
|
if (scan2 === "missing") {
|
|
7746
7850
|
overall = "initialized";
|
|
7747
7851
|
next = { label: "Run `kaddo scan` to capture deterministic signals from the existing code.", command: "kaddo scan" };
|
|
@@ -7759,7 +7863,7 @@ function buildReadinessReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
7759
7863
|
next = { label: "Run `kaddo understand` to summarize the project context.", command: "kaddo understand" };
|
|
7760
7864
|
} else if (firstWeak) {
|
|
7761
7865
|
overall = "knowledge-incomplete";
|
|
7762
|
-
next = { label: `
|
|
7866
|
+
next = { label: `Use ${firstWeak.agent} to complete \`${firstWeak.label}\` (it is ${presence[firstWeak.key]}).` };
|
|
7763
7867
|
} else if (oq.summary.blocking_open > 0) {
|
|
7764
7868
|
overall = "needs-decisions";
|
|
7765
7869
|
next = { label: "Resolve, assume or defer the blocking open questions (`kaddo questions`).", command: "kaddo questions" };
|
|
@@ -8183,6 +8287,9 @@ function renderExplanationHuman(exp) {
|
|
|
8183
8287
|
lines.push(`- understand: ${s.understand}`);
|
|
8184
8288
|
lines.push(`- agents: ${s.agents}`);
|
|
8185
8289
|
lines.push(`- skills: ${s.skills}`);
|
|
8290
|
+
lines.push(`- business: ${s.business}`);
|
|
8291
|
+
lines.push(`- product: ${s.product}`);
|
|
8292
|
+
lines.push(`- capabilities: ${s.capabilities}`);
|
|
8186
8293
|
lines.push(`- current-state: ${s.current_state}`);
|
|
8187
8294
|
lines.push(`- codebase: ${s.codebase}`);
|
|
8188
8295
|
lines.push(`- capabilities: ${s.capabilities}`);
|
|
@@ -8522,6 +8629,24 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
8522
8629
|
}
|
|
8523
8630
|
const mappedModules = loadMappedModules(dir);
|
|
8524
8631
|
const layers = knowledgeLayers(dir);
|
|
8632
|
+
const qa = (rel) => analyzeKnowledgeArtifact(dir, rel);
|
|
8633
|
+
const qBusiness = qa("knowledge/business/business.md");
|
|
8634
|
+
const qProduct = qa("knowledge/product/product.md");
|
|
8635
|
+
const qCapabilities = qa("knowledge/product/capabilities.md");
|
|
8636
|
+
const qCodebase = qa("knowledge/tech/codebase.md");
|
|
8637
|
+
const qCurrentState = qa("knowledge/tech/current-state.md");
|
|
8638
|
+
const qRoadmap = qa("knowledge/delivery/roadmap.md");
|
|
8639
|
+
const layerStatusOf = (name) => layers.find((l) => l.layer === name)?.status ?? "Missing";
|
|
8640
|
+
const knowledgeQuality = {
|
|
8641
|
+
business: { status: layerStatusOf("Business"), artifacts: { "knowledge/business/business.md": qBusiness } },
|
|
8642
|
+
product: { status: layerStatusOf("Product"), artifacts: { "knowledge/product/product.md": qProduct, "knowledge/product/capabilities.md": qCapabilities } },
|
|
8643
|
+
tech: { status: layerStatusOf("Tech"), artifacts: { "knowledge/tech/codebase.md": qCodebase, "knowledge/tech/current-state.md": qCurrentState } },
|
|
8644
|
+
delivery: { status: layerStatusOf("Delivery"), artifacts: { "knowledge/delivery/roadmap.md": qRoadmap } }
|
|
8645
|
+
};
|
|
8646
|
+
if (qBusiness === "placeholder") missing.push("Business context exists but still looks like a bootstrap placeholder.");
|
|
8647
|
+
if (qProduct === "placeholder" || qCapabilities === "placeholder") missing.push("Product capabilities exist but still look like a bootstrap placeholder.");
|
|
8648
|
+
if (qCurrentState === "placeholder") missing.push("Current state exists but still looks like a bootstrap placeholder.");
|
|
8649
|
+
if (qCodebase === "placeholder") missing.push("Codebase map exists but still looks like a bootstrap placeholder.");
|
|
8525
8650
|
const allWorkItems = allArtifacts.filter((a) => a.isWorkItem);
|
|
8526
8651
|
const wiWithOwnership = allWorkItems.filter((a) => a.codeGlobs.length > 0).length;
|
|
8527
8652
|
const phase = assessPhase({
|
|
@@ -8572,6 +8697,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
8572
8697
|
artifacts: allArtifacts.filter((a) => a.codeGlobs.length > 0).map(toContextArtifact)
|
|
8573
8698
|
},
|
|
8574
8699
|
layers,
|
|
8700
|
+
knowledgeQuality,
|
|
8575
8701
|
roadmap,
|
|
8576
8702
|
phase,
|
|
8577
8703
|
deliveryMix,
|
|
@@ -11846,6 +11972,8 @@ function fm(type, state) {
|
|
|
11846
11972
|
return `---
|
|
11847
11973
|
type: ${type}
|
|
11848
11974
|
project_state: ${state}
|
|
11975
|
+
generated_by: kaddo-bootstrap
|
|
11976
|
+
template_version: 1
|
|
11849
11977
|
---
|
|
11850
11978
|
|
|
11851
11979
|
`;
|