|
#!/usr/bin/env bash |
|
# AI Agent - Secure container-based autonomous code modification |
|
set -e |
|
|
|
# Colors |
|
RED='\033[0;31m' |
|
GREEN='\033[0;32m' |
|
YELLOW='\033[1;33m' |
|
BLUE='\033[0;34m' |
|
NC='\033[0m' # No Color |
|
|
|
# Helper functions |
|
info() { |
|
echo -e "${GREEN}✓${NC} $1" |
|
} |
|
|
|
warn() { |
|
echo -e "${YELLOW}⚠${NC} $1" |
|
} |
|
|
|
error() { |
|
echo -e "${RED}✗${NC} $1" |
|
} |
|
|
|
# Find the script directory |
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" |
|
|
|
# Parse arguments |
|
show_help() { |
|
cat << EOF |
|
AI Agent - Secure container-based autonomous code modification |
|
|
|
Usage: |
|
ai_agent <directory> <prompt> |
|
ai_agent [options] |
|
|
|
Arguments: |
|
<directory> Target directory (default: current directory) |
|
<prompt> Task prompt for Claude |
|
|
|
Options: |
|
-h, --help Show this help message |
|
--memory SIZE Memory limit (default: 4g) |
|
--cpus NUM CPU limit (default: 2) |
|
--rebuild Force rebuild the container image |
|
|
|
Examples: |
|
ai_agent . "Add error handling to all API endpoints" |
|
ai_agent ~/Code/MyProject "Refactor database layer" |
|
ai_agent --memory 8g . "Add comprehensive tests" |
|
|
|
Prerequisites: |
|
1. Docker installed and running |
|
2. Authentication: ai_auth login <profile> |
|
EOF |
|
} |
|
|
|
# Default values |
|
MEMORY="4g" |
|
CPUS="2" |
|
REBUILD=false |
|
TARGET_DIR="" |
|
PROMPT="" |
|
|
|
# Parse options |
|
while [[ $# -gt 0 ]]; do |
|
case $1 in |
|
-h|--help) |
|
show_help |
|
exit 0 |
|
;; |
|
--memory) |
|
MEMORY="$2" |
|
shift 2 |
|
;; |
|
--cpus) |
|
CPUS="$2" |
|
shift 2 |
|
;; |
|
--rebuild) |
|
REBUILD=true |
|
shift |
|
;; |
|
*) |
|
if [ -z "$TARGET_DIR" ]; then |
|
TARGET_DIR="$1" |
|
else |
|
# Everything else is part of the prompt |
|
PROMPT="$PROMPT $1" |
|
fi |
|
shift |
|
;; |
|
esac |
|
done |
|
|
|
# Trim whitespace from prompt |
|
PROMPT=$(echo "$PROMPT" | xargs) |
|
|
|
# Validate arguments |
|
if [ -z "$TARGET_DIR" ]; then |
|
error "No target directory specified" |
|
echo "" |
|
show_help |
|
exit 1 |
|
fi |
|
|
|
if [ -z "$PROMPT" ]; then |
|
error "No prompt specified" |
|
echo "" |
|
show_help |
|
exit 1 |
|
fi |
|
|
|
# Resolve absolute path |
|
TARGET_DIR=$(cd "$TARGET_DIR" && pwd) |
|
|
|
# Verify directory exists |
|
if [ ! -d "$TARGET_DIR" ]; then |
|
error "Directory not found: $TARGET_DIR" |
|
exit 1 |
|
fi |
|
|
|
# Find git root if we're in a git repository |
|
GIT_ROOT="$TARGET_DIR" |
|
if [ -d "$TARGET_DIR/.git" ]; then |
|
GIT_ROOT="$TARGET_DIR" |
|
else |
|
# Try to find git root from target directory |
|
cd "$TARGET_DIR" |
|
if git rev-parse --git-dir > /dev/null 2>&1; then |
|
GIT_ROOT=$(git rev-parse --show-toplevel) |
|
info "Found git root: $GIT_ROOT" |
|
fi |
|
cd - > /dev/null |
|
fi |
|
|
|
echo "" |
|
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" |
|
echo -e "${BLUE}🤖 AI Agent - Autonomous Code Modification${NC}" |
|
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" |
|
echo "" |
|
info "Working directory: $GIT_ROOT" |
|
if [ "$GIT_ROOT" != "$TARGET_DIR" ]; then |
|
info "Target directory: $TARGET_DIR" |
|
fi |
|
info "Prompt: $PROMPT" |
|
echo "" |
|
|
|
# Get authentication from ai_auth |
|
echo "Fetching authentication..." |
|
AUTH_TOKEN=$(bb -cp "$PROJECT_ROOT/src" -e "(require '[common.auth.profiles :as p]) (let [auth (p/get-current-auth)] (or (:session-key auth) (:api-key auth)))" 2>/dev/null) |
|
|
|
if [ -z "$AUTH_TOKEN" ] || [ "$AUTH_TOKEN" = "nil" ]; then |
|
error "No authentication found" |
|
echo "Run: ai_auth login <profile>" |
|
exit 1 |
|
fi |
|
|
|
info "Authentication found" |
|
echo "" |
|
|
|
# Check if git repo exists |
|
if [ -d "$GIT_ROOT/.git" ] || (cd "$GIT_ROOT" && git rev-parse --git-dir > /dev/null 2>&1); then |
|
info "Git repository detected" |
|
|
|
# Check for uncommitted changes |
|
cd "$GIT_ROOT" |
|
if ! git diff-index --quiet HEAD -- 2>/dev/null; then |
|
warn "Uncommitted changes detected" |
|
echo " Consider committing before running" |
|
fi |
|
cd - > /dev/null |
|
else |
|
warn "Not a git repository - changes won't be tracked" |
|
fi |
|
|
|
echo "" |
|
|
|
# Build or rebuild container image |
|
IMAGE_NAME="ai-agent:latest" |
|
|
|
if [ "$REBUILD" = true ] || ! docker images | grep -q "ai-agent"; then |
|
echo "Building container image..." |
|
docker build -t "$IMAGE_NAME" -f "$SCRIPT_DIR/Dockerfile" "$SCRIPT_DIR" |
|
info "Container image ready" |
|
echo "" |
|
fi |
|
|
|
# Check for AGENTS.md at git root and prepend instruction to read it |
|
if [ -f "$GIT_ROOT/AGENTS.md" ]; then |
|
info "Found AGENTS.md - instructing Claude to read it" |
|
PROMPT="Read and follow the instructions in AGENTS.md first, then: $PROMPT" |
|
fi |
|
|
|
# Run container |
|
echo "Launching container..." |
|
echo "" |
|
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" |
|
echo "" |
|
|
|
docker run --rm \ |
|
-it \ |
|
-v "$GIT_ROOT:/workspace" \ |
|
-v "$HOME/.claude:/home/llmuser/.claude" \ |
|
-v "$HOME/.claude.json:/home/llmuser/.claude.json" \ |
|
-v "$SCRIPT_DIR/mcp-config.json:/home/llmuser/.claude/mcp-config.json:ro" \ |
|
-e "CLAUDE_SESSION_KEY=$AUTH_TOKEN" \ |
|
--memory "$MEMORY" \ |
|
--cpus "$CPUS" \ |
|
--user llmuser \ |
|
--add-host=host.docker.internal:host-gateway \ |
|
"$IMAGE_NAME" \ |
|
"$PROMPT" |
|
|
|
EXIT_CODE=$? |
|
|
|
echo "" |
|
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" |
|
echo "" |
|
|
|
if [ $EXIT_CODE -eq 0 ]; then |
|
info "Agent completed successfully" |
|
echo "" |
|
echo "Review changes:" |
|
echo " cd $GIT_ROOT" |
|
echo " git diff HEAD~1" |
|
echo "" |
|
echo "Rollback if needed:" |
|
echo " git reset --hard HEAD~1" |
|
else |
|
error "Agent failed with exit code $EXIT_CODE" |
|
exit $EXIT_CODE |
|
fi |