initial
This commit is contained in:
21
.claude/plan.md
Normal file
21
.claude/plan.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# This is a test project
|
||||
|
||||
## IMPORTANT (when working on a plan versus ccsb aka CLAUDE.MD)
|
||||
- Never use hello world as a string in testing
|
||||
- Use gemini-cli when you need to ask about documentation
|
||||
|
||||
## PLAN
|
||||
|
||||
### Basic file
|
||||
- Create a python file called main.py
|
||||
|
||||
### Architecture
|
||||
- The file should be able to print whatever it is given into split by space returned as json array
|
||||
|
||||
## POST TASK TASKS
|
||||
- Git commit with comprehensive changes
|
||||
- Restructure project architecture for scalability
|
||||
- Merge duplicate systems and optimize code quality
|
||||
- Create utility libraries for common operations
|
||||
- Performance testing and optimization
|
||||
- Final git commit
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.claude/settings.local.json
|
||||
test.*
|
||||
|
||||
59
Dockerfile
Normal file
59
Dockerfile
Normal file
@@ -0,0 +1,59 @@
|
||||
FROM node:18-alpine
|
||||
|
||||
# Install system dependencies
|
||||
RUN apk add --no-cache \
|
||||
git \
|
||||
curl \
|
||||
bash \
|
||||
vim \
|
||||
nano \
|
||||
openssh-client \
|
||||
python3 \
|
||||
py3-pip \
|
||||
build-base \
|
||||
ca-certificates \
|
||||
jq
|
||||
|
||||
# Set working directory to existing node user's home
|
||||
WORKDIR /home/node
|
||||
|
||||
# Install Claude Code
|
||||
RUN npm install -g @anthropic-ai/claude-code
|
||||
|
||||
# Install additional development tools
|
||||
RUN npm install -g \
|
||||
typescript \
|
||||
ts-node \
|
||||
nodemon \
|
||||
prettier \
|
||||
eslint
|
||||
|
||||
# Install gemini-cli
|
||||
RUN npm install -g @google/gemini-cli
|
||||
|
||||
# Create necessary directories for node user
|
||||
RUN mkdir -p /home/node/.claude \
|
||||
/home/node/.config \
|
||||
/home/node/.ssh \
|
||||
/home/node/logs
|
||||
|
||||
# Copy claude-loop script
|
||||
COPY claude-loop.sh /usr/local/bin/claude-loop
|
||||
RUN chmod +x /usr/local/bin/claude-loop
|
||||
|
||||
# Set ownership of node user directories
|
||||
RUN chown -R node:node /home/node
|
||||
|
||||
# Switch to node user
|
||||
USER node
|
||||
|
||||
RUN curl -fsSL https://bun.sh/install | bash
|
||||
|
||||
# Set environment variables for Claude Code
|
||||
ENV TERM=xterm-256color
|
||||
ENV COLORTERM=truecolor
|
||||
ENV NODE_ENV=development
|
||||
ENV PATH="/home/node/.bun/bin:$PATH"
|
||||
|
||||
# Default command
|
||||
CMD ["claude"]
|
||||
155
README.md
Normal file
155
README.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# Claude Loop Project
|
||||
|
||||
A Docker-based automated task execution system using Claude AI to process and complete tasks defined in `.claude/plan.md`.
|
||||
|
||||
## Demo
|
||||
|
||||

|
||||
|
||||
## Overview
|
||||
|
||||
This project provides two main scripts for interacting with Claude AI in a sandboxed Docker environment:
|
||||
|
||||
- **`ccl`** (Claude Code Loop) - Runs an automated loop that processes tasks from `.claude/plan.md`
|
||||
- **`ccsb`** (Claude Code Sandbox) - Executes single Claude commands with full tool access
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **Docker**
|
||||
2. **Claude CLI**
|
||||
3. **Gemini CLI** (optional) - For documentation queries / gemini-cli mcp (https://github.com/jamubc/gemini-mcp-tool)
|
||||
4. **User Home Directory** - Scripts must be run from within your home directory for security
|
||||
5. **Linux** (or WSL) - with user 1000:1000
|
||||
|
||||
## Security Warning
|
||||
|
||||
- While rootless docker is very safe, it still has access to the internet and you local network and can possibly cause (limited) havoc.
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Build the Docker Image
|
||||
|
||||
```bash
|
||||
docker compose build
|
||||
```
|
||||
|
||||
### 2. Configure Claude CLI
|
||||
|
||||
Ensure you have:
|
||||
- `~/.claude.json` - Claude CLI configuration
|
||||
- `~/.claude/` - Claude settings directory
|
||||
|
||||
### 3. Configure Gemini CLI (Optional)
|
||||
|
||||
If using gemini-cli for documentation queries:
|
||||
- `~/.gemini/` - Gemini settings
|
||||
- `GEMINI_API_KEY` environment variable
|
||||
|
||||
### 4. symlink (or move) to /bin
|
||||
```bash
|
||||
ln -s ccl /bin/ccl
|
||||
ln -s ccsb /bin/ccsb
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### CCL (Claude Code Loop)
|
||||
|
||||
Automatically processes tasks defined in `.claude/plan.md`:
|
||||
|
||||
```bash
|
||||
ccl
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Reads tasks from `.claude/plan.md`
|
||||
- Updates task statuses: `(Not Started)` → `(In Progress)` → `(Completed)/(Aborted)`
|
||||
- Continues until all tasks show `(Completed)`
|
||||
- Creates `/tmp/plan_complete` when finished
|
||||
- Pretty formatted output with progress tracking
|
||||
|
||||
**Task Status Format:**
|
||||
```markdown
|
||||
- (Status) Task description
|
||||
```
|
||||
|
||||
Status options: `Not Started | In Progress | Aborted | Completed`
|
||||
|
||||
### CCSB (Claude Code Sandbox)
|
||||
|
||||
Execute claude in a sandbox with all permissions.
|
||||
|
||||
```bash
|
||||
ccsb
|
||||
```
|
||||
|
||||
## Plan File Structure
|
||||
|
||||
The `.claude/plan.md` file defines tasks to be executed:
|
||||
|
||||
```markdown
|
||||
# Project Name
|
||||
|
||||
## IMPORTANT (instructions for Claude when in ccl mode, does not apply to ccsb)
|
||||
- Project-specific guidelines
|
||||
- Tool preferences
|
||||
- Constraints
|
||||
|
||||
## PLAN
|
||||
|
||||
### Section 1
|
||||
- Task 1 description
|
||||
- Task 2 description
|
||||
|
||||
### Section 2
|
||||
- Task 3 description
|
||||
|
||||
## POST TASK TASKS
|
||||
- Cleanup tasks
|
||||
- Final commits
|
||||
- Documentation updates
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
- You can ask ccsb to run claude-loop
|
||||
- You can ask claude to keep a work-log such as:
|
||||
```markdown
|
||||
- Append work to .claude/work-log.md, never read entire file into context with format $(date): <task>\n\n
|
||||
- tail work-log.md before starting
|
||||
- Focus on words with !! for accuracy
|
||||
- Look for (Changes Needed) and view all the changes requested below
|
||||
```
|
||||
- Changes Needed example
|
||||
```markdown
|
||||
### Some Tasks Topic
|
||||
- (Completed) Task1
|
||||
- (Changes Needed) Original task to build a snowman
|
||||
- You built a snowman without a head, add a head
|
||||
```
|
||||
|
||||
## Security Features
|
||||
|
||||
- **Home Directory Restriction**: Scripts only run from within user home directory
|
||||
- **Docker Isolation**: All Claude operations run in isolated container
|
||||
- **Non-root Execution**: Container runs as user `1000:1000`
|
||||
- **Limited File Access**: Only mounted directories are accessible
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **"Must be run from within user home directory"**
|
||||
- Ensure you're running the script from a subdirectory of `$HOME`
|
||||
|
||||
2. **Docker permission errors**
|
||||
- Check Docker is running and user has permissions
|
||||
- Verify volume mounts point to existing directories
|
||||
|
||||
3. **Claude CLI not configured**
|
||||
- Run `claude` to set up claude
|
||||
- Ensure `~/.claude.json` exists
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
30
ccl
Executable file
30
ccl
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Check if current directory is under user home
|
||||
if [[ "$PWD" != "$HOME"* ]]; then
|
||||
echo "Error: Must be run from within user home directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build docker volume arguments
|
||||
VOLUME_ARGS=""
|
||||
VOLUME_ARGS="$VOLUME_ARGS -v $HOME/.claude:/home/node/.claude"
|
||||
VOLUME_ARGS="$VOLUME_ARGS -v $HOME/.claude.json:/home/node/.claude.json"
|
||||
|
||||
# Only mount gemini directories if they exist
|
||||
if [[ -d $HOME/.config/gemini-cli ]]; then
|
||||
VOLUME_ARGS="$VOLUME_ARGS -v $HOME/.config/gemini-cli:/home/node/.config/gemini-cli"
|
||||
fi
|
||||
|
||||
if [[ -d $HOME/.gemini ]]; then
|
||||
VOLUME_ARGS="$VOLUME_ARGS -v $HOME/.gemini:/home/node/.gemini"
|
||||
fi
|
||||
|
||||
VOLUME_ARGS="$VOLUME_ARGS -v $PWD:/home/node/workspace"
|
||||
|
||||
docker run --rm -it \
|
||||
--user "1000:1000" \
|
||||
-e GEMINI_API_KEY \
|
||||
$VOLUME_ARGS \
|
||||
claude \
|
||||
sh -c 'cd workspace && claude-loop'
|
||||
30
ccsb
Executable file
30
ccsb
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Check if current directory is under user home
|
||||
if [[ "$PWD" != "$HOME"* ]]; then
|
||||
echo "Error: Must be run from within user home directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build docker volume arguments
|
||||
VOLUME_ARGS=""
|
||||
VOLUME_ARGS="$VOLUME_ARGS -v $HOME/.claude:/home/node/.claude"
|
||||
VOLUME_ARGS="$VOLUME_ARGS -v $HOME/.claude.json:/home/node/.claude.json"
|
||||
|
||||
# Only mount gemini directories if they exist
|
||||
if [[ -d $HOME/.config/gemini-cli ]]; then
|
||||
VOLUME_ARGS="$VOLUME_ARGS -v $HOME/.config/gemini-cli:/home/node/.config/gemini-cli"
|
||||
fi
|
||||
|
||||
if [[ -d $HOME/.gemini ]]; then
|
||||
VOLUME_ARGS="$VOLUME_ARGS -v $HOME/.gemini:/home/node/.gemini"
|
||||
fi
|
||||
|
||||
VOLUME_ARGS="$VOLUME_ARGS -v $PWD:/home/node/workspace"
|
||||
|
||||
docker run --rm -it \
|
||||
--user "1000:1000" \
|
||||
-e GEMINI_API_KEY \
|
||||
$VOLUME_ARGS \
|
||||
claude \
|
||||
sh -c 'cd workspace && claude --dangerously-skip-permissions'
|
||||
242
claude-loop.sh
Executable file
242
claude-loop.sh
Executable file
@@ -0,0 +1,242 @@
|
||||
#!/bin/bash
|
||||
# claude-loop.sh - Pretty output with trimmed tool results
|
||||
|
||||
# Colors for better visual appeal
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
MAGENTA='\033[0;35m'
|
||||
CYAN='\033[0;36m'
|
||||
WHITE='\033[1;37m'
|
||||
GRAY='\033[0;90m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Box drawing characters for prettier output
|
||||
BOX_H="─"
|
||||
BOX_V="│"
|
||||
BOX_TL="┌"
|
||||
BOX_TR="┐"
|
||||
BOX_BL="└"
|
||||
BOX_BR="┘"
|
||||
|
||||
rm -f /tmp/plan_complete
|
||||
|
||||
iteration=1
|
||||
total_cost=0
|
||||
total_input_tokens=0
|
||||
total_output_tokens=0
|
||||
|
||||
# Function to print a fancy header
|
||||
print_header() {
|
||||
local text="$1"
|
||||
local width=60
|
||||
echo -e "${CYAN}${BOX_TL}$(printf "%.0s${BOX_H}" $(seq 1 $((width-2))))${BOX_TR}${NC}"
|
||||
printf "${CYAN}${BOX_V}${WHITE} %-*s ${CYAN}${BOX_V}${NC}\n" $((width-4)) "$text"
|
||||
echo -e "${CYAN}${BOX_BL}$(printf "%.0s${BOX_H}" $(seq 1 $((width-2))))${BOX_BR}${NC}"
|
||||
}
|
||||
|
||||
# Function to trim and format text nicely
|
||||
trim_text() {
|
||||
local text="$1"
|
||||
local max_length="${2:-200}"
|
||||
|
||||
# Remove excessive whitespace and newlines
|
||||
text=$(echo "$text" | tr '\n' ' ' | sed 's/[[:space:]]\+/ /g' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||
|
||||
if [[ ${#text} -le $max_length ]]; then
|
||||
echo "$text"
|
||||
else
|
||||
echo "${text:0:$max_length}..."
|
||||
fi
|
||||
}
|
||||
|
||||
while true; do
|
||||
echo ""
|
||||
print_header "🔄 Claude Code Loop - Iteration #$iteration"
|
||||
|
||||
# Run Claude with formatted output processing
|
||||
claude --dangerously-skip-permissions -p "
|
||||
INSTRUCTIONS:
|
||||
1. Read .claude/plan.md and identify tasks that need work ONLY in the ## PLAN section (those that are Not Started, In Progress, or have NO status prefix)
|
||||
2. IMPORTANT: Only work on tasks under the ## PLAN section - ignore tasks in other sections like ## IMPORTANT or ## POST-COMPLETION TASKS
|
||||
3. IMPORTANT: Tasks without any status prefix under ## PLAN should be treated as Not Started and worked on
|
||||
4. Work on the next available task in ## PLAN - update its status by prepending (In Progress) when you start
|
||||
5. Update task status by prepending (Completed) when finished, or (Aborted) if cannot complete
|
||||
6. Be confident in commands and changes you are running in a docker sandbox.
|
||||
7. Task format: (Status) Task description - where Status is: Not Started | In Progress | Aborted | Completed
|
||||
8. Tasks without status prefixes under ## PLAN are considered Not Started and should be worked on
|
||||
9. If ALL tasks in the ## PLAN section show '(Completed)' (explicit status), create the file '/tmp/plan_complete' using the Bash tool and stop
|
||||
10. Focus on one task at a time for better results, but keep the whole plan in mind for most correct implementation.
|
||||
|
||||
Current objective: Process tasks in the ## PLAN section of .claude/plan.md systematically until all tasks explicitly show '(Completed)'.
|
||||
" --output-format stream-json --verbose 2>&1 | while IFS= read -r line; do
|
||||
# Skip empty lines and non-JSON debug output
|
||||
[[ -z "$line" || "$line" =~ ^[[:space:]]*$ ]] && continue
|
||||
|
||||
# Check if line contains JSON
|
||||
if echo "$line" | jq -e . >/dev/null 2>&1; then
|
||||
# Extract message type and content
|
||||
msg_type=$(echo "$line" | jq -r '.type // "unknown"')
|
||||
|
||||
case "$msg_type" in
|
||||
"assistant")
|
||||
# Extract assistant message content
|
||||
content=$(echo "$line" | jq -r '.message.content[]? | select(.type=="text") | .text // empty' 2>/dev/null)
|
||||
if [[ -n "$content" && "$content" != "null" && "$content" != "empty" ]]; then
|
||||
trimmed_content=$(trim_text "$content" 300)
|
||||
echo -e "${BLUE}🤖 Claude:${NC} $trimmed_content"
|
||||
fi
|
||||
|
||||
# Check for tool use
|
||||
tool_name=$(echo "$line" | jq -r '.message.content[]? | select(.type=="tool_use") | .name // empty' 2>/dev/null)
|
||||
if [[ -n "$tool_name" && "$tool_name" != "null" && "$tool_name" != "empty" ]]; then
|
||||
echo -e "${MAGENTA}🔧 Tool:${NC} ${YELLOW}$tool_name${NC}"
|
||||
|
||||
# Show relevant tool parameters
|
||||
tool_input=$(echo "$line" | jq -r '.message.content[]? | select(.type=="tool_use") | .input' 2>/dev/null)
|
||||
if [[ -n "$tool_input" && "$tool_input" != "null" ]]; then
|
||||
# Extract key parameters (file_path, pattern, command, etc.)
|
||||
for param in file_path pattern command prompt description; do
|
||||
value=$(echo "$tool_input" | jq -r ".$param // empty" 2>/dev/null)
|
||||
if [[ -n "$value" && "$value" != "null" && "$value" != "empty" ]]; then
|
||||
trimmed_value=$(trim_text "$value" 80)
|
||||
echo -e " ${GRAY}$param:${NC} $trimmed_value"
|
||||
break # Show only the first relevant parameter
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
"user")
|
||||
# Extract and format tool results
|
||||
tool_result=$(echo "$line" | jq -r '.message.content[]?.content // empty' 2>/dev/null)
|
||||
if [[ -n "$tool_result" && "$tool_result" != "null" && "$tool_result" != "empty" ]]; then
|
||||
# Check if it's a file content, error, or other result
|
||||
if [[ "$tool_result" =~ ^[[:space:]]*[0-9]+→ ]]; then
|
||||
# File content with line numbers
|
||||
line_count=$(echo "$tool_result" | wc -l)
|
||||
first_lines=$(echo "$tool_result" | head -3 | tr '\n' ' ')
|
||||
trimmed_first=$(trim_text "$first_lines" 100)
|
||||
echo -e "${GREEN}📄 File content:${NC} $trimmed_first ${GRAY}($line_count lines)${NC}"
|
||||
elif [[ "$tool_result" =~ ^Error: ]] || [[ "$tool_result" =~ failed ]]; then
|
||||
# Error message
|
||||
trimmed_error=$(trim_text "$tool_result" 150)
|
||||
echo -e "${RED}❌ Error:${NC} $trimmed_error"
|
||||
elif [[ ${#tool_result} -gt 500 ]]; then
|
||||
# Long output - show beginning and stats
|
||||
trimmed_result=$(trim_text "$tool_result" 200)
|
||||
echo -e "${GREEN}📤 Output:${NC} $trimmed_result ${GRAY}(${#tool_result} chars total)${NC}"
|
||||
else
|
||||
# Short result - show it all
|
||||
trimmed_result=$(trim_text "$tool_result" 300)
|
||||
echo -e "${GREEN}📤 Result:${NC} $trimmed_result"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
"result")
|
||||
# Final result with colored status
|
||||
success=$(echo "$line" | jq -r '.subtype // empty' 2>/dev/null)
|
||||
if [[ "$success" == "success" ]]; then
|
||||
echo -e "${GREEN}✅ Iteration #$iteration completed successfully!${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Iteration #$iteration completed with issues${NC}"
|
||||
fi
|
||||
|
||||
# Show cost information if available
|
||||
cost_usd=$(echo "$line" | jq -r '.total_cost_usd // 0' 2>/dev/null)
|
||||
input_tokens=$(echo "$line" | jq -r '.usage.input_tokens // 0' 2>/dev/null)
|
||||
output_tokens=$(echo "$line" | jq -r '.usage.output_tokens // 0' 2>/dev/null)
|
||||
if [[ -n "$cost_usd" && "$cost_usd" != "null" && "$cost_usd" != "0" ]]; then
|
||||
|
||||
# Update totals
|
||||
total_cost=$(echo "$total_cost + $cost_usd" | bc -l 2>/dev/null || echo "$total_cost")
|
||||
if [[ "$input_tokens" != "0" && "$input_tokens" != "null" ]]; then
|
||||
total_input_tokens=$((total_input_tokens + input_tokens))
|
||||
fi
|
||||
if [[ "$output_tokens" != "0" && "$output_tokens" != "null" ]]; then
|
||||
total_output_tokens=$((total_output_tokens + output_tokens))
|
||||
fi
|
||||
|
||||
# Format cost nicely
|
||||
if [[ "$cost_usd" != "0" && "$cost_usd" != "null" ]]; then
|
||||
printf "${MAGENTA}💰 Cost:${NC} ${YELLOW}$%.4f${NC} ${GRAY}(in: %s, out: %s tokens)${NC}\n" "$cost_usd" "$input_tokens" "$output_tokens"
|
||||
elif [[ "$input_tokens" != "0" || "$output_tokens" != "0" ]]; then
|
||||
echo -e "${MAGENTA}💰 Tokens:${NC} ${GRAY}in: $input_tokens, out: $output_tokens${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Show brief final result
|
||||
result=$(echo "$line" | jq -r '.result // empty' 2>/dev/null)
|
||||
if [[ -n "$result" && "$result" != "null" ]]; then
|
||||
trimmed_result=$(trim_text "$result" 250)
|
||||
echo -e "${WHITE}📋 Summary:${NC} $trimmed_result"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
else
|
||||
# Filter out verbose debug output - only show actual error messages
|
||||
if [[ "$line" =~ ^Error: && ! "$line" =~ ^[[:space:]]*$ ]]; then
|
||||
echo -e "${RED}⚠️ $line${NC}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Check if plan is complete
|
||||
if [ -f /tmp/plan_complete ]; then
|
||||
echo ""
|
||||
echo -e "${GREEN}┌─────────────────────────────────────────────────────────┐${NC}"
|
||||
echo -e "${GREEN}│${WHITE} 🎉 ALL TASKS COMPLETED! 🎉 ${GREEN}│${NC}"
|
||||
echo -e "${GREEN}└─────────────────────────────────────────────────────────┘${NC}"
|
||||
|
||||
if [ -s /tmp/plan_complete ]; then
|
||||
echo -e "${CYAN}📄 Completion details:${NC}"
|
||||
completion_content=$(cat /tmp/plan_complete)
|
||||
if [[ ${#completion_content} -gt 300 ]]; then
|
||||
trimmed_completion=$(trim_text "$completion_content" 300)
|
||||
echo -e "${WHITE}$trimmed_completion${NC}"
|
||||
else
|
||||
echo -e "${WHITE}$completion_content${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Show total cost summary
|
||||
if [[ $(echo "$total_cost > 0" | bc -l 2>/dev/null) == "1" ]] || [[ $total_input_tokens -gt 0 ]] || [[ $total_output_tokens -gt 0 ]]; then
|
||||
echo ""
|
||||
if [[ $(echo "$total_cost > 0" | bc -l 2>/dev/null) == "1" ]]; then
|
||||
printf "${MAGENTA}💰 Total Cost:${NC} ${YELLOW}$%.4f${NC} ${GRAY}(%d iterations, %s input + %s output tokens)${NC}\n" \
|
||||
"$total_cost" "$((iteration-1))" "$total_input_tokens" "$total_output_tokens"
|
||||
else
|
||||
echo -e "${MAGENTA}💰 Total Tokens:${NC} ${GRAY}$total_input_tokens input + $total_output_tokens output across $((iteration-1)) iterations${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GRAY}✅ Task complete - exiting...${NC}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Show iteration completion with progress indicator
|
||||
echo ""
|
||||
echo -e "${CYAN}┌─────────────────────────────────────────────────────────┐${NC}"
|
||||
echo -e "${CYAN}│${WHITE} ⏸️ Iteration #$iteration complete - preparing next... ${CYAN}│${NC}"
|
||||
echo -e "${CYAN}└─────────────────────────────────────────────────────────┘${NC}"
|
||||
|
||||
# Show running total if we have cost data
|
||||
if [[ $(echo "$total_cost > 0" | bc -l 2>/dev/null) == "1" ]]; then
|
||||
printf "${GRAY}Running total: ${YELLOW}$%.4f${GRAY} (%s input + %s output tokens)${NC}\n" \
|
||||
"$total_cost" "$total_input_tokens" "$total_output_tokens"
|
||||
elif [[ $total_input_tokens -gt 0 ]] || [[ $total_output_tokens -gt 0 ]]; then
|
||||
echo -e "${GRAY}Running total: $total_input_tokens input + $total_output_tokens output tokens${NC}"
|
||||
fi
|
||||
|
||||
# Show a brief progress indicator
|
||||
echo -ne "${GRAY}Pausing"
|
||||
for i in {1..3}; do
|
||||
sleep 0.7
|
||||
echo -ne "."
|
||||
done
|
||||
echo -e " ready!${NC}"
|
||||
|
||||
((iteration++))
|
||||
done
|
||||
14
compose.yaml
Normal file
14
compose.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
services:
|
||||
claude-code:
|
||||
user: "1000:1000"
|
||||
image: claude
|
||||
build: .
|
||||
volumes:
|
||||
- ~/.claude:/home/node/.claude
|
||||
- ~/.claude.json:/home/node/.claude.json
|
||||
- ~/.config/gemini-cli:/home/node/.config/gemini-cli
|
||||
- ~/.gemini:/home/node/.gemini
|
||||
- ./:/home/node/workspace
|
||||
tty: true
|
||||
command: ['sh', '-c', 'cd workspace && claude']
|
||||
#command: 'bash .claude/claude-loop.sh'
|
||||
Reference in New Issue
Block a user