@kraftapps-ai/kai 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -115
- package/kai +165 -423
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,160 +1,121 @@
|
|
|
1
1
|
# Kai
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Your AI product manager and development team. One command.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
9
|
-
│ kai.json │────▶│ Implementer │────▶│ Reviewer │
|
|
10
|
-
│ (stories) │ │ (claude -p) │ │ (claude -p) │
|
|
11
|
-
└─────────────┘ └──────┬───────┘ └──────┬───────┘
|
|
12
|
-
│ │
|
|
13
|
-
commits code verifies against
|
|
14
|
-
marks passes:true acceptance criteria
|
|
15
|
-
│ │
|
|
16
|
-
│ ┌────────┐ │
|
|
17
|
-
└───▶│ Loop │◀──────┘
|
|
18
|
-
│ repeat │ (fail = retry)
|
|
19
|
-
└────────┘
|
|
5
|
+
```bash
|
|
6
|
+
npm i -g @kraftapps-ai/kai
|
|
7
|
+
kai
|
|
20
8
|
```
|
|
21
9
|
|
|
22
|
-
|
|
23
|
-
2. **Implements** it using Claude Code (one `claude -p` call)
|
|
24
|
-
3. **Reviews** the implementation with a separate Claude call that verifies each acceptance criterion against the actual git diff
|
|
25
|
-
4. **Retries** if the review fails (up to 3 attempts, then skips)
|
|
26
|
-
5. **Loops** to the next story
|
|
10
|
+
## What is Kai?
|
|
27
11
|
|
|
28
|
-
|
|
12
|
+
Kai is a CLI that opens an interactive AI session acting as your **product manager**. Tell it what you want to build. It breaks the work into stories, then dispatches an autonomous AI developer to implement them — with a built-in reviewer that catches when the AI cuts corners.
|
|
29
13
|
|
|
30
|
-
##
|
|
14
|
+
## How it works
|
|
31
15
|
|
|
32
|
-
```bash
|
|
33
|
-
npm i -g @kraftapps-ai/kai
|
|
34
16
|
```
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
17
|
+
You ←→ Kai (PM) # brainstorm, plan, decide
|
|
18
|
+
↓
|
|
19
|
+
kai.json # stories with acceptance criteria
|
|
20
|
+
↓
|
|
21
|
+
┌────────┴────────┐
|
|
22
|
+
│ Developer AI │ # implements one story at a time
|
|
23
|
+
│ (claude -p) │
|
|
24
|
+
└────────┬────────┘
|
|
25
|
+
↓
|
|
26
|
+
┌────────┴────────┐
|
|
27
|
+
│ Reviewer AI │ # verifies each criterion independently
|
|
28
|
+
│ (claude -p) │
|
|
29
|
+
└────────┬────────┘
|
|
30
|
+
↓
|
|
31
|
+
Committed code # repeat until all stories done
|
|
42
32
|
```
|
|
43
33
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
## Quick start
|
|
34
|
+
## Usage
|
|
47
35
|
|
|
48
36
|
```bash
|
|
49
37
|
cd your-project
|
|
38
|
+
kai
|
|
39
|
+
```
|
|
50
40
|
|
|
51
|
-
|
|
52
|
-
kai init
|
|
41
|
+
That's it. Kai will:
|
|
53
42
|
|
|
54
|
-
|
|
55
|
-
|
|
43
|
+
1. Auto-initialize if this is a new project
|
|
44
|
+
2. Ask what you want to build
|
|
45
|
+
3. Help you refine the idea and break it into stories
|
|
46
|
+
4. Write the stories to `kai.json`
|
|
47
|
+
5. When you say "go" — dispatch the AI developer loop
|
|
48
|
+
6. Report back when everything is done
|
|
56
49
|
|
|
57
|
-
|
|
58
|
-
kai plan "add user authentication with OAuth"
|
|
59
|
-
# → AI generates stories → confirms → adds to kai.json
|
|
50
|
+
Next time you run `kai`, it picks up where you left off — reads `kai.json` for stories and `kai-progress.txt` for what's been done.
|
|
60
51
|
|
|
61
|
-
|
|
62
|
-
kai status
|
|
52
|
+
## Example
|
|
63
53
|
|
|
64
|
-
# Let Kai implement everything
|
|
65
|
-
kai go
|
|
66
54
|
```
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
55
|
+
$ kai
|
|
56
|
+
> I want to add Stripe payments to my Next.js app
|
|
57
|
+
|
|
58
|
+
Kai: Let me break that down. A few questions first:
|
|
59
|
+
- Checkout or subscriptions?
|
|
60
|
+
- Do you need a customer portal?
|
|
61
|
+
...
|
|
62
|
+
|
|
63
|
+
> Just one-time checkout for now. Ship it.
|
|
64
|
+
|
|
65
|
+
Kai: Created 8 stories in kai.json. Starting the dev loop...
|
|
66
|
+
#1: Add Stripe SDK and env config
|
|
67
|
+
#2: Create checkout API route
|
|
68
|
+
#3: Add checkout button component
|
|
69
|
+
...
|
|
70
|
+
All 8 stories complete!
|
|
71
|
+
```
|
|
78
72
|
|
|
79
73
|
## Files
|
|
80
74
|
|
|
81
|
-
After `kai init`, your project gets:
|
|
82
|
-
|
|
83
75
|
```
|
|
84
76
|
your-project/
|
|
85
|
-
├── kai.json
|
|
86
|
-
├── kai-progress.txt
|
|
77
|
+
├── kai.json # Stories (version control this)
|
|
78
|
+
├── kai-progress.txt # Implementation log
|
|
87
79
|
└── .kai/
|
|
88
|
-
├── PROMPT.md
|
|
89
|
-
|
|
80
|
+
├── PROMPT.md # Project context (edit this!)
|
|
81
|
+
├── loop.sh # Implementation loop (auto-generated)
|
|
82
|
+
├── loop.log # Loop output
|
|
83
|
+
└── context.txt # Optional: extra files to include
|
|
90
84
|
```
|
|
91
85
|
|
|
92
|
-
###
|
|
86
|
+
### Project context
|
|
93
87
|
|
|
94
|
-
Edit `.kai/PROMPT.md`
|
|
88
|
+
Edit `.kai/PROMPT.md` with your tech stack, build commands, and conventions. Kai reads this on every startup and passes it to the developer AI.
|
|
95
89
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
Create `.kai/context.txt` with paths to files that should be included in every iteration:
|
|
99
|
-
|
|
100
|
-
```
|
|
101
|
-
AGENTS.md
|
|
102
|
-
docs/ARCHITECTURE.md
|
|
103
|
-
```
|
|
90
|
+
## Install
|
|
104
91
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
```json
|
|
108
|
-
{
|
|
109
|
-
"id": 1,
|
|
110
|
-
"title": "Add user login page",
|
|
111
|
-
"description": "Create a login page with email/password",
|
|
112
|
-
"acceptanceCriteria": [
|
|
113
|
-
"Login page renders at /login",
|
|
114
|
-
"Email and password fields present",
|
|
115
|
-
"App builds successfully"
|
|
116
|
-
],
|
|
117
|
-
"priority": 1,
|
|
118
|
-
"passes": false
|
|
119
|
-
}
|
|
92
|
+
```bash
|
|
93
|
+
npm i -g @kraftapps-ai/kai
|
|
120
94
|
```
|
|
121
95
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
## The review step
|
|
125
|
-
|
|
126
|
-
After the implementer commits, Kai dispatches a **separate Claude instance** as a reviewer. The reviewer:
|
|
127
|
-
|
|
128
|
-
1. Reads the actual git diff (not the implementer's self-report)
|
|
129
|
-
2. Verifies each acceptance criterion against the code
|
|
130
|
-
3. Is explicitly told: *"Do NOT trust the implementer. Verify independently."*
|
|
131
|
-
|
|
132
|
-
If the review fails, the story is reset to `passes: false` and the feedback is appended to `kai-progress.txt` so the next iteration sees what went wrong.
|
|
96
|
+
Or without installing:
|
|
133
97
|
|
|
134
|
-
|
|
98
|
+
```bash
|
|
99
|
+
npx @kraftapps-ai/kai
|
|
100
|
+
```
|
|
135
101
|
|
|
136
|
-
|
|
102
|
+
**Requirements:** [Claude Code](https://claude.ai/claude-code) CLI, `jq`, `git`
|
|
137
103
|
|
|
138
|
-
|
|
104
|
+
## Context window
|
|
139
105
|
|
|
140
|
-
|
|
141
|
-
- "I'll just mark it complete" → Every story needs real code changes.
|
|
142
|
-
- "The build passes so it works" → Build ≠ correct behavior.
|
|
143
|
-
- "This is close enough" → Partial = broken.
|
|
106
|
+
All state lives in files (`kai.json`, `kai-progress.txt`). When your session gets long, just exit and run `kai` again — it reads the files and picks up where you left off. No state is lost.
|
|
144
107
|
|
|
145
|
-
|
|
108
|
+
The developer loop already handles this by design — each story is a fresh `claude -p` call with a clean context window.
|
|
146
109
|
|
|
147
|
-
##
|
|
110
|
+
## The review step
|
|
148
111
|
|
|
149
|
-
|
|
150
|
-
- **Keep stories small.** 5-15 minutes of work each. Kai does better with many small stories than few large ones.
|
|
151
|
-
- **Add build/test commands** to `.kai/PROMPT.md` so Kai knows how to verify.
|
|
152
|
-
- **Use `kai status`** to monitor progress, or `git log --oneline` to see commits.
|
|
153
|
-
- **Run overnight.** `nohup kai go > kai.log 2>&1 &` and check in the morning.
|
|
112
|
+
After each story is implemented, a separate Claude instance reviews the code:
|
|
154
113
|
|
|
155
|
-
|
|
114
|
+
1. Reads the actual git diff (not the implementer's self-report)
|
|
115
|
+
2. Verifies each acceptance criterion
|
|
116
|
+
3. Is told: *"Be skeptical. The implementer may have cut corners."*
|
|
156
117
|
|
|
157
|
-
|
|
118
|
+
Failed reviews reset the story and feed back the failure reason for the next attempt. After 3 failures, the story is skipped.
|
|
158
119
|
|
|
159
120
|
## License
|
|
160
121
|
|
package/kai
CHANGED
|
@@ -1,495 +1,237 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# Kai —
|
|
2
|
+
# Kai — Your AI product manager and development team
|
|
3
3
|
# https://github.com/kraftapps-ai/kai
|
|
4
4
|
#
|
|
5
|
-
#
|
|
6
|
-
# kai — Show help
|
|
7
|
-
# kai init — Initialize kai in current project
|
|
8
|
-
# kai plan [topic] — Interactive brainstorm with AI PM, generates stories
|
|
9
|
-
# kai import — Import stories from JSON
|
|
10
|
-
# kai go — Run the implement + review loop
|
|
11
|
-
# kai status — Show remaining stories
|
|
12
|
-
# kai reset <id> — Reset a story to incomplete
|
|
5
|
+
# Just run: kai
|
|
13
6
|
|
|
14
7
|
set -e
|
|
15
8
|
|
|
16
|
-
KAI_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
17
|
-
PROJECT_DIR="$(pwd)"
|
|
18
9
|
PRD_FILE="kai.json"
|
|
19
10
|
PROGRESS_FILE="kai-progress.txt"
|
|
20
11
|
PROMPT_FILE=".kai/PROMPT.md"
|
|
12
|
+
LOOP_SCRIPT=".kai/loop.sh"
|
|
21
13
|
|
|
22
|
-
# ──
|
|
14
|
+
# ── Auto-init if needed ───────────────────────────────
|
|
23
15
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
Kai — Autonomous AI developer loop for Claude Code
|
|
16
|
+
if [ ! -f "$PRD_FILE" ]; then
|
|
17
|
+
mkdir -p .kai
|
|
27
18
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
kai help Show this help
|
|
19
|
+
cat > "$PRD_FILE" << 'EOF'
|
|
20
|
+
{
|
|
21
|
+
"branchName": "main",
|
|
22
|
+
"userStories": []
|
|
23
|
+
}
|
|
24
|
+
EOF
|
|
35
25
|
|
|
36
|
-
|
|
37
|
-
1. kai init # setup
|
|
38
|
-
2. vim .kai/PROMPT.md # add project context
|
|
39
|
-
3. kai plan "add user auth" # AI generates stories
|
|
40
|
-
4. kai go # AI implements everything
|
|
26
|
+
echo "# Kai Progress Log" > "$PROGRESS_FILE"
|
|
41
27
|
|
|
42
|
-
|
|
28
|
+
cat > "$PROMPT_FILE" << 'EOF'
|
|
29
|
+
# Project Context
|
|
43
30
|
|
|
44
|
-
|
|
45
|
-
EOF
|
|
46
|
-
}
|
|
31
|
+
Add your project-specific context here. The more you provide, the better Kai performs.
|
|
47
32
|
|
|
48
|
-
|
|
33
|
+
## Tech stack
|
|
34
|
+
<!-- e.g., React + TypeScript, Laravel, Python Flask -->
|
|
49
35
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
echo "kai.json already exists. Aborting."
|
|
53
|
-
exit 1
|
|
54
|
-
fi
|
|
36
|
+
## Build command
|
|
37
|
+
<!-- e.g., npm run build, cargo build, go build -->
|
|
55
38
|
|
|
56
|
-
|
|
39
|
+
## Test command
|
|
40
|
+
<!-- e.g., npm test, pytest, go test ./... -->
|
|
57
41
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"branchName": "main",
|
|
61
|
-
"userStories": []
|
|
62
|
-
}
|
|
63
|
-
JSONEOF
|
|
42
|
+
## Project structure
|
|
43
|
+
<!-- Key directories and files -->
|
|
64
44
|
|
|
65
|
-
|
|
66
|
-
|
|
45
|
+
## Conventions
|
|
46
|
+
<!-- Coding style, naming, patterns -->
|
|
47
|
+
EOF
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# ── Create the implementation loop script ─────────────
|
|
51
|
+
|
|
52
|
+
cat > "$LOOP_SCRIPT" << 'LOOPEOF'
|
|
53
|
+
#!/bin/bash
|
|
54
|
+
# Kai implementation loop — called by the PM when stories are ready
|
|
55
|
+
set -e
|
|
56
|
+
|
|
57
|
+
PRD_FILE="kai.json"
|
|
58
|
+
PROGRESS_FILE="kai-progress.txt"
|
|
59
|
+
PROMPT_FILE=".kai/PROMPT.md"
|
|
67
60
|
|
|
68
|
-
|
|
69
|
-
You are an autonomous AI developer.
|
|
61
|
+
WORKER_PROMPT='You are an autonomous AI developer.
|
|
70
62
|
|
|
71
63
|
## Your inputs
|
|
72
64
|
- `@kai.json` — user stories with `passes: true/false`.
|
|
73
|
-
- `@kai-progress.txt` —
|
|
65
|
+
- `@kai-progress.txt` — log of what has been done.
|
|
74
66
|
|
|
75
67
|
## Your task (ONE story per loop)
|
|
76
68
|
|
|
77
|
-
1. Read kai.json
|
|
78
|
-
2. Read kai-progress.txt
|
|
79
|
-
3. **PLAN before coding.** List
|
|
69
|
+
1. Read kai.json, find the highest-priority story where `passes: false`.
|
|
70
|
+
2. Read kai-progress.txt for context on previous work.
|
|
71
|
+
3. **PLAN before coding.** List files and changes before touching code.
|
|
80
72
|
4. Implement ONLY that one story.
|
|
81
|
-
5. Run verification (build, tests, lint
|
|
82
|
-
6. **SELF-REVIEW.** For each acceptance criterion,
|
|
83
|
-
7.
|
|
73
|
+
5. Run verification (build, tests, lint).
|
|
74
|
+
6. **SELF-REVIEW.** For each acceptance criterion, cite evidence (file, line, command output). No "should work" or "probably".
|
|
75
|
+
7. Commit with a descriptive message.
|
|
84
76
|
8. Update kai.json: set `passes` to `true`.
|
|
85
|
-
9. Append to kai-progress.txt
|
|
86
|
-
|
|
87
|
-
## Verification Protocol
|
|
88
|
-
|
|
89
|
-
- Run the project's build/test command and read the output.
|
|
90
|
-
- You cannot claim a criterion is met without evidence.
|
|
91
|
-
- "I added the component" is NOT evidence. "I added FooComponent at src/Foo.tsx:45 and the build passes" IS evidence.
|
|
77
|
+
9. Append to kai-progress.txt.
|
|
92
78
|
|
|
93
79
|
## Rules
|
|
94
|
-
|
|
95
|
-
-
|
|
96
|
-
- Search the codebase before assuming anything.
|
|
97
|
-
- No placeholder implementations. Fully implement or report BLOCKED.
|
|
80
|
+
- ONE STORY PER LOOP.
|
|
81
|
+
- No placeholders. Fully implement or report BLOCKED.
|
|
98
82
|
- Do not break existing functionality.
|
|
99
|
-
- Keep commits atomic — one commit per story.
|
|
100
83
|
- If ALL stories have `passes: true`, output: <promise>COMPLETE</promise>
|
|
101
84
|
|
|
102
85
|
## Rationalization Blockers
|
|
103
|
-
|
|
104
|
-
| Your thought | Why it's wrong | Do this instead |
|
|
86
|
+
| Thought | Reality | Action |
|
|
105
87
|
|---|---|---|
|
|
106
|
-
| "
|
|
107
|
-
| "
|
|
108
|
-
| "
|
|
109
|
-
| "
|
|
110
|
-
| "
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
88
|
+
| "Already implemented" | You have not verified. | Read code, run build. |
|
|
89
|
+
| "Just mark complete" | No code changes = not done. | Implement it. |
|
|
90
|
+
| "Build passes = works" | Build ≠ correct. | Check each criterion. |
|
|
91
|
+
| "Close enough" | Partial = broken. | All criteria or BLOCKED. |
|
|
92
|
+
| "Skip review" | Simple code has bugs. | Always self-review. |'
|
|
93
|
+
|
|
94
|
+
context_files="@${PROMPT_FILE} @${PRD_FILE} @${PROGRESS_FILE}"
|
|
95
|
+
if [ -f ".kai/context.txt" ]; then
|
|
96
|
+
while IFS= read -r file; do
|
|
97
|
+
[ -n "$file" ] && [ -f "$file" ] && context_files="$context_files @${file}"
|
|
98
|
+
done < .kai/context.txt
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
iteration=0
|
|
102
|
+
review_failures=0
|
|
103
|
+
|
|
104
|
+
while :; do
|
|
105
|
+
iteration=$((iteration + 1))
|
|
106
|
+
REMAINING=$(jq '[.userStories[] | select(.passes == false)] | length' "$PRD_FILE")
|
|
107
|
+
|
|
108
|
+
if [ "$REMAINING" -eq 0 ]; then
|
|
109
|
+
echo "All stories complete!"
|
|
110
|
+
break
|
|
111
|
+
fi
|
|
131
112
|
|
|
132
|
-
|
|
133
|
-
|
|
113
|
+
NEXT_ID=$(jq -r '[.userStories[] | select(.passes == false)] | sort_by(.priority) | .[0].id' "$PRD_FILE")
|
|
114
|
+
NEXT=$(jq -r '[.userStories[] | select(.passes == false)] | sort_by(.priority) | .[0].title' "$PRD_FILE")
|
|
134
115
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
116
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
117
|
+
echo "#${iteration} — $(date)"
|
|
118
|
+
echo " Remaining: $REMAINING | Next: $NEXT"
|
|
119
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
139
120
|
|
|
140
|
-
|
|
141
|
-
existing=""
|
|
142
|
-
story_count=$(jq '.userStories | length' "$PRD_FILE")
|
|
143
|
-
if [ "$story_count" -gt 0 ]; then
|
|
144
|
-
existing="
|
|
121
|
+
start_time=$(date +%s)
|
|
145
122
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
123
|
+
set +e
|
|
124
|
+
result=$(claude -p --dangerously-skip-permissions $context_files)
|
|
125
|
+
exit_code=$?
|
|
126
|
+
set -e
|
|
149
127
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if [ -f "$PROMPT_FILE" ]; then
|
|
153
|
-
project_context="
|
|
128
|
+
elapsed=$(( $(date +%s) - start_time ))
|
|
129
|
+
echo "$result" | tail -20
|
|
154
130
|
|
|
155
|
-
|
|
156
|
-
|
|
131
|
+
if [ $exit_code -ne 0 ]; then
|
|
132
|
+
echo "Failed (code ${exit_code}). Restarting..."
|
|
133
|
+
continue
|
|
157
134
|
fi
|
|
158
135
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
Your job:
|
|
163
|
-
1. Understand what they want to build or improve
|
|
164
|
-
2. Ask clarifying questions (target user, constraints, scope)
|
|
165
|
-
3. Break the work into small, implementable stories (5-15 min each)
|
|
166
|
-
4. Each story needs: title, description, and specific acceptance criteria
|
|
167
|
-
5. Stories should be ordered by dependency (build foundations first)
|
|
168
|
-
|
|
169
|
-
Rules for good stories:
|
|
170
|
-
- Each story should be independently committable
|
|
171
|
-
- Last acceptance criterion should always be \"App builds successfully\" or equivalent
|
|
172
|
-
- Acceptance criteria must be verifiable (not vague like \"looks good\")
|
|
173
|
-
- Keep stories small — if it takes more than 15 min, split it
|
|
174
|
-
- Include edge cases and error handling as separate stories
|
|
175
|
-
- Don't forget: tests, error states, loading states, empty states
|
|
176
|
-
|
|
177
|
-
When the user is happy with the plan, output the stories in this EXACT format — one JSON block with all stories:
|
|
178
|
-
|
|
179
|
-
\`\`\`kai-stories
|
|
180
|
-
[
|
|
181
|
-
{
|
|
182
|
-
\"title\": \"Story title here\",
|
|
183
|
-
\"description\": \"What to implement and why\",
|
|
184
|
-
\"acceptanceCriteria\": [\"Criterion 1\", \"Criterion 2\", \"App builds successfully\"]
|
|
185
|
-
}
|
|
186
|
-
]
|
|
187
|
-
\`\`\`
|
|
188
|
-
|
|
189
|
-
The user will say \"looks good\" or \"ship it\" when they want you to output the final stories.
|
|
190
|
-
${existing}${project_context}"
|
|
191
|
-
|
|
192
|
-
# Generate a session ID so we can find the transcript after
|
|
193
|
-
SESSION_ID=$(python3 -c "import uuid; print(uuid.uuid4())" 2>/dev/null || uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid 2>/dev/null)
|
|
194
|
-
|
|
195
|
-
if [ -n "$topic" ]; then
|
|
196
|
-
echo "Planning: $topic"
|
|
197
|
-
echo "Say 'ship it' when you're happy with the stories."
|
|
198
|
-
echo ""
|
|
199
|
-
claude --session-id "$SESSION_ID" --system-prompt "$pm_prompt" "Let's plan: $topic"
|
|
200
|
-
else
|
|
201
|
-
echo "Describe what you want to build. Say 'ship it' when ready."
|
|
202
|
-
echo ""
|
|
203
|
-
claude --session-id "$SESSION_ID" --system-prompt "$pm_prompt"
|
|
136
|
+
if [[ "$result" == *"<promise>COMPLETE</promise>"* ]]; then
|
|
137
|
+
echo "All stories complete after $iteration iterations!"
|
|
138
|
+
exit 0
|
|
204
139
|
fi
|
|
205
140
|
|
|
206
|
-
#
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
project_dir=$(echo "$PROJECT_DIR" | sed 's|/|-|g; s|^-||')
|
|
212
|
-
transcript=""
|
|
213
|
-
for dir in ~/.claude/projects/*/; do
|
|
214
|
-
if [ -f "${dir}${SESSION_ID}.jsonl" ]; then
|
|
215
|
-
transcript="${dir}${SESSION_ID}.jsonl"
|
|
216
|
-
break
|
|
217
|
-
fi
|
|
218
|
-
done
|
|
141
|
+
# Review
|
|
142
|
+
STORY_PASSES=$(jq -r ".userStories[] | select(.id == $NEXT_ID) | .passes" "$PRD_FILE")
|
|
143
|
+
if [ "$STORY_PASSES" = "true" ]; then
|
|
144
|
+
last_commit=$(git log --oneline -1 2>/dev/null || echo "no git")
|
|
145
|
+
ACCEPTANCE=$(jq -r ".userStories[] | select(.id == $NEXT_ID) | .acceptanceCriteria | join(\"\n- \")" "$PRD_FILE")
|
|
219
146
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
147
|
+
set +e
|
|
148
|
+
review=$(claude -p --dangerously-skip-permissions \
|
|
149
|
+
"You are a code reviewer. Story #${NEXT_ID} ('${NEXT}') claims complete.
|
|
150
|
+
Last commit: ${last_commit}
|
|
151
|
+
Acceptance criteria:
|
|
152
|
+
- ${ACCEPTANCE}
|
|
153
|
+
Read git diff HEAD~1 HEAD. Verify EACH criterion. Be skeptical.
|
|
154
|
+
If ANY fails: REVIEW_FAIL: [reason]
|
|
155
|
+
If all pass: REVIEW_PASS")
|
|
156
|
+
set -e
|
|
225
157
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
content = d.get('message', {}).get('content', [])
|
|
237
|
-
if isinstance(content, list):
|
|
238
|
-
for c in content:
|
|
239
|
-
if c.get('type') == 'text':
|
|
240
|
-
text = c['text']
|
|
241
|
-
# Find kai-stories block
|
|
242
|
-
match = re.search(r'\`\`\`kai-stories\s*\n(.*?)\n\`\`\`', text, re.DOTALL)
|
|
243
|
-
if match:
|
|
244
|
-
stories = match.group(1)
|
|
245
|
-
except: pass
|
|
246
|
-
|
|
247
|
-
if stories:
|
|
248
|
-
# Validate it's valid JSON
|
|
249
|
-
parsed = json.loads(stories)
|
|
250
|
-
print(json.dumps(parsed))
|
|
251
|
-
" 2>/dev/null)
|
|
252
|
-
|
|
253
|
-
if [ -n "$stories_json" ] && echo "$stories_json" | jq empty 2>/dev/null; then
|
|
254
|
-
count=$(echo "$stories_json" | jq 'length')
|
|
255
|
-
echo ""
|
|
256
|
-
echo "Found ${count} stories:"
|
|
257
|
-
echo "$stories_json" | jq -r '.[] | " - \(.title)"'
|
|
258
|
-
echo ""
|
|
259
|
-
echo "Add to kai.json? (y/n)"
|
|
260
|
-
read -r confirm
|
|
261
|
-
if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ] || [ "$confirm" = "" ]; then
|
|
262
|
-
_import_stories "$stories_json"
|
|
158
|
+
echo "$review" | tail -5
|
|
159
|
+
|
|
160
|
+
if [[ "$review" == *"REVIEW_FAIL"* ]]; then
|
|
161
|
+
jq --argjson id "$NEXT_ID" '(.userStories[] | select(.id == $id)).passes = false' "$PRD_FILE" > "${PRD_FILE}.tmp" && mv "${PRD_FILE}.tmp" "$PRD_FILE"
|
|
162
|
+
echo "REVIEW FEEDBACK: $review" >> "$PROGRESS_FILE"
|
|
163
|
+
review_failures=$((review_failures + 1))
|
|
164
|
+
[ $review_failures -ge 3 ] && {
|
|
165
|
+
jq --argjson id "$NEXT_ID" '(.userStories[] | select(.id == $id)).passes = true' "$PRD_FILE" > "${PRD_FILE}.tmp" && mv "${PRD_FILE}.tmp" "$PRD_FILE"
|
|
166
|
+
review_failures=0
|
|
167
|
+
}
|
|
263
168
|
else
|
|
264
|
-
|
|
169
|
+
review_failures=0
|
|
265
170
|
fi
|
|
266
|
-
else
|
|
267
|
-
echo "No stories found in session."
|
|
268
|
-
echo "If the PM generated stories, you can copy the JSON and add manually to kai.json."
|
|
269
|
-
fi
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
_import_stories() {
|
|
273
|
-
local stories_json="$1"
|
|
274
|
-
|
|
275
|
-
if ! echo "$stories_json" | jq empty 2>/dev/null; then
|
|
276
|
-
echo "Invalid JSON."
|
|
277
|
-
return 1
|
|
278
171
|
fi
|
|
279
172
|
|
|
280
|
-
|
|
281
|
-
count=$(echo "$stories_json" | jq 'length')
|
|
282
|
-
|
|
283
|
-
echo "$stories_json" | jq -c '.[]' | while IFS= read -r story; do
|
|
284
|
-
max_id=$((max_id + 1))
|
|
285
|
-
title=$(echo "$story" | jq -r '.title')
|
|
286
|
-
desc=$(echo "$story" | jq -r '.description')
|
|
287
|
-
criteria=$(echo "$story" | jq '.acceptanceCriteria')
|
|
288
|
-
|
|
289
|
-
jq --arg title "$title" \
|
|
290
|
-
--arg desc "$desc" \
|
|
291
|
-
--argjson id "$max_id" \
|
|
292
|
-
--argjson criteria "$criteria" \
|
|
293
|
-
'.userStories += [{
|
|
294
|
-
id: $id,
|
|
295
|
-
title: $title,
|
|
296
|
-
description: $desc,
|
|
297
|
-
acceptanceCriteria: $criteria,
|
|
298
|
-
priority: $id,
|
|
299
|
-
passes: false
|
|
300
|
-
}]' "$PRD_FILE" > "${PRD_FILE}.tmp" && mv "${PRD_FILE}.tmp" "$PRD_FILE"
|
|
301
|
-
|
|
302
|
-
echo " #${max_id}: ${title}"
|
|
303
|
-
done
|
|
304
|
-
|
|
173
|
+
echo "Done in $((elapsed / 60))m $((elapsed % 60))s"
|
|
305
174
|
echo ""
|
|
306
|
-
|
|
307
|
-
|
|
175
|
+
done
|
|
176
|
+
LOOPEOF
|
|
177
|
+
chmod +x "$LOOP_SCRIPT"
|
|
308
178
|
|
|
309
|
-
|
|
310
|
-
if [ ! -f "$PRD_FILE" ]; then
|
|
311
|
-
echo "kai.json not found. Run 'kai init' first."
|
|
312
|
-
exit 1
|
|
313
|
-
fi
|
|
179
|
+
# ── Build status summary ──────────────────────────────
|
|
314
180
|
|
|
181
|
+
status=""
|
|
182
|
+
if [ -f "$PRD_FILE" ]; then
|
|
315
183
|
total=$(jq '.userStories | length' "$PRD_FILE")
|
|
316
184
|
done_count=$(jq '[.userStories[] | select(.passes == true)] | length' "$PRD_FILE")
|
|
317
185
|
remaining=$((total - done_count))
|
|
186
|
+
if [ "$total" -gt 0 ]; then
|
|
187
|
+
status="
|
|
318
188
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if [ "$remaining" -gt 0 ]; then
|
|
323
|
-
echo "Remaining:"
|
|
324
|
-
jq -r '.userStories[] | select(.passes == false) | " \(.id): \(.title)"' "$PRD_FILE"
|
|
325
|
-
fi
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
cmd_reset() {
|
|
329
|
-
id="$1"
|
|
330
|
-
if [ -z "$id" ]; then
|
|
331
|
-
echo "Usage: kai reset <story-id>"
|
|
332
|
-
exit 1
|
|
189
|
+
Current progress: ${done_count}/${total} stories complete (${remaining} remaining).
|
|
190
|
+
$(jq -r '.userStories[] | "- [\(if .passes then "DONE" else "TODO" end)] #\(.id): \(.title)"' "$PRD_FILE")"
|
|
333
191
|
fi
|
|
192
|
+
fi
|
|
334
193
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
cmd_go() {
|
|
340
|
-
if [ ! -f "$PRD_FILE" ]; then
|
|
341
|
-
echo "kai.json not found. Run 'kai init' first."
|
|
342
|
-
exit 1
|
|
343
|
-
fi
|
|
194
|
+
project_context=""
|
|
195
|
+
[ -f "$PROMPT_FILE" ] && project_context="
|
|
344
196
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
exit 1
|
|
348
|
-
fi
|
|
349
|
-
|
|
350
|
-
# Collect context files
|
|
351
|
-
context_files="@${PROMPT_FILE} @${PRD_FILE} @${PROGRESS_FILE}"
|
|
352
|
-
if [ -f ".kai/context.txt" ]; then
|
|
353
|
-
while IFS= read -r file; do
|
|
354
|
-
[ -n "$file" ] && [ -f "$file" ] && context_files="$context_files @${file}"
|
|
355
|
-
done < .kai/context.txt
|
|
356
|
-
fi
|
|
357
|
-
|
|
358
|
-
echo "Starting Kai..."
|
|
359
|
-
echo " Stories remaining: $(jq '[.userStories[] | select(.passes == false)] | length' "$PRD_FILE")"
|
|
360
|
-
echo " Ctrl+C to stop"
|
|
361
|
-
echo ""
|
|
362
|
-
|
|
363
|
-
iteration=0
|
|
364
|
-
review_failures=0
|
|
365
|
-
|
|
366
|
-
while :; do
|
|
367
|
-
iteration=$((iteration + 1))
|
|
368
|
-
REMAINING=$(jq '[.userStories[] | select(.passes == false)] | length' "$PRD_FILE")
|
|
369
|
-
|
|
370
|
-
if [ "$REMAINING" -eq 0 ]; then
|
|
371
|
-
echo "All stories complete!"
|
|
372
|
-
break
|
|
373
|
-
fi
|
|
374
|
-
|
|
375
|
-
NEXT_ID=$(jq -r '[.userStories[] | select(.passes == false)] | sort_by(.priority) | .[0].id' "$PRD_FILE")
|
|
376
|
-
NEXT=$(jq -r '[.userStories[] | select(.passes == false)] | sort_by(.priority) | .[0].title' "$PRD_FILE")
|
|
377
|
-
|
|
378
|
-
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
379
|
-
echo "#${iteration} — $(date)"
|
|
380
|
-
echo " Remaining: $REMAINING | Next: $NEXT"
|
|
381
|
-
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
382
|
-
|
|
383
|
-
start_time=$(date +%s)
|
|
384
|
-
|
|
385
|
-
# ── Implement ──────────────────────────────────
|
|
386
|
-
set +e
|
|
387
|
-
result=$(claude -p \
|
|
388
|
-
--dangerously-skip-permissions \
|
|
389
|
-
$context_files)
|
|
390
|
-
exit_code=$?
|
|
391
|
-
set -e
|
|
392
|
-
|
|
393
|
-
end_time=$(date +%s)
|
|
394
|
-
elapsed=$(( end_time - start_time ))
|
|
395
|
-
minutes=$(( elapsed / 60 ))
|
|
396
|
-
seconds=$(( elapsed % 60 ))
|
|
197
|
+
Project context (.kai/PROMPT.md):
|
|
198
|
+
$(cat "$PROMPT_FILE")"
|
|
397
199
|
|
|
398
|
-
|
|
399
|
-
|
|
200
|
+
progress=""
|
|
201
|
+
[ -f "$PROGRESS_FILE" ] && [ "$(wc -l < "$PROGRESS_FILE")" -gt 2 ] && progress="
|
|
400
202
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
continue
|
|
404
|
-
fi
|
|
203
|
+
Recent progress (kai-progress.txt):
|
|
204
|
+
$(tail -30 "$PROGRESS_FILE")"
|
|
405
205
|
|
|
406
|
-
|
|
407
|
-
echo "All stories complete after $iteration iterations!"
|
|
408
|
-
exit 0
|
|
409
|
-
fi
|
|
206
|
+
# ── Launch the PM ─────────────────────────────────────
|
|
410
207
|
|
|
411
|
-
|
|
412
|
-
STORY_PASSES=$(jq -r ".userStories[] | select(.id == $NEXT_ID) | .passes" "$PRD_FILE")
|
|
413
|
-
|
|
414
|
-
if [ "$STORY_PASSES" = "true" ]; then
|
|
415
|
-
last_commit=$(git log --oneline -1 2>/dev/null || echo "no git")
|
|
416
|
-
last_diff_stat=$(git diff --stat HEAD~1 HEAD 2>/dev/null | tail -1)
|
|
417
|
-
|
|
418
|
-
# Sanity: real code changes?
|
|
419
|
-
if [ -z "$last_diff_stat" ] || [[ "$last_diff_stat" == *"0 insertions"*"0 deletions"* ]]; then
|
|
420
|
-
echo "REVIEW FAIL: No code changes in commit!"
|
|
421
|
-
jq --argjson id "$NEXT_ID" '(.userStories[] | select(.id == $id)).passes = false' "$PRD_FILE" > "${PRD_FILE}.tmp" && mv "${PRD_FILE}.tmp" "$PRD_FILE"
|
|
422
|
-
review_failures=$((review_failures + 1))
|
|
423
|
-
if [ $review_failures -ge 3 ]; then
|
|
424
|
-
echo "3 failures on story #${NEXT_ID}. Skipping."
|
|
425
|
-
jq --argjson id "$NEXT_ID" '(.userStories[] | select(.id == $id)).passes = true | (.userStories[] | select(.id == $id)).concerns = "Skipped after 3 failures"' "$PRD_FILE" > "${PRD_FILE}.tmp" && mv "${PRD_FILE}.tmp" "$PRD_FILE"
|
|
426
|
-
review_failures=0
|
|
427
|
-
fi
|
|
428
|
-
continue
|
|
429
|
-
fi
|
|
430
|
-
|
|
431
|
-
# Dispatch reviewer
|
|
432
|
-
echo "Reviewing story #${NEXT_ID}..."
|
|
433
|
-
ACCEPTANCE=$(jq -r ".userStories[] | select(.id == $NEXT_ID) | .acceptanceCriteria | join(\"\n- \")" "$PRD_FILE")
|
|
434
|
-
|
|
435
|
-
set +e
|
|
436
|
-
review_result=$(claude -p \
|
|
437
|
-
--dangerously-skip-permissions \
|
|
438
|
-
"You are a code reviewer. The implementer claims story #${NEXT_ID} ('${NEXT}') is complete.
|
|
439
|
-
|
|
440
|
-
Last commit: ${last_commit}
|
|
441
|
-
Changes: ${last_diff_stat}
|
|
208
|
+
exec claude --system-prompt "You are Kai — an AI product manager and tech lead. You work directly with the developer to plan and ship software.
|
|
442
209
|
|
|
443
|
-
|
|
444
|
-
- ${ACCEPTANCE}
|
|
210
|
+
## What you can do
|
|
445
211
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
5. If all criteria met: REVIEW_PASS
|
|
452
|
-
|
|
453
|
-
Do NOT trust the implementer's self-report. Verify independently.")
|
|
454
|
-
review_exit=$?
|
|
455
|
-
set -e
|
|
456
|
-
|
|
457
|
-
echo "$review_result" | tail -10
|
|
458
|
-
|
|
459
|
-
if [[ "$review_result" == *"REVIEW_FAIL"* ]]; then
|
|
460
|
-
echo "Review failed!"
|
|
461
|
-
jq --argjson id "$NEXT_ID" '(.userStories[] | select(.id == $id)).passes = false' "$PRD_FILE" > "${PRD_FILE}.tmp" && mv "${PRD_FILE}.tmp" "$PRD_FILE"
|
|
462
|
-
echo "" >> "$PROGRESS_FILE"
|
|
463
|
-
echo "## REVIEW FEEDBACK for Story $NEXT_ID" >> "$PROGRESS_FILE"
|
|
464
|
-
echo "$review_result" | grep -i "REVIEW_FAIL" >> "$PROGRESS_FILE" 2>/dev/null
|
|
465
|
-
review_failures=$((review_failures + 1))
|
|
466
|
-
if [ $review_failures -ge 3 ]; then
|
|
467
|
-
echo "3 review failures on story #${NEXT_ID}. Skipping."
|
|
468
|
-
jq --argjson id "$NEXT_ID" '(.userStories[] | select(.id == $id)).passes = true | (.userStories[] | select(.id == $id)).concerns = "Passed after 3 review attempts"' "$PRD_FILE" > "${PRD_FILE}.tmp" && mv "${PRD_FILE}.tmp" "$PRD_FILE"
|
|
469
|
-
review_failures=0
|
|
470
|
-
fi
|
|
471
|
-
else
|
|
472
|
-
echo "Review passed."
|
|
473
|
-
review_failures=0
|
|
474
|
-
fi
|
|
475
|
-
fi
|
|
212
|
+
1. **Brainstorm** — Help the developer think through ideas, make decisions, define scope
|
|
213
|
+
2. **Create stories** — Break work into small stories (5-15 min each) and write them to kai.json
|
|
214
|
+
3. **Run the dev loop** — Execute \`.kai/loop.sh\` to have an AI developer implement all stories autonomously
|
|
215
|
+
4. **Check progress** — Read kai.json and kai-progress.txt to report status
|
|
216
|
+
5. **Adjust the plan** — Edit stories, reprioritize, add new ones, reset failed ones
|
|
476
217
|
|
|
477
|
-
|
|
478
|
-
last_commit=$(git log --oneline -1 2>/dev/null || echo "")
|
|
479
|
-
[ -n "$last_commit" ] && echo " Last commit: ${last_commit}"
|
|
480
|
-
echo ""
|
|
481
|
-
done
|
|
218
|
+
## How stories work
|
|
482
219
|
|
|
483
|
-
|
|
484
|
-
|
|
220
|
+
Stories live in kai.json. Each has: id, title, description, acceptanceCriteria, priority, passes (true/false).
|
|
221
|
+
To add stories, edit kai.json directly using the Edit or Write tool.
|
|
222
|
+
To run implementation, execute: \`nohup .kai/loop.sh > .kai/loop.log 2>&1 &\` then monitor with \`tail -f .kai/loop.log\`
|
|
485
223
|
|
|
486
|
-
|
|
224
|
+
## Rules for good stories
|
|
225
|
+
- Each story: independently committable, 5-15 min of work
|
|
226
|
+
- Acceptance criteria must be objectively verifiable
|
|
227
|
+
- Last criterion should be a build/test command passing
|
|
228
|
+
- Order by dependency (foundations first)
|
|
229
|
+
- Include error handling, edge cases as separate stories
|
|
487
230
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
esac
|
|
231
|
+
## Your personality
|
|
232
|
+
- You're a hands-on PM who understands code
|
|
233
|
+
- Be direct and concise
|
|
234
|
+
- Make decisions, don't just list options
|
|
235
|
+
- When the developer says \"ship it\" or \"go\" or \"do it\", start the loop
|
|
236
|
+
- Don't ask unnecessary questions — use good defaults
|
|
237
|
+
${status}${project_context}${progress}" "$@"
|