@kraftapps-ai/kai 1.8.6 → 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 +46 -59
  2. package/package.json +1 -1
package/kai CHANGED
@@ -61,11 +61,25 @@ MEMORY_FILE=".kai/memory.md"
61
61
  LOOP_SCRIPT=".kai/loop.sh"
62
62
 
63
63
 
64
- # ── Ensure .kai/ is git-ignored ───────────────────────
64
+ # ── Ensure .kai/ and Playwright artifacts are git-ignored
65
65
 
66
66
  mkdir -p .kai
67
- if [ ! -f ".kai/.gitignore" ]; then
68
- echo "*" > .kai/.gitignore
67
+
68
+ # Add to project .gitignore (not just .kai/.gitignore) to prevent tracking
69
+ if [ -f ".gitignore" ]; then
70
+ for pattern in ".kai/" ".playwright-mcp/"; do
71
+ grep -qxF "$pattern" .gitignore 2>/dev/null || echo "$pattern" >> .gitignore
72
+ done
73
+ else
74
+ printf '%s\n' ".kai/" ".playwright-mcp/" > .gitignore
75
+ fi
76
+
77
+ # Untrack .kai/ files if they were committed before the gitignore existed
78
+ if git ls-files --cached .kai/ 2>/dev/null | grep -q .; then
79
+ git rm -r --cached .kai/ > /dev/null 2>&1 || true
80
+ fi
81
+ if git ls-files --cached .playwright-mcp/ 2>/dev/null | grep -q .; then
82
+ git rm -r --cached .playwright-mcp/ > /dev/null 2>&1 || true
69
83
  fi
70
84
 
71
85
  # ── Auto-init if needed ───────────────────────────────
@@ -230,12 +244,6 @@ LOOP_MCP_ARGS="--mcp-config .kai/loop-mcp.json"
230
244
  # Cap Node.js memory to prevent test runners from consuming all RAM
231
245
  export NODE_OPTIONS="--max-old-space-size=2048"
232
246
 
233
- # Stream output live when running in a tmux pane
234
- KAI_STREAM=false
235
- if [ -n "${TMUX:-}" ]; then
236
- KAI_STREAM=true
237
- fi
238
-
239
247
  WORKER_PROMPT='You are an autonomous AI developer specializing in Shopify app and theme development.
240
248
 
241
249
  ## Your Shopify expertise
@@ -338,51 +346,37 @@ while :; do
338
346
 
339
347
  start_time=$(date +%s)
340
348
 
341
- # 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
342
364
  set +e
343
- touch .kai/worker-heartbeat
344
- claude -p --dangerously-skip-permissions $LOOP_MCP_ARGS --system-prompt "$WORKER_PROMPT" $context_files > .kai/worker-output.tmp 2>&1 &
345
- worker_pid=$!
346
-
347
- echo " Worker started (PID $worker_pid)"
348
- while kill -0 $worker_pid 2>/dev/null; do
349
- sleep 30
350
- elapsed_now=$(( $(date +%s) - start_time ))
351
- mins=$((elapsed_now / 60))
352
- secs=$((elapsed_now % 60))
353
-
354
- # Detect what's running
355
- activity=""
356
- pgrep -f "vitest" > /dev/null 2>&1 && activity="running tests"
357
- pgrep -f "esbuild" > /dev/null 2>&1 && activity="${activity:+$activity + }building"
358
- pgrep -f "npm run build" > /dev/null 2>&1 && activity="${activity:+$activity + }building"
359
- [ -z "$activity" ] && activity="thinking"
360
-
361
- # Show recently changed files
362
- changed=$(find . \( -name '*.ts' -o -name '*.tsx' -o -name '*.php' -o -name '*.less' -o -name '*.css' -o -name '*.json' \) \
363
- -not -path './node_modules/*' -not -path './.kai/*' -not -path './vendor/*' \
364
- -newer .kai/worker-heartbeat -printf '%f\n' 2>/dev/null | sort -u | head -5 | tr '\n' ', ')
365
-
366
- echo " [${mins}m${secs}s] ${activity}${changed:+ | files: ${changed%,}}"
367
- touch .kai/worker-heartbeat
368
- done
369
-
370
- wait $worker_pid
371
- exit_code=$?
372
- 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)
373
371
  set -e
374
372
 
375
373
  elapsed=$(( $(date +%s) - start_time ))
376
374
 
377
375
  if [ $exit_code -ne 0 ]; then
378
- echo " Worker failed (code ${exit_code}). Last output:"
379
- echo "$result" | tail -10
380
- echo " Restarting..."
376
+ echo " Worker failed (code ${exit_code}). Restarting..."
381
377
  continue
382
378
  fi
383
379
 
384
- echo "$result" | tail -20
385
-
386
380
  if [[ "$result" == *"<promise>COMPLETE</promise>"* ]]; then
387
381
  echo "All stories complete after $iteration iterations!"
388
382
  exit 0
@@ -391,7 +385,8 @@ while :; do
391
385
  # Review
392
386
  STORY_PASSES=$(jq -r --arg id "$NEXT_ID" '.userStories[] | select(.id == $id) | .passes' "$PRD_FILE")
393
387
  if [ "$STORY_PASSES" = "true" ]; then
394
- echo " Reviewing story #${NEXT_ID}..."
388
+ echo ""
389
+ echo " 📋 Reviewing story #${NEXT_ID}..."
395
390
  last_commit=$(git log --oneline -1 2>/dev/null || echo "no git")
396
391
  ACCEPTANCE=$(jq -r --arg id "$NEXT_ID" '.userStories[] | select(.id == $id) | .acceptanceCriteria | join("\n- ")' "$PRD_FILE")
397
392
 
@@ -406,21 +401,13 @@ If ANY fails: REVIEW_FAIL: [reason]
406
401
  If all pass: REVIEW_PASS"
407
402
 
408
403
  set +e
409
- claude -p --dangerously-skip-permissions $LOOP_MCP_ARGS "$REVIEW_PROMPT" > .kai/review-output.tmp 2>&1 &
410
- review_pid=$!
411
-
412
- while kill -0 $review_pid 2>/dev/null; do
413
- sleep 15
414
- elapsed_now=$(( $(date +%s) - start_time ))
415
- echo " [${elapsed_now}s] reviewing..."
416
- done
417
-
418
- wait $review_pid
419
- 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)
420
409
  set -e
421
410
 
422
- echo "$review" | tail -5
423
-
424
411
  if [[ "$review" == *"REVIEW_FAIL"* ]]; then
425
412
  jq --arg id "$NEXT_ID" '(.userStories[] | select(.id == $id)).passes = false' "$PRD_FILE" > "${PRD_FILE}.tmp" && mv "${PRD_FILE}.tmp" "$PRD_FILE"
426
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.6",
3
+ "version": "1.9.0",
4
4
  "description": "Autonomous AI developer loop for Claude Code",
5
5
  "bin": {
6
6
  "kai": "kai"