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 +256 -78
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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
|
|
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
|
|
593
|
-
4. A \`docs/
|
|
594
|
-
5. A \`
|
|
595
|
-
6. A \`
|
|
596
|
-
7.
|
|
597
|
-
8. A \`
|
|
598
|
-
9.
|
|
599
|
-
10. A
|
|
600
|
-
11. A
|
|
601
|
-
12. A
|
|
602
|
-
13. A "
|
|
603
|
-
14. A "
|
|
604
|
-
15.
|
|
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,
|
|
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
|
|
780
|
-
"commands": { "help": "...", "
|
|
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": { "
|
|
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
|
|
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
|
|
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
|
|
864
|
-
4. A \`docs/
|
|
865
|
-
5. A \`
|
|
866
|
-
6. A \`
|
|
867
|
-
7.
|
|
868
|
-
8. A \`
|
|
869
|
-
9.
|
|
870
|
-
10. A
|
|
871
|
-
11. A
|
|
872
|
-
12. A
|
|
873
|
-
13. A "
|
|
874
|
-
14. A "
|
|
875
|
-
15.
|
|
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
|
|
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
|
|
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
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
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
|
|
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: \
|
|
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
|
-
|
|
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?.("
|
|
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
|
-
|
|
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?.("
|
|
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
|
-
|
|
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
|
|
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
|
|
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:
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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
|
|
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/
|
|
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/
|
|
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
|
|
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, (
|
|
2073
|
-
|
|
2250
|
+
spec = await compile(finalIntent, (progress) => {
|
|
2251
|
+
renderer.update(progress);
|
|
2074
2252
|
});
|
|
2075
2253
|
spec.autonomy_level = autonomyLevel;
|
|
2076
|
-
|
|
2254
|
+
renderer.finish();
|
|
2077
2255
|
} catch (err) {
|
|
2078
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
2895
|
+
const spinner = ora({ text: "Compiling optimized environment...", indent: 2 }).start();
|
|
2718
2896
|
try {
|
|
2719
|
-
spec = await compile(intent, (
|
|
2720
|
-
spinner.text =
|
|
2897
|
+
spec = await compile(intent, (progress) => {
|
|
2898
|
+
spinner.text = progress.message;
|
|
2721
2899
|
});
|
|
2722
2900
|
spinner.succeed("Environment compiled");
|
|
2723
2901
|
} catch (err) {
|