kairn-cli 1.10.1 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -256,6 +256,113 @@ var ui = {
256
256
  `);
257
257
  }
258
258
  };
259
+ function formatTime(seconds) {
260
+ if (seconds < 60) return `${seconds}s`;
261
+ const min = Math.floor(seconds / 60);
262
+ const sec = seconds % 60;
263
+ return sec > 0 ? `${min}m ${sec}s` : `${min} min`;
264
+ }
265
+ function estimateTime(model, intent) {
266
+ const wordCount = intent.split(/\s+/).length;
267
+ const isComplex = wordCount > 40;
268
+ const perPass = {
269
+ "haiku": 5,
270
+ "sonnet": 20,
271
+ "opus": 60,
272
+ "gpt-4.1-mini": 10,
273
+ "gpt-4.1": 25,
274
+ "gpt-5": 15,
275
+ "o4-mini": 12,
276
+ "gemini-2.5-flash": 8,
277
+ "gemini-3-flash": 8,
278
+ "gemini-2.5-pro": 30,
279
+ "gemini-3.1-pro": 30,
280
+ "grok-4.1-fast": 10,
281
+ "grok-4.20": 25,
282
+ "deepseek": 15,
283
+ "mistral-large": 20,
284
+ "codestral": 15,
285
+ "mistral-small": 10,
286
+ "llama": 10,
287
+ "qwen": 10
288
+ };
289
+ const basePerPass = Object.entries(perPass).find(([k]) => model.toLowerCase().includes(k))?.[1] ?? 20;
290
+ const totalBase = basePerPass * 2;
291
+ if (isComplex) {
292
+ const low = Math.floor(totalBase * 1.5);
293
+ const high = Math.floor(totalBase * 4);
294
+ return `~${formatTime(low)}-${formatTime(high)} (complex workflow)`;
295
+ }
296
+ return `~${formatTime(totalBase)}`;
297
+ }
298
+ function createProgressRenderer() {
299
+ const lines = [];
300
+ let intervalId = null;
301
+ let currentPhase = "";
302
+ let phaseStart = Date.now();
303
+ let lineCount = 0;
304
+ function render() {
305
+ if (lineCount > 0) {
306
+ process.stdout.write(`\x1B[${lineCount}A`);
307
+ }
308
+ for (const line of lines) {
309
+ process.stdout.write("\x1B[2K" + line + "\n");
310
+ }
311
+ lineCount = lines.length;
312
+ }
313
+ function updateElapsed() {
314
+ if (!currentPhase) return;
315
+ const elapsed = Math.floor((Date.now() - phaseStart) / 1e3);
316
+ const lastIdx = lines.length - 1;
317
+ if (lastIdx >= 0) {
318
+ lines[lastIdx] = lines[lastIdx].replace(/\[\d+s\]/, `[${elapsed}s]`);
319
+ render();
320
+ }
321
+ }
322
+ return {
323
+ update(progress) {
324
+ if (progress.status === "running") {
325
+ currentPhase = progress.phase;
326
+ phaseStart = Date.now();
327
+ lines.push(` ${warmStone("\u25D0")} ${progress.message} ${chalk.dim("[0s]")}`);
328
+ if (!intervalId) {
329
+ intervalId = setInterval(updateElapsed, 1e3);
330
+ }
331
+ } else if (progress.status === "success") {
332
+ const lastIdx = lines.length - 1;
333
+ const elapsed = progress.elapsed != null ? ` ${chalk.dim("\u2014")} ${chalk.dim(Math.floor(progress.elapsed) + "s")}` : "";
334
+ const detail = progress.detail ? ` ${chalk.dim("(" + progress.detail + ")")}` : "";
335
+ if (lastIdx >= 0) {
336
+ lines[lastIdx] = ` ${chalk.green("\u2714")} ${progress.message}${detail}${elapsed}`;
337
+ }
338
+ currentPhase = "";
339
+ } else if (progress.status === "warning") {
340
+ const lastIdx = lines.length - 1;
341
+ if (lastIdx >= 0) {
342
+ lines[lastIdx] = ` ${chalk.yellow("\u26A0")} ${progress.message}`;
343
+ }
344
+ currentPhase = progress.phase;
345
+ phaseStart = Date.now();
346
+ lines.push(` ${warmStone("\u25D0")} Retrying in concise mode... ${chalk.dim("[0s]")}`);
347
+ }
348
+ render();
349
+ },
350
+ finish() {
351
+ if (intervalId) clearInterval(intervalId);
352
+ currentPhase = "";
353
+ render();
354
+ },
355
+ fail(err) {
356
+ if (intervalId) clearInterval(intervalId);
357
+ currentPhase = "";
358
+ const lastIdx = lines.length - 1;
359
+ if (lastIdx >= 0) {
360
+ lines[lastIdx] = ` ${chalk.red("\u2716")} Compilation failed`;
361
+ }
362
+ render();
363
+ }
364
+ };
365
+ }
259
366
 
260
367
  // src/logo.ts
261
368
  import chalk2 from "chalk";
@@ -450,7 +557,6 @@ var initCommand = new Command("init").description("Set up Kairn with your API ke
450
557
  import { Command as Command2 } from "commander";
451
558
  import { input as input2, confirm, select as select2 } from "@inquirer/prompts";
452
559
  import chalk5 from "chalk";
453
- import ora from "ora";
454
560
 
455
561
  // src/compiler/compile.ts
456
562
  import fs4 from "fs/promises";
@@ -487,7 +593,7 @@ You must output a JSON object matching the SkeletonSpec schema.
487
593
 
488
594
  - MCP servers: maximum 6. Prefer fewer.
489
595
  - Skills: maximum 3. Only include directly relevant ones.
490
- - Agents: maximum 3. QA pipeline + one specialist.
596
+ - Agents: maximum 5. Orchestration pipeline (/develop) agents.
491
597
  - Hooks: maximum 4 (auto-format, block-destructive, PostCompact, plus one contextual).
492
598
 
493
599
  If the workflow doesn't clearly need a tool, DO NOT include it.
@@ -527,12 +633,12 @@ You must generate all harness content: CLAUDE.md, commands, rules, agents, skill
527
633
  ## Core Principles
528
634
 
529
635
  - **Workflow-specific, not generic.** Every instruction, command, and rule must relate to the user's actual workflow.
530
- - **Concise CLAUDE.md.** Under 120 lines. No generic text like "be helpful." Include build/test commands, reference docs/ and skills/.
636
+ - **Concise CLAUDE.md.** Under 150 lines. No generic text like "be helpful." Include build/test commands, reference docs/ and skills/.
531
637
  - **Security by default.** Always include deny rules for destructive commands and secret file access.
532
638
 
533
639
  ## CLAUDE.md Template (mandatory structure)
534
640
 
535
- The \`claude_md\` field MUST follow this exact structure (max 120 lines):
641
+ The \`claude_md\` field MUST follow this exact structure (max 150 lines):
536
642
 
537
643
  \`\`\`
538
644
  # {Project Name}
@@ -581,6 +687,25 @@ Use subagents for deep investigation to keep main context clean.
581
687
  - Prefer small, focused commits (one feature or fix per commit)
582
688
  - Use conventional commits: feat:, fix:, docs:, refactor:, test:
583
689
  - Target < 200 lines per PR when possible
690
+
691
+ ## Engineering Standards
692
+ - Lead with answers over reasoning. Be concise.
693
+ - Use absolute file paths in all references.
694
+ - No filler, no inner monologue, no time estimates.
695
+ - Produce load-bearing code \u2014 every line of output should be actionable.
696
+
697
+ ## Tool Usage Policy
698
+ - Prefer Edit tool over sed/awk for file modifications
699
+ - Prefer Grep tool over rg for searching
700
+ - Prefer Read tool over cat for file reading
701
+ - Reserve Bash for: builds, installs, git, network, processes
702
+ - Read and understand existing code before modifying
703
+ - Delete unused code completely \u2014 no compatibility shims
704
+
705
+ ## Code Philosophy
706
+ - Do not create abstractions for one-time operations
707
+ - Complete the task fully \u2014 don't gold-plate, but don't leave it half-done
708
+ - Prefer editing existing files over creating new ones
584
709
  \`\`\`
585
710
 
586
711
  Do not add generic filler. Every line must be specific to the user's workflow.
@@ -589,20 +714,19 @@ Do not add generic filler. Every line must be specific to the user's workflow.
589
714
 
590
715
  1. A concise, workflow-specific \`claude_md\` (the CLAUDE.md content)
591
716
  2. A \`/project:help\` command that explains the environment
592
- 3. A \`/project:tasks\` command for task management via TODO.md
593
- 4. A \`docs/TODO.md\` file for continuity
594
- 5. A \`docs/DECISIONS.md\` file for architectural decisions
595
- 6. A \`docs/LEARNINGS.md\` file for non-obvious discoveries
596
- 7. A \`rules/continuity.md\` rule encouraging updates to DECISIONS.md and LEARNINGS.md
597
- 8. A \`rules/security.md\` rule with essential security instructions
598
- 9. settings.json with deny rules for \`rm -rf\`, \`curl|sh\`, reading \`.env\` and \`secrets/\`
599
- 10. A \`/project:status\` command for code projects (uses ! for live git/test output)
600
- 11. A \`/project:fix\` command for code projects (uses $ARGUMENTS for issue number)
601
- 12. A \`docs/SPRINT.md\` file for sprint contracts (acceptance criteria, verification steps)
602
- 13. A "Verification" section in CLAUDE.md with concrete verify commands for the project
603
- 14. A "Known Gotchas" section in CLAUDE.md (starts empty, grows with corrections)
604
- 15. A "Debugging" section in CLAUDE.md (2 lines: paste raw errors, use subagents)
605
- 16. A "Git Workflow" section in CLAUDE.md (3 rules: small commits, conventional format, <200 lines PR)
717
+ 3. A \`docs/DECISIONS.md\` file for architectural decisions
718
+ 4. A \`docs/LEARNINGS.md\` file for non-obvious discoveries
719
+ 5. A \`rules/continuity.md\` rule encouraging updates to DECISIONS.md and LEARNINGS.md
720
+ 6. A \`rules/security.md\` rule with essential security instructions
721
+ 7. settings.json with deny rules for \`rm -rf\`, \`curl|sh\`, reading \`.env\` and \`secrets/\`
722
+ 8. A \`/project:status\` command for code projects (uses ! for live git/SPRINT.md output)
723
+ 9. A \`/project:fix\` command for code projects (uses $ARGUMENTS for issue number)
724
+ 10. A \`docs/SPRINT.md\` file as the living spec/plan (replaces TODO.md \u2014 acceptance criteria, verification steps)
725
+ 11. A "Verification" section in CLAUDE.md with concrete verify commands for the project
726
+ 12. A "Known Gotchas" section in CLAUDE.md (starts empty, grows with corrections)
727
+ 13. A "Debugging" section in CLAUDE.md (2 lines: paste raw errors, use subagents)
728
+ 14. A "Git Workflow" section in CLAUDE.md (3 rules: small commits, conventional format, <200 lines PR)
729
+ 15. "Engineering Standards", "Tool Usage Policy", and "Code Philosophy" sections in CLAUDE.md
606
730
 
607
731
  ## Shell-Integrated Commands
608
732
 
@@ -717,9 +841,10 @@ Merge this into the settings hooks alongside the PreToolUse and PostToolUse hook
717
841
  - \`/project:review\` command (review changes)
718
842
  - \`/project:test\` command (run and fix tests)
719
843
  - \`/project:commit\` command (conventional commits)
720
- - \`/project:status\` command (live git status, recent commits, TODO overview using ! prefix)
844
+ - \`/project:status\` command (live git status, recent commits, SPRINT.md overview using ! prefix)
721
845
  - \`/project:fix\` command (takes $ARGUMENTS as issue number, plans fix, implements, tests, commits)
722
846
  - \`/project:sprint\` command (define acceptance criteria before coding, writes to docs/SPRINT.md)
847
+ - \`/project:develop\` command (full development pipeline \u2014 orchestrates @architect \u2192 @planner \u2192 @implementer \u2192 @verifier \u2192 @fixer \u2192 @grill \u2192 @doc-updater through spec, plan, TDD implement, review, and doc update phases)
723
848
  - A TDD skill using the 3-phase isolation pattern (RED \u2192 GREEN \u2192 REFACTOR):
724
849
  - RED: Write failing test only. Verify it FAILS.
725
850
  - GREEN: Write MINIMUM code to pass. Nothing extra.
@@ -729,6 +854,12 @@ Merge this into the settings hooks alongside the PreToolUse and PostToolUse hook
729
854
  - \`@qa-orchestrator\` (sonnet) \u2014 delegates to linter and e2e-tester, compiles QA report
730
855
  - \`@linter\` (haiku) \u2014 runs formatters, linters, security scanners
731
856
  - \`@e2e-tester\` (sonnet, only when Playwright is in tools) \u2014 browser-based QA via Playwright
857
+ - Development pipeline agents (used by /project:develop):
858
+ - \`@architect\` (opus) \u2014 conducts spec interview with user, writes confirmed spec to docs/SPRINT.md
859
+ - \`@planner\` (opus) \u2014 reads spec and codebase, creates step-by-step implementation plan in docs/PLAN.md
860
+ - \`@implementer\` (sonnet) \u2014 TDD-focused implementation, writes failing tests then minimum code to pass
861
+ - \`@fixer\` (sonnet) \u2014 targeted bug fixing from verifier/review feedback
862
+ - \`@doc-updater\` (haiku) \u2014 extracts decisions and learnings from completed work, updates docs/DECISIONS.md and docs/LEARNINGS.md
732
863
  - \`/project:spec\` command (interview-based spec creation \u2014 asks 5-8 questions one at a time, writes structured spec to docs/SPRINT.md, does NOT start coding until confirmed)
733
864
  - \`/project:prove\` command (runs tests, shows git diff vs main, rates confidence HIGH/MEDIUM/LOW with evidence)
734
865
  - \`/project:grill\` command (adversarial code review \u2014 challenges each change with "why this approach?", "what if X input?", rates BLOCKER/SHOULD-FIX/NITPICK, blocks until BLOCKERs resolved)
@@ -776,12 +907,12 @@ Return ONLY valid JSON matching this structure:
776
907
 
777
908
  \`\`\`json
778
909
  {
779
- "claude_md": "Full CLAUDE.md content (under 120 lines)",
780
- "commands": { "help": "...", "tasks": "...", "status": "...", "fix": "...", "sprint": "...", "spec": "...", "prove": "...", "grill": "...", "reset": "..." },
910
+ "claude_md": "Full CLAUDE.md content (under 150 lines)",
911
+ "commands": { "help": "...", "develop": "...", "status": "...", "fix": "...", "sprint": "...", "spec": "...", "prove": "...", "grill": "...", "reset": "..." },
781
912
  "rules": { "continuity": "...", "security": "..." },
782
- "agents": { "qa-orchestrator": "...", "linter": "...", "e2e-tester": "..." },
913
+ "agents": { "architect": "...", "planner": "...", "implementer": "...", "fixer": "...", "doc-updater": "...", "qa-orchestrator": "...", "linter": "...", "e2e-tester": "..." },
783
914
  "skills": { "skill-name/SKILL": "..." },
784
- "docs": { "TODO": "...", "DECISIONS": "...", "LEARNINGS": "...", "SPRINT": "..." }
915
+ "docs": { "DECISIONS": "...", "LEARNINGS": "...", "SPRINT": "..." }
785
916
  }
786
917
  \`\`\`
787
918
 
@@ -798,12 +929,12 @@ You must output a JSON object matching the EnvironmentSpec schema.
798
929
 
799
930
  - **Minimalism over completeness.** Fewer, well-chosen tools beat many generic ones. Each MCP server costs 500-2000 context tokens.
800
931
  - **Workflow-specific, not generic.** Every instruction, command, and rule must relate to the user's actual workflow.
801
- - **Concise CLAUDE.md.** Under 120 lines. No generic text like "be helpful." Include build/test commands, reference docs/ and skills/.
932
+ - **Concise CLAUDE.md.** Under 150 lines. No generic text like "be helpful." Include build/test commands, reference docs/ and skills/.
802
933
  - **Security by default.** Always include deny rules for destructive commands and secret file access.
803
934
 
804
935
  ## CLAUDE.md Template (mandatory structure)
805
936
 
806
- The \`claude_md\` field MUST follow this exact structure (max 120 lines):
937
+ The \`claude_md\` field MUST follow this exact structure (max 150 lines):
807
938
 
808
939
  \`\`\`
809
940
  # {Project Name}
@@ -852,6 +983,25 @@ Use subagents for deep investigation to keep main context clean.
852
983
  - Prefer small, focused commits (one feature or fix per commit)
853
984
  - Use conventional commits: feat:, fix:, docs:, refactor:, test:
854
985
  - Target < 200 lines per PR when possible
986
+
987
+ ## Engineering Standards
988
+ - Lead with answers over reasoning. Be concise.
989
+ - Use absolute file paths in all references.
990
+ - No filler, no inner monologue, no time estimates.
991
+ - Produce load-bearing code \u2014 every line of output should be actionable.
992
+
993
+ ## Tool Usage Policy
994
+ - Prefer Edit tool over sed/awk for file modifications
995
+ - Prefer Grep tool over rg for searching
996
+ - Prefer Read tool over cat for file reading
997
+ - Reserve Bash for: builds, installs, git, network, processes
998
+ - Read and understand existing code before modifying
999
+ - Delete unused code completely \u2014 no compatibility shims
1000
+
1001
+ ## Code Philosophy
1002
+ - Do not create abstractions for one-time operations
1003
+ - Complete the task fully \u2014 don't gold-plate, but don't leave it half-done
1004
+ - Prefer editing existing files over creating new ones
855
1005
  \`\`\`
856
1006
 
857
1007
  Do not add generic filler. Every line must be specific to the user's workflow.
@@ -860,20 +1010,19 @@ Do not add generic filler. Every line must be specific to the user's workflow.
860
1010
 
861
1011
  1. A concise, workflow-specific \`claude_md\` (the CLAUDE.md content)
862
1012
  2. A \`/project:help\` command that explains the environment
863
- 3. A \`/project:tasks\` command for task management via TODO.md
864
- 4. A \`docs/TODO.md\` file for continuity
865
- 5. A \`docs/DECISIONS.md\` file for architectural decisions
866
- 6. A \`docs/LEARNINGS.md\` file for non-obvious discoveries
867
- 7. A \`rules/continuity.md\` rule encouraging updates to DECISIONS.md and LEARNINGS.md
868
- 8. A \`rules/security.md\` rule with essential security instructions
869
- 9. settings.json with deny rules for \`rm -rf\`, \`curl|sh\`, reading \`.env\` and \`secrets/\`
870
- 10. A \`/project:status\` command for code projects (uses ! for live git/test output)
871
- 11. A \`/project:fix\` command for code projects (uses $ARGUMENTS for issue number)
872
- 12. A \`docs/SPRINT.md\` file for sprint contracts (acceptance criteria, verification steps)
873
- 13. A "Verification" section in CLAUDE.md with concrete verify commands for the project
874
- 14. A "Known Gotchas" section in CLAUDE.md (starts empty, grows with corrections)
875
- 15. A "Debugging" section in CLAUDE.md (2 lines: paste raw errors, use subagents)
876
- 16. A "Git Workflow" section in CLAUDE.md (3 rules: small commits, conventional format, <200 lines PR)
1013
+ 3. A \`docs/DECISIONS.md\` file for architectural decisions
1014
+ 4. A \`docs/LEARNINGS.md\` file for non-obvious discoveries
1015
+ 5. A \`rules/continuity.md\` rule encouraging updates to DECISIONS.md and LEARNINGS.md
1016
+ 6. A \`rules/security.md\` rule with essential security instructions
1017
+ 7. settings.json with deny rules for \`rm -rf\`, \`curl|sh\`, reading \`.env\` and \`secrets/\`
1018
+ 8. A \`/project:status\` command for code projects (uses ! for live git/SPRINT.md output)
1019
+ 9. A \`/project:fix\` command for code projects (uses $ARGUMENTS for issue number)
1020
+ 10. A \`docs/SPRINT.md\` file as the living spec/plan (replaces TODO.md \u2014 acceptance criteria, verification steps)
1021
+ 11. A "Verification" section in CLAUDE.md with concrete verify commands for the project
1022
+ 12. A "Known Gotchas" section in CLAUDE.md (starts empty, grows with corrections)
1023
+ 13. A "Debugging" section in CLAUDE.md (2 lines: paste raw errors, use subagents)
1024
+ 14. A "Git Workflow" section in CLAUDE.md (3 rules: small commits, conventional format, <200 lines PR)
1025
+ 15. "Engineering Standards", "Tool Usage Policy", and "Code Philosophy" sections in CLAUDE.md
877
1026
 
878
1027
  ## Tool Selection Rules
879
1028
 
@@ -887,10 +1036,10 @@ Do not add generic filler. Every line must be specific to the user's workflow.
887
1036
  ## Context Budget (STRICT)
888
1037
 
889
1038
  - MCP servers: maximum 6. Prefer fewer.
890
- - CLAUDE.md: maximum 120 lines.
1039
+ - CLAUDE.md: maximum 150 lines.
891
1040
  - Rules: maximum 5 files, each under 20 lines.
892
1041
  - Skills: maximum 3. Only include directly relevant ones.
893
- - Agents: maximum 3. QA pipeline + one specialist.
1042
+ - Agents: maximum 5. Orchestration pipeline (/develop) agents.
894
1043
  - Commands: no limit (loaded on demand, zero context cost).
895
1044
  - Hooks: maximum 4 (auto-format, block-destructive, PostCompact, plus one contextual).
896
1045
 
@@ -909,7 +1058,7 @@ Return ONLY valid JSON matching this structure:
909
1058
  { "tool_id": "id-from-registry", "reason": "why this tool fits" }
910
1059
  ],
911
1060
  "harness": {
912
- "claude_md": "The full CLAUDE.md content (under 120 lines)",
1061
+ "claude_md": "The full CLAUDE.md content (under 150 lines)",
913
1062
  "settings": {
914
1063
  "permissions": {
915
1064
  "allow": ["Bash(npm run *)", "Read", "Write", "Edit"],
@@ -921,7 +1070,7 @@ Return ONLY valid JSON matching this structure:
921
1070
  },
922
1071
  "commands": {
923
1072
  "help": "markdown content for /project:help",
924
- "tasks": "markdown content for /project:tasks"
1073
+ "develop": "markdown content for /project:develop"
925
1074
  },
926
1075
  "rules": {
927
1076
  "continuity": "markdown content for continuity rule",
@@ -931,13 +1080,16 @@ Return ONLY valid JSON matching this structure:
931
1080
  "skill-name/SKILL": "markdown content with YAML frontmatter"
932
1081
  },
933
1082
  "agents": {
934
- "qa-orchestrator": "agent markdown with YAML frontmatter"
1083
+ "architect": "agent markdown with YAML frontmatter",
1084
+ "planner": "agent markdown with YAML frontmatter",
1085
+ "implementer": "agent markdown with YAML frontmatter",
1086
+ "fixer": "agent markdown with YAML frontmatter",
1087
+ "doc-updater": "agent markdown with YAML frontmatter"
935
1088
  },
936
1089
  "docs": {
937
- "TODO": "# TODO\\n\\n- [ ] First task",
938
1090
  "DECISIONS": "# Decisions\\n\\nArchitectural decisions.",
939
1091
  "LEARNINGS": "# Learnings\\n\\nNon-obvious discoveries.",
940
- "SPRINT": "# Sprint Contract\\n\\nDefine acceptance criteria."
1092
+ "SPRINT": "# Sprint\\n\\nLiving spec and plan."
941
1093
  }
942
1094
  }
943
1095
  }
@@ -1218,7 +1370,7 @@ function buildMcpConfig(skeleton, registry) {
1218
1370
  }
1219
1371
  return config;
1220
1372
  }
1221
- function validateSpec(spec, onProgress) {
1373
+ function validateSpec(spec) {
1222
1374
  const warnings = [];
1223
1375
  if (spec.tools.length > 8) {
1224
1376
  warnings.push(`${spec.tools.length} MCP servers selected (recommended: \u22646)`);
@@ -1226,31 +1378,39 @@ function validateSpec(spec, onProgress) {
1226
1378
  if (spec.harness.claude_md) {
1227
1379
  const lines = spec.harness.claude_md.split("\n").length;
1228
1380
  if (lines > 150) {
1229
- warnings.push(`CLAUDE.md is ${lines} lines (recommended: \u2264100)`);
1381
+ warnings.push(`CLAUDE.md is ${lines} lines (recommended: \u2264150)`);
1230
1382
  }
1231
1383
  }
1232
1384
  if (spec.harness.skills && Object.keys(spec.harness.skills).length > 5) {
1233
1385
  warnings.push(`${Object.keys(spec.harness.skills).length} skills (recommended: \u22643)`);
1234
1386
  }
1235
- for (const warning of warnings) {
1236
- onProgress?.(`\u26A0 ${warning}`);
1237
- }
1387
+ return warnings;
1238
1388
  }
1239
1389
  async function compile(intent, onProgress) {
1390
+ const startTime = Date.now();
1240
1391
  const config = await loadConfig();
1241
1392
  if (!config) {
1242
1393
  throw new Error("No config found. Run `kairn init` first.");
1243
1394
  }
1244
- onProgress?.("Loading tool registry...");
1395
+ onProgress?.({ phase: "registry", status: "running", message: "Loading tool registry..." });
1245
1396
  const registry = await loadRegistry();
1246
- onProgress?.("Analyzing workflow...");
1397
+ onProgress?.({ phase: "registry", status: "success", message: "Tool registry loaded", detail: `${registry.length} tools` });
1398
+ onProgress?.({ phase: "pass1", status: "running", message: "Pass 1: Analyzing workflow & selecting tools..." });
1247
1399
  const skeletonMsg = buildSkeletonMessage(intent, registry);
1248
1400
  const skeletonText = await callLLM(config, skeletonMsg, {
1249
1401
  maxTokens: 2048,
1250
1402
  systemPrompt: SKELETON_PROMPT
1251
1403
  });
1252
1404
  const skeleton = parseSkeletonResponse(skeletonText);
1253
- onProgress?.("Generating environment...");
1405
+ const toolNames = skeleton.tools.map((t) => t.tool_id).join(", ");
1406
+ onProgress?.({
1407
+ phase: "pass1",
1408
+ status: "success",
1409
+ message: `Pass 1: Selected ${skeleton.tools.length} tools`,
1410
+ detail: toolNames,
1411
+ elapsed: (Date.now() - startTime) / 1e3
1412
+ });
1413
+ onProgress?.({ phase: "pass2", status: "running", message: "Pass 2: Generating CLAUDE.md, commands, agents..." });
1254
1414
  const harnessMsg = buildHarnessMessage(intent, skeleton);
1255
1415
  let harness;
1256
1416
  try {
@@ -1260,7 +1420,7 @@ async function compile(intent, onProgress) {
1260
1420
  });
1261
1421
  harness = parseHarnessResponse(harnessText);
1262
1422
  } catch {
1263
- onProgress?.("Retrying with concise mode...");
1423
+ onProgress?.({ phase: "pass2-retry", status: "warning", message: "Pass 2: Response too large, retrying in concise mode..." });
1264
1424
  const retryMsg = buildHarnessMessage(intent, skeleton, true);
1265
1425
  const retryText = await callLLM(config, retryMsg, {
1266
1426
  maxTokens: 8192,
@@ -1268,9 +1428,19 @@ async function compile(intent, onProgress) {
1268
1428
  });
1269
1429
  harness = parseHarnessResponse(retryText);
1270
1430
  }
1271
- onProgress?.("Configuring tools...");
1431
+ const cmdCount = Object.keys(harness.commands).length;
1432
+ const agentCount = Object.keys(harness.agents ?? {}).length;
1433
+ const ruleCount = Object.keys(harness.rules).length;
1434
+ onProgress?.({
1435
+ phase: "pass2",
1436
+ status: "success",
1437
+ message: `Pass 2: Generated ${cmdCount} commands, ${agentCount} agents, ${ruleCount} rules`,
1438
+ elapsed: (Date.now() - startTime) / 1e3
1439
+ });
1440
+ onProgress?.({ phase: "pass3", status: "running", message: "Pass 3: Configuring MCP servers & settings..." });
1272
1441
  const settings = buildSettings(skeleton, registry);
1273
1442
  const mcpConfig = buildMcpConfig(skeleton, registry);
1443
+ onProgress?.({ phase: "pass3", status: "success", message: "Pass 3: Configured MCP servers & settings" });
1274
1444
  const spec = {
1275
1445
  id: `env_${crypto.randomUUID()}`,
1276
1446
  intent,
@@ -1290,7 +1460,12 @@ async function compile(intent, onProgress) {
1290
1460
  docs: harness.docs
1291
1461
  }
1292
1462
  };
1293
- validateSpec(spec, onProgress);
1463
+ const warnings = validateSpec(spec);
1464
+ for (const w of warnings) {
1465
+ onProgress?.({ phase: "done", status: "warning", message: `\u26A0 ${w}` });
1466
+ }
1467
+ const totalElapsed = ((Date.now() - startTime) / 1e3).toFixed(0);
1468
+ onProgress?.({ phase: "done", status: "success", message: `Environment compiled in ${totalElapsed}s`, elapsed: (Date.now() - startTime) / 1e3 });
1294
1469
  await ensureDirs();
1295
1470
  const envPath = path4.join(getEnvsDir(), `${spec.id}.json`);
1296
1471
  await fs4.writeFile(envPath, JSON.stringify(spec, null, 2), "utf-8");
@@ -1345,10 +1520,10 @@ Read .claude/commands/ and list each one with a one-line description.
1345
1520
  Group them by workflow phase:
1346
1521
 
1347
1522
  PLAN: /project:spec, /project:sprint, /project:plan
1348
- BUILD: (just start coding \u2014 Claude reads CLAUDE.md automatically)
1523
+ BUILD: /project:develop (full pipeline), or just start coding
1349
1524
  VERIFY: /project:prove, /project:grill, /project:test
1350
1525
  SHIP: /project:commit, /project:review
1351
- MANAGE: /project:status, /project:tasks, /project:reset
1526
+ MANAGE: /project:status, /project:reset
1352
1527
 
1353
1528
  ## Your Agents
1354
1529
  Read .claude/agents/ and explain each one with how to invoke it.
@@ -1407,7 +1582,7 @@ var LOOP_COMMAND_CODE = `# Development Loop
1407
1582
  Run an assisted development cycle for the next feature.
1408
1583
 
1409
1584
  ## Phase 1: SPEC
1410
- Review docs/TODO.md and docs/SPRINT.md.
1585
+ Review docs/SPRINT.md.
1411
1586
  If no sprint is defined, run /project:spec to interview the user.
1412
1587
  Wait for user approval of the spec.
1413
1588
 
@@ -1432,7 +1607,7 @@ Fix any BLOCKERs.
1432
1607
 
1433
1608
  ## Phase 6: SHIP
1434
1609
  Run /project:commit.
1435
- Report what was built and what's next from docs/TODO.md.
1610
+ Report what was built and what's next from docs/SPRINT.md.
1436
1611
 
1437
1612
  Then ask: "Continue to next feature?"
1438
1613
  If yes, return to Phase 1.`;
@@ -1441,7 +1616,7 @@ var LOOP_COMMAND_RESEARCH = `# Research Loop
1441
1616
  Run an assisted research cycle.
1442
1617
 
1443
1618
  ## Phase 1: QUESTION
1444
- Review docs/TODO.md for the next research question.
1619
+ Review docs/SPRINT.md for the next research question.
1445
1620
  If none, ask the user what to investigate.
1446
1621
 
1447
1622
  ## Phase 2: RESEARCH
@@ -1457,7 +1632,7 @@ Present the summary. Ask the user for feedback.
1457
1632
  Revise based on feedback.
1458
1633
 
1459
1634
  ## Phase 5: NEXT
1460
- Update docs/TODO.md \u2014 mark question as done, identify follow-ups.
1635
+ Update docs/SPRINT.md \u2014 mark question as done, identify follow-ups.
1461
1636
  Ask: "Continue to next question?"`;
1462
1637
  var PM_AGENT = `---
1463
1638
  name: pm
@@ -1468,7 +1643,7 @@ model: opus
1468
1643
  You are a project manager for this codebase.
1469
1644
 
1470
1645
  Your responsibilities:
1471
- 1. Maintain docs/TODO.md \u2014 keep it prioritized and current
1646
+ 1. Maintain docs/SPRINT.md \u2014 keep it prioritized and current
1472
1647
  2. Write specs to docs/SPRINT.md when asked
1473
1648
  3. Review completed work and suggest what's next
1474
1649
  4. Track decisions in docs/DECISIONS.md
@@ -1487,7 +1662,7 @@ PM-driven development loop with PR delivery.
1487
1662
 
1488
1663
  ## Phase 1: PLAN (@pm)
1489
1664
  Use @pm to:
1490
- - Read docs/TODO.md and docs/SPRINT.md
1665
+ - Read docs/SPRINT.md
1491
1666
  - Select the highest-priority unfinished task
1492
1667
  - Write a spec to docs/SPRINT.md
1493
1668
  - Present the spec for approval
@@ -1517,7 +1692,7 @@ Create a pull request:
1517
1692
  ## Phase 6: NEXT
1518
1693
  Report:
1519
1694
  "PR #{N} ready for review: {link}
1520
- Next priority from TODO.md: {next task}
1695
+ Next priority from SPRINT.md: {next task}
1521
1696
  Continue? (y/n)"
1522
1697
 
1523
1698
  If yes, return to Phase 1 with next task.`;
@@ -1534,7 +1709,7 @@ PRs are opened automatically. You review when ready.
1534
1709
 
1535
1710
  ## The Loop
1536
1711
  Repeat until max features reached or stopped:
1537
- 1. @pm selects next priority from docs/TODO.md
1712
+ 1. @pm selects next priority from docs/SPRINT.md
1538
1713
  2. Create worktree + branch
1539
1714
  3. Implement the feature
1540
1715
  4. Run verification (build, test, lint)
@@ -1606,7 +1781,7 @@ function autonomyLabel(level) {
1606
1781
 
1607
1782
  // src/adapter/claude-code.ts
1608
1783
  var STATUS_LINE = {
1609
- command: `printf '%s | %s tasks' "$(git branch --show-current 2>/dev/null || echo 'no-git')" "$(grep -c '\\- \\[ \\]' docs/TODO.md 2>/dev/null || echo 0)"`
1784
+ command: `printf '%s | %s tasks' "$(git branch --show-current 2>/dev/null || echo 'no-git')" "$(grep -c '\\- \\[ \\]' docs/SPRINT.md 2>/dev/null || echo 0)"`
1610
1785
  };
1611
1786
  function isCodeProject(spec) {
1612
1787
  const commands = spec.harness.commands ?? {};
@@ -2066,16 +2241,19 @@ ${clarificationLines}`;
2066
2241
  Autonomy level: ${autonomyLevel} (${autonomyLabel(autonomyLevel)})`;
2067
2242
  }
2068
2243
  console.log(ui.section("Compilation"));
2069
- const spinner = ora({ text: "Loading tool registry...", indent: 2 }).start();
2244
+ const estimate = estimateTime(config.model, finalIntent);
2245
+ console.log(chalk5.dim(` Estimated time: ${estimate} (${config.model})`));
2246
+ console.log("");
2247
+ const renderer = createProgressRenderer();
2070
2248
  let spec;
2071
2249
  try {
2072
- spec = await compile(finalIntent, (msg) => {
2073
- spinner.text = msg;
2250
+ spec = await compile(finalIntent, (progress) => {
2251
+ renderer.update(progress);
2074
2252
  });
2075
2253
  spec.autonomy_level = autonomyLevel;
2076
- spinner.succeed("Environment compiled");
2254
+ renderer.finish();
2077
2255
  } catch (err) {
2078
- spinner.fail("Compilation failed");
2256
+ renderer.fail(err);
2079
2257
  const msg = err instanceof Error ? err.message : String(err);
2080
2258
  console.log(chalk5.red(`
2081
2259
  ${msg}
@@ -2320,7 +2498,7 @@ var updateRegistryCommand = new Command5("update-registry").description("Fetch t
2320
2498
  import { Command as Command6 } from "commander";
2321
2499
  import { confirm as confirm2 } from "@inquirer/prompts";
2322
2500
  import chalk9 from "chalk";
2323
- import ora2 from "ora";
2501
+ import ora from "ora";
2324
2502
  import fs12 from "fs/promises";
2325
2503
  import path12 from "path";
2326
2504
 
@@ -2645,7 +2823,7 @@ var optimizeCommand = new Command6("optimize").description("Scan an existing pro
2645
2823
  }
2646
2824
  const targetDir = process.cwd();
2647
2825
  console.log(ui.section("Project Scan"));
2648
- const scanSpinner = ora2({ text: "Scanning project...", indent: 2 }).start();
2826
+ const scanSpinner = ora({ text: "Scanning project...", indent: 2 }).start();
2649
2827
  const profile = await scanProject(targetDir);
2650
2828
  scanSpinner.stop();
2651
2829
  if (profile.language) console.log(ui.kv("Language:", profile.language));
@@ -2714,10 +2892,10 @@ var optimizeCommand = new Command6("optimize").description("Scan an existing pro
2714
2892
  }
2715
2893
  const intent = buildOptimizeIntent(profile);
2716
2894
  let spec;
2717
- const spinner = ora2({ text: "Compiling optimized environment...", indent: 2 }).start();
2895
+ const spinner = ora({ text: "Compiling optimized environment...", indent: 2 }).start();
2718
2896
  try {
2719
- spec = await compile(intent, (msg) => {
2720
- spinner.text = msg;
2897
+ spec = await compile(intent, (progress) => {
2898
+ spinner.text = progress.message;
2721
2899
  });
2722
2900
  spinner.succeed("Environment compiled");
2723
2901
  } catch (err) {