@kraftapps-ai/kai 1.8.7 → 1.9.1
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/kai +30 -56
- package/package.json +1 -1
package/kai
CHANGED
|
@@ -244,12 +244,6 @@ LOOP_MCP_ARGS="--mcp-config .kai/loop-mcp.json"
|
|
|
244
244
|
# Cap Node.js memory to prevent test runners from consuming all RAM
|
|
245
245
|
export NODE_OPTIONS="--max-old-space-size=2048"
|
|
246
246
|
|
|
247
|
-
# Stream output live when running in a tmux pane
|
|
248
|
-
KAI_STREAM=false
|
|
249
|
-
if [ -n "${TMUX:-}" ]; then
|
|
250
|
-
KAI_STREAM=true
|
|
251
|
-
fi
|
|
252
|
-
|
|
253
247
|
WORKER_PROMPT='You are an autonomous AI developer specializing in Shopify app and theme development.
|
|
254
248
|
|
|
255
249
|
## Your Shopify expertise
|
|
@@ -297,6 +291,7 @@ You are an expert Shopify developer. You have access to the Shopify Dev MCP tool
|
|
|
297
291
|
|
|
298
292
|
## Rules
|
|
299
293
|
- ONE STORY PER LOOP.
|
|
294
|
+
- **NEVER switch branches.** Always commit to the current branch. Do NOT run `git checkout`, `git switch`, or `git branch`. The PM manages branches — you just commit your work.
|
|
300
295
|
- **TDD is mandatory.** Tests first, then implementation. No exceptions.
|
|
301
296
|
- No placeholders. Fully implement or report BLOCKED.
|
|
302
297
|
- Do not break existing functionality. Run the full test suite ONCE at the end as a final check.
|
|
@@ -352,51 +347,37 @@ while :; do
|
|
|
352
347
|
|
|
353
348
|
start_time=$(date +%s)
|
|
354
349
|
|
|
355
|
-
#
|
|
350
|
+
# jq filter for streaming claude output — shows tool calls, text, and results
|
|
351
|
+
STREAM_FILTER='
|
|
352
|
+
if .type == "assistant" then
|
|
353
|
+
([.message.content[] |
|
|
354
|
+
if .type == "tool_use" then "⚡ " + .name + ": " + (.input.command // .input.file_path // .input.pattern // "" | tostring | .[0:150])
|
|
355
|
+
elif .type == "text" then .text
|
|
356
|
+
else empty end
|
|
357
|
+
] | join("\n"))
|
|
358
|
+
elif .type == "user" and .tool_use_result then
|
|
359
|
+
" → " + (.tool_use_result.stdout // "" | .[0:200] | gsub("\n"; " "))
|
|
360
|
+
elif .type == "result" then
|
|
361
|
+
"✅ Done (" + (.duration_ms / 1000 | tostring | .[0:5]) + "s, $" + (.total_cost_usd | tostring | .[0:6]) + ")"
|
|
362
|
+
else empty end'
|
|
363
|
+
|
|
364
|
+
# Stream worker output live
|
|
356
365
|
set +e
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
sleep 30
|
|
364
|
-
elapsed_now=$(( $(date +%s) - start_time ))
|
|
365
|
-
mins=$((elapsed_now / 60))
|
|
366
|
-
secs=$((elapsed_now % 60))
|
|
367
|
-
|
|
368
|
-
# Detect what's running
|
|
369
|
-
activity=""
|
|
370
|
-
pgrep -f "vitest" > /dev/null 2>&1 && activity="running tests"
|
|
371
|
-
pgrep -f "esbuild" > /dev/null 2>&1 && activity="${activity:+$activity + }building"
|
|
372
|
-
pgrep -f "npm run build" > /dev/null 2>&1 && activity="${activity:+$activity + }building"
|
|
373
|
-
[ -z "$activity" ] && activity="thinking"
|
|
374
|
-
|
|
375
|
-
# Show recently changed files
|
|
376
|
-
changed=$(find . \( -name '*.ts' -o -name '*.tsx' -o -name '*.php' -o -name '*.less' -o -name '*.css' -o -name '*.json' \) \
|
|
377
|
-
-not -path './node_modules/*' -not -path './.kai/*' -not -path './vendor/*' \
|
|
378
|
-
-newer .kai/worker-heartbeat -printf '%f\n' 2>/dev/null | sort -u | head -5 | tr '\n' ', ')
|
|
379
|
-
|
|
380
|
-
echo " [${mins}m${secs}s] ${activity}${changed:+ | files: ${changed%,}}"
|
|
381
|
-
touch .kai/worker-heartbeat
|
|
382
|
-
done
|
|
383
|
-
|
|
384
|
-
wait $worker_pid
|
|
385
|
-
exit_code=$?
|
|
386
|
-
result=$(cat .kai/worker-output.tmp)
|
|
366
|
+
claude -p --output-format stream-json --verbose --dangerously-skip-permissions $LOOP_MCP_ARGS \
|
|
367
|
+
--system-prompt "$WORKER_PROMPT" $context_files 2>&1 | \
|
|
368
|
+
tee .kai/worker-stream.tmp | \
|
|
369
|
+
jq --unbuffered -r "$STREAM_FILTER" 2>/dev/null
|
|
370
|
+
exit_code=${PIPESTATUS[0]}
|
|
371
|
+
result=$(jq -r 'select(.type == "result") | .result // empty' .kai/worker-stream.tmp 2>/dev/null)
|
|
387
372
|
set -e
|
|
388
373
|
|
|
389
374
|
elapsed=$(( $(date +%s) - start_time ))
|
|
390
375
|
|
|
391
376
|
if [ $exit_code -ne 0 ]; then
|
|
392
|
-
echo " Worker failed (code ${exit_code}).
|
|
393
|
-
echo "$result" | tail -10
|
|
394
|
-
echo " Restarting..."
|
|
377
|
+
echo " Worker failed (code ${exit_code}). Restarting..."
|
|
395
378
|
continue
|
|
396
379
|
fi
|
|
397
380
|
|
|
398
|
-
echo "$result" | tail -20
|
|
399
|
-
|
|
400
381
|
if [[ "$result" == *"<promise>COMPLETE</promise>"* ]]; then
|
|
401
382
|
echo "All stories complete after $iteration iterations!"
|
|
402
383
|
exit 0
|
|
@@ -405,7 +386,8 @@ while :; do
|
|
|
405
386
|
# Review
|
|
406
387
|
STORY_PASSES=$(jq -r --arg id "$NEXT_ID" '.userStories[] | select(.id == $id) | .passes' "$PRD_FILE")
|
|
407
388
|
if [ "$STORY_PASSES" = "true" ]; then
|
|
408
|
-
echo "
|
|
389
|
+
echo ""
|
|
390
|
+
echo " 📋 Reviewing story #${NEXT_ID}..."
|
|
409
391
|
last_commit=$(git log --oneline -1 2>/dev/null || echo "no git")
|
|
410
392
|
ACCEPTANCE=$(jq -r --arg id "$NEXT_ID" '.userStories[] | select(.id == $id) | .acceptanceCriteria | join("\n- ")' "$PRD_FILE")
|
|
411
393
|
|
|
@@ -420,21 +402,13 @@ If ANY fails: REVIEW_FAIL: [reason]
|
|
|
420
402
|
If all pass: REVIEW_PASS"
|
|
421
403
|
|
|
422
404
|
set +e
|
|
423
|
-
claude -p --dangerously-skip-permissions $LOOP_MCP_ARGS
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
elapsed_now=$(( $(date +%s) - start_time ))
|
|
429
|
-
echo " [${elapsed_now}s] reviewing..."
|
|
430
|
-
done
|
|
431
|
-
|
|
432
|
-
wait $review_pid
|
|
433
|
-
review=$(cat .kai/review-output.tmp)
|
|
405
|
+
claude -p --output-format stream-json --verbose --dangerously-skip-permissions $LOOP_MCP_ARGS \
|
|
406
|
+
"$REVIEW_PROMPT" 2>&1 | \
|
|
407
|
+
tee .kai/review-stream.tmp | \
|
|
408
|
+
jq --unbuffered -r "$STREAM_FILTER" 2>/dev/null
|
|
409
|
+
review=$(jq -r 'select(.type == "result") | .result // empty' .kai/review-stream.tmp 2>/dev/null)
|
|
434
410
|
set -e
|
|
435
411
|
|
|
436
|
-
echo "$review" | tail -5
|
|
437
|
-
|
|
438
412
|
if [[ "$review" == *"REVIEW_FAIL"* ]]; then
|
|
439
413
|
jq --arg id "$NEXT_ID" '(.userStories[] | select(.id == $id)).passes = false' "$PRD_FILE" > "${PRD_FILE}.tmp" && mv "${PRD_FILE}.tmp" "$PRD_FILE"
|
|
440
414
|
echo "REVIEW FEEDBACK: $review" >> "$PROGRESS_FILE"
|