Skip to content

Instantly share code, notes, and snippets.

@leek
Last active January 15, 2026 22:59
Show Gist options
  • Select an option

  • Save leek/b36fe0c60a801e4b2d2d8f72d4634c1f to your computer and use it in GitHub Desktop.

Select an option

Save leek/b36fe0c60a801e4b2d2d8f72d4634c1f to your computer and use it in GitHub Desktop.
My Ralph Setup for Claude Code, Codex, Gemini, or Amp using Laravel + Filament

Project Learnings

Project Setup

Quick Setup (Recommended)

composer setup

This single command runs the complete setup: installs dependencies, copies .env, generates app key, runs migrations, installs npm packages, and builds assets.

Manual Setup

# Install PHP dependencies
composer install

# Copy environment file and configure
cp .env.example .env

# Generate application key
php artisan key:generate

# Run database migrations
php artisan migrate

# Install Node dependencies
npm install

# Build frontend assets
npm run build

Git Workflow Requirements

Before moving to the next feature, ALL changes must be:

  1. Committed with Clear Messages:

    git add .
    git commit -m "feat(module): descriptive message following conventional commits"
    • Use conventional commit format: feat:, fix:, docs:, test:, refactor:, etc.
    • Include scope when applicable: feat(api):, fix(ui):, test(auth):
    • Write descriptive messages that explain WHAT changed and WHY
  2. Pushed to Remote Repository:

    git push origin <branch-name>
    • Never leave completed features uncommitted
    • Push regularly to maintain backup and enable collaboration
    • Ensure CI/CD pipelines pass before considering feature complete
  3. Branch Hygiene:

    • Work on feature branches, never directly on main
    • Branch naming convention: feature/<feature-name>, fix/<issue-name>, docs/<doc-update>
    • Create pull requests for all significant changes

Environment Configuration

Key .env variables to configure:

  • DB_DATABASE, DB_USERNAME, DB_PASSWORD - MySQL connection
  • REDIS_HOST, REDIS_PORT - Redis for cache/sessions
  • APP_DOMAIN - Local domain (default: aba.test for Herd)

Technology Stack

  • PHP: 8.4
  • Laravel: v12 (streamlined structure since Laravel 11)
  • Filament: v4 (SDUI framework)
  • Livewire: v3
  • Tailwind CSS: v4
  • Testing: Pest v4

Build & Test Commands

# Run all tests (clears cache first)
composer run test

# Run specific test file
php artisan test tests/Feature/SomeTest.php

# Run with filter
php artisan test --filter=SomeTest

# Static analysis
composer run test:phpstan

# Production build (frontend assets)
npm run build

# Deploy Filament assets after JS/CSS changes
php artisan filament:assets

# Code formatting
composer run pint

# Completely rebuild the database (migrations and seed data)
composer run db:reset

Early-Stage Development Rules

Database & Migrations

  • NO NEW MIGRATIONS: Do not create new migration files during early development
  • MODIFY EXISTING: Edit existing migration files directly to keep schema clean
  • REBUILD: Apply changes by running php artisan migrate:fresh --seed

Seeders & Factories

  • ALWAYS SYNC: When you change a migration or model, immediately update the corresponding Factory and Seeder
  • VERIFY: Ensure php artisan db:seed runs successfully
  • CONSISTENCY: Keep seed data consistent with expected application state
  • TEST DATA: Use realistic data in factories for better testing scenarios

Technical Constraints

  • All code must pass typecheck (./vendor/bin/phpstan analyse)
  • Run vendor/bin/pint --dirty before commits
  • Migrations must run successfully with php artisan migrate:fresh --seed
  • Use private disk for sensitive file uploads (insurance cards, documents)
  • Follow HIPAA compliance for PHI data handling

Codebase Patterns

General Architecture & Conventions

  • Frameworks: Laravel v12, Filament v4, Livewire v3, Tailwind v4.
  • Strict Imports:
    • Schemas: Import from Filament\Schemas\Components\*. NEVER use Filament\Forms\Components\*.
    • Actions: Import from Filament\Actions\*. NEVER use Filament\Tables\Actions\*.
  • Naming:
    • Classes: Suffix with type (e.g., ConvertLeadToClientAction, LeadProfileObserver).
    • Database: snake_case columns. Index names < 64 chars (use prefixes like sip_, rpc_).
    • Enums: TitleCase keys. Implement HasLabel, HasColor, HasIcon via Kongulov\Traits\InteractWithEnum.
  • Laravel Best Practices:
    • Use Model::query() (not DB::).
    • Prefer Eloquent relationships over raw queries.
    • Use Form Requests for validation.
    • Queue time-consuming operations.

Filament & UI Architecture

  • Livewire Integration: Use ViewField with Blade views to embed components.
  • Custom Pages: Implement HasTable, InteractsWithTable, and define table() for custom tables.
  • Assets: Always run npm run build && php artisan filament:assets after CSS/JS changes.
  • Virtual Groups: Use getKeyFromRecordUsing + scopeQueryByKeyUsing for computed groupings.

Testing Patterns

  • Tooling: Pest v4 (LazilyRefreshDatabase enabled). Browser tests (dev-browser skill) for UI.
  • Filament Tests:
    • Table Actions: mountTableAction -> set -> callMountedTableAction.
    • Page Actions: mountAction -> set -> callMountedAction.
    • Wizards: Use set('data.field', val) (not fillForm).
  • Context: Call setPermissionsTeamId($company->id) for roles.
  • Data: Use UploadedFile::fake()->create(...).

Development Instructions

You are an autonomous AI development agent working on the project.

Your Task

  1. Read the PRD at <prd.json>
  2. Read the progress log at <progress.txt> (check Codebase Patterns section first)
  3. Check you're on the correct branch from PRD branchName. If not, check it out or create from main.
  4. Pick the highest priority user story where passes: false
  5. Implement that single user story
  6. Run quality checks (typecheck, lint, test)
  7. Update AGENT.md if you discover reusable patterns
  8. If checks pass, commit ALL changes with message: feat: [Story ID] - [Story Title]
  9. Update the PRD to set passes: true for the completed story
  10. Append your progress to <progress.txt>

Progress Report Format

APPEND to progress.txt (never replace, always append):

## [Date/Time] - [Story ID]
Thread: https://ampcode.com/threads/$AMP_CURRENT_THREAD_ID (if applicable)
- What was implemented
- Files changed
- **Learnings for future iterations:**
  - Patterns discovered (e.g., "this codebase uses X for Y")
  - Gotchas encountered (e.g., "don't forget to update Z when changing W")
  - Useful context (e.g., "the evaluation panel is in component X")
---

Consolidate Patterns

If you discover a reusable pattern specific to this PRD that future iterations should know, add it to a/the ## PRD Patterns section at the TOP of <progress.txt> (create it if it doesn't exist). This section should consolidate the most important learnings:

  • API patterns or conventions specific to that module
  • Gotchas or non-obvious requirements
  • Dependencies between files`
  • Testing approaches for that area
## PRD Patterns
- Example: Use `sql<number>` template for aggregations
- Example: Always use `IF NOT EXISTS` for migrations
- Example: Export types from actions.ts for UI components

Only add patterns that are story/prd-specific, not general.

Update AGENT.md

Before committing, check if you discovered learnings worth preserving for the whole codebase or other developers/agents and store them in AGENT.md.

Do NOT add:

  • Story-specific implementation details (those go in <progress.txt>)
  • Temporary debugging notes
  • Information already in <progress.txt>

Only add patterns that are general and reusable, not story-specific details.

Quality Requirements

  • ALL commits must pass project quality checks (typecheck, lint, test)
  • Do NOT commit broken code
  • Keep changes focused and minimal
  • Follow existing code patterns

Browser Testing (Required for Frontend Stories)

For any story that changes UI, you MUST verify it works in the browser:

  1. Load the dev-browser skill
  2. Navigate to the relevant page
  3. Verify the UI changes work as expected
  4. Take a screenshot if helpful for the progress log
  5. End-to-End Verification: Ensure features are visible and accessible in the actual application

A frontend story is NOT complete until browser verification passes.

Stop Condition

After completing a user story, check if ALL stories have passes: true.

If ALL stories are complete and passing, reply with: COMPLETE

If there are still stories with passes: false, end your response normally (another iteration will pick up the next story).

Important

  • Work on ONE story per iteration
  • Commit frequently
  • Keep CI green
  • Read the Codebase Patterns section in progress.txt before starting
  • Search the codebase before assuming something isn't implemented
  • Use subagents for expensive operations (file searching, analysis)
  • Write comprehensive tests with clear documentation
  • Update AGENT.md with your learnings
  • Commit working changes with descriptive messages
  • Make sure there is a way to actually navigate to and use any new feature you build

Testing Guidelines

  • LIMIT testing to ~20% of your total effort per loop
  • PRIORITIZE: Implementation > Documentation > Tests
  • Only write tests for NEW functionality you implement
  • Do NOT refactor existing tests unless broken
  • Focus on CORE functionality first, comprehensive testing later
  • Use Pest for all tests (php artisan make:test --pest {name})

Success Criteria

  • All user stories implemented and passing
  • Typecheck passes on all code
  • Test coverage meets 85% minimum for new code
  • All migrations run successfully
  • UI verified in browser for applicable stories
  • Git commits with conventional commit format

Current Task

Read prd.json and choose the highest priority incomplete story to implement. Use your judgment to prioritize what will have the biggest impact on project progress.

Remember: Quality over speed. Build it right the first time. Know when you're done.

#!/bin/bash
# Ralph Wiggum - Long-running AI agent loop
# Author: Chris Jones (@leek)
# Usage: ./ralph.sh [directory] [max_iterations]
# ./ralph.sh --work-dir <dir> --max-iterations <n> --prd <path> --progress <path> --prompt <path> --tool <cmd>
set -e
# --- Configuration & Setup ---
usage() {
cat <<EOF
Usage:
./ralph.sh [directory] [max_iterations]
./ralph.sh --work-dir <dir> --max-iterations <n> --prd <path> --progress <path> --prompt <path> --tool <cmd>
Options:
--work-dir <dir> Working directory to locate context files
--max-iterations <n> Max iterations (default: 50)
--prd <path> Override PRD file path
--progress <path> Override progress file path
--prompt <path> Override prompt file path
--tool <cmd> Override tool command (also supports RALPH_TOOL env)
-h, --help Show this help
EOF
}
find_file_insensitive() {
local dir="$1"
local name="$2"
find "$dir" -maxdepth 1 -type f -iname "$name" 2>/dev/null | head -n 1
}
resolve_override_file() {
local path="$1"
local expected_name="$2"
if [ -f "$path" ]; then
echo "$path"
return 0
fi
local override_dir
local override_base
override_dir="$(dirname "$path")"
override_base="$(basename "$path")"
local found
found="$(find_file_insensitive "$override_dir" "$override_base")"
if [ -n "$found" ]; then
echo "$found"
return 0
fi
echo ""
return 1
}
escape_sed_replacement() {
printf '%s' "$1" | sed -e 's/[\/&]/\\&/g'
}
setup_environment() {
RUN_DIR="$(pwd)"
WORK_DIR=""
MAX_ITERATIONS=""
PRD_PATH_OVERRIDE=""
PROGRESS_PATH_OVERRIDE=""
PROMPT_PATH_OVERRIDE=""
AI_TOOL_OVERRIDE="${RALPH_TOOL:-}"
while [ $# -gt 0 ]; do
case "$1" in
--work-dir)
WORK_DIR="$2"
shift 2
;;
--max-iterations)
MAX_ITERATIONS="$2"
shift 2
;;
--prd)
PRD_PATH_OVERRIDE="$2"
shift 2
;;
--progress)
PROGRESS_PATH_OVERRIDE="$2"
shift 2
;;
--prompt)
PROMPT_PATH_OVERRIDE="$2"
shift 2
;;
--tool)
AI_TOOL_OVERRIDE="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
--)
shift
break
;;
-*)
echo "Error: Unknown option '$1'"
usage
exit 1
;;
*)
if [ -z "$WORK_DIR" ]; then
WORK_DIR="$1"
elif [ -z "$MAX_ITERATIONS" ]; then
MAX_ITERATIONS="$1"
else
echo "Error: Unexpected argument '$1'"
usage
exit 1
fi
shift
;;
esac
done
MAX_ITERATIONS=${MAX_ITERATIONS:-50}
if [ -z "$WORK_DIR" ]; then
read -p "Enter working directory (default: .): " WORK_DIR
WORK_DIR=${WORK_DIR:-.}
fi
if [ ! -d "$WORK_DIR" ]; then
echo "Error: Directory '$WORK_DIR' does not exist."
exit 1
fi
# Resolve absolute path
WORK_DIR=$(cd "$WORK_DIR" && pwd)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [ -n "$PRD_PATH_OVERRIDE" ]; then
PRD_FILE="$(resolve_override_file "$PRD_PATH_OVERRIDE" "prd.json")"
if [ -z "$PRD_FILE" ]; then
echo "Error: PRD file not found at '$PRD_PATH_OVERRIDE'."
exit 1
fi
else
PRD_FILE="$(find_file_insensitive "$WORK_DIR" "prd.json")"
if [ -z "$PRD_FILE" ]; then
echo "Error: No prd.json file found in '$WORK_DIR' (checked: prd.json, PRD.json, etc.)"
exit 1
fi
fi
if [ -n "$PROGRESS_PATH_OVERRIDE" ]; then
PROGRESS_FILE="$(resolve_override_file "$PROGRESS_PATH_OVERRIDE" "progress.txt")"
if [ -z "$PROGRESS_FILE" ]; then
PROGRESS_FILE="$PROGRESS_PATH_OVERRIDE"
fi
else
PROGRESS_FILE="$(find_file_insensitive "$WORK_DIR" "progress.txt")"
if [ -z "$PROGRESS_FILE" ]; then
PROGRESS_FILE="$WORK_DIR/progress.txt"
fi
fi
ARCHIVE_DIR="$WORK_DIR/archive"
LAST_BRANCH_FILE="$WORK_DIR/.last-branch"
# Find prompt file (case-insensitive)
if [ -n "$PROMPT_PATH_OVERRIDE" ]; then
PROMPT_FILE="$(resolve_override_file "$PROMPT_PATH_OVERRIDE" "prompt.md")"
if [ -z "$PROMPT_FILE" ]; then
echo "Error: Prompt file not found at '$PROMPT_PATH_OVERRIDE'."
exit 1
fi
else
PROMPT_FILE="$(find_file_insensitive "$WORK_DIR" "prompt.md")"
if [ -z "$PROMPT_FILE" ]; then
PROMPT_FILE="$(find_file_insensitive "$RUN_DIR" "prompt.md")"
fi
if [ -z "$PROMPT_FILE" ]; then
echo "Error: No prompt.md file found in '$WORK_DIR' or '$RUN_DIR' (checked: prompt.md, PROMPT.md, etc.)"
exit 1
fi
fi
}
archive_previous_run() {
if [ -f "$PRD_FILE" ] && [ -f "$LAST_BRANCH_FILE" ]; then
CURRENT_BRANCH=$(jq -r '.branchName // empty' "$PRD_FILE" 2>/dev/null || echo "")
LAST_BRANCH=$(cat "$LAST_BRANCH_FILE" 2>/dev/null || echo "")
if [ -n "$CURRENT_BRANCH" ] && [ -n "$LAST_BRANCH" ] && [ "$CURRENT_BRANCH" != "$LAST_BRANCH" ]; then
# Archive the previous run
DATE=$(date +%Y-%m-%d)
# Strip "ralph/" prefix from branch name for folder
FOLDER_NAME=$(echo "$LAST_BRANCH" | sed 's|^ralph/||')
ARCHIVE_FOLDER="$ARCHIVE_DIR/$DATE-$FOLDER_NAME"
echo "Archiving previous run: $LAST_BRANCH"
mkdir -p "$ARCHIVE_FOLDER"
[ -f "$PRD_FILE" ] && cp "$PRD_FILE" "$ARCHIVE_FOLDER/"
[ -f "$PROGRESS_FILE" ] && cp "$PROGRESS_FILE" "$ARCHIVE_FOLDER/"
echo " Archived to: $ARCHIVE_FOLDER"
# Reset progress file for new run
echo "# Ralph Progress Log" > "$PROGRESS_FILE"
echo "Started: $(date)" >> "$PROGRESS_FILE"
echo "---" >> "$PROGRESS_FILE"
fi
fi
}
track_current_branch() {
if [ -f "$PRD_FILE" ]; then
CURRENT_BRANCH=$(jq -r '.branchName // empty' "$PRD_FILE" 2>/dev/null || echo "")
if [ -n "$CURRENT_BRANCH" ]; then
echo "$CURRENT_BRANCH" > "$LAST_BRANCH_FILE"
fi
fi
}
init_progress_file() {
if [ ! -f "$PROGRESS_FILE" ]; then
echo "# Ralph Progress Log" > "$PROGRESS_FILE"
echo "Started: $(date)" >> "$PROGRESS_FILE"
echo "---" >> "$PROGRESS_FILE"
fi
}
select_tool() {
if [ -n "$AI_TOOL_OVERRIDE" ]; then
AI_TOOL="$AI_TOOL_OVERRIDE"
else
echo "Select AI Tool:"
echo "1) amp"
echo "2) claude"
echo "3) codex"
echo "4) gemini"
read -p "Choose (1-4, default: 1): " TOOL_CHOICE
case "$TOOL_CHOICE" in
2) AI_TOOL="claude";;
3) AI_TOOL="codex";;
4) AI_TOOL="gemini";;
*) AI_TOOL="amp";;
esac
fi
# Check if tool exists
if ! command -v "$AI_TOOL" &> /dev/null; then
echo "Error: AI tool '$AI_TOOL' is not installed or not in PATH."
exit 1
fi
}
run_tool() {
# Prepend context about the working directory so the AI knows where "this directory" is
CONTEXT_HEADER="CONTEXT: The active working directory for this task is: $WORK_DIR. Use this path when resolving files mentioned as 'in this directory'."
PRD_REPLACEMENT="$(escape_sed_replacement "$PRD_FILE")"
PROGRESS_REPLACEMENT="$(escape_sed_replacement "$PROGRESS_FILE")"
PROMPT_CONTENT="$(cat "$PROMPT_FILE" | sed -e "s/<prd\\.json>/$PRD_REPLACEMENT/g" -e "s/<progress\\.txt>/$PROGRESS_REPLACEMENT/g")"
if [ "$AI_TOOL" == "amp" ]; then
{ echo "$CONTEXT_HEADER"; echo ""; echo "$PROMPT_CONTENT"; } | amp --dangerously-allow-all 2>&1 | tee /dev/stderr
elif [ "$AI_TOOL" == "claude" ]; then
{ echo "$CONTEXT_HEADER"; echo ""; echo "$PROMPT_CONTENT"; } | claude --dangerously-skip-permissions 2>&1 | tee /dev/stderr
else
{ echo "$CONTEXT_HEADER"; echo ""; echo "$PROMPT_CONTENT"; } | "$AI_TOOL" 2>&1 | tee /dev/stderr
fi
}
# --- Main Execution ---
setup_environment "$@"
archive_previous_run
track_current_branch
init_progress_file
select_tool
echo "Starting Ralph in '$WORK_DIR' using '$AI_TOOL' - Max iterations: $MAX_ITERATIONS"
echo "Using prompt file: $(basename "$PROMPT_FILE")"
for i in $(seq 1 $MAX_ITERATIONS); do
echo ""
echo "═══════════════════════════════════════════════════════"
echo " Ralph Iteration $i of $MAX_ITERATIONS"
echo "═══════════════════════════════════════════════════════"
# Run the selected tool with the ralph prompt from the working directory
OUTPUT=$(run_tool) || true
# Check for completion signal
if echo "$OUTPUT" | grep -q "<promise>COMPLETE</promise>"; then
echo ""
echo "Ralph completed all tasks!"
echo "Completed at iteration $i of $MAX_ITERATIONS"
exit 0
fi
echo "Iteration $i complete. Continuing..."
sleep 2
done
echo ""
echo "Ralph reached max iterations ($MAX_ITERATIONS) without completing all tasks."
echo "Check $PROGRESS_FILE for status."
exit 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment