Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .agent/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 <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 <start>` 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
Expand Down
39 changes: 31 additions & 8 deletions .agent/scripts/commands/full-loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down
167 changes: 164 additions & 3 deletions .agent/scripts/localhost-helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines +18 to +24
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fallback constants are incomplete (shared-constants absent => confusing errors).

You added fallbacks for ERROR_UNKNOWN_COMMAND / HELP_USAGE_INFO, but the script also uses other constants (e.g., ERROR_CONFIG_NOT_FOUND in check_config()). If shared-constants.sh isn’t present, error output becomes blank-ish.

Proposed fix
 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
+if [[ -z "${ERROR_CONFIG_NOT_FOUND:-}" ]]; then
+    readonly ERROR_CONFIG_NOT_FOUND="Config file not found:"
+fi

As per coding guidelines, automation scripts should fail with clear, actionable messaging.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# 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
# 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
if [[ -z "${ERROR_CONFIG_NOT_FOUND:-}" ]]; then
readonly ERROR_CONFIG_NOT_FOUND="Config file not found:"
fi
🤖 Prompt for AI Agents
In @.agent/scripts/localhost-helper.sh around lines 18 - 24, The script
currently defines fallbacks for ERROR_UNKNOWN_COMMAND and HELP_USAGE_INFO but
other constants used (e.g., ERROR_CONFIG_NOT_FOUND referenced in check_config())
are not defined when shared-constants.sh is missing, producing unclear errors;
update the top of the script to attempt sourcing shared-constants.sh and, if
absent, provide clear fallback readonly definitions for every constant the
script references (at minimum ERROR_CONFIG_NOT_FOUND and any
ERROR_INVALID_CONFIG / ERROR_MISSING_DEPENDENCY or HELP_* tokens used
elsewhere), and ensure check_config() and other functions use those fallbacks so
error messages are always informative.


print_info() {
local msg="$1"
Expand Down Expand Up @@ -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"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_port_free treats failures to run lsof/nc (e.g., missing command or non-numeric port) as “port is free” because it just falls through. Consider validating port is an integer and returning an error when checks can’t be performed to avoid false “available” results and downstream arithmetic/comparison failures.

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎

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
}
Comment on lines +62 to +136
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's verify the current state of the file and examine lines 62-136
cat -n .agent/scripts/localhost-helper.sh | head -150

Repository: marcusquinn/aidevops

Length of output: 5547


🏁 Script executed:

# Check if there's any bootstrap or setup script that documents tool availability
fd -t f -e sh -e bash | head -20

Repository: marcusquinn/aidevops

Length of output: 776


🏁 Script executed:

# Look for any environment setup or requirements documentation
git ls-files | grep -E "(README|setup|bootstrap|requirements|install)" | head -10

Repository: marcusquinn/aidevops

Length of output: 421


🏁 Script executed:

# Check if there are other scripts that validate tool availability
rg -t sh "lsof|command -v" --max-count=20 -A 2 -B 2

Repository: marcusquinn/aidevops

Length of output: 50376


Add input validation and tool availability guards to port helper functions

The port management functions assume lsof is available but don't verify it—on systems without lsof, checks silently pass or output noise. Additionally, port parameters lack validation, risking arithmetic errors when non-numeric input reaches $((port+1)) or ((port++)).

Three instances require fixing:

  • is_port_free() line 76: calls lsof without checking availability (inconsistent with the nc fallback check on line 81)
  • check_port_or_suggest() line 125: calls lsof directly without guard
  • list_dev_ports() line 149: calls lsof directly without guard

Add numeric port validation (1–65535) in is_port_free() and find_available_port() to prevent downstream arithmetic failures. This aligns with the codebase pattern: 80+ scripts defensively check tool availability with if ! command -v <tool> >/dev/null 2>&1; then before use.

🤖 Prompt for AI Agents
In @.agent/scripts/localhost-helper.sh around lines 62 - 136, The port helpers
assume lsof exists and accept any input; update is_port_free(),
find_available_port(), and check_port_or_suggest() (and list_dev_ports()) to: 1)
validate the port argument is numeric and within 1–65535 (use a digits regex and
numeric bounds) and return error if not, 2) guard all lsof usages with a command
-v lsof >/dev/null 2>&1 check before invoking it (fall back to nc or skip lsof
output if unavailable), and 3) ensure find_available_port only iterates when
start_port is valid so downstream arithmetic like ((port++)) or $((port+1))
never runs on invalid input; apply the same guarded lsof pattern in
list_dev_ports() and surface clear error messages when tools are missing or
ports are invalid.


# 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 :<port>' 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
Expand Down Expand Up @@ -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]"
Expand All @@ -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"
Expand Down
74 changes: 74 additions & 0 deletions .agent/scripts/pre-edit-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,72 @@
#
# 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.
# =============================================================================

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"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With set -euo pipefail, calling --task without a following value will hit an unbound $2 and abort the script. Consider guarding for a missing/empty task argument so loop agents don’t stall on an argument-parsing error.

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎

shift 2
;;
*)
shift
;;
esac
done
Comment on lines +33 to +47
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Harden --task parsing (currently can crash under set -u).

--task without a value will expand $2 while unset. Fail fast with a clear message (and consider rejecting unknown flags rather than silently ignoring them).

Proposed fix
         --task)
-            TASK_DESC="$2"
-            shift 2
+            if [[ $# -lt 2 ]]; then
+                echo "ERROR: --task requires a value" >&2
+                exit 1
+            fi
+            TASK_DESC="$2"
+            shift 2
             ;;
         *)
-            shift
+            echo "WARNING: ignoring unknown arg: $1" >&2
+            shift
             ;;

As per coding guidelines, automation scripts should fail deterministically with clear feedback.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
while [[ $# -gt 0 ]]; do
case $1 in
--loop-mode)
LOOP_MODE=true
shift
;;
--task)
TASK_DESC="$2"
shift 2
;;
*)
shift
;;
esac
done
while [[ $# -gt 0 ]]; do
case $1 in
--loop-mode)
LOOP_MODE=true
shift
;;
--task)
if [[ $# -lt 2 ]]; then
echo "ERROR: --task requires a value" >&2
exit 1
fi
TASK_DESC="$2"
shift 2
;;
*)
echo "WARNING: ignoring unknown arg: $1" >&2
shift
;;
esac
done
🤖 Prompt for AI Agents
In @.agent/scripts/pre-edit-check.sh around lines 33 - 47, The flag parsing loop
can crash under set -u because --task uses an unset $2; update the while/case
that handles --task to first verify a non-empty next argument exists and is not
another flag before assigning TASK_DESC, and if missing print a clear error and
exit nonzero; also change the default case (currently *) to treat unknown flags
as errors (print the flag name and exit) rather than silently shifting so
failures are deterministic.


# 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
}
Comment on lines +49 to +74
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, check the shebang and overall structure of the script
head -20 .agent/scripts/pre-edit-check.sh

Repository: marcusquinn/aidevops

Length of output: 958


🏁 Script executed:

# Search for any documentation about Bash version requirements
find . -type f \( -name "*.md" -o -name "*.txt" -o -name "README*" \) | xargs grep -l "bash" -i | head -10

Repository: marcusquinn/aidevops

Length of output: 457


🏁 Script executed:

# Check other scripts in the .agent/scripts directory for patterns
ls -la .agent/scripts/*.sh | head -20

Repository: marcusquinn/aidevops

Length of output: 1776


🏁 Script executed:

# Examine shebang lines across all scripts
for script in .agent/scripts/*.sh; do
  echo "=== $script ==="
  head -1 "$script"
done

Repository: marcusquinn/aidevops

Length of output: 6876


🏁 Script executed:

# Look for Bash version checks or requirements in the codebase
rg "bash.*4|version.*bash|BASH_VERSION" --type sh -A 2 -B 2

Repository: marcusquinn/aidevops

Length of output: 921


🏁 Script executed:

# Check CI configuration files for environment specifications
find . -type f \( -name ".github" -o -name "*.yml" -o -name "*.yaml" \) -path "*/.github/*" | head -20

Repository: marcusquinn/aidevops

Length of output: 342


🏁 Script executed:

# Check CI workflow configurations for Bash/runner specifications
cat .github/workflows/code-quality.yml | head -50

Repository: marcusquinn/aidevops

Length of output: 1829


🏁 Script executed:

# Get the full pre-edit-check.sh script to understand context
cat -n .agent/scripts/pre-edit-check.sh

Repository: marcusquinn/aidevops

Length of output: 7744


🏁 Script executed:

# Look for any Bash version checks or requirements in documentation
rg "bash.*version|minimum.*bash|bash.*3|bash.*4" -i --type md -A 2 -B 2

Repository: marcusquinn/aidevops

Length of output: 5889


🏁 Script executed:

# Test the regex patterns with example task descriptions
python3 << 'EOF'
import re
import subprocess

# Simulate the is_docs_only function
def test_is_docs_only(task):
    task_lower = task.lower()
    
    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_patterns = "^readme|^changelog|^documentation|docs/|typo|spelling|comment only|license only|^update readme|^update changelog|^update docs"
    
    # Check code patterns first
    if re.search(code_patterns, task_lower):
        return "NOT docs-only (code pattern match)"
    
    # Check docs patterns
    if re.search(docs_patterns, task_lower):
        return "IS docs-only (docs pattern match)"
    
    # Default
    return "NOT docs-only (default)"

# Test cases
test_tasks = [
    "Update docs/localhost.md for SSL",  # Review's example
    "Update README.md with SSL configuration",
    "Fix SSL certificate docs",
    "Add helper script for SSL",
    "Update docs/ssl-setup.md",
    "Document SSL port configuration",
    "Fix typo in docs/ssl.md",
    "Add SSL section to docs/README",
    "Refactor SSL helper function",
    "Update changelog for SSL fix",
]

print("Testing regex patterns:")
print("-" * 70)
for task in test_tasks:
    result = test_is_docs_only(task)
    print(f"Task: {task:<45} -> {result}")
EOF

Repository: marcusquinn/aidevops

Length of output: 150


🏁 Script executed:

# Also check if there's any documentation about Bash version requirements
rg "bash.*3\.|bash.*4\.|compatibility|version.*requirement" -i --type md

Repository: marcusquinn/aidevops

Length of output: 5395


🏁 Script executed:

# Check how pre-edit-check.sh is invoked in the codebase
rg "pre-edit-check\.sh" -B 2 -A 2

Repository: marcusquinn/aidevops

Length of output: 4315


🏁 Script executed:

# Manually test the regex patterns with grep
echo "Testing regex patterns:"
echo ""

# Test tasks
echo "Test 1: 'Update docs/localhost.md for SSL'"
echo "update docs/localhost.md for ssl" | grep -qE "feature|fix|bug|implement|refactor|add.*function|update.*code|create.*script|modify.*config|change.*logic|new.*api|endpoint|enhance|port|ssl|helper" && echo "  → Matches CODE_PATTERNS (NOT docs-only)" || echo "  → No code pattern"

echo ""
echo "Test 2: 'Update README.md with SSL configuration'"
echo "update readme.md with ssl configuration" | grep -qE "feature|fix|bug|implement|refactor|add.*function|update.*code|create.*script|modify.*config|change.*logic|new.*api|endpoint|enhance|port|ssl|helper" && echo "  → Matches CODE_PATTERNS (NOT docs-only)" || echo "  → No code pattern"

echo ""
echo "Test 3: 'Add helper script for SSL'"
echo "add helper script for ssl" | grep -qE "feature|fix|bug|implement|refactor|add.*function|update.*code|create.*script|modify.*config|change.*logic|new.*api|endpoint|enhance|port|ssl|helper" && echo "  → Matches CODE_PATTERNS (NOT docs-only)" || echo "  → No code pattern"

echo ""
echo "Test 4: 'Update docs/port-configuration.md (pure docs)'"
echo "update docs/port-configuration.md" | grep -qE "feature|fix|bug|implement|refactor|add.*function|update.*code|create.*script|modify.*config|change.*logic|new.*api|endpoint|enhance|port|ssl|helper" && echo "  → Matches CODE_PATTERNS (NOT docs-only - FALSE POSITIVE)" || echo "  → No code pattern"

echo ""
echo "Test 5: 'Fix localhost helper (code change)'"
echo "fix localhost helper" | grep -qE "feature|fix|bug|implement|refactor|add.*function|update.*code|create.*script|modify.*config|change.*logic|new.*api|endpoint|enhance|port|ssl|helper" && echo "  → Matches CODE_PATTERNS (NOT docs-only - CORRECT)" || echo "  → No code pattern"

Repository: marcusquinn/aidevops

Length of output: 559


🏁 Script executed:

# Check if pre-edit-check.sh is tested and what the test expectations are
find . -name "*test*" -o -name "*spec*" | xargs grep -l "pre-edit-check\|is_docs_only" 2>/dev/null | head -10

Repository: marcusquinn/aidevops

Length of output: 46


🏁 Script executed:

# Look for any Bash 3 vs 4 compatibility workarounds in the codebase
rg "tr.*lower|tolower|parameter.*expansion" -i --type sh

Repository: marcusquinn/aidevops

Length of output: 2129


Fix Bash 4+ incompatibility and tighten regex to eliminate false positives.

Two confirmed issues:

  1. Bash 4+ required but not guaranteed: ${task,,} requires Bash 4+. On macOS, /bin/bash is 3.2 by default and will fail with syntax error. Other scripts in the codebase use tr for Bash 3 compatibility (see setup.sh, beads-sync-helper.sh, etc.), and keyword-research-helper.sh is explicitly noted as "bash 3.2 compatible" in CHANGELOG.md. This script needs a compatibility workaround.

  2. Regex patterns cause false positives: The port|ssl|helper patterns misclassify pure docs tasks:

    • "Update docs/port-configuration.md" → incorrectly routed as code task
    • "Update docs/localhost.md for SSL" → incorrectly routed as code task
      These contradict the stated purpose. Removing these overly-broad patterns preserves correct behavior for legitimate code tasks while eliminating false routing.
Proposed fix

Replace ${task,,} with tr for Bash 3 compatibility and tighten the code patterns:

 is_docs_only() {
     local task="$1"
-    # Use bash 4.0+ parameter expansion for lowercase (more efficient than tr)
-    local task_lower="${task,,}"
+    # Use tr for bash 3.2+ compatibility (consistent with other scripts in framework)
+    local task_lower="$(echo "$task" | tr '[:upper:]' '[:lower:]')"
     
     # 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"
+    local code_patterns="feature|fix|bug|implement|refactor|add.*function|update.*code|create.*script|modify.*config|change.*logic|new.*api|endpoint|enhance"
     
     # Docs-only indicators (positive match)
     local docs_patterns="^readme|^changelog|^documentation|docs/|typo|spelling|comment only|license only|^update readme|^update changelog|^update docs"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# 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
}
# Function to detect if task is docs-only
is_docs_only() {
local task="$1"
# Use tr for bash 3.2+ compatibility (consistent with other scripts in framework)
local task_lower="$(echo "$task" | tr '[:upper:]' '[:lower:]')"
# 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"
# 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'
Expand All @@ -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}"
Expand Down
Loading
Loading