@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.
Files changed (3) hide show
  1. package/README.md +2 -0
  2. package/dist/index.js +351 -45
  3. 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
- If readiness is \`needs_decisions\` (there are **blocking** open questions), do **not** generate the
2418
- roadmap yet. Instead, list the blocking questions, propose reasonable assumptions for each, and ask
2419
- the user to confirm, e.g.:
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** open questions related to this Work Item's scope. If any are unresolved, surface them
2739
- and propose assumptions for the user to confirm before refining \u2014 don't bake in invisible
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 on
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** open questions about stack, architecture, persistence,
3365
- authentication or the Work Item's scope. If any are unresolved, pause and ask the user to confirm
3366
- assumptions or resolve them before writing code.
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 q = m[1].trim();
8304
- if (q && !/^_.*_$/.test(q)) out.push(q);
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 q of extractFromMarkdown(md)) {
8387
+ for (const eq of extractFromMarkdown(md)) {
8339
8388
  seq += 1;
8340
- const { classification, reason } = classifyQuestion(q);
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: q,
8394
+ question: eq.text,
8346
8395
  classification,
8396
+ resolution_status: eq.resolution_status,
8347
8397
  reason,
8348
- ...classification === "blocking" ? { suggested_assumption: suggestedAssumption(q) } : {}
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 (blocking.length > 0) readiness = "needs_decisions";
8414
+ else if (blockingOpen.length > 0) readiness = "needs_decisions";
8358
8415
  else readiness = "ready";
8359
- const suggested_assumptions = blocking.map((q) => q.suggested_assumption).filter(Boolean);
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 (review important questions when convenient)." : "No open questions found. Run `kaddo bootstrap` to capture early questions, or proceed.";
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(`- Blocking: ${r.summary.blocking}`);
8389
- L.push(`- Important: ${r.summary.important}`);
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
- L.push("## Blocking Questions", "");
8398
- if (r.blocking_questions.length === 0) L.push("_None._", "");
8399
- for (const q of r.blocking_questions) {
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.blocking > 0) {
8573
+ if (oq.summary.blocking_open > 0) {
8501
8574
  console.log("");
8502
- console.log(`Open questions: ${oq.summary.blocking} blocking decision(s) before the roadmap (roadmap readiness: ${oq.summary.roadmap_readiness}).`);
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: ${report.summary.open_questions}`);
12285
- console.log(`Roadmap readiness: ${report.summary.roadmap_readiness === "needs_decisions" ? "needs decisions" : report.summary.roadmap_readiness}`);
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("Important", report.important_questions);
12295
- list2("Deferred", report.deferred_questions);
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 open questions exist, ask the user to");
12448
- L.push("resolve, assume or defer them before generating the roadmap** \u2014 never build the roadmap on");
12449
- L.push("invisible assumptions.");
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 open questions exist, ask the user to");
12572
- L.push("resolve, assume or defer them before generating the roadmap.");
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaddo/cli",
3
- "version": "3.32.0",
3
+ "version": "3.34.0",
4
4
  "description": "Knowledge Driven Development toolkit",
5
5
  "license": "MIT",
6
6
  "repository": {