diff --git a/.agent/AGENTS.md b/.agent/AGENTS.md index 73e323d0e..106d64be0 100644 --- a/.agent/AGENTS.md +++ b/.agent/AGENTS.md @@ -42,6 +42,22 @@ If the script outputs "STOP - ON PROTECTED BRANCH", you MUST NOT proceed with ed 3. **Do NOT proceed until user replies with 1, 2, or 3** +**Loop mode (autonomous agents)**: Loop agents (`/full-loop`, `/ralph-loop`) use auto-decision to avoid stalling: + +```bash +~/.aidevops/agents/scripts/pre-edit-check.sh --loop-mode --task "task description" +``` + +Auto-decision rules: +- **Docs-only tasks** (README, CHANGELOG, docs/, typos) -> Option 3 (stay on main) +- **Code tasks** (feature, fix, implement, refactor, enhance) -> Option 1 (create worktree automatically) + +Exit codes: `0` = proceed, `1` = interactive stop, `2` = create worktree needed + +Detection keywords: +- Docs-only: `readme`, `changelog`, `documentation`, `docs/`, `typo`, `spelling` +- Code (overrides docs): `feature`, `fix`, `bug`, `implement`, `refactor`, `add`, `update`, `enhance`, `port`, `ssl`, `helper` + **Why worktrees are the default**: The main repo directory (`~/Git/{repo}/`) should ALWAYS stay on `main`. This prevents: - Uncommitted changes blocking branch switches - Parallel sessions inheriting wrong branch state @@ -93,6 +109,13 @@ Run pre-edit-check.sh in `~/Git/aidevops/` BEFORE any changes to either location **Quality Standards**: SonarCloud A-grade, ShellCheck zero violations +**Localhost Standards** (for any local service setup): +- **Always check port first**: `localhost-helper.sh check-port ` before starting services +- **Use .local domains**: `myapp.local` not `localhost:3000` (enables password manager autofill) +- **Always use SSL**: Via Traefik proxy with mkcert certificates +- **Auto-find ports**: `localhost-helper.sh find-port ` if preferred port is taken +- See `services/hosting/localhost.md` for full setup guide + **SonarCloud Hotspot Patterns** (auto-excluded via `sonar-project.properties`): - Scripts matching `*-helper.sh`, `*-setup.sh`, `*-cli.sh`, `*-verify.sh` are excluded from: - S5332 (clear-text protocol) - `http://localhost` for local dev diff --git a/.agent/scripts/commands/full-loop.md b/.agent/scripts/commands/full-loop.md index 76407fea5..5c9271146 100644 --- a/.agent/scripts/commands/full-loop.md +++ b/.agent/scripts/commands/full-loop.md @@ -16,19 +16,42 @@ Task Development → Preflight → PR Create → PR Review → Postflight → De ## Workflow -### Step 1: Validate Prerequisites +### Step 1: Auto-Branch Setup -Before starting, verify: +The loop automatically handles branch setup when on main/master: -1. **On feature branch**: Must not be on main/master -2. **Clean working directory**: Uncommitted changes should be committed or stashed -3. **Git remote configured**: Need to push and create PR +```bash +# Run pre-edit check in loop mode with task description +~/.aidevops/agents/scripts/pre-edit-check.sh --loop-mode --task "$ARGUMENTS" +``` + +**Exit codes:** +- `0` - Already on feature branch OR docs-only task (proceed) +- `1` - Interactive mode fallback (shouldn't happen in loop) +- `2` - Code task on main (auto-create worktree) + +**Auto-decision logic:** +- **Docs-only tasks** (README, CHANGELOG, docs/, typos): Stay on main +- **Code tasks** (features, fixes, refactors, enhancements): Auto-create worktree + +**Detection keywords:** +- Docs-only: `readme`, `changelog`, `documentation`, `docs/`, `typo`, `spelling` +- Code (overrides docs): `feature`, `fix`, `bug`, `implement`, `refactor`, `add`, `update`, `enhance`, `port`, `ssl` + +**When worktree is needed:** ```bash -# Check branch -git branch --show-current +# Generate branch name from task (sanitized, truncated to 40 chars) +branch_name=$(echo "$ARGUMENTS" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | cut -c1-40) +~/.aidevops/agents/scripts/worktree-helper.sh add "feature/$branch_name" +# Continue in new worktree directory +``` -# Check for uncommitted changes +Also verify: +- **Clean working directory**: Uncommitted changes should be committed or stashed +- **Git remote configured**: Need to push and create PR + +```bash git status --short ``` diff --git a/.agent/scripts/localhost-helper.sh b/.agent/scripts/localhost-helper.sh index 0872886be..758d979d8 100755 --- a/.agent/scripts/localhost-helper.sh +++ b/.agent/scripts/localhost-helper.sh @@ -15,9 +15,13 @@ YELLOW='\033[1;33m' RED='\033[0;31m' NC='\033[0m' # No Color -# Error message constants -readonly ERROR_UNKNOWN_COMMAND="Unknown command:" -readonly HELP_USAGE_INFO="Use '$0 help' for usage information" +# Error message constants (use shared-constants.sh if available, otherwise define locally) +if [[ -z "${ERROR_UNKNOWN_COMMAND:-}" ]]; then + readonly ERROR_UNKNOWN_COMMAND="Unknown command:" +fi +if [[ -z "${HELP_USAGE_INFO:-}" ]]; then + readonly HELP_USAGE_INFO="Use '$0 help' for usage information" +fi print_info() { local msg="$1" @@ -46,6 +50,145 @@ print_error() { # Configuration file CONFIG_FILE="../configs/localhost-config.json" +# ============================================================================= +# Port Management Functions +# ============================================================================= +# These functions ensure port conflicts are avoided and services use available ports. +# This is critical for: +# 1. Avoiding "port already in use" errors +# 2. Enabling SSL via reverse proxy (password managers require HTTPS) +# 3. Consistent .local domain access across all local services + +# Default port range for auto-assignment +readonly PORT_RANGE_START=3000 +readonly PORT_RANGE_END=9999 + +# Check if a port is in use +# Returns 0 if port is FREE, 1 if port is IN USE +is_port_free() { + local port="$1" + if [[ -z "$port" ]]; then + print_error "Port number required" + return 1 + fi + + # Check using lsof (works on macOS and Linux) + if lsof -i :"$port" >/dev/null 2>&1; then + return 1 # Port is in use + fi + + # Double-check with nc if available + if command -v nc >/dev/null 2>&1; then + if nc -z 127.0.0.1 "$port" 2>/dev/null; then + return 1 # Port is in use + fi + fi + + return 0 # Port is free +} + +# Find the next available port starting from a given port +# Usage: find_available_port [start_port] +# Returns: Prints the first available port +find_available_port() { + local start_port="${1:-$PORT_RANGE_START}" + local port="$start_port" + + while [[ $port -le $PORT_RANGE_END ]]; do + if is_port_free "$port"; then + echo "$port" + return 0 + fi + ((port++)) + done + + print_error "No available ports found in range $start_port-$PORT_RANGE_END" + return 1 +} + +# Check port and suggest alternative if in use +# Usage: check_port_or_suggest [port] +# Returns: 0 if port is free, 1 if in use (prints suggestion) +check_port_or_suggest() { + local port="$1" + + if [[ -z "$port" ]]; then + print_error "Port number required" + return 1 + fi + + if is_port_free "$port"; then + print_success "Port $port is available" + return 0 + else + local process_info + process_info=$(lsof -i :"$port" 2>/dev/null | tail -1 | awk '{print $1, $2}') + print_warning "Port $port is in use by: $process_info" + + local suggested_port + suggested_port=$(find_available_port "$((port + 1))") + if [[ -n "$suggested_port" ]]; then + print_info "Suggested alternative: $suggested_port" + echo "SUGGESTED_PORT=$suggested_port" + fi + return 1 + fi +} + +# List all ports currently in use by local development services +list_dev_ports() { + print_info "Ports in use by local development services:" + echo "" + + # Common development port ranges + local dev_ports=(3000 3001 3002 3003 4000 5000 5173 5174 8000 8080 8085 8888 9000 9222 11235) + + for port in "${dev_ports[@]}"; do + if ! is_port_free "$port"; then + local process_info + process_info=$(lsof -i :"$port" 2>/dev/null | tail -1 | awk '{print $1}') + echo " :$port - $process_info" + fi + done + + echo "" + print_info "Use 'lsof -i :' for detailed process info" + return 0 +} + +# Kill process on a specific port +kill_port() { + local port="$1" + + if [[ -z "$port" ]]; then + print_error "Port number required" + return 1 + fi + + local pids + pids=$(lsof -ti :"$port" 2>/dev/null) + + if [[ -z "$pids" ]]; then + print_info "No process found on port $port" + return 0 + fi + + print_warning "Killing process(es) on port $port: $pids" + echo "$pids" | xargs kill -9 2>/dev/null + + # Wait up to 3 seconds for the port to become free + for _ in {1..6}; do + if is_port_free "$port"; then + print_success "Port $port is now free" + return 0 + fi + sleep 0.5 + done + + print_error "Failed to free port $port" + return 1 +} + # Check if config file exists check_config() { if [[ ! -f "$CONFIG_FILE" ]]; then @@ -534,6 +677,18 @@ main() { "stop-mcp") stop_localwp_mcp ;; + "check-port") + check_port_or_suggest "$param2" + ;; + "find-port") + find_available_port "$param2" + ;; + "list-ports") + list_dev_ports + ;; + "kill-port") + kill_port "$param2" + ;; "help"|"-h"|"--help"|"") echo "Localhost Development Helper Script" echo "Usage: $0 [command] [options]" @@ -553,6 +708,12 @@ main() { echo " start-mcp - Start LocalWP MCP server for AI database access" echo " stop-mcp - Stop LocalWP MCP server" echo "" + echo "Port Management Commands:" + echo " check-port [port] - Check if port is available, suggest alternative" + echo " find-port [start] - Find next available port from start (default: 3000)" + echo " list-ports - List common dev ports in use" + echo " kill-port [port] - Kill process using specified port" + echo "" echo "Examples:" echo " $0 setup-dns" echo " $0 setup-proxy" diff --git a/.agent/scripts/pre-edit-check.sh b/.agent/scripts/pre-edit-check.sh index 8fc478e2e..ef878ca93 100755 --- a/.agent/scripts/pre-edit-check.sh +++ b/.agent/scripts/pre-edit-check.sh @@ -7,6 +7,12 @@ # # Usage: # ~/.aidevops/agents/scripts/pre-edit-check.sh +# ~/.aidevops/agents/scripts/pre-edit-check.sh --loop-mode --task "description" +# +# Exit codes: +# 0 - OK to proceed (on feature branch, or docs-only on main in loop mode) +# 1 - STOP (on protected branch, interactive mode) +# 2 - Create worktree (loop mode detected code task on main) # # AI assistants should call this before any Edit/Write tool and STOP if it # returns a warning about being on main branch. @@ -14,6 +20,59 @@ set -euo pipefail +# ============================================================================= +# Loop Mode Support +# ============================================================================= +# When --loop-mode is passed, the script auto-decides based on task description: +# - Docs-only tasks (README, CHANGELOG, docs/) -> stay on main (exit 0) +# - Code tasks (feature, fix, implement, etc.) -> signal worktree needed (exit 2) + +LOOP_MODE=false +TASK_DESC="" + +while [[ $# -gt 0 ]]; do + case $1 in + --loop-mode) + LOOP_MODE=true + shift + ;; + --task) + TASK_DESC="$2" + shift 2 + ;; + *) + shift + ;; + esac +done + +# Function to detect if task is docs-only +is_docs_only() { + local task="$1" + # Use bash 4.0+ parameter expansion for lowercase (more efficient than tr) + local task_lower="${task,,}" + + # Code change indicators (negative match - if present, NOT docs-only) + # These take precedence over docs patterns + local code_patterns="feature|fix|bug|implement|refactor|add.*function|update.*code|create.*script|modify.*config|change.*logic|new.*api|endpoint|enhance|port|ssl|helper" + + # Docs-only indicators (positive match) + local docs_patterns="^readme|^changelog|^documentation|docs/|typo|spelling|comment only|license only|^update readme|^update changelog|^update docs" + + # Check for code patterns first (takes precedence) + if echo "$task_lower" | grep -qE "$code_patterns"; then + return 1 # Not docs-only + fi + + # Check for docs patterns + if echo "$task_lower" | grep -qE "$docs_patterns"; then + return 0 # Is docs-only + fi + + # Default: not docs-only (safer to create branch) + return 1 +} + # Colors RED='\033[0;31m' GREEN='\033[0;32m' @@ -37,6 +96,21 @@ fi # Check if on main or master if [[ "$current_branch" == "main" || "$current_branch" == "master" ]]; then + # Loop mode: auto-decide based on task description + if [[ "$LOOP_MODE" == "true" ]]; then + if is_docs_only "$TASK_DESC"; then + echo -e "${YELLOW}LOOP-AUTO${NC}: Docs-only task detected, staying on $current_branch" + echo "LOOP_DECISION=stay" + exit 0 + else + # Auto-create worktree for code changes + echo -e "${YELLOW}LOOP-AUTO${NC}: Code task detected, worktree required" + echo "LOOP_DECISION=worktree" + exit 2 # Special exit code for "create worktree" + fi + fi + + # Interactive mode: show warning and exit echo "" echo -e "${RED}${BOLD}======================================================${NC}" echo -e "${RED}${BOLD} STOP - ON PROTECTED BRANCH: $current_branch${NC}" diff --git a/.agent/services/hosting/localhost.md b/.agent/services/hosting/localhost.md index 966ec1623..dd71eaa8c 100644 --- a/.agent/services/hosting/localhost.md +++ b/.agent/services/hosting/localhost.md @@ -19,16 +19,82 @@ tools: - **Type**: Local development environment management - **Config**: `configs/localhost-config.json` -- **Commands**: `localhost-helper.sh [environments|start|stop|status|localwp-sites|start-site|stop-site|generate-ssl|list-ports|check-port|kill-port|start-mcp] [env] [args]` -- **LocalWP**: Sites in `/Users/username/Local Sites`, MCP on port 3001 -- **SSL**: `generate-ssl`, `install-ssl`, `trust-cert` for local HTTPS -- **Ports**: `list-ports`, `check-port`, `kill-port`, `forward-port` -- **Docker**: `docker-up`, `docker-down`, `docker-logs`, `docker-exec` -- **MCP query**: `mcp-query "SELECT * FROM wp_posts LIMIT 5"` +- **Commands**: `localhost-helper.sh [check-port|find-port|list-ports|kill-port|generate-cert|setup-dns|setup-proxy|create-app|start-mcp] [args]` +- **LocalWP**: Sites in `/Users/username/Local Sites`, MCP on port 8085 +- **SSL**: `generate-cert`, `setup-proxy` for local HTTPS via Traefik +- **Ports**: `check-port`, `find-port`, `list-ports`, `kill-port` + +**CRITICAL: Why .local + SSL + Port Checking?** + +| Problem | Solution | Why It Matters | +|---------|----------|----------------| +| Port conflicts | `check-port`, `find-port` | Avoids "address already in use" errors | +| Password managers don't work | SSL via Traefik proxy | 1Password, Bitwarden require HTTPS to autofill | +| Inconsistent URLs | `.local` domains | `myapp.local` instead of `localhost:3847` | +| Browser security warnings | `mkcert` trusted certs | No "proceed anyway" clicks | + +**Standard Setup Pattern:** + +```bash +# 1. Check port availability first +localhost-helper.sh check-port 3000 + +# 2. If conflict, find next available (returns e.g., 3001) +available_port=$(localhost-helper.sh find-port 3000) + +# 3. Create app with .local domain + SSL using available port +localhost-helper.sh create-app myapp myapp.local "$available_port" true docker +``` + Localhost development provides local development capabilities with .local domain support, perfect for development workflows and testing environments. +## Why .local Domains + SSL + Port Management? + +### The Problem with `localhost:port` + +Traditional local development uses URLs like `http://localhost:3000`. This causes several issues: + +1. **Password managers don't work** - 1Password, Bitwarden, and other password managers require HTTPS to autofill credentials. They won't save or fill passwords on `http://` URLs for security reasons. + +2. **Port conflicts are common** - Multiple projects fighting for port 3000, 8080, etc. leads to "EADDRINUSE" errors and manual port hunting. + +3. **Inconsistent URLs** - Remembering which project is on which port is cognitive overhead. Was it 3000 or 3001? + +4. **No SSL testing** - Can't test SSL-specific features, secure cookies, or HSTS locally. + +### The Solution: .local + SSL + Port Checking + +| Component | Tool | Purpose | +|-----------|------|---------| +| `.local` domains | dnsmasq | Consistent, memorable URLs (`myapp.local`) | +| SSL certificates | mkcert | Browser-trusted HTTPS locally | +| Reverse proxy | Traefik | Routes `.local` domains to correct ports | +| Port management | localhost-helper.sh | Avoids conflicts, finds available ports | + +### Standard Workflow + +**Before starting any local service:** + +```bash +# 1. Check if desired port is available +~/.aidevops/agents/scripts/localhost-helper.sh check-port 3000 + +# 2. If in use, find next available +available_port=$(~/.aidevops/agents/scripts/localhost-helper.sh find-port 3000) +echo "Using port: $available_port" + +# 3. Create app with .local domain + SSL using available port +~/.aidevops/agents/scripts/localhost-helper.sh create-app myapp myapp.local "$available_port" true docker +``` + +**Result:** Access your app at `https://myapp.local` with: +- Password manager autofill working +- No port conflicts +- Browser-trusted SSL +- Consistent URL across sessions + ## Provider Overview ### **Localhost Characteristics:** diff --git a/.agent/workflows/ralph-loop.md b/.agent/workflows/ralph-loop.md index f59803ad8..c73c0efcc 100644 --- a/.agent/workflows/ralph-loop.md +++ b/.agent/workflows/ralph-loop.md @@ -75,6 +75,29 @@ The loop creates a **self-referential feedback loop** where: ~/.aidevops/agents/scripts/ralph-loop-helper.sh cancel ``` +### Auto-Branch Handling (Loop Mode) + +When running in loop mode on main/master, Ralph automatically handles branch decisions: + +```bash +# The helper script checks branch and auto-decides +~/.aidevops/agents/scripts/pre-edit-check.sh --loop-mode --task "Build a REST API for todos" +``` + +**Auto-decision rules:** + +| Task Type | Detection Keywords | Action | +|-----------|-------------------|--------| +| Docs-only | readme, changelog, docs/, documentation, typo, spelling | Stay on main (exit 0) | +| Code | feature, fix, bug, implement, refactor, add, update, enhance | Create worktree (exit 2) | + +**Exit codes:** +- `0` - Proceed (on feature branch or docs-only on main) +- `2` - Create worktree (code task detected on main) +- `1` - Error (shouldn't occur in loop mode) + +This eliminates the interactive prompt that previously stalled loop agents. + ### Legacy: Same Session ```bash