agentic-loop 3.22.1 → 3.26.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/.claude/commands/color.md +74 -0
- package/.claude/commands/tab-rename.md +53 -0
- package/.claude/commands/tour.md +2 -2
- package/.claude/commands/vibe-help.md +1 -1
- package/.claude/commands/vibe-list.md +7 -7
- package/.claude/skills/color/SKILL.md +74 -0
- package/.claude/skills/my-dna/SKILL.md +3 -1
- package/.claude/skills/prd/SKILL.md +169 -22
- package/.claude/skills/prd-check/SKILL.md +67 -10
- package/.claude/skills/tour/SKILL.md +2 -2
- package/.claude/skills/vibe-help/SKILL.md +1 -1
- package/.claude/skills/vibe-list/SKILL.md +8 -8
- package/README.md +39 -30
- package/package.json +1 -1
- package/ralph/hooks/install.sh +47 -63
- package/ralph/init.sh +6 -6
- package/ralph/loop.sh +127 -3
- package/ralph/prd-check.sh +36 -5
- package/ralph/prd.sh +1 -1
- package/ralph/setup/feature-tour.sh +1 -1
- package/ralph/setup/tutorial.sh +3 -3
- package/ralph/setup.sh +152 -18
- package/ralph/utils.sh +48 -0
- package/templates/examples/CLAUDE-fullstack.md +3 -3
- package/templates/signs.json +7 -0
- package/.claude/commands/idea.md +0 -216
- package/.claude/skills/idea/SKILL.md +0 -272
package/ralph/prd-check.sh
CHANGED
|
@@ -119,7 +119,7 @@ validate_prd() {
|
|
|
119
119
|
print_error "prd.json is not valid JSON."
|
|
120
120
|
echo ""
|
|
121
121
|
echo "Fix it manually or regenerate with:"
|
|
122
|
-
echo " /
|
|
122
|
+
echo " /prd 'your feature'"
|
|
123
123
|
echo ""
|
|
124
124
|
return 1
|
|
125
125
|
fi
|
|
@@ -131,7 +131,7 @@ validate_prd() {
|
|
|
131
131
|
print_error "prd.json is missing .feature.name"
|
|
132
132
|
echo ""
|
|
133
133
|
echo "Add a feature name to your PRD or regenerate with:"
|
|
134
|
-
echo " /
|
|
134
|
+
echo " /prd 'your feature'"
|
|
135
135
|
echo ""
|
|
136
136
|
return 1
|
|
137
137
|
fi
|
|
@@ -140,7 +140,7 @@ validate_prd() {
|
|
|
140
140
|
if ! jq -e '.stories' "$prd_file" >/dev/null 2>&1; then
|
|
141
141
|
print_error "prd.json is missing 'stories' array."
|
|
142
142
|
echo ""
|
|
143
|
-
echo "Regenerate with: /
|
|
143
|
+
echo "Regenerate with: /prd 'your feature'"
|
|
144
144
|
echo ""
|
|
145
145
|
return 1
|
|
146
146
|
fi
|
|
@@ -151,7 +151,7 @@ validate_prd() {
|
|
|
151
151
|
if [[ "$story_count" == "0" ]]; then
|
|
152
152
|
print_error "prd.json has no stories."
|
|
153
153
|
echo ""
|
|
154
|
-
echo "Regenerate with: /
|
|
154
|
+
echo "Regenerate with: /prd 'your feature'"
|
|
155
155
|
echo ""
|
|
156
156
|
return 1
|
|
157
157
|
fi
|
|
@@ -163,7 +163,7 @@ validate_prd() {
|
|
|
163
163
|
print_error "Some stories are missing required fields (id, title):"
|
|
164
164
|
echo "$invalid_stories" | head -5
|
|
165
165
|
echo ""
|
|
166
|
-
echo "Fix the PRD or regenerate with: /
|
|
166
|
+
echo "Fix the PRD or regenerate with: /prd 'your feature'"
|
|
167
167
|
echo ""
|
|
168
168
|
return 1
|
|
169
169
|
fi
|
|
@@ -405,6 +405,20 @@ _check_story_issues() {
|
|
|
405
405
|
fi
|
|
406
406
|
fi
|
|
407
407
|
|
|
408
|
+
# Import-check anti-pattern: python -c "from X import Y" or hasattr()
|
|
409
|
+
if [[ -n "$test_steps" ]] && echo "$test_steps" | grep -qE '(python[3]? -c .*(from |import |hasattr))'; then
|
|
410
|
+
echo "testSteps use import-checks (python -c 'from/import/hasattr') — replace with real behavioral tests"
|
|
411
|
+
fi
|
|
412
|
+
|
|
413
|
+
# Frontend stories must include Playwright MCP visual verification guidance in notes
|
|
414
|
+
if [[ "$story_type" == "frontend" ]]; then
|
|
415
|
+
local story_notes
|
|
416
|
+
story_notes=$(jq -r --arg id "$story_id" '.stories[] | select(.id==$id) | .notes // ""' "$prd_file")
|
|
417
|
+
if ! echo "$story_notes" | grep -qiE "(playwright.*mcp|mcp.*playwright|visual.*verif|screenshot|navigate.*screenshot)"; then
|
|
418
|
+
echo "frontend notes should include Playwright MCP visual verification guidance"
|
|
419
|
+
fi
|
|
420
|
+
fi
|
|
421
|
+
|
|
408
422
|
# All testSteps are server-dependent
|
|
409
423
|
if [[ -n "$test_steps" ]]; then
|
|
410
424
|
local has_offline=false has_server=false
|
|
@@ -443,6 +457,8 @@ _validate_and_fix_stories() {
|
|
|
443
457
|
local cnt_naming_convention=0 cnt_bare_pytest=0 cnt_bare_python=0
|
|
444
458
|
local cnt_server_only=0
|
|
445
459
|
local cnt_custom=0
|
|
460
|
+
local cnt_import_check=0
|
|
461
|
+
local cnt_playwright_notes=0
|
|
446
462
|
|
|
447
463
|
echo " Checking test coverage..."
|
|
448
464
|
|
|
@@ -508,6 +524,8 @@ _validate_and_fix_stories() {
|
|
|
508
524
|
"missing pagination criteria") cnt_list_pagination=$((cnt_list_pagination + 1)) ;;
|
|
509
525
|
"API consumer needs camelCase transformation note") cnt_naming_convention=$((cnt_naming_convention + 1)) ;;
|
|
510
526
|
"all testSteps need a live server"*) cnt_server_only=$((cnt_server_only + 1)) ;;
|
|
527
|
+
"testSteps use import-checks"*) cnt_import_check=$((cnt_import_check + 1)) ;;
|
|
528
|
+
"frontend notes should include Playwright"*) cnt_playwright_notes=$((cnt_playwright_notes + 1)) ;;
|
|
511
529
|
esac
|
|
512
530
|
done <<< "$shared_issues"
|
|
513
531
|
|
|
@@ -539,6 +557,17 @@ _validate_and_fix_stories() {
|
|
|
539
557
|
fi
|
|
540
558
|
done <<< "$story_ids"
|
|
541
559
|
|
|
560
|
+
# Global check: if any frontend stories exist, at least one story should have E2E tests
|
|
561
|
+
local has_frontend_stories has_e2e_story
|
|
562
|
+
has_frontend_stories=$(jq -r '[.stories[] | select(.type == "frontend")] | length' "$prd_file" 2>/dev/null)
|
|
563
|
+
has_e2e_story=$(jq -r '[.stories[] | select(.testing.types[]? == "e2e")] | length' "$prd_file" 2>/dev/null)
|
|
564
|
+
if [[ "$has_frontend_stories" -gt 0 && "$has_e2e_story" == "0" ]]; then
|
|
565
|
+
echo ""
|
|
566
|
+
print_warning "No E2E story found — frontend features should have at least one Playwright E2E story"
|
|
567
|
+
echo " Add a final story with testing.types: [\"e2e\"] and Playwright testSteps"
|
|
568
|
+
echo ""
|
|
569
|
+
fi
|
|
570
|
+
|
|
542
571
|
# If issues found, show summary and attempt fix
|
|
543
572
|
if [[ "$needs_fix" == "true" ]]; then
|
|
544
573
|
echo " Optimizing test coverage for $story_count stories..."
|
|
@@ -558,6 +587,8 @@ _validate_and_fix_stories() {
|
|
|
558
587
|
[[ $cnt_bare_pytest -gt 0 ]] && echo " ${cnt_bare_pytest}x use '${py_runner:-python3} pytest' not bare 'pytest'"
|
|
559
588
|
[[ $cnt_bare_python -gt 0 ]] && echo " ${cnt_bare_python}x use 'python3' not bare 'python' (macOS compatibility)"
|
|
560
589
|
[[ $cnt_server_only -gt 0 ]] && echo " ${cnt_server_only}x all testSteps need live server (add offline fallback)"
|
|
590
|
+
[[ $cnt_import_check -gt 0 ]] && echo " ${cnt_import_check}x testSteps use import-checks (replace with real tests)"
|
|
591
|
+
[[ $cnt_playwright_notes -gt 0 ]] && echo " ${cnt_playwright_notes}x frontend: add Playwright MCP visual verification to notes"
|
|
561
592
|
[[ $cnt_custom -gt 0 ]] && echo " ${cnt_custom} stories with custom check issues"
|
|
562
593
|
|
|
563
594
|
# Skip auto-fix in dry-run mode
|
package/ralph/prd.sh
CHANGED
|
@@ -118,7 +118,7 @@ ralph_prd() {
|
|
|
118
118
|
printf '%s\n' ' "acceptanceCriteria": ["AC 1", "AC 2"],' >> "$prompt_file"
|
|
119
119
|
printf '%s\n' ' "errorHandling": ["what happens on failure"],' >> "$prompt_file"
|
|
120
120
|
printf '%s\n' ' "testSteps": ["executable shell commands only"],' >> "$prompt_file"
|
|
121
|
-
printf '%s\n' ' "contextFiles": ["docs/ideas/feature.md"],' >> "$prompt_file"
|
|
121
|
+
printf '%s\n' ' "contextFiles": ["docs/ideas/feature.md", "docs/plans/feature-plan.md"],' >> "$prompt_file"
|
|
122
122
|
printf '%s\n' ' "notes": ""' >> "$prompt_file"
|
|
123
123
|
printf '%s\n' ' }' >> "$prompt_file"
|
|
124
124
|
printf '%s\n' ' ]' >> "$prompt_file"
|
|
@@ -130,7 +130,7 @@ demo_skills() {
|
|
|
130
130
|
|
|
131
131
|
echo -e " ${BOLD}In Claude Code, type these commands:${NC}\n"
|
|
132
132
|
|
|
133
|
-
echo -e " ${CYAN}/
|
|
133
|
+
echo -e " ${CYAN}/prd${NC} Brainstorm → PRD → Ready for Ralph"
|
|
134
134
|
sleep 0.1
|
|
135
135
|
echo -e " ${CYAN}/vibe-help${NC} Quick reference cheatsheet"
|
|
136
136
|
sleep 0.1
|
package/ralph/setup/tutorial.sh
CHANGED
|
@@ -83,7 +83,7 @@ lesson_slash_commands() {
|
|
|
83
83
|
echo ""
|
|
84
84
|
echo -e " ${BOLD}Custom commands${NC} (from ${DIM}.claude/skills/${NC}):"
|
|
85
85
|
echo ""
|
|
86
|
-
echo -e " ${CYAN}/
|
|
86
|
+
echo -e " ${CYAN}/prd${NC} Brainstorm → PRD → Ready for Ralph"
|
|
87
87
|
echo -e " ${CYAN}/vibe-check${NC} Code quality audit"
|
|
88
88
|
echo -e " ${CYAN}/review${NC} Code review with security checks"
|
|
89
89
|
echo ""
|
|
@@ -135,7 +135,7 @@ lesson_integration() {
|
|
|
135
135
|
|
|
136
136
|
echo -e " ${BOLD}agentic-loop${NC} adds superpowers to Claude Code:"
|
|
137
137
|
echo ""
|
|
138
|
-
echo -e " ${BOLD}1. The Workflow (/
|
|
138
|
+
echo -e " ${BOLD}1. The Workflow (/prd → Ralph → Ship)${NC}"
|
|
139
139
|
echo -e " ${DIM}Brainstorm ideas, generate PRDs, execute autonomously${NC}"
|
|
140
140
|
echo ""
|
|
141
141
|
echo -e " ${BOLD}2. Code Quality (/vibe-check)${NC}"
|
|
@@ -145,7 +145,7 @@ lesson_integration() {
|
|
|
145
145
|
echo -e " ${DIM}Automatically blocks problematic commits${NC}"
|
|
146
146
|
echo ""
|
|
147
147
|
echo -e " ${BOLD}4. Slash Commands (7 included)${NC}"
|
|
148
|
-
echo -e " ${DIM}/
|
|
148
|
+
echo -e " ${DIM}/prd, /vibe-help, /tour, /vibe-check, /review, /explain, /styleguide${NC}"
|
|
149
149
|
echo ""
|
|
150
150
|
echo -e " ${BOLD}5. MCP Configuration${NC}"
|
|
151
151
|
echo -e " ${DIM}Chrome DevTools for visual verification${NC}"
|
package/ralph/setup.sh
CHANGED
|
@@ -169,6 +169,7 @@ ralph_setup() {
|
|
|
169
169
|
setup_custom_checks
|
|
170
170
|
setup_gitignore
|
|
171
171
|
setup_claude_hooks "$pkg_root"
|
|
172
|
+
setup_claude_hud
|
|
172
173
|
setup_slash_commands "$pkg_root"
|
|
173
174
|
setup_claude_md
|
|
174
175
|
setup_mcp
|
|
@@ -185,7 +186,7 @@ ralph_setup() {
|
|
|
185
186
|
echo " --- Terminal 1: Claude Code ---"
|
|
186
187
|
echo " claude --dangerously-skip-permissions"
|
|
187
188
|
echo " /tour # Guided walkthrough"
|
|
188
|
-
echo " /
|
|
189
|
+
echo " /prd 'your feature' # Generate a PRD"
|
|
189
190
|
echo ""
|
|
190
191
|
echo " --- Terminal 2: Ralph Loop ---"
|
|
191
192
|
echo " npx agentic-loop run # Execute PRDs autonomously"
|
|
@@ -197,7 +198,7 @@ setup_ralph_dir() {
|
|
|
197
198
|
local pkg_root="$1"
|
|
198
199
|
|
|
199
200
|
echo "Creating .ralph/ directory..."
|
|
200
|
-
mkdir -p ".ralph/archive" ".ralph/screenshots"
|
|
201
|
+
mkdir -p ".ralph/archive" ".ralph/screenshots" ".ralph/hooks"
|
|
201
202
|
|
|
202
203
|
# Copy config template based on detected project type
|
|
203
204
|
if [[ ! -f ".ralph/config.json" ]]; then
|
|
@@ -401,26 +402,34 @@ setup_claude_hooks() {
|
|
|
401
402
|
inject_context=$(_resolve_hook "inject-context.sh")
|
|
402
403
|
save_learnings=$(_resolve_hook "save-learnings.sh")
|
|
403
404
|
|
|
405
|
+
# Wrap hook path with existence check so missing files don't produce errors.
|
|
406
|
+
# If the hook script is deleted after setup (git clean, etc.), this prevents
|
|
407
|
+
# "No such file or directory" errors on every Claude session end.
|
|
408
|
+
_safe_cmd() {
|
|
409
|
+
local path="$1"
|
|
410
|
+
printf 'if [ -f "%s" ]; then "%s"; else echo '"'"'{"continue": true}'"'"'; fi' "$path" "$path"
|
|
411
|
+
}
|
|
412
|
+
|
|
404
413
|
# Build hooks arrays using jq for proper JSON
|
|
405
414
|
local post_edit_hooks post_all_hooks session_start_hooks stop_hooks
|
|
406
415
|
|
|
407
416
|
# PostToolUse: warn-* hooks on Edit|Write
|
|
408
417
|
post_edit_hooks="[]"
|
|
409
418
|
for hook_path in "$warn_debug" "$warn_secrets" "$warn_urls" "$warn_empty_catch"; do
|
|
410
|
-
[[ -n "$hook_path" ]] && post_edit_hooks=$(echo "$post_edit_hooks" | jq --arg cmd "$hook_path" '. + [{"type": "command", "command": $cmd, "timeout": 5}]')
|
|
419
|
+
[[ -n "$hook_path" ]] && post_edit_hooks=$(echo "$post_edit_hooks" | jq --arg cmd "$(_safe_cmd "$hook_path")" '. + [{"type": "command", "command": $cmd, "timeout": 5}]')
|
|
411
420
|
done
|
|
412
421
|
|
|
413
422
|
# PostToolUse: log-tools on all
|
|
414
423
|
post_all_hooks="[]"
|
|
415
|
-
[[ -n "$log_tools" ]] && post_all_hooks=$(jq -n --arg cmd "$log_tools" '[{"type": "command", "command": $cmd, "timeout": 3}]')
|
|
424
|
+
[[ -n "$log_tools" ]] && post_all_hooks=$(jq -n --arg cmd "$(_safe_cmd "$log_tools")" '[{"type": "command", "command": $cmd, "timeout": 3}]')
|
|
416
425
|
|
|
417
426
|
# SessionStart: inject-context
|
|
418
427
|
session_start_hooks="[]"
|
|
419
|
-
[[ -n "$inject_context" ]] && session_start_hooks=$(jq -n --arg cmd "$inject_context" '[{"type": "command", "command": $cmd, "timeout": 5}]')
|
|
428
|
+
[[ -n "$inject_context" ]] && session_start_hooks=$(jq -n --arg cmd "$(_safe_cmd "$inject_context")" '[{"type": "command", "command": $cmd, "timeout": 5}]')
|
|
420
429
|
|
|
421
430
|
# Stop: save-learnings
|
|
422
431
|
stop_hooks="[]"
|
|
423
|
-
[[ -n "$save_learnings" ]] && stop_hooks=$(jq -n --arg cmd "$save_learnings" '[{"type": "command", "command": $cmd, "timeout": 10}]')
|
|
432
|
+
[[ -n "$save_learnings" ]] && stop_hooks=$(jq -n --arg cmd "$(_safe_cmd "$save_learnings")" '[{"type": "command", "command": $cmd, "timeout": 10}]')
|
|
424
433
|
|
|
425
434
|
# Build the complete hooks config
|
|
426
435
|
local hooks_config
|
|
@@ -446,6 +455,106 @@ setup_claude_hooks() {
|
|
|
446
455
|
echo " Configured .claude/settings.json"
|
|
447
456
|
}
|
|
448
457
|
|
|
458
|
+
# Install claude-hud statusline plugin for Claude Code
|
|
459
|
+
setup_claude_hud() {
|
|
460
|
+
local hud_dir="$HOME/.claude/plugins/claude-hud"
|
|
461
|
+
local marketplaces_file="$HOME/.claude/plugins/known_marketplaces.json"
|
|
462
|
+
|
|
463
|
+
# Check dependencies
|
|
464
|
+
if ! command -v git &>/dev/null; then
|
|
465
|
+
print_warning "git not found — skipping claude-hud install"
|
|
466
|
+
return 0
|
|
467
|
+
fi
|
|
468
|
+
if ! command -v node &>/dev/null; then
|
|
469
|
+
print_warning "node not found — skipping claude-hud install"
|
|
470
|
+
return 0
|
|
471
|
+
fi
|
|
472
|
+
if ! command -v jq &>/dev/null; then
|
|
473
|
+
print_warning "jq not found — skipping claude-hud install"
|
|
474
|
+
return 0
|
|
475
|
+
fi
|
|
476
|
+
|
|
477
|
+
echo "Setting up claude-hud statusline..."
|
|
478
|
+
|
|
479
|
+
# Clone if not already present
|
|
480
|
+
if [[ ! -d "$hud_dir" ]]; then
|
|
481
|
+
mkdir -p "$HOME/.claude/plugins"
|
|
482
|
+
if ! git clone --depth 1 https://github.com/jarrodwatts/claude-hud.git "$hud_dir" 2>/dev/null; then
|
|
483
|
+
print_warning "Failed to clone claude-hud — skipping"
|
|
484
|
+
return 0
|
|
485
|
+
fi
|
|
486
|
+
echo " Cloned claude-hud plugin"
|
|
487
|
+
else
|
|
488
|
+
echo " claude-hud already installed"
|
|
489
|
+
fi
|
|
490
|
+
|
|
491
|
+
# Build if dist/index.js is missing
|
|
492
|
+
if [[ ! -f "$hud_dir/dist/index.js" ]]; then
|
|
493
|
+
echo " Building claude-hud..."
|
|
494
|
+
if ! (cd "$hud_dir" && npm ci --production=false --ignore-scripts 2>/dev/null && npm run build 2>/dev/null); then
|
|
495
|
+
print_warning "Failed to build claude-hud — statusline may not work"
|
|
496
|
+
echo " Try manually: cd $hud_dir && npm ci && npm run build"
|
|
497
|
+
return 0
|
|
498
|
+
fi
|
|
499
|
+
echo " Built successfully"
|
|
500
|
+
fi
|
|
501
|
+
|
|
502
|
+
# Register in known_marketplaces.json
|
|
503
|
+
if [[ -f "$marketplaces_file" ]]; then
|
|
504
|
+
if ! jq -e '."claude-hud"' "$marketplaces_file" > /dev/null 2>&1; then
|
|
505
|
+
local tmp
|
|
506
|
+
tmp=$(mktemp)
|
|
507
|
+
jq '."claude-hud" = {
|
|
508
|
+
"source": {
|
|
509
|
+
"source": "github",
|
|
510
|
+
"repo": "jarrodwatts/claude-hud"
|
|
511
|
+
},
|
|
512
|
+
"installLocation": ($ENV.HOME + "/.claude/plugins/claude-hud"),
|
|
513
|
+
"lastUpdated": (now | todate)
|
|
514
|
+
}' "$marketplaces_file" > "$tmp" && mv "$tmp" "$marketplaces_file"
|
|
515
|
+
echo " Registered in known_marketplaces.json"
|
|
516
|
+
fi
|
|
517
|
+
else
|
|
518
|
+
mkdir -p "$(dirname "$marketplaces_file")"
|
|
519
|
+
jq -n '{
|
|
520
|
+
"claude-hud": {
|
|
521
|
+
"source": {
|
|
522
|
+
"source": "github",
|
|
523
|
+
"repo": "jarrodwatts/claude-hud"
|
|
524
|
+
},
|
|
525
|
+
"installLocation": ($ENV.HOME + "/.claude/plugins/claude-hud"),
|
|
526
|
+
"lastUpdated": (now | todate)
|
|
527
|
+
}
|
|
528
|
+
}' > "$marketplaces_file"
|
|
529
|
+
echo " Created known_marketplaces.json with claude-hud"
|
|
530
|
+
fi
|
|
531
|
+
|
|
532
|
+
# Write agentic-loop-tuned config
|
|
533
|
+
cat > "$hud_dir/config.json" << 'EOF'
|
|
534
|
+
{
|
|
535
|
+
"lineLayout": "expanded",
|
|
536
|
+
"pathLevels": 2,
|
|
537
|
+
"gitStatus": {
|
|
538
|
+
"enabled": true,
|
|
539
|
+
"showDirty": true,
|
|
540
|
+
"showAheadBehind": true
|
|
541
|
+
},
|
|
542
|
+
"display": {
|
|
543
|
+
"showContextBar": true,
|
|
544
|
+
"contextValue": "percent",
|
|
545
|
+
"showModel": true,
|
|
546
|
+
"showDuration": true,
|
|
547
|
+
"showSpeed": false,
|
|
548
|
+
"showUsage": true,
|
|
549
|
+
"showTools": true,
|
|
550
|
+
"showAgents": true,
|
|
551
|
+
"showTodos": true
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
EOF
|
|
555
|
+
echo " Configured claude-hud for agentic-loop (expanded layout, all panels)"
|
|
556
|
+
}
|
|
557
|
+
|
|
449
558
|
# Copy slash commands (skills format for Claude Code)
|
|
450
559
|
setup_slash_commands() {
|
|
451
560
|
local pkg_root="$1"
|
|
@@ -569,6 +678,8 @@ ${python_runner:+- Python: Use \`$python_runner\` (not bare \`python\`)}
|
|
|
569
678
|
framework_template="$pkg_root/templates/examples/CLAUDE-${framework_type}.md"
|
|
570
679
|
fi
|
|
571
680
|
|
|
681
|
+
local style_marker="<!-- vibe-and-thrive-detected -->"
|
|
682
|
+
|
|
572
683
|
if [[ -f "CLAUDE.md" ]]; then
|
|
573
684
|
# Append framework template if it exists and not already included
|
|
574
685
|
if [[ -n "$framework_template" && -f "$framework_template" ]]; then
|
|
@@ -581,6 +692,16 @@ ${python_runner:+- Python: Use \`$python_runner\` (not bare \`python\`)}
|
|
|
581
692
|
echo " Appended ${framework_type} conventions to CLAUDE.md"
|
|
582
693
|
fi
|
|
583
694
|
fi
|
|
695
|
+
# Add writing style defaults if not already present
|
|
696
|
+
if ! grep -q "$style_marker" "CLAUDE.md" 2>/dev/null; then
|
|
697
|
+
cat >> CLAUDE.md << EOF
|
|
698
|
+
|
|
699
|
+
$style_marker
|
|
700
|
+
## Writing Style
|
|
701
|
+
- Active voice only. Never use passive voice.
|
|
702
|
+
- Never use em dashes. Use commas, periods, or parentheses instead.
|
|
703
|
+
EOF
|
|
704
|
+
fi
|
|
584
705
|
echo "$detected_section" >> CLAUDE.md
|
|
585
706
|
echo " Updated CLAUDE.md"
|
|
586
707
|
else
|
|
@@ -590,6 +711,11 @@ ${python_runner:+- Python: Use \`$python_runner\` (not bare \`python\`)}
|
|
|
590
711
|
|
|
591
712
|
## Your Rules
|
|
592
713
|
<!-- Add your project-specific rules, patterns, and conventions here -->
|
|
714
|
+
|
|
715
|
+
<!-- vibe-and-thrive-detected -->
|
|
716
|
+
## Writing Style
|
|
717
|
+
- Active voice only. Never use passive voice.
|
|
718
|
+
- Never use em dashes. Use commas, periods, or parentheses instead.
|
|
593
719
|
EOF
|
|
594
720
|
|
|
595
721
|
# Include framework template if available
|
|
@@ -715,20 +841,28 @@ setup_precommit_hooks() {
|
|
|
715
841
|
# Create .pre-commit-config.yaml if it doesn't exist
|
|
716
842
|
if [[ ! -f ".pre-commit-config.yaml" ]]; then
|
|
717
843
|
cat > .pre-commit-config.yaml << 'EOF'
|
|
844
|
+
# Pre-commit hooks powered by agentic-loop vibe-check
|
|
845
|
+
# Runs on staged files before each commit
|
|
718
846
|
repos:
|
|
719
|
-
- repo:
|
|
720
|
-
|
|
847
|
+
- repo: local
|
|
848
|
+
hooks:
|
|
849
|
+
- id: vibe-check
|
|
850
|
+
name: Vibe Check
|
|
851
|
+
entry: npx vibe-check --fail-on error
|
|
852
|
+
language: node
|
|
853
|
+
types_or: [javascript, ts, python, json]
|
|
854
|
+
pass_filenames: true
|
|
855
|
+
|
|
856
|
+
# Standard hooks
|
|
857
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
858
|
+
rev: v4.5.0
|
|
721
859
|
hooks:
|
|
722
|
-
- id:
|
|
723
|
-
|
|
724
|
-
- id:
|
|
725
|
-
|
|
726
|
-
- id: check-
|
|
727
|
-
|
|
728
|
-
- id: check-debug
|
|
729
|
-
name: Check for debug statements
|
|
730
|
-
- id: check-signs-secrets
|
|
731
|
-
name: Check signs.json for credentials
|
|
860
|
+
- id: trailing-whitespace
|
|
861
|
+
exclude: '\.txt$'
|
|
862
|
+
- id: end-of-file-fixer
|
|
863
|
+
exclude: '\.txt$'
|
|
864
|
+
- id: check-yaml
|
|
865
|
+
- id: check-added-large-files
|
|
732
866
|
EOF
|
|
733
867
|
echo " Created .pre-commit-config.yaml"
|
|
734
868
|
else
|
package/ralph/utils.sh
CHANGED
|
@@ -34,6 +34,7 @@ readonly BROWSER_PAGE_TIMEOUT_MS=30000
|
|
|
34
34
|
readonly CURL_TIMEOUT_SECONDS=10
|
|
35
35
|
readonly SIGN_EXTRACTION_TIMEOUT_SECONDS=30
|
|
36
36
|
readonly PREFLIGHT_CACHE_TTL_SECONDS=600
|
|
37
|
+
readonly UPDATE_CHECK_TTL_SECONDS=86400 # Check for updates once per day
|
|
37
38
|
|
|
38
39
|
# Common project directories (avoid duplication across files)
|
|
39
40
|
readonly FRONTEND_DIRS=("apps/web" "frontend" "client" "web")
|
|
@@ -49,6 +50,51 @@ YELLOW='\033[1;33m'
|
|
|
49
50
|
BLUE='\033[0;34m'
|
|
50
51
|
NC='\033[0m' # No Color
|
|
51
52
|
|
|
53
|
+
# Terminal background theming for Ralph
|
|
54
|
+
# Saves original background color and applies a teal tint to visually
|
|
55
|
+
# distinguish the Ralph terminal from Claude Code.
|
|
56
|
+
# Only works in macOS Terminal.app — no-op on Linux, iTerm2, VS Code, etc.
|
|
57
|
+
# Note: AppleScript targets "front window" (focused window) not the specific
|
|
58
|
+
# window running the script. In practice this works because the user just
|
|
59
|
+
# ran the command, but rapid window switching can cause a mismatch.
|
|
60
|
+
_ORIGINAL_TERMINAL_BG=""
|
|
61
|
+
|
|
62
|
+
set_terminal_bg() {
|
|
63
|
+
local hex="${1:-#1a2e2e}" # Default: subtle dark teal
|
|
64
|
+
|
|
65
|
+
# Only works in Terminal.app
|
|
66
|
+
[[ "$TERM_PROGRAM" != "Apple_Terminal" ]] && return 0
|
|
67
|
+
|
|
68
|
+
# Save current background color for restore
|
|
69
|
+
_ORIGINAL_TERMINAL_BG=$(osascript -e 'tell application "Terminal" to get background color of front window' 2>/dev/null) || return 0
|
|
70
|
+
|
|
71
|
+
# Parse hex to 16-bit RGB values (Terminal.app uses 0-65535 range)
|
|
72
|
+
local r=$((16#${hex:1:2} * 257))
|
|
73
|
+
local g=$((16#${hex:3:2} * 257))
|
|
74
|
+
local b=$((16#${hex:5:2} * 257))
|
|
75
|
+
|
|
76
|
+
osascript -e "tell application \"Terminal\" to set background color of front window to {$r, $g, $b}" 2>/dev/null || true
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
restore_terminal_bg() {
|
|
80
|
+
[[ -z "$_ORIGINAL_TERMINAL_BG" ]] && return 0
|
|
81
|
+
[[ "$TERM_PROGRAM" != "Apple_Terminal" ]] && return 0
|
|
82
|
+
|
|
83
|
+
osascript -e "tell application \"Terminal\" to set background color of front window to {$_ORIGINAL_TERMINAL_BG}" 2>/dev/null || true
|
|
84
|
+
_ORIGINAL_TERMINAL_BG=""
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# Set terminal tab title (works in Terminal.app, iTerm2, and most xterm-compatible terminals)
|
|
88
|
+
set_tab_title() {
|
|
89
|
+
local title="$1"
|
|
90
|
+
printf '\033]0;%s\007' "$title"
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# Restore tab title to default (empty = terminal decides)
|
|
94
|
+
restore_tab_title() {
|
|
95
|
+
printf '\033]0;\007'
|
|
96
|
+
}
|
|
97
|
+
|
|
52
98
|
# Get existing frontend directories in this project
|
|
53
99
|
get_frontend_dirs() {
|
|
54
100
|
local dirs=()
|
|
@@ -410,6 +456,8 @@ create_temp_file() {
|
|
|
410
456
|
|
|
411
457
|
# Clean up only tracked temp files on exit
|
|
412
458
|
cleanup() {
|
|
459
|
+
restore_tab_title
|
|
460
|
+
restore_terminal_bg
|
|
413
461
|
if [[ ${#RALPH_TEMP_FILES[@]} -gt 0 ]]; then
|
|
414
462
|
for f in "${RALPH_TEMP_FILES[@]}"; do
|
|
415
463
|
rm -f "$f" 2>/dev/null
|
|
@@ -249,9 +249,9 @@ make typecheck # Check TypeScript types
|
|
|
249
249
|
|
|
250
250
|
## Workflow
|
|
251
251
|
|
|
252
|
-
1. **
|
|
253
|
-
2. **
|
|
254
|
-
3. **
|
|
252
|
+
1. **PRD** - Run `/prd "feature description"` to generate stories
|
|
253
|
+
2. **Review** - Review generated stories in `.ralph/prd.json`
|
|
254
|
+
3. **Validate** - PRD validation checks story coherence
|
|
255
255
|
4. **Run** - Execute `ralph run` for autonomous coding
|
|
256
256
|
5. **Audit** - Run `/vibe-check` before shipping
|
|
257
257
|
6. **Commit** - Pre-commit hooks catch remaining issues
|
package/templates/signs.json
CHANGED
|
@@ -48,6 +48,13 @@
|
|
|
48
48
|
"category": "testing",
|
|
49
49
|
"learnedFrom": null,
|
|
50
50
|
"createdAt": "2026-01-25T00:00:00-08:00"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "sign-008",
|
|
54
|
+
"pattern": "NEVER use import-checks as test steps (python -c 'from X import Y', hasattr, test -f). These only verify a symbol exists, not that it works. A function that raises on every call still passes an import check. Always use real behavioral tests: curl for API endpoints, pytest with actual function calls, or Playwright for UI.",
|
|
55
|
+
"category": "testing",
|
|
56
|
+
"learnedFrom": "Okta SSO PRD review — all 4 backend stories had import-check testSteps that would pass even with completely broken code",
|
|
57
|
+
"createdAt": "2026-02-17T00:00:00-08:00"
|
|
51
58
|
}
|
|
52
59
|
]
|
|
53
60
|
}
|