@kaddo/cli 3.32.0 → 3.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.js +351 -45
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -528,6 +528,8 @@ create --from roadmap → owners → guard → explain`.
|
|
|
528
528
|
| v3.30 | Antigravity adapter: `kaddo adapters install antigravity` (alias `kaddo export antigravity`) generates an `AGENTS.md` projection reusing the shared adapter common core (`--dry-run`/`--force`/`--inject` + inject guard) |
|
|
529
529
|
| v3.31 | Kiro adapter: `kaddo adapters install kiro` (alias `kaddo export kiro`) generates an `AGENTS.md` projection reusing the shared adapter common core (`--dry-run`/`--force`/`--inject` + inject guard) |
|
|
530
530
|
| v3.32 | Adapter discovery & status: `kaddo adapters list` (alias `ls`) and `kaddo adapters status` (alias `check`) — read-only catalog + per-adapter install state (missing/team-owned/injected/legacy-injected/full-generated/broken-markers), shared-`AGENTS.md` origin detection, `--json` |
|
|
531
|
+
| v3.33 | Open questions resolution tracking: mark questions `[open]`/`[resolved]`/`[assumed]`/`[deferred]` (EN+ES); only `open` blocks readiness, so assumed/resolved/deferred decisions stop false blocks. `kaddo questions` shows status counts + `resolution_status` in JSON |
|
|
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` |
|
|
531
533
|
|
|
532
534
|
**Optional modules (installed with `kaddo add`):**
|
|
533
535
|
|
package/dist/index.js
CHANGED
|
@@ -704,6 +704,10 @@ var COMMAND_HELP = {
|
|
|
704
704
|
"adapters install codex": {
|
|
705
705
|
question: "How does Codex work with this Kaddo project?",
|
|
706
706
|
next: "Open AGENTS.md, or regenerate it after knowledge changes"
|
|
707
|
+
},
|
|
708
|
+
onboarding: {
|
|
709
|
+
question: "How ready is this pre-AI project to work with agents?",
|
|
710
|
+
next: "Run the single recommended next step, then re-run `kaddo onboarding`"
|
|
707
711
|
}
|
|
708
712
|
};
|
|
709
713
|
function commandFooterLines(name) {
|
|
@@ -2414,9 +2418,13 @@ open question):
|
|
|
2414
2418
|
Before generating the roadmap, check **roadmap readiness** for open questions that affect scope,
|
|
2415
2419
|
architecture or the MVP. Read \`kaddo://roadmap-readiness\` (MCP) or run \`kaddo questions\`.
|
|
2416
2420
|
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2421
|
+
Only questions with \`resolution_status = open\` block readiness. Questions marked \`[resolved]\`,
|
|
2422
|
+
\`[assumed]\` or \`[deferred]\` (EN) / \`[resuelta]\` \`[asumida]\` \`[diferida]\` (ES) do **not** block \u2014
|
|
2423
|
+
surface assumed ones as assumptions and deferred ones as out-of-scope, then continue.
|
|
2424
|
+
|
|
2425
|
+
If readiness is \`needs_decisions\` (there are **blocking open** questions), do **not** generate the
|
|
2426
|
+
roadmap yet. Instead, list the blocking open questions, propose reasonable assumptions for each, and
|
|
2427
|
+
ask the user to confirm, e.g.:
|
|
2420
2428
|
|
|
2421
2429
|
> Before generating the roadmap I found blocking open questions that affect the MVP scope.
|
|
2422
2430
|
> I can proceed with these assumptions: \u2026 Confirm and continue?
|
|
@@ -2735,9 +2743,11 @@ Work Item actionable for a human.
|
|
|
2735
2743
|
## Readiness Gate (check first)
|
|
2736
2744
|
|
|
2737
2745
|
For high-impact Work Items, check \`kaddo://roadmap-readiness\` (or \`kaddo questions\`) for
|
|
2738
|
-
**blocking
|
|
2739
|
-
and propose assumptions for the user to confirm before refining \u2014 don't bake in
|
|
2740
|
-
assumptions.
|
|
2746
|
+
**blocking open** questions (\`resolution_status = open\`) related to this Work Item's scope. If any are
|
|
2747
|
+
open, surface them and propose assumptions for the user to confirm before refining \u2014 don't bake in
|
|
2748
|
+
invisible assumptions. Convert each open question into an explicit decision (\`[resolved]\`), an
|
|
2749
|
+
explicit assumption (\`[assumed]\`), or move it out of scope (\`[deferred]\`). Questions already marked
|
|
2750
|
+
resolved/assumed/deferred do not block.
|
|
2741
2751
|
|
|
2742
2752
|
## When to Use
|
|
2743
2753
|
|
|
@@ -3244,8 +3254,20 @@ new project. You propose; the human decides.
|
|
|
3244
3254
|
|
|
3245
3255
|
Bootstrap surfaces \`## Open Questions\` in the knowledge files. Before moving to the roadmap,
|
|
3246
3256
|
explicitly recommend reviewing them (\`kaddo questions\` / \`kaddo://roadmap-readiness\`) and turning
|
|
3247
|
-
**blocking** ones into resolved decisions or confirmed assumptions \u2014 so the roadmap isn't built
|
|
3248
|
-
invisible assumptions.
|
|
3257
|
+
**blocking open** ones into resolved decisions or confirmed assumptions \u2014 so the roadmap isn't built
|
|
3258
|
+
on invisible assumptions. When you find unresolved questions, suggest marking each with a resolution
|
|
3259
|
+
token so Kaddo can track them: \`[open]\`, \`[resolved]\`, \`[assumed]\` or \`[deferred]\` (ES: \`[abierta]\`,
|
|
3260
|
+
\`[resuelta]\`, \`[asumida]\`, \`[diferida]\`). Only \`open\` questions block readiness.
|
|
3261
|
+
|
|
3262
|
+
## Pre-AI projects
|
|
3263
|
+
|
|
3264
|
+
For **pre-AI** projects (existing code, little structured knowledge), use \`kaddo onboarding\` as the
|
|
3265
|
+
compass: it diagnoses the current state and recommends a single next step along the cycle
|
|
3266
|
+
\`init \u2192 scan \u2192 understand \u2192 onboarding \u2192 questions \u2192 roadmap \u2192 create --from roadmap \u2192 adapter \u2192
|
|
3267
|
+
implement \u2192 guard\`. Use the \`scan\` and \`understand\` outputs as input \u2014 **do not invent project
|
|
3268
|
+
goals or capabilities**. Capture unknowns as \`[open]\` questions and safe, explicit defaults as
|
|
3269
|
+
\`[assumed]\`. Build \`knowledge/tech/current-state.md\` and \`knowledge/tech/codebase.md\` from real
|
|
3270
|
+
repo signals before drafting the roadmap or the first Work Item.
|
|
3249
3271
|
|
|
3250
3272
|
## When to Use
|
|
3251
3273
|
|
|
@@ -3361,9 +3383,11 @@ human's, and commits/pushes/merges happen only with explicit human confirmation.
|
|
|
3361
3383
|
## Readiness Gate (check first)
|
|
3362
3384
|
|
|
3363
3385
|
Before implementing a high-impact Work Item, check \`kaddo://roadmap-readiness\` (or
|
|
3364
|
-
\`kaddo questions\`) for **blocking
|
|
3365
|
-
authentication or the Work Item's scope.
|
|
3366
|
-
|
|
3386
|
+
\`kaddo questions\`) for **blocking open** questions (\`resolution_status = open\`) about stack,
|
|
3387
|
+
architecture, persistence, authentication or the Work Item's scope. Block only on questions still
|
|
3388
|
+
\`open\`; if a related question is already \`[assumed]\`, \`[resolved]\` or \`[deferred]\`, proceed and
|
|
3389
|
+
mention the relevant assumptions instead of pausing. If any blocking question is still open, pause and
|
|
3390
|
+
ask the user to confirm assumptions or resolve them before writing code.
|
|
3367
3391
|
|
|
3368
3392
|
## When to Use
|
|
3369
3393
|
|
|
@@ -8207,6 +8231,24 @@ function renderDeliveryLifecycle(wi) {
|
|
|
8207
8231
|
}
|
|
8208
8232
|
|
|
8209
8233
|
// src/core/open-questions.ts
|
|
8234
|
+
var RESOLUTION_TOKENS = {
|
|
8235
|
+
open: "open",
|
|
8236
|
+
resolved: "resolved",
|
|
8237
|
+
assumed: "assumed",
|
|
8238
|
+
deferred: "deferred",
|
|
8239
|
+
abierta: "open",
|
|
8240
|
+
resuelta: "resolved",
|
|
8241
|
+
asumida: "assumed",
|
|
8242
|
+
diferida: "deferred"
|
|
8243
|
+
};
|
|
8244
|
+
function parseResolution(line) {
|
|
8245
|
+
const m = line.match(/^\[([a-zA-Záéí]+)\]\s*(.*)$/);
|
|
8246
|
+
if (m) {
|
|
8247
|
+
const status = RESOLUTION_TOKENS[m[1].toLowerCase()];
|
|
8248
|
+
if (status) return { text: m[2].trim(), resolution_status: status };
|
|
8249
|
+
}
|
|
8250
|
+
return { text: line.trim(), resolution_status: "open" };
|
|
8251
|
+
}
|
|
8210
8252
|
var SOURCES = [
|
|
8211
8253
|
"knowledge/business/business.md",
|
|
8212
8254
|
"knowledge/product/product.md",
|
|
@@ -8298,10 +8340,17 @@ function extractFromMarkdown(md) {
|
|
|
8298
8340
|
for (let i = start + 1; i < lines.length; i++) {
|
|
8299
8341
|
const line = lines[i];
|
|
8300
8342
|
if (/^#{1,6}\s+/.test(line)) break;
|
|
8343
|
+
const meta = line.match(/^\s+(?:[-*])\s+(\w+)\s*:\s*(.+?)\s*$/);
|
|
8344
|
+
if (meta && out.length > 0) {
|
|
8345
|
+
if (meta[1].toLowerCase() === "note") out[out.length - 1].resolution_note = meta[2].trim();
|
|
8346
|
+
continue;
|
|
8347
|
+
}
|
|
8301
8348
|
const m = line.match(/^\s*(?:[-*]|\d+\.)\s+(.+?)\s*$/);
|
|
8302
8349
|
if (m) {
|
|
8303
|
-
const
|
|
8304
|
-
if (
|
|
8350
|
+
const raw = m[1].trim();
|
|
8351
|
+
if (!raw || /^_.*_$/.test(raw)) continue;
|
|
8352
|
+
const { text: text3, resolution_status } = parseResolution(raw);
|
|
8353
|
+
if (text3) out.push({ text: text3, resolution_status });
|
|
8305
8354
|
}
|
|
8306
8355
|
}
|
|
8307
8356
|
return out;
|
|
@@ -8335,29 +8384,37 @@ function buildOpenQuestionsReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
8335
8384
|
} catch {
|
|
8336
8385
|
continue;
|
|
8337
8386
|
}
|
|
8338
|
-
for (const
|
|
8387
|
+
for (const eq of extractFromMarkdown(md)) {
|
|
8339
8388
|
seq += 1;
|
|
8340
|
-
const { classification, reason } = classifyQuestion(
|
|
8389
|
+
const { classification, reason } = classifyQuestion(eq.text);
|
|
8341
8390
|
questions.push({
|
|
8342
8391
|
id: `OQ-${String(seq).padStart(3, "0")}`,
|
|
8343
8392
|
source,
|
|
8344
8393
|
section: "Open Questions",
|
|
8345
|
-
question:
|
|
8394
|
+
question: eq.text,
|
|
8346
8395
|
classification,
|
|
8396
|
+
resolution_status: eq.resolution_status,
|
|
8347
8397
|
reason,
|
|
8348
|
-
...
|
|
8398
|
+
...eq.resolution_note ? { resolution_note: eq.resolution_note } : {},
|
|
8399
|
+
...classification === "blocking" && eq.resolution_status === "open" ? { suggested_assumption: suggestedAssumption(eq.text) } : {}
|
|
8349
8400
|
});
|
|
8350
8401
|
}
|
|
8351
8402
|
}
|
|
8352
8403
|
const blocking = questions.filter((q) => q.classification === "blocking");
|
|
8353
8404
|
const important = questions.filter((q) => q.classification === "important");
|
|
8354
8405
|
const deferred = questions.filter((q) => q.classification === "deferred");
|
|
8406
|
+
const blockingOpen = blocking.filter((q) => q.resolution_status === "open");
|
|
8407
|
+
const importantOpen = important.filter((q) => q.resolution_status === "open");
|
|
8408
|
+
const byStatus = (s) => questions.filter((q) => q.resolution_status === s);
|
|
8409
|
+
const resolved = byStatus("resolved");
|
|
8410
|
+
const assumed = byStatus("assumed");
|
|
8411
|
+
const deferredStatus = byStatus("deferred");
|
|
8355
8412
|
let readiness;
|
|
8356
8413
|
if (questions.length === 0) readiness = "unknown";
|
|
8357
|
-
else if (
|
|
8414
|
+
else if (blockingOpen.length > 0) readiness = "needs_decisions";
|
|
8358
8415
|
else readiness = "ready";
|
|
8359
|
-
const suggested_assumptions =
|
|
8360
|
-
const recommended_next_step = readiness === "needs_decisions" ? "Confirm suggested assumptions with the user (resolve, assume or defer blocking questions) before generating the roadmap." : readiness === "ready" ? "No blocking questions \u2014 you can proceed to the roadmap
|
|
8416
|
+
const suggested_assumptions = blockingOpen.map((q) => q.suggested_assumption).filter(Boolean);
|
|
8417
|
+
const recommended_next_step = readiness === "needs_decisions" ? "Confirm suggested assumptions with the user (resolve, assume or defer blocking open questions) before generating the roadmap." : readiness === "ready" ? "No blocking open questions \u2014 you can proceed to the roadmap. Surfaced assumptions/deferred items are informational." : "No open questions found. Run `kaddo bootstrap` to capture early questions, or proceed.";
|
|
8361
8418
|
return {
|
|
8362
8419
|
generated_at: now.toISOString(),
|
|
8363
8420
|
project,
|
|
@@ -8366,12 +8423,23 @@ function buildOpenQuestionsReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
8366
8423
|
roadmap_readiness: readiness,
|
|
8367
8424
|
blocking: blocking.length,
|
|
8368
8425
|
important: important.length,
|
|
8369
|
-
deferred: deferred.length
|
|
8426
|
+
deferred: deferred.length,
|
|
8427
|
+
blocking_open: blockingOpen.length,
|
|
8428
|
+
important_open: importantOpen.length,
|
|
8429
|
+
resolution: {
|
|
8430
|
+
open: byStatus("open").length,
|
|
8431
|
+
resolved: resolved.length,
|
|
8432
|
+
assumed: assumed.length,
|
|
8433
|
+
deferred: deferredStatus.length
|
|
8434
|
+
}
|
|
8370
8435
|
},
|
|
8371
8436
|
questions,
|
|
8372
8437
|
blocking_questions: blocking,
|
|
8373
8438
|
important_questions: important,
|
|
8374
8439
|
deferred_questions: deferred,
|
|
8440
|
+
resolved_questions: resolved,
|
|
8441
|
+
assumed_questions: assumed,
|
|
8442
|
+
deferred_status_questions: deferredStatus,
|
|
8375
8443
|
suggested_assumptions,
|
|
8376
8444
|
recommended_next_step
|
|
8377
8445
|
};
|
|
@@ -8385,18 +8453,18 @@ function renderOpenQuestionsMarkdown(r) {
|
|
|
8385
8453
|
L.push("## Summary", "");
|
|
8386
8454
|
L.push(`- Open questions found: ${r.summary.open_questions}`);
|
|
8387
8455
|
L.push(`- Roadmap readiness: ${r.summary.roadmap_readiness}`);
|
|
8388
|
-
L.push(`-
|
|
8389
|
-
L.push(`-
|
|
8390
|
-
L.push(`- Deferred: ${r.summary.deferred}`);
|
|
8456
|
+
L.push(`- By resolution \u2014 open: ${r.summary.resolution.open}, resolved: ${r.summary.resolution.resolved}, assumed: ${r.summary.resolution.assumed}, deferred: ${r.summary.resolution.deferred}`);
|
|
8457
|
+
L.push(`- Blocking open: ${r.summary.blocking_open}, important open: ${r.summary.important_open}`);
|
|
8391
8458
|
L.push("");
|
|
8392
8459
|
if (r.summary.open_questions === 0) {
|
|
8393
8460
|
L.push("No `## Open Questions` sections found in business / product / codebase / roadmap.", "");
|
|
8394
8461
|
L.push("## Recommended Next Step", "", r.recommended_next_step, "");
|
|
8395
8462
|
return L.join("\n");
|
|
8396
8463
|
}
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8464
|
+
const blockingOpen = r.blocking_questions.filter((q) => q.resolution_status === "open");
|
|
8465
|
+
L.push("## Blocking Open Questions", "");
|
|
8466
|
+
if (blockingOpen.length === 0) L.push("_None \u2014 nothing blocks readiness._", "");
|
|
8467
|
+
for (const q of blockingOpen) {
|
|
8400
8468
|
L.push(`### ${q.id}`, "");
|
|
8401
8469
|
L.push(`- Source: \`${q.source}\``);
|
|
8402
8470
|
L.push(`- Question: ${q.question}`);
|
|
@@ -8404,13 +8472,18 @@ function renderOpenQuestionsMarkdown(r) {
|
|
|
8404
8472
|
if (q.suggested_assumption) L.push(`- Suggested assumption: ${q.suggested_assumption}`);
|
|
8405
8473
|
L.push("");
|
|
8406
8474
|
}
|
|
8475
|
+
const renderStatusList = (title, items) => {
|
|
8476
|
+
L.push(`## ${title}`, "");
|
|
8477
|
+
if (items.length === 0) L.push("_None._");
|
|
8478
|
+
for (const q of items) L.push(`- ${q.question}${q.resolution_note ? ` \u2014 _${q.resolution_note}_` : ""}`);
|
|
8479
|
+
L.push("");
|
|
8480
|
+
};
|
|
8481
|
+
renderStatusList("Resolved", r.resolved_questions);
|
|
8482
|
+
renderStatusList("Assumed (decisions to revisit)", r.assumed_questions);
|
|
8483
|
+
renderStatusList("Deferred (out of current scope)", r.deferred_status_questions);
|
|
8407
8484
|
L.push("## Important Questions", "");
|
|
8408
8485
|
if (r.important_questions.length === 0) L.push("_None._");
|
|
8409
|
-
for (const q of r.important_questions) L.push(`- ${q.question}`);
|
|
8410
|
-
L.push("");
|
|
8411
|
-
L.push("## Deferred Questions", "");
|
|
8412
|
-
if (r.deferred_questions.length === 0) L.push("_None._");
|
|
8413
|
-
for (const q of r.deferred_questions) L.push(`- ${q.question}`);
|
|
8486
|
+
for (const q of r.important_questions) L.push(`- [${q.resolution_status}] ${q.question}`);
|
|
8414
8487
|
L.push("");
|
|
8415
8488
|
if (r.suggested_assumptions.length > 0) {
|
|
8416
8489
|
L.push("## Suggested Assumptions", "");
|
|
@@ -8497,9 +8570,9 @@ function runUnderstand() {
|
|
|
8497
8570
|
console.log(" Suggested agent: graph-agent (see .kaddo/graph-hints.md)");
|
|
8498
8571
|
}
|
|
8499
8572
|
const oq = buildOpenQuestionsReport(dir);
|
|
8500
|
-
if (oq.summary.
|
|
8573
|
+
if (oq.summary.blocking_open > 0) {
|
|
8501
8574
|
console.log("");
|
|
8502
|
-
console.log(`Open questions: ${oq.summary.
|
|
8575
|
+
console.log(`Open questions: ${oq.summary.blocking_open} blocking open decision(s) before the roadmap (roadmap readiness: ${oq.summary.roadmap_readiness}).`);
|
|
8503
8576
|
console.log(" \u2192 Run `kaddo questions` to review them, then resolve, assume or defer before `kaddo create --from roadmap`.");
|
|
8504
8577
|
}
|
|
8505
8578
|
const active = activeWorkItems(dir);
|
|
@@ -12280,19 +12353,22 @@ function runQuestions(opts = {}) {
|
|
|
12280
12353
|
console.log(serializeOpenQuestionsJson(report));
|
|
12281
12354
|
return;
|
|
12282
12355
|
}
|
|
12356
|
+
const s = report.summary;
|
|
12283
12357
|
console.log("");
|
|
12284
|
-
console.log(`Open questions detected: ${
|
|
12285
|
-
console.log(`
|
|
12358
|
+
console.log(`Open questions detected: ${s.open_questions}`);
|
|
12359
|
+
console.log(`By resolution \u2014 open: ${s.resolution.open}, resolved: ${s.resolution.resolved}, assumed: ${s.resolution.assumed}, deferred: ${s.resolution.deferred}`);
|
|
12360
|
+
console.log(`Roadmap readiness: ${s.roadmap_readiness === "needs_decisions" ? "needs decisions" : s.roadmap_readiness} (blocking open: ${s.blocking_open}, important open: ${s.important_open})`);
|
|
12286
12361
|
const list2 = (title, qs) => {
|
|
12287
12362
|
if (qs.length === 0) return;
|
|
12288
12363
|
console.log("");
|
|
12289
12364
|
console.log(`${title}:`);
|
|
12290
|
-
for (const q of qs.slice(0, 8)) console.log(`- ${q.question}`);
|
|
12365
|
+
for (const q of qs.slice(0, 8)) console.log(`- ${q.question}${q.resolution_note ? ` \u2014 ${q.resolution_note}` : ""}`);
|
|
12291
12366
|
if (qs.length > 8) console.log(` \u2026and ${qs.length - 8} more`);
|
|
12292
12367
|
};
|
|
12293
|
-
list2("Blocking", report.blocking_questions);
|
|
12294
|
-
list2("
|
|
12295
|
-
list2("
|
|
12368
|
+
list2("Blocking (open)", report.blocking_questions.filter((q) => q.resolution_status === "open"));
|
|
12369
|
+
list2("Resolved", report.resolved_questions);
|
|
12370
|
+
list2("Assumed", report.assumed_questions);
|
|
12371
|
+
list2("Deferred", report.deferred_status_questions);
|
|
12296
12372
|
console.log("");
|
|
12297
12373
|
console.log("Suggested next:");
|
|
12298
12374
|
console.log(report.recommended_next_step);
|
|
@@ -12444,9 +12520,9 @@ function renderAdapterMarkdown(ctx, target) {
|
|
|
12444
12520
|
L.push("Read `knowledge/business/business.md`, `knowledge/product/product.md`,");
|
|
12445
12521
|
L.push("`knowledge/tech/codebase.md`, and `.kaddo/reports/questions-report.md` if available.");
|
|
12446
12522
|
L.push("");
|
|
12447
|
-
L.push("Check open-questions readiness first. **If blocking
|
|
12448
|
-
L.push("resolve, assume or defer them before generating the roadmap
|
|
12449
|
-
L.push("
|
|
12523
|
+
L.push("Check open-questions readiness first. **If blocking questions with resolution_status = open");
|
|
12524
|
+
L.push("exist, ask the user to resolve, assume or defer them before generating the roadmap.** Assumed,");
|
|
12525
|
+
L.push("resolved or deferred questions should be surfaced for context, but should not block execution.");
|
|
12450
12526
|
L.push("");
|
|
12451
12527
|
L.push("## Before implementation", "");
|
|
12452
12528
|
L.push("Read the target Work Item in `knowledge/delivery/work-items/`, `.kaddo/context-pack.md`,");
|
|
@@ -12568,8 +12644,9 @@ function renderKaddoBlock(ctx) {
|
|
|
12568
12644
|
L.push("");
|
|
12569
12645
|
for (const line of commandFallbackSection(ctx.packageManager, "### ")) L.push(line);
|
|
12570
12646
|
L.push("### Before roadmap work", "");
|
|
12571
|
-
L.push("Check open-questions readiness first. If blocking
|
|
12572
|
-
L.push("resolve, assume or defer them
|
|
12647
|
+
L.push("Check open-questions readiness first. If blocking questions with resolution_status = open");
|
|
12648
|
+
L.push("exist, ask the user to resolve, assume or defer them. Assumed/resolved/deferred questions");
|
|
12649
|
+
L.push("should be surfaced, but should not block execution.");
|
|
12573
12650
|
L.push("");
|
|
12574
12651
|
L.push("### Before implementation", "");
|
|
12575
12652
|
L.push("Read the target Work Item in `knowledge/delivery/work-items/`, `.kaddo/context-pack.md`,");
|
|
@@ -12828,6 +12905,229 @@ function runAdaptersStatus(opts = {}) {
|
|
|
12828
12905
|
}
|
|
12829
12906
|
}
|
|
12830
12907
|
|
|
12908
|
+
// src/core/onboarding.ts
|
|
12909
|
+
var KNOWLEDGE_FILES = [
|
|
12910
|
+
{ key: "current_state", path: "knowledge/tech/current-state.md", label: "knowledge/tech/current-state.md" },
|
|
12911
|
+
{ key: "codebase", path: "knowledge/tech/codebase.md", label: "knowledge/tech/codebase.md" },
|
|
12912
|
+
{ key: "capabilities", path: "knowledge/product/capabilities.md", label: "knowledge/product/capabilities.md" },
|
|
12913
|
+
{ key: "product", path: "knowledge/product/product.md", label: "knowledge/product/product.md" },
|
|
12914
|
+
{ key: "business", path: "knowledge/business/business.md", label: "knowledge/business/business.md" }
|
|
12915
|
+
];
|
|
12916
|
+
function knowledgePresence(dir, rel) {
|
|
12917
|
+
const p2 = join(dir, rel);
|
|
12918
|
+
if (!exists(p2)) return "missing";
|
|
12919
|
+
let md;
|
|
12920
|
+
try {
|
|
12921
|
+
md = readFile(p2);
|
|
12922
|
+
} catch {
|
|
12923
|
+
return "missing";
|
|
12924
|
+
}
|
|
12925
|
+
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();
|
|
12926
|
+
return body.length >= 40 ? "present" : "weak";
|
|
12927
|
+
}
|
|
12928
|
+
function roadmapSignal(dir) {
|
|
12929
|
+
const p2 = join(dir, "knowledge/delivery/roadmap.md");
|
|
12930
|
+
if (!exists(p2)) return "missing";
|
|
12931
|
+
let md;
|
|
12932
|
+
try {
|
|
12933
|
+
md = readFile(p2);
|
|
12934
|
+
} catch {
|
|
12935
|
+
return "missing";
|
|
12936
|
+
}
|
|
12937
|
+
const stripped = md.replace(/^---[\s\S]*?---/m, "");
|
|
12938
|
+
const lines = stripped.split(/\r?\n/);
|
|
12939
|
+
let inOpenQuestions = false;
|
|
12940
|
+
let candidates = 0;
|
|
12941
|
+
for (const l of lines) {
|
|
12942
|
+
if (/^#{1,6}\s/.test(l)) inOpenQuestions = /open questions|preguntas abiertas/i.test(l);
|
|
12943
|
+
else if (!inOpenQuestions && /^\s*(?:[-*]\s+|\|)/.test(l)) candidates += 1;
|
|
12944
|
+
}
|
|
12945
|
+
return candidates > 0 ? "has-candidates" : "empty";
|
|
12946
|
+
}
|
|
12947
|
+
function workItemsSignal(dir) {
|
|
12948
|
+
const wis = discoverWorkItems(dir);
|
|
12949
|
+
if (wis.length === 0) return "none";
|
|
12950
|
+
if (wis.some((w) => w.lifecycle === "in-progress")) return "in-progress";
|
|
12951
|
+
if (wis.some((w) => w.lifecycle === "ready")) return "ready";
|
|
12952
|
+
return "none-ready";
|
|
12953
|
+
}
|
|
12954
|
+
function installedAdapters(dir) {
|
|
12955
|
+
return buildAdapterStatuses(dir).filter(
|
|
12956
|
+
(s) => s.state === "injected" || s.state === "legacy-injected" || s.state === "full-generated" && (s.originAdapter === s.id || s.originAdapter === null)
|
|
12957
|
+
).map((s) => s.id);
|
|
12958
|
+
}
|
|
12959
|
+
function buildOnboardingReport(dir, now = /* @__PURE__ */ new Date()) {
|
|
12960
|
+
const config = loadConfig(dir);
|
|
12961
|
+
const ts = now.toISOString();
|
|
12962
|
+
const notReady = (status2, label, command) => ({
|
|
12963
|
+
generated_at: ts,
|
|
12964
|
+
project_name: config?.project.name ?? "unknown",
|
|
12965
|
+
project_type: config?.project.state ?? "unknown",
|
|
12966
|
+
status: status2,
|
|
12967
|
+
signals: {
|
|
12968
|
+
scan: "missing",
|
|
12969
|
+
understand: "missing",
|
|
12970
|
+
current_state: "missing",
|
|
12971
|
+
codebase: "missing",
|
|
12972
|
+
capabilities: "missing",
|
|
12973
|
+
product: "missing",
|
|
12974
|
+
business: "missing",
|
|
12975
|
+
roadmap: "missing",
|
|
12976
|
+
work_items: "none",
|
|
12977
|
+
adapters: [],
|
|
12978
|
+
blocking_open_questions: 0,
|
|
12979
|
+
assumed_questions: 0,
|
|
12980
|
+
resolved_questions: 0,
|
|
12981
|
+
deferred_questions: 0
|
|
12982
|
+
},
|
|
12983
|
+
recommended_next_step: { label, ...command ? { command } : {} }
|
|
12984
|
+
});
|
|
12985
|
+
if (!config) return notReady("not-initialized", "Run `kaddo init` to initialize Kaddo.", "kaddo init");
|
|
12986
|
+
if (config.project.state === "new") return notReady("not-applicable", "This guide is optimized for pre-AI projects. Use the standard new-project Kaddo flow.");
|
|
12987
|
+
if (config.project.state === "legacy") return notReady("legacy-project", "Use the legacy project flow. The onboarding guide targets pre-AI projects.");
|
|
12988
|
+
const scan2 = exists(join(dir, ".kaddo", "scan.json")) ? "available" : "missing";
|
|
12989
|
+
const understand = exists(join(dir, ".kaddo", "understand.md")) ? "available" : "missing";
|
|
12990
|
+
const presence = Object.fromEntries(KNOWLEDGE_FILES.map((f) => [f.key, knowledgePresence(dir, f.path)]));
|
|
12991
|
+
const roadmap = roadmapSignal(dir);
|
|
12992
|
+
const work_items = workItemsSignal(dir);
|
|
12993
|
+
const adapters = installedAdapters(dir);
|
|
12994
|
+
const oq = buildOpenQuestionsReport(dir, now);
|
|
12995
|
+
const signals = {
|
|
12996
|
+
scan: scan2,
|
|
12997
|
+
understand,
|
|
12998
|
+
current_state: presence.current_state,
|
|
12999
|
+
codebase: presence.codebase,
|
|
13000
|
+
capabilities: presence.capabilities,
|
|
13001
|
+
product: presence.product,
|
|
13002
|
+
business: presence.business,
|
|
13003
|
+
roadmap,
|
|
13004
|
+
work_items,
|
|
13005
|
+
adapters,
|
|
13006
|
+
blocking_open_questions: oq.summary.blocking_open,
|
|
13007
|
+
assumed_questions: oq.summary.resolution.assumed,
|
|
13008
|
+
resolved_questions: oq.summary.resolution.resolved,
|
|
13009
|
+
deferred_questions: oq.summary.resolution.deferred
|
|
13010
|
+
};
|
|
13011
|
+
let status;
|
|
13012
|
+
let next;
|
|
13013
|
+
const firstWeak = KNOWLEDGE_FILES.find((f) => presence[f.key] !== "present");
|
|
13014
|
+
if (scan2 === "missing") {
|
|
13015
|
+
status = "initialized";
|
|
13016
|
+
next = { label: "Run `kaddo scan` to capture deterministic signals from the existing code.", command: "kaddo scan" };
|
|
13017
|
+
} else if (understand === "missing") {
|
|
13018
|
+
status = "scanned";
|
|
13019
|
+
next = { label: "Run `kaddo understand` to summarize the project context.", command: "kaddo understand" };
|
|
13020
|
+
} else if (firstWeak) {
|
|
13021
|
+
status = "knowledge-incomplete";
|
|
13022
|
+
next = { label: `Complete \`${firstWeak.label}\` (it is ${presence[firstWeak.key]}).` };
|
|
13023
|
+
} else if (oq.summary.blocking_open > 0) {
|
|
13024
|
+
status = "needs-decisions";
|
|
13025
|
+
next = { label: "Resolve, assume or defer the blocking open questions (`kaddo questions`).", command: "kaddo questions" };
|
|
13026
|
+
} else if (roadmap !== "has-candidates") {
|
|
13027
|
+
status = "ready-for-roadmap";
|
|
13028
|
+
next = { label: "Run `kaddo roadmap` to draft the roadmap from the current knowledge.", command: "kaddo roadmap" };
|
|
13029
|
+
} else if (work_items === "none" || work_items === "none-ready") {
|
|
13030
|
+
status = "ready-for-work-item";
|
|
13031
|
+
next = { label: "Run `kaddo create --from roadmap` to materialize the first Work Item.", command: "kaddo create --from roadmap" };
|
|
13032
|
+
} else {
|
|
13033
|
+
status = "ready-for-implementation";
|
|
13034
|
+
next = adapters.length > 0 ? { label: "Implement the ready Work Item with an installed adapter, then run `kaddo guard`." } : { label: "Install an adapter, then implement the ready Work Item.", command: "kaddo adapters list" };
|
|
13035
|
+
}
|
|
13036
|
+
return { generated_at: ts, project_name: config.project.name, project_type: config.project.state, status, signals, recommended_next_step: next };
|
|
13037
|
+
}
|
|
13038
|
+
function serializeOnboardingJson(r) {
|
|
13039
|
+
return JSON.stringify(r, null, 2) + "\n";
|
|
13040
|
+
}
|
|
13041
|
+
function renderOnboardingMarkdown(r) {
|
|
13042
|
+
const s = r.signals;
|
|
13043
|
+
const L = [];
|
|
13044
|
+
L.push("# Pre-AI Onboarding Report", "");
|
|
13045
|
+
L.push("## Summary", "");
|
|
13046
|
+
L.push(`- Project: ${r.project_name}`);
|
|
13047
|
+
L.push(`- Project type: ${r.project_type}`);
|
|
13048
|
+
L.push(`- Status: ${r.status}`);
|
|
13049
|
+
L.push("");
|
|
13050
|
+
if (r.status === "not-initialized" || r.status === "not-applicable" || r.status === "legacy-project") {
|
|
13051
|
+
L.push("## Recommended Next Step", "", r.recommended_next_step.label, "");
|
|
13052
|
+
return L.join("\n");
|
|
13053
|
+
}
|
|
13054
|
+
L.push("## Signals", "");
|
|
13055
|
+
L.push(`- Scan: ${s.scan}`);
|
|
13056
|
+
L.push(`- Understand: ${s.understand}`);
|
|
13057
|
+
L.push(`- Current state: ${s.current_state}`);
|
|
13058
|
+
L.push(`- Codebase: ${s.codebase}`);
|
|
13059
|
+
L.push(`- Capabilities: ${s.capabilities}`);
|
|
13060
|
+
L.push(`- Product: ${s.product}`);
|
|
13061
|
+
L.push(`- Business: ${s.business}`);
|
|
13062
|
+
L.push(`- Roadmap: ${s.roadmap}`);
|
|
13063
|
+
L.push(`- Work Items: ${s.work_items}`);
|
|
13064
|
+
L.push(`- Adapters: ${s.adapters.length > 0 ? s.adapters.join(", ") + " installed" : "none installed"}`);
|
|
13065
|
+
L.push("");
|
|
13066
|
+
L.push("## Questions", "");
|
|
13067
|
+
L.push(`- Blocking open: ${s.blocking_open_questions}`);
|
|
13068
|
+
L.push(`- Assumed: ${s.assumed_questions}`);
|
|
13069
|
+
L.push(`- Resolved: ${s.resolved_questions}`);
|
|
13070
|
+
L.push(`- Deferred: ${s.deferred_questions}`);
|
|
13071
|
+
L.push("");
|
|
13072
|
+
L.push("## Recommended Next Step", "", r.recommended_next_step.label, "");
|
|
13073
|
+
return L.join("\n");
|
|
13074
|
+
}
|
|
13075
|
+
|
|
13076
|
+
// src/commands/onboarding.ts
|
|
13077
|
+
function printConsole(r) {
|
|
13078
|
+
const s = r.signals;
|
|
13079
|
+
console.log("");
|
|
13080
|
+
console.log("Pre-AI Onboarding");
|
|
13081
|
+
console.log("");
|
|
13082
|
+
console.log(`Project: ${r.project_name}`);
|
|
13083
|
+
console.log(`Project type: ${r.project_type}`);
|
|
13084
|
+
console.log("");
|
|
13085
|
+
console.log("Status:");
|
|
13086
|
+
console.log(` overall: ${r.status}`);
|
|
13087
|
+
if (r.status !== "not-initialized" && r.status !== "not-applicable" && r.status !== "legacy-project") {
|
|
13088
|
+
console.log(` scan: ${s.scan}`);
|
|
13089
|
+
console.log(` understand: ${s.understand}`);
|
|
13090
|
+
console.log(` current-state: ${s.current_state}`);
|
|
13091
|
+
console.log(` codebase: ${s.codebase}`);
|
|
13092
|
+
console.log(` capabilities: ${s.capabilities}`);
|
|
13093
|
+
console.log(` roadmap: ${s.roadmap}`);
|
|
13094
|
+
console.log(` work-items: ${s.work_items}`);
|
|
13095
|
+
console.log(` adapters: ${s.adapters.length > 0 ? s.adapters.join(", ") + " installed" : "none installed"}`);
|
|
13096
|
+
console.log(` blocking open questions: ${s.blocking_open_questions}`);
|
|
13097
|
+
console.log(` assumptions: ${s.assumed_questions}`);
|
|
13098
|
+
console.log(` deferred: ${s.deferred_questions}`);
|
|
13099
|
+
}
|
|
13100
|
+
console.log("");
|
|
13101
|
+
console.log("Recommended next step:");
|
|
13102
|
+
console.log(` ${r.recommended_next_step.label}`);
|
|
13103
|
+
}
|
|
13104
|
+
function runOnboarding(opts = {}) {
|
|
13105
|
+
const dir = cwd();
|
|
13106
|
+
const report = buildOnboardingReport(dir);
|
|
13107
|
+
if (opts.json) {
|
|
13108
|
+
console.log(serializeOnboardingJson(report));
|
|
13109
|
+
return;
|
|
13110
|
+
}
|
|
13111
|
+
printConsole(report);
|
|
13112
|
+
printCommandFooter("onboarding");
|
|
13113
|
+
}
|
|
13114
|
+
function runOnboardingReport(opts = {}) {
|
|
13115
|
+
const dir = cwd();
|
|
13116
|
+
const report = buildOnboardingReport(dir);
|
|
13117
|
+
if (report.status === "not-initialized") {
|
|
13118
|
+
console.error("Kaddo is not initialized here. Run `kaddo init` first.");
|
|
13119
|
+
return;
|
|
13120
|
+
}
|
|
13121
|
+
intro2("kaddo report onboarding");
|
|
13122
|
+
const base = ".kaddo/reports";
|
|
13123
|
+
writeFile(join(dir, base, "onboarding-report.md"), renderOnboardingMarkdown(report));
|
|
13124
|
+
writeFile(join(dir, base, "onboarding-report.json"), serializeOnboardingJson(report));
|
|
13125
|
+
log2.success(`Wrote ${base}/onboarding-report.md and ${base}/onboarding-report.json`);
|
|
13126
|
+
log2.info(`Status: ${report.status} \u2014 ${report.recommended_next_step.label}`);
|
|
13127
|
+
printCommandFooter("onboarding");
|
|
13128
|
+
outro2("Onboarding report written.");
|
|
13129
|
+
}
|
|
13130
|
+
|
|
12831
13131
|
// src/index.ts
|
|
12832
13132
|
var require2 = createRequire(import.meta.url);
|
|
12833
13133
|
var { version } = require2("../package.json");
|
|
@@ -12875,12 +13175,18 @@ savingsCmd.command("init").description("Create an editable `.kaddo/savings.yml`
|
|
|
12875
13175
|
reportCmd.command("drift").description("Drift Trend Report from recorded guard history (deterministic, no LLM)").option("--json", "Output JSON instead of Markdown").option("--output <path>", "Write the report to a file (e.g. .kaddo/reports/drift-report.md)").action((opts) => {
|
|
12876
13176
|
runDrift(opts);
|
|
12877
13177
|
});
|
|
13178
|
+
reportCmd.command("onboarding").description("Write the pre-AI onboarding report to .kaddo/reports/ (deterministic, no LLM)").action(() => {
|
|
13179
|
+
runOnboardingReport();
|
|
13180
|
+
});
|
|
12878
13181
|
program.command("drift").description("Drift Trend Report from recorded `kaddo guard --record` history").option("--json", "Output JSON instead of Markdown").option("--output <path>", "Write the report to a file").action((opts) => {
|
|
12879
13182
|
runDrift(opts);
|
|
12880
13183
|
});
|
|
12881
13184
|
var questionsAction = (opts) => runQuestions(opts);
|
|
12882
13185
|
program.command("questions").description("Open-questions readiness gate: blocking/important/deferred decisions before the roadmap").option("--json", "Output JSON instead of a summary").option("--output <path>", "Write the report to a file (e.g. .kaddo/reports/questions-report.md)").action(questionsAction);
|
|
12883
13186
|
program.command("readiness").description("Alias for `kaddo questions`").option("--json", "Output JSON instead of a summary").option("--output <path>", "Write the report to a file").action(questionsAction);
|
|
13187
|
+
program.command("onboarding").alias("onboard").description("Diagnose a pre-AI project and recommend the next step (read-only)").option("--json", "Output JSON").action((opts) => {
|
|
13188
|
+
runOnboarding(opts);
|
|
13189
|
+
});
|
|
12884
13190
|
var adaptersCmd = program.command("adapters").description("Generate adapters that project Kaddo knowledge for external coding agents");
|
|
12885
13191
|
adaptersCmd.command("install <adapter>").description("Generate an adapter file (codex/opencode/antigravity/kiro \u2192 AGENTS.md, claude \u2192 CLAUDE.md) from Kaddo knowledge").option("--force", "Overwrite an existing output file").option("--inject", "Add or update only the Kaddo block in an existing file, preserving the rest").option("--dry-run", "Print the content without writing files").action((adapter, opts) => {
|
|
12886
13192
|
runAdaptersInstall(adapter, opts);
|