@kraftapps-ai/kai 1.8.7 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/kai +29 -56
  2. 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
@@ -352,51 +346,37 @@ while :; do
352
346
 
353
347
  start_time=$(date +%s)
354
348
 
355
- # Run worker in background with heartbeat monitor
349
+ # jq filter for streaming claude output — shows tool calls, text, and results
350
+ STREAM_FILTER='
351
+ if .type == "assistant" then
352
+ ([.message.content[] |
353
+ if .type == "tool_use" then "⚡ " + .name + ": " + (.input.command // .input.file_path // .input.pattern // "" | tostring | .[0:150])
354
+ elif .type == "text" then .text
355
+ else empty end
356
+ ] | join("\n"))
357
+ elif .type == "user" and .tool_use_result then
358
+ " → " + (.tool_use_result.stdout // "" | .[0:200] | gsub("\n"; " "))
359
+ elif .type == "result" then
360
+ "✅ Done (" + (.duration_ms / 1000 | tostring | .[0:5]) + "s, $" + (.total_cost_usd | tostring | .[0:6]) + ")"
361
+ else empty end'
362
+
363
+ # Stream worker output live
356
364
  set +e
357
- touch .kai/worker-heartbeat
358
- claude -p --dangerously-skip-permissions $LOOP_MCP_ARGS --system-prompt "$WORKER_PROMPT" $context_files > .kai/worker-output.tmp 2>&1 &
359
- worker_pid=$!
360
-
361
- echo " Worker started (PID $worker_pid)"
362
- while kill -0 $worker_pid 2>/dev/null; do
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)
365
+ claude -p --output-format stream-json --verbose --dangerously-skip-permissions $LOOP_MCP_ARGS \
366
+ --system-prompt "$WORKER_PROMPT" $context_files 2>&1 | \
367
+ tee .kai/worker-stream.tmp | \
368
+ jq --unbuffered -r "$STREAM_FILTER" 2>/dev/null
369
+ exit_code=${PIPESTATUS[0]}
370
+ result=$(jq -r 'select(.type == "result") | .result // empty' .kai/worker-stream.tmp 2>/dev/null)
387
371
  set -e
388
372
 
389
373
  elapsed=$(( $(date +%s) - start_time ))
390
374
 
391
375
  if [ $exit_code -ne 0 ]; then
392
- echo " Worker failed (code ${exit_code}). Last output:"
393
- echo "$result" | tail -10
394
- echo " Restarting..."
376
+ echo " Worker failed (code ${exit_code}). Restarting..."
395
377
  continue
396
378
  fi
397
379
 
398
- echo "$result" | tail -20
399
-
400
380
  if [[ "$result" == *"<promise>COMPLETE</promise>"* ]]; then
401
381
  echo "All stories complete after $iteration iterations!"
402
382
  exit 0
@@ -405,7 +385,8 @@ while :; do
405
385
  # Review
406
386
  STORY_PASSES=$(jq -r --arg id "$NEXT_ID" '.userStories[] | select(.id == $id) | .passes' "$PRD_FILE")
407
387
  if [ "$STORY_PASSES" = "true" ]; then
408
- echo " Reviewing story #${NEXT_ID}..."
388
+ echo ""
389
+ echo " 📋 Reviewing story #${NEXT_ID}..."
409
390
  last_commit=$(git log --oneline -1 2>/dev/null || echo "no git")
410
391
  ACCEPTANCE=$(jq -r --arg id "$NEXT_ID" '.userStories[] | select(.id == $id) | .acceptanceCriteria | join("\n- ")' "$PRD_FILE")
411
392
 
@@ -420,21 +401,13 @@ If ANY fails: REVIEW_FAIL: [reason]
420
401
  If all pass: REVIEW_PASS"
421
402
 
422
403
  set +e
423
- claude -p --dangerously-skip-permissions $LOOP_MCP_ARGS "$REVIEW_PROMPT" > .kai/review-output.tmp 2>&1 &
424
- review_pid=$!
425
-
426
- while kill -0 $review_pid 2>/dev/null; do
427
- sleep 15
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)
404
+ claude -p --output-format stream-json --verbose --dangerously-skip-permissions $LOOP_MCP_ARGS \
405
+ "$REVIEW_PROMPT" 2>&1 | \
406
+ tee .kai/review-stream.tmp | \
407
+ jq --unbuffered -r "$STREAM_FILTER" 2>/dev/null
408
+ review=$(jq -r 'select(.type == "result") | .result // empty' .kai/review-stream.tmp 2>/dev/null)
434
409
  set -e
435
410
 
436
- echo "$review" | tail -5
437
-
438
411
  if [[ "$review" == *"REVIEW_FAIL"* ]]; then
439
412
  jq --arg id "$NEXT_ID" '(.userStories[] | select(.id == $id)).passes = false' "$PRD_FILE" > "${PRD_FILE}.tmp" && mv "${PRD_FILE}.tmp" "$PRD_FILE"
440
413
  echo "REVIEW FEEDBACK: $review" >> "$PROGRESS_FILE"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kraftapps-ai/kai",
3
- "version": "1.8.7",
3
+ "version": "1.9.0",
4
4
  "description": "Autonomous AI developer loop for Claude Code",
5
5
  "bin": {
6
6
  "kai": "kai"