wile 0.4.3 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/.env.example +55 -0
- package/dist/agent/Dockerfile +97 -0
- package/dist/agent/README.md +150 -0
- package/dist/agent/docker-compose.yml +27 -0
- package/dist/agent/entrypoint.sh +277 -0
- package/dist/agent/package.json +6 -0
- package/dist/agent/scripts/browser-test.ts +106 -0
- package/dist/agent/scripts/host-setup.sh +35 -0
- package/dist/agent/scripts/prompt-setup.md +23 -0
- package/dist/agent/scripts/prompt.md +168 -0
- package/dist/agent/scripts/test-additional-instructions.sh +82 -0
- package/dist/agent/scripts/wile.sh +104 -0
- package/dist/cli.js +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Wile Agent Configuration
|
|
2
|
+
# Copy this file to .env and fill in your values
|
|
3
|
+
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# REQUIRED - Claude Code Authentication (choose ONE)
|
|
6
|
+
# =============================================================================
|
|
7
|
+
|
|
8
|
+
# OPTION 1: OAuth Token (RECOMMENDED - uses your Pro/Max subscription)
|
|
9
|
+
# Generate with: claude setup-token
|
|
10
|
+
# This uses your subscription, NOT API credits!
|
|
11
|
+
CC_CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-xxxxx
|
|
12
|
+
|
|
13
|
+
# OPTION 2: API Key (uses API credits - pay per token)
|
|
14
|
+
# Get one at: https://console.anthropic.com/
|
|
15
|
+
# Only use this if you don't have a Pro/Max subscription
|
|
16
|
+
# CC_ANTHROPIC_API_KEY=sk-ant-xxxxx
|
|
17
|
+
|
|
18
|
+
# =============================================================================
|
|
19
|
+
# GitHub Authentication (required when WILE_REPO_SOURCE=github)
|
|
20
|
+
# =============================================================================
|
|
21
|
+
|
|
22
|
+
# GitHub Personal Access Token (fine-grained recommended)
|
|
23
|
+
# Create at: https://github.com/settings/tokens?type=beta
|
|
24
|
+
# Required permissions: Contents (read/write), Metadata (read)
|
|
25
|
+
# Scope to specific repositories only for security
|
|
26
|
+
GITHUB_TOKEN=github_pat_xxxxx
|
|
27
|
+
|
|
28
|
+
# =============================================================================
|
|
29
|
+
# Target Repository (required when WILE_REPO_SOURCE=github)
|
|
30
|
+
# =============================================================================
|
|
31
|
+
|
|
32
|
+
# Repo source: github (default) or local
|
|
33
|
+
WILE_REPO_SOURCE=github
|
|
34
|
+
|
|
35
|
+
# Full HTTPS URL to the GitHub repository
|
|
36
|
+
GITHUB_REPO_URL=https://github.com/username/repo
|
|
37
|
+
|
|
38
|
+
# Branch to work on (must contain .wile/prd.json at root)
|
|
39
|
+
BRANCH_NAME=feature/my-feature
|
|
40
|
+
|
|
41
|
+
# =============================================================================
|
|
42
|
+
# OPTIONAL - Configuration
|
|
43
|
+
# =============================================================================
|
|
44
|
+
|
|
45
|
+
# Maximum loop iterations before stopping (default: 25)
|
|
46
|
+
MAX_ITERATIONS=25
|
|
47
|
+
|
|
48
|
+
# Claude model alias or full model name (default: sonnet)
|
|
49
|
+
CC_CLAUDE_MODEL=sonnet
|
|
50
|
+
|
|
51
|
+
# =============================================================================
|
|
52
|
+
# PROJECT ENVIRONMENT VARIABLES
|
|
53
|
+
# =============================================================================
|
|
54
|
+
# Wile forwards variables from .wile/secrets/.env.project into the container
|
|
55
|
+
# as-is. Put your project-specific values there (no ENV_FORWARD_ prefix).
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
FROM ubuntu:22.04
|
|
2
|
+
|
|
3
|
+
# Prevent interactive prompts during package installation
|
|
4
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
|
5
|
+
|
|
6
|
+
# Install base dependencies
|
|
7
|
+
RUN apt-get update && apt-get install -y \
|
|
8
|
+
curl \
|
|
9
|
+
git \
|
|
10
|
+
ca-certificates \
|
|
11
|
+
gnupg \
|
|
12
|
+
unzip \
|
|
13
|
+
build-essential \
|
|
14
|
+
software-properties-common \
|
|
15
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
16
|
+
|
|
17
|
+
# Install Node.js 20 LTS
|
|
18
|
+
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
|
19
|
+
&& apt-get install -y nodejs \
|
|
20
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
21
|
+
|
|
22
|
+
# Install Bun to /usr/local (globally accessible)
|
|
23
|
+
RUN curl -fsSL https://bun.sh/install | BUN_INSTALL=/usr/local bash
|
|
24
|
+
|
|
25
|
+
# Install Python 3.11
|
|
26
|
+
RUN add-apt-repository ppa:deadsnakes/ppa -y \
|
|
27
|
+
&& apt-get update \
|
|
28
|
+
&& apt-get install -y python3.11 python3.11-venv python3-pip \
|
|
29
|
+
&& update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1 \
|
|
30
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
31
|
+
|
|
32
|
+
# Create non-root user
|
|
33
|
+
RUN useradd -m -s /bin/bash wile \
|
|
34
|
+
&& mkdir -p /home/wile/workspace \
|
|
35
|
+
&& chown -R wile:wile /home/wile
|
|
36
|
+
|
|
37
|
+
# Memory optimization settings per Claude Code specs
|
|
38
|
+
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
|
39
|
+
|
|
40
|
+
# Note: The following settings must be configured on the HOST (EC2 instance), not in Docker:
|
|
41
|
+
# - Swap (8GB): fallocate -l 8G /swapfile && mkswap /swapfile && swapon /swapfile
|
|
42
|
+
# - zswap with lz4: echo lz4 > /sys/module/zswap/parameters/compressor && echo 1 > /sys/module/zswap/parameters/enabled
|
|
43
|
+
# - vm.swappiness=10: sysctl -w vm.swappiness=10
|
|
44
|
+
# - earlyoom: apt install earlyoom && systemctl enable earlyoom
|
|
45
|
+
# See packages/agent/scripts/host-setup.sh for EC2 bootstrap
|
|
46
|
+
|
|
47
|
+
# Install Claude Code CLI
|
|
48
|
+
# https://www.npmjs.com/package/@anthropic-ai/claude-code
|
|
49
|
+
RUN npm install -g @anthropic-ai/claude-code
|
|
50
|
+
|
|
51
|
+
# CC_ANTHROPIC_API_KEY is passed at runtime via environment variable (mapped to ANTHROPIC_API_KEY in entrypoint)
|
|
52
|
+
# Usage: claude --dangerously-skip-permissions
|
|
53
|
+
|
|
54
|
+
# Install Playwright with Chromium for browser testing
|
|
55
|
+
# Dependencies for headless Chromium
|
|
56
|
+
RUN apt-get update && apt-get install -y \
|
|
57
|
+
libnss3 \
|
|
58
|
+
libnspr4 \
|
|
59
|
+
libatk1.0-0 \
|
|
60
|
+
libatk-bridge2.0-0 \
|
|
61
|
+
libcups2 \
|
|
62
|
+
libdrm2 \
|
|
63
|
+
libxkbcommon0 \
|
|
64
|
+
libxcomposite1 \
|
|
65
|
+
libxdamage1 \
|
|
66
|
+
libxfixes3 \
|
|
67
|
+
libxrandr2 \
|
|
68
|
+
libgbm1 \
|
|
69
|
+
libasound2 \
|
|
70
|
+
libpango-1.0-0 \
|
|
71
|
+
libcairo2 \
|
|
72
|
+
libatspi2.0-0 \
|
|
73
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
74
|
+
|
|
75
|
+
# Install Playwright globally and download Chromium to shared location
|
|
76
|
+
ENV PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-browsers
|
|
77
|
+
RUN npm install -g playwright \
|
|
78
|
+
&& mkdir -p /opt/playwright-browsers \
|
|
79
|
+
&& npx playwright install chromium \
|
|
80
|
+
&& chmod -R 755 /opt/playwright-browsers
|
|
81
|
+
|
|
82
|
+
# Copy scripts
|
|
83
|
+
COPY scripts/ /home/wile/scripts/
|
|
84
|
+
COPY entrypoint.sh /home/wile/entrypoint.sh
|
|
85
|
+
|
|
86
|
+
RUN chmod +x /home/wile/entrypoint.sh \
|
|
87
|
+
&& chmod +x /home/wile/scripts/*.sh 2>/dev/null || true \
|
|
88
|
+
&& chown -R wile:wile /home/wile
|
|
89
|
+
|
|
90
|
+
# Working directory
|
|
91
|
+
WORKDIR /home/wile/workspace
|
|
92
|
+
|
|
93
|
+
# Switch to non-root user
|
|
94
|
+
USER wile
|
|
95
|
+
|
|
96
|
+
# Default command
|
|
97
|
+
ENTRYPOINT ["/home/wile/entrypoint.sh"]
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Wile Agent
|
|
2
|
+
|
|
3
|
+
Autonomous AI coding agent that runs in a Docker container. Ships features while you sleep.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### 1. Set up your environment
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
cp .env.example .env
|
|
11
|
+
# Edit .env with your credentials
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### 2. Build and run
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
docker compose up --build
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How It Works
|
|
21
|
+
|
|
22
|
+
1. Container clones your repository
|
|
23
|
+
2. Checks out the specified branch
|
|
24
|
+
3. **Iteration 0 (Setup)**: Sets up .gitignore, initializes progress.txt
|
|
25
|
+
4. Reads `.wile/prd.json` from repo
|
|
26
|
+
5. Loops through user stories, implementing one per iteration:
|
|
27
|
+
- Picks highest priority story where `passes: false`
|
|
28
|
+
- Implements the feature/fix
|
|
29
|
+
- Runs tests and typecheck
|
|
30
|
+
- Commits and pushes
|
|
31
|
+
- Marks story as complete
|
|
32
|
+
- Logs learnings
|
|
33
|
+
6. Repeats until all stories pass or max iterations reached
|
|
34
|
+
|
|
35
|
+
## Project Structure
|
|
36
|
+
|
|
37
|
+
Your repository must have a `.wile/` folder:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
your-repo/
|
|
41
|
+
├── .wile/
|
|
42
|
+
│ ├── prd.json # User stories (required)
|
|
43
|
+
│ ├── progress.txt # Learnings log (created by agent)
|
|
44
|
+
│ └── screenshots/ # Visual verification (created by agent)
|
|
45
|
+
└── ... your code ...
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## .wile/prd.json Format
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"userStories": [
|
|
53
|
+
{
|
|
54
|
+
"id": "US-001",
|
|
55
|
+
"title": "Add login form",
|
|
56
|
+
"acceptanceCriteria": [
|
|
57
|
+
"Email/password fields exist",
|
|
58
|
+
"Form validates email format",
|
|
59
|
+
"typecheck passes"
|
|
60
|
+
],
|
|
61
|
+
"priority": 1,
|
|
62
|
+
"passes": false,
|
|
63
|
+
"notes": "Optional notes"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Fields
|
|
70
|
+
|
|
71
|
+
- `id`: Unique identifier (used in commit messages)
|
|
72
|
+
- `title`: Short description
|
|
73
|
+
- `acceptanceCriteria`: List of requirements (be specific!)
|
|
74
|
+
- `priority`: Lower number = done first
|
|
75
|
+
- `passes`: Set to `true` by the agent when complete
|
|
76
|
+
- `notes`: Optional context for the agent
|
|
77
|
+
|
|
78
|
+
## Tips for Success
|
|
79
|
+
|
|
80
|
+
### Write Small Stories
|
|
81
|
+
Each story should fit in one context window. Break large features into steps.
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
❌ "Build entire auth system"
|
|
85
|
+
✅ "Add login form"
|
|
86
|
+
✅ "Add email validation"
|
|
87
|
+
✅ "Add login API endpoint"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Be Explicit in Criteria
|
|
91
|
+
Vague criteria lead to incomplete work.
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
❌ "Users can log in"
|
|
95
|
+
✅ "Email field with type=email"
|
|
96
|
+
✅ "Password field with minlength=8"
|
|
97
|
+
✅ "Submit button disabled when fields empty"
|
|
98
|
+
✅ "Shows error toast on invalid credentials"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Include Verification Steps
|
|
102
|
+
For UI work, tell the agent how to verify:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
"Verify login form at localhost:3000/login"
|
|
106
|
+
"Take screenshot of completed form"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Environment Variables
|
|
110
|
+
|
|
111
|
+
| Variable | Required | Description |
|
|
112
|
+
|----------|----------|-------------|
|
|
113
|
+
| `CC_CLAUDE_CODE_OAUTH_TOKEN` | Yes* | OAuth token from `claude setup-token` (uses Pro/Max subscription) |
|
|
114
|
+
| `CC_ANTHROPIC_API_KEY` | Yes* | API key (uses API credits - alternative to OAuth) |
|
|
115
|
+
| `WILE_REPO_SOURCE` | No | `github` (default) or `local` |
|
|
116
|
+
| `GITHUB_TOKEN` | Yes (github) | GitHub PAT with repo access |
|
|
117
|
+
| `GITHUB_REPO_URL` | Yes (github) | HTTPS URL to repository |
|
|
118
|
+
| `BRANCH_NAME` | Yes (github) | Branch to work on |
|
|
119
|
+
| `MAX_ITERATIONS` | No | Max loops (default: 25) |
|
|
120
|
+
| `CC_CLAUDE_MODEL` | No | Claude model alias/name (default: sonnet) |
|
|
121
|
+
|
|
122
|
+
*Either `CC_CLAUDE_CODE_OAUTH_TOKEN` or `CC_ANTHROPIC_API_KEY` is required, not both.
|
|
123
|
+
|
|
124
|
+
## Output Files
|
|
125
|
+
|
|
126
|
+
The `.wile/` folder is **tracked in git** (except screenshots):
|
|
127
|
+
|
|
128
|
+
- `prd.json` - User stories / Product Requirements Document
|
|
129
|
+
- `progress.txt` - Log of completed work and learnings
|
|
130
|
+
- `screenshots/` - Visual verification screenshots (gitignored)
|
|
131
|
+
|
|
132
|
+
## Monitoring Progress
|
|
133
|
+
|
|
134
|
+
Watch commits appear on your branch:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
git log --oneline origin/your-branch
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Check the progress log:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
git show origin/your-branch:.wile/progress.txt
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Check story status:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
git show origin/your-branch:.wile/prd.json | jq '.userStories[] | {id, title, passes}'
|
|
150
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
version: '3.8'
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
wile:
|
|
5
|
+
build: .
|
|
6
|
+
image: wile-agent:local
|
|
7
|
+
container_name: wile-agent
|
|
8
|
+
|
|
9
|
+
# Environment variables from .env file
|
|
10
|
+
env_file:
|
|
11
|
+
- .env
|
|
12
|
+
|
|
13
|
+
# Override for local testing (optional - mount local repo)
|
|
14
|
+
# volumes:
|
|
15
|
+
# - /path/to/local/repo:/home/wile/workspace/repo
|
|
16
|
+
|
|
17
|
+
# Keep stdin open for debugging
|
|
18
|
+
stdin_open: true
|
|
19
|
+
tty: true
|
|
20
|
+
|
|
21
|
+
# Resource limits (optional - adjust based on your machine)
|
|
22
|
+
# deploy:
|
|
23
|
+
# resources:
|
|
24
|
+
# limits:
|
|
25
|
+
# memory: 8G
|
|
26
|
+
# reservations:
|
|
27
|
+
# memory: 4G
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Wile Container Entrypoint
|
|
4
|
+
# Clones repo, sets up workspace, runs the autonomous coding loop
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
set -e
|
|
8
|
+
|
|
9
|
+
echo "══════════════════════════════════════════════════════"
|
|
10
|
+
echo " 🗡️ WILE - Container Startup"
|
|
11
|
+
echo "══════════════════════════════════════════════════════"
|
|
12
|
+
|
|
13
|
+
if [ "${WILE_TEST:-}" = "true" ]; then
|
|
14
|
+
TEST_REPO="${WILE_TEST_REPO_PATH:-/home/wile/workspace/repo}"
|
|
15
|
+
|
|
16
|
+
if [ ! -d "$TEST_REPO" ]; then
|
|
17
|
+
echo "ERROR: WILE_TEST_REPO_PATH does not exist: $TEST_REPO"
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
cd "$TEST_REPO"
|
|
22
|
+
|
|
23
|
+
if [ ! -f ".wile/prd.json" ]; then
|
|
24
|
+
echo "ERROR: .wile/prd.json not found in test repo"
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
echo "TEST MODE: running mocked agent (no GitHub, no Claude)"
|
|
29
|
+
|
|
30
|
+
node - << 'NODE'
|
|
31
|
+
const fs = require('fs');
|
|
32
|
+
const path = require('path');
|
|
33
|
+
|
|
34
|
+
const prdPath = path.join('.wile', 'prd.json');
|
|
35
|
+
const progressPath = path.join('.wile', 'progress.txt');
|
|
36
|
+
|
|
37
|
+
const prd = JSON.parse(fs.readFileSync(prdPath, 'utf8'));
|
|
38
|
+
const stories = Array.isArray(prd.userStories) ? prd.userStories : [];
|
|
39
|
+
const pending = stories.filter((story) => story.passes === false);
|
|
40
|
+
|
|
41
|
+
if (pending.length === 0) {
|
|
42
|
+
console.log('No pending stories to complete in test mode.');
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
pending.sort((a, b) => {
|
|
47
|
+
const aPriority = typeof a.priority === 'number' ? a.priority : Number.MAX_SAFE_INTEGER;
|
|
48
|
+
const bPriority = typeof b.priority === 'number' ? b.priority : Number.MAX_SAFE_INTEGER;
|
|
49
|
+
return aPriority - bPriority;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const story = pending[0];
|
|
53
|
+
story.passes = true;
|
|
54
|
+
fs.writeFileSync(prdPath, JSON.stringify(prd, null, 2) + '\n');
|
|
55
|
+
|
|
56
|
+
if (!fs.existsSync(progressPath)) {
|
|
57
|
+
fs.writeFileSync(progressPath, '# Wile Progress Log\n\n## Codebase Patterns\n(Autogenerated in test mode)\n\n---\n');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const date = new Date().toISOString().split('T')[0];
|
|
61
|
+
const entry = `\n---\n\n## ${date} - ${story.id}: ${story.title}\n\n**Implemented:**\n- Mocked test mode completion\n\n**Files changed:**\n- .wile/prd.json\n- .wile/progress.txt\n\n**Learnings:**\n- Test mode mock run\n`;
|
|
62
|
+
|
|
63
|
+
fs.appendFileSync(progressPath, entry);
|
|
64
|
+
console.log(`Marked ${story.id} complete (test mode).`);
|
|
65
|
+
NODE
|
|
66
|
+
|
|
67
|
+
exit 0
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
REPO_SOURCE="${WILE_REPO_SOURCE:-github}"
|
|
71
|
+
LOCAL_REPO_PATH="${WILE_LOCAL_REPO_PATH:-/home/wile/workspace/repo}"
|
|
72
|
+
ADDITIONAL_INSTRUCTIONS_PATH="${WILE_ADDITIONAL_INSTRUCTIONS:-}"
|
|
73
|
+
|
|
74
|
+
if [ "$REPO_SOURCE" = "local" ]; then
|
|
75
|
+
if [ ! -d "$LOCAL_REPO_PATH" ]; then
|
|
76
|
+
echo "ERROR: WILE_LOCAL_REPO_PATH does not exist: $LOCAL_REPO_PATH"
|
|
77
|
+
exit 1
|
|
78
|
+
fi
|
|
79
|
+
else
|
|
80
|
+
# Required environment variables
|
|
81
|
+
: "${GITHUB_REPO_URL:?GITHUB_REPO_URL is required}"
|
|
82
|
+
: "${BRANCH_NAME:?BRANCH_NAME is required}"
|
|
83
|
+
: "${GITHUB_TOKEN:?GITHUB_TOKEN is required}"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# Authentication: Either CC_CLAUDE_CODE_OAUTH_TOKEN (Pro/Max subscription) or CC_ANTHROPIC_API_KEY (API credits)
|
|
87
|
+
if [ -z "$CC_CLAUDE_CODE_OAUTH_TOKEN" ] && [ -z "$CC_ANTHROPIC_API_KEY" ]; then
|
|
88
|
+
echo "ERROR: Either CC_CLAUDE_CODE_OAUTH_TOKEN or CC_ANTHROPIC_API_KEY is required"
|
|
89
|
+
echo ""
|
|
90
|
+
echo " CC_CLAUDE_CODE_OAUTH_TOKEN - Uses your Pro/Max subscription (recommended)"
|
|
91
|
+
echo " CC_ANTHROPIC_API_KEY - Uses API credits (pay per token)"
|
|
92
|
+
echo ""
|
|
93
|
+
echo "Run 'claude setup-token' on your local machine to get an OAuth token."
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
MAX_ITERATIONS=${MAX_ITERATIONS:-25}
|
|
98
|
+
SCRIPT_DIR="/home/wile/scripts"
|
|
99
|
+
WORKSPACE="/home/wile/workspace"
|
|
100
|
+
|
|
101
|
+
# Set up Claude Code authentication
|
|
102
|
+
if [ -n "$CC_CLAUDE_CODE_OAUTH_TOKEN" ]; then
|
|
103
|
+
echo " Auth: OAuth (Pro/Max subscription)"
|
|
104
|
+
|
|
105
|
+
# Create required directories
|
|
106
|
+
mkdir -p ~/.claude ~/.config/claude
|
|
107
|
+
|
|
108
|
+
# Create ~/.claude.json (THE CRITICAL FILE!)
|
|
109
|
+
# Without this, Claude Code thinks it's a fresh install and breaks
|
|
110
|
+
cat > ~/.claude.json << 'CLAUDEJSON'
|
|
111
|
+
{
|
|
112
|
+
"hasCompletedOnboarding": true,
|
|
113
|
+
"theme": "dark"
|
|
114
|
+
}
|
|
115
|
+
CLAUDEJSON
|
|
116
|
+
|
|
117
|
+
# Create credentials file with the OAuth token
|
|
118
|
+
cat > ~/.claude/.credentials.json << CREDSJSON
|
|
119
|
+
{
|
|
120
|
+
"claudeAiOauth": {
|
|
121
|
+
"accessToken": "$CC_CLAUDE_CODE_OAUTH_TOKEN",
|
|
122
|
+
"refreshToken": "$CC_CLAUDE_CODE_OAUTH_TOKEN",
|
|
123
|
+
"expiresAt": 9999999999999,
|
|
124
|
+
"scopes": ["user:inference", "user:profile"]
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
CREDSJSON
|
|
128
|
+
|
|
129
|
+
# Copy to alternate location too
|
|
130
|
+
cp ~/.claude/.credentials.json ~/.config/claude/.credentials.json
|
|
131
|
+
|
|
132
|
+
# Ensure ANTHROPIC_API_KEY is not set (it overrides OAuth)
|
|
133
|
+
unset ANTHROPIC_API_KEY
|
|
134
|
+
else
|
|
135
|
+
echo " Auth: API Key (credits)"
|
|
136
|
+
export ANTHROPIC_API_KEY="$CC_ANTHROPIC_API_KEY"
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
if [ "$REPO_SOURCE" = "local" ]; then
|
|
140
|
+
echo " Repo: local ($LOCAL_REPO_PATH)"
|
|
141
|
+
else
|
|
142
|
+
echo " Repo: $GITHUB_REPO_URL"
|
|
143
|
+
echo " Branch: $BRANCH_NAME"
|
|
144
|
+
fi
|
|
145
|
+
echo " Iterations: $MAX_ITERATIONS"
|
|
146
|
+
echo "══════════════════════════════════════════════════════"
|
|
147
|
+
echo ""
|
|
148
|
+
|
|
149
|
+
# Configure git
|
|
150
|
+
echo "Configuring git..."
|
|
151
|
+
git config --global user.name "Wile Bot"
|
|
152
|
+
git config --global user.email "wile@bot.local"
|
|
153
|
+
git config --global credential.helper store
|
|
154
|
+
|
|
155
|
+
if [ "$REPO_SOURCE" = "local" ]; then
|
|
156
|
+
echo "Using local repo mount..."
|
|
157
|
+
cd "$LOCAL_REPO_PATH"
|
|
158
|
+
else
|
|
159
|
+
# Set up GitHub token authentication
|
|
160
|
+
# Extract host from URL (handles both https://github.com/... and git@github.com:...)
|
|
161
|
+
if [[ "$GITHUB_REPO_URL" =~ ^https://([^/]+)/ ]]; then
|
|
162
|
+
GIT_HOST="${BASH_REMATCH[1]}"
|
|
163
|
+
# Store credentials for HTTPS
|
|
164
|
+
echo "https://x-access-token:${GITHUB_TOKEN}@${GIT_HOST}" > ~/.git-credentials
|
|
165
|
+
elif [[ "$GITHUB_REPO_URL" =~ ^git@([^:]+): ]]; then
|
|
166
|
+
GIT_HOST="${BASH_REMATCH[1]}"
|
|
167
|
+
# Convert to HTTPS for token auth
|
|
168
|
+
GITHUB_REPO_URL=$(echo "$GITHUB_REPO_URL" | sed 's|git@\([^:]*\):|https://\1/|')
|
|
169
|
+
echo "https://x-access-token:${GITHUB_TOKEN}@${GIT_HOST}" > ~/.git-credentials
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
# Clone the repository
|
|
173
|
+
echo "Cloning repository..."
|
|
174
|
+
cd "$WORKSPACE"
|
|
175
|
+
git clone "$GITHUB_REPO_URL" repo
|
|
176
|
+
cd repo
|
|
177
|
+
|
|
178
|
+
# Checkout the branch
|
|
179
|
+
echo "Checking out branch: $BRANCH_NAME"
|
|
180
|
+
git fetch origin
|
|
181
|
+
if git show-ref --verify --quiet "refs/remotes/origin/$BRANCH_NAME"; then
|
|
182
|
+
git checkout "$BRANCH_NAME"
|
|
183
|
+
else
|
|
184
|
+
echo "Branch $BRANCH_NAME does not exist remotely. Creating it..."
|
|
185
|
+
git checkout -b "$BRANCH_NAME"
|
|
186
|
+
git push -u origin "$BRANCH_NAME"
|
|
187
|
+
fi
|
|
188
|
+
fi
|
|
189
|
+
|
|
190
|
+
if [ -n "$ADDITIONAL_INSTRUCTIONS_PATH" ] && [ -f "$ADDITIONAL_INSTRUCTIONS_PATH" ]; then
|
|
191
|
+
if [ -s "$ADDITIONAL_INSTRUCTIONS_PATH" ]; then
|
|
192
|
+
echo " Extra: Using additional instructions"
|
|
193
|
+
fi
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
# Verify .wile/prd.json exists
|
|
197
|
+
echo "Checking for .wile/prd.json..."
|
|
198
|
+
if [ ! -f ".wile/prd.json" ]; then
|
|
199
|
+
echo "ERROR: .wile/prd.json not found!"
|
|
200
|
+
echo ""
|
|
201
|
+
echo "Your repository must have a .wile/prd.json file at the root."
|
|
202
|
+
echo "This file contains the user stories for Wile to implement."
|
|
203
|
+
echo ""
|
|
204
|
+
echo "Example structure:"
|
|
205
|
+
echo ' {'
|
|
206
|
+
echo ' "userStories": ['
|
|
207
|
+
echo ' {'
|
|
208
|
+
echo ' "id": "US-001",'
|
|
209
|
+
echo ' "title": "My feature",'
|
|
210
|
+
echo ' "acceptanceCriteria": ["..."],'
|
|
211
|
+
echo ' "priority": 1,'
|
|
212
|
+
echo ' "passes": false'
|
|
213
|
+
echo ' }'
|
|
214
|
+
echo ' ]'
|
|
215
|
+
echo ' }'
|
|
216
|
+
exit 1
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
# Set up .wile/screenshots directory
|
|
220
|
+
echo "Setting up .wile directory..."
|
|
221
|
+
mkdir -p .wile/screenshots
|
|
222
|
+
|
|
223
|
+
echo ""
|
|
224
|
+
echo "══════════════════════════════════════════════════════"
|
|
225
|
+
echo " Starting Wile Loop"
|
|
226
|
+
echo "══════════════════════════════════════════════════════"
|
|
227
|
+
echo ""
|
|
228
|
+
|
|
229
|
+
# Run the main loop
|
|
230
|
+
EXIT_CODE=0
|
|
231
|
+
"$SCRIPT_DIR/wile.sh" "$MAX_ITERATIONS" || EXIT_CODE=$?
|
|
232
|
+
|
|
233
|
+
echo ""
|
|
234
|
+
echo "══════════════════════════════════════════════════════"
|
|
235
|
+
echo " Wile Loop Complete (exit code: $EXIT_CODE)"
|
|
236
|
+
echo "══════════════════════════════════════════════════════"
|
|
237
|
+
|
|
238
|
+
# Handle completion/partial completion
|
|
239
|
+
if [ $EXIT_CODE -eq 0 ]; then
|
|
240
|
+
echo "All tasks completed successfully!"
|
|
241
|
+
# Ensure final state is pushed
|
|
242
|
+
git push || true
|
|
243
|
+
elif [ $EXIT_CODE -eq 1 ]; then
|
|
244
|
+
echo "Max iterations reached. Committing partial work..."
|
|
245
|
+
|
|
246
|
+
# Check if there are uncommitted changes
|
|
247
|
+
if ! git diff --quiet || ! git diff --staged --quiet; then
|
|
248
|
+
git add -A
|
|
249
|
+
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
|
250
|
+
git commit -m "WIP: wile stopped at max iterations ($TIMESTAMP)
|
|
251
|
+
|
|
252
|
+
Partial work committed. Check .wile/progress.txt for details.
|
|
253
|
+
Some stories may still have passes=false in .wile/prd.json."
|
|
254
|
+
git push
|
|
255
|
+
fi
|
|
256
|
+
elif [ $EXIT_CODE -eq 2 ]; then
|
|
257
|
+
echo "Setup failed. Check the logs above for details."
|
|
258
|
+
else
|
|
259
|
+
echo "Wile exited with error code: $EXIT_CODE"
|
|
260
|
+
# Try to commit whatever state we have
|
|
261
|
+
if ! git diff --quiet || ! git diff --staged --quiet; then
|
|
262
|
+
git add -A
|
|
263
|
+
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
|
264
|
+
git commit -m "WIP: wile error exit ($TIMESTAMP)
|
|
265
|
+
|
|
266
|
+
Exit code: $EXIT_CODE
|
|
267
|
+
Check logs for details."
|
|
268
|
+
git push || true
|
|
269
|
+
fi
|
|
270
|
+
fi
|
|
271
|
+
|
|
272
|
+
echo ""
|
|
273
|
+
echo "══════════════════════════════════════════════════════"
|
|
274
|
+
echo " Wile Container Finished"
|
|
275
|
+
echo "══════════════════════════════════════════════════════"
|
|
276
|
+
|
|
277
|
+
exit $EXIT_CODE
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser testing helper for Wile agent
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* npx tsx browser-test.ts screenshot <url> <output-path>
|
|
6
|
+
* npx tsx browser-test.ts verify <url> <selector>
|
|
7
|
+
*
|
|
8
|
+
* Examples:
|
|
9
|
+
* npx tsx browser-test.ts screenshot http://localhost:3000 .wile-temp/screenshots/home.png
|
|
10
|
+
* npx tsx browser-test.ts verify http://localhost:3000/login "form#login"
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { chromium, type Page, type Browser } from 'playwright';
|
|
14
|
+
|
|
15
|
+
const VIEWPORT = { width: 1280, height: 900 };
|
|
16
|
+
const TIMEOUT = 30000;
|
|
17
|
+
|
|
18
|
+
async function waitForPageLoad(page: Page): Promise<void> {
|
|
19
|
+
await page.waitForLoadState('networkidle', { timeout: TIMEOUT });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function screenshot(url: string, outputPath: string): Promise<void> {
|
|
23
|
+
let browser: Browser | null = null;
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
browser = await chromium.launch({ headless: true });
|
|
27
|
+
const context = await browser.newContext({ viewport: VIEWPORT });
|
|
28
|
+
const page = await context.newPage();
|
|
29
|
+
|
|
30
|
+
console.log(`Navigating to ${url}...`);
|
|
31
|
+
await page.goto(url, { timeout: TIMEOUT });
|
|
32
|
+
await waitForPageLoad(page);
|
|
33
|
+
|
|
34
|
+
console.log(`Taking screenshot...`);
|
|
35
|
+
await page.screenshot({ path: outputPath, fullPage: true });
|
|
36
|
+
|
|
37
|
+
console.log(`Screenshot saved to ${outputPath}`);
|
|
38
|
+
} finally {
|
|
39
|
+
if (browser) await browser.close();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function verify(url: string, selector: string): Promise<boolean> {
|
|
44
|
+
let browser: Browser | null = null;
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
browser = await chromium.launch({ headless: true });
|
|
48
|
+
const context = await browser.newContext({ viewport: VIEWPORT });
|
|
49
|
+
const page = await context.newPage();
|
|
50
|
+
|
|
51
|
+
console.log(`Navigating to ${url}...`);
|
|
52
|
+
await page.goto(url, { timeout: TIMEOUT });
|
|
53
|
+
await waitForPageLoad(page);
|
|
54
|
+
|
|
55
|
+
console.log(`Looking for selector: ${selector}...`);
|
|
56
|
+
const element = await page.$(selector);
|
|
57
|
+
|
|
58
|
+
if (element) {
|
|
59
|
+
console.log(`✓ Found element matching "${selector}"`);
|
|
60
|
+
return true;
|
|
61
|
+
} else {
|
|
62
|
+
console.log(`✗ Element not found: "${selector}"`);
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
} finally {
|
|
66
|
+
if (browser) await browser.close();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function main(): Promise<void> {
|
|
71
|
+
const [, , command, ...args] = process.argv;
|
|
72
|
+
|
|
73
|
+
switch (command) {
|
|
74
|
+
case 'screenshot': {
|
|
75
|
+
const [url, outputPath] = args;
|
|
76
|
+
if (!url || !outputPath) {
|
|
77
|
+
console.error('Usage: browser-test.ts screenshot <url> <output-path>');
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
await screenshot(url, outputPath);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
case 'verify': {
|
|
85
|
+
const [url, selector] = args;
|
|
86
|
+
if (!url || !selector) {
|
|
87
|
+
console.error('Usage: browser-test.ts verify <url> <selector>');
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
const found = await verify(url, selector);
|
|
91
|
+
process.exit(found ? 0 : 1);
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
default:
|
|
96
|
+
console.error('Commands: screenshot, verify');
|
|
97
|
+
console.error(' screenshot <url> <output-path> - Take a full page screenshot');
|
|
98
|
+
console.error(' verify <url> <selector> - Check if selector exists on page');
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
main().catch((error) => {
|
|
104
|
+
console.error('Error:', error.message);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Host-level setup for EC2 instances running Wile
|
|
3
|
+
# These settings cannot be configured inside Docker containers
|
|
4
|
+
# Run this script on EC2 UserData or manually on the host
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
echo "=== Wile Host Setup ==="
|
|
9
|
+
|
|
10
|
+
# Configure swap (8GB)
|
|
11
|
+
echo "Configuring 8GB swap..."
|
|
12
|
+
fallocate -l 8G /swapfile
|
|
13
|
+
chmod 600 /swapfile
|
|
14
|
+
mkswap /swapfile
|
|
15
|
+
swapon /swapfile
|
|
16
|
+
echo '/swapfile none swap sw 0 0' >> /etc/fstab
|
|
17
|
+
|
|
18
|
+
# Set vm.swappiness to 10 (prefer RAM, swap only when needed)
|
|
19
|
+
echo "Setting vm.swappiness=10..."
|
|
20
|
+
sysctl -w vm.swappiness=10
|
|
21
|
+
echo 'vm.swappiness=10' >> /etc/sysctl.conf
|
|
22
|
+
|
|
23
|
+
# Enable zswap with lz4 compression (3x effective swap, faster access)
|
|
24
|
+
echo "Enabling zswap with lz4..."
|
|
25
|
+
echo lz4 > /sys/module/zswap/parameters/compressor
|
|
26
|
+
echo 1 > /sys/module/zswap/parameters/enabled
|
|
27
|
+
|
|
28
|
+
# Install and enable earlyoom (prevents hard freeze)
|
|
29
|
+
echo "Installing earlyoom..."
|
|
30
|
+
apt-get update
|
|
31
|
+
apt-get install -y earlyoom
|
|
32
|
+
systemctl enable earlyoom
|
|
33
|
+
systemctl start earlyoom
|
|
34
|
+
|
|
35
|
+
echo "=== Host setup complete ==="
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Wile Setup (Iteration 0)
|
|
2
|
+
|
|
3
|
+
You are running the setup phase for a Wile autonomous coding session.
|
|
4
|
+
|
|
5
|
+
## Tasks
|
|
6
|
+
|
|
7
|
+
1. Verify `.wile/prd.json` exists and is valid JSON:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
cat .wile/prd.json
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
If the file is missing or invalid JSON, respond with:
|
|
14
|
+
```
|
|
15
|
+
<promise>SETUP_FAILED</promise>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Otherwise, **do nothing else**. Do not modify files, do not run git commands, and do not print extra output.
|
|
19
|
+
|
|
20
|
+
## Notes
|
|
21
|
+
|
|
22
|
+
- Setup should have no side effects.
|
|
23
|
+
- Only emit `<promise>SETUP_FAILED</promise>` on failure.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Wile Agent Instructions
|
|
2
|
+
|
|
3
|
+
You are an autonomous coding agent running in a loop. Each iteration, you complete ONE user story from the backlog, then exit. The loop will call you again for the next story.
|
|
4
|
+
|
|
5
|
+
## Your Task (Execute in Order)
|
|
6
|
+
|
|
7
|
+
### 1. Read the Backlog
|
|
8
|
+
```bash
|
|
9
|
+
cat .wile/prd.json
|
|
10
|
+
```
|
|
11
|
+
Parse the `userStories` array. Find the highest priority story where `passes: false`.
|
|
12
|
+
|
|
13
|
+
### 2. Read Progress Log
|
|
14
|
+
```bash
|
|
15
|
+
cat .wile/progress.txt
|
|
16
|
+
```
|
|
17
|
+
Check the **Codebase Patterns** section at the top for learnings from previous iterations. Apply these patterns to avoid repeating mistakes.
|
|
18
|
+
|
|
19
|
+
### 3. Check Current Branch
|
|
20
|
+
```bash
|
|
21
|
+
git branch --show-current
|
|
22
|
+
git status
|
|
23
|
+
```
|
|
24
|
+
Ensure you're on the correct branch specified by the `BRANCH_NAME` environment variable.
|
|
25
|
+
|
|
26
|
+
### 4. Implement the Story
|
|
27
|
+
Pick the highest priority story where `passes: false` and implement it completely.
|
|
28
|
+
|
|
29
|
+
- Read and understand all acceptance criteria
|
|
30
|
+
- Implement the feature/fix
|
|
31
|
+
- Follow existing code patterns in the codebase
|
|
32
|
+
- Keep changes minimal and focused
|
|
33
|
+
|
|
34
|
+
### 5. Verify Your Work
|
|
35
|
+
Run the project's tests and type checking:
|
|
36
|
+
```bash
|
|
37
|
+
# Try common commands (adapt to the project)
|
|
38
|
+
npm run typecheck || npm run tsc || npx tsc --noEmit || true
|
|
39
|
+
npm test || npm run test || true
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
If tests or typecheck fail, fix the issues before proceeding.
|
|
43
|
+
|
|
44
|
+
### 6. Update prd.json
|
|
45
|
+
Set `passes: true` for the completed story:
|
|
46
|
+
```bash
|
|
47
|
+
# Edit .wile/prd.json to mark the story as complete
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 7. Log Your Progress
|
|
51
|
+
**APPEND** to `.wile/progress.txt`:
|
|
52
|
+
|
|
53
|
+
```markdown
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## [DATE] - [STORY-ID]: [Story Title]
|
|
57
|
+
|
|
58
|
+
**Implemented:**
|
|
59
|
+
- What was done
|
|
60
|
+
|
|
61
|
+
**Files changed:**
|
|
62
|
+
- file1.ts
|
|
63
|
+
- file2.ts
|
|
64
|
+
|
|
65
|
+
**Learnings:**
|
|
66
|
+
- Any patterns discovered
|
|
67
|
+
- Gotchas encountered
|
|
68
|
+
- Things to remember for future iterations
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
If you discovered important patterns, also add them to the **Codebase Patterns** section at the TOP of progress.txt.
|
|
72
|
+
|
|
73
|
+
### 8. Commit and Push
|
|
74
|
+
```bash
|
|
75
|
+
git add -A
|
|
76
|
+
git commit -m "feat: [STORY-ID] - [Story Title]"
|
|
77
|
+
git push
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Use the exact story ID and title from `.wile/prd.json`.
|
|
81
|
+
|
|
82
|
+
## Stop Condition
|
|
83
|
+
|
|
84
|
+
After completing steps 1-8, check if ALL stories in `.wile/prd.json` have `passes: true`.
|
|
85
|
+
|
|
86
|
+
**If ALL stories pass**, respond with exactly:
|
|
87
|
+
```
|
|
88
|
+
<promise>COMPLETE</promise>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**If there are still stories with `passes: false`**, end your response normally. The loop will call you again for the next story.
|
|
92
|
+
|
|
93
|
+
## Important Rules
|
|
94
|
+
|
|
95
|
+
1. **ONE story per iteration** - Do not implement multiple stories
|
|
96
|
+
2. **Always push** - Push after every commit so progress is visible
|
|
97
|
+
3. **One commit per feature** - Include the implementation, prd.json update, and progress log in a single commit
|
|
98
|
+
4. **Fix related files** - If typecheck requires changes in other files, make them (this is not scope creep)
|
|
99
|
+
5. **Be idempotent** - Use `IF NOT EXISTS` for migrations, check before creating files
|
|
100
|
+
6. **No interactive prompts** - Use `echo -e "\n\n\n" |` if a command might prompt
|
|
101
|
+
7. **NEVER commit node_modules, dist, or build artifacts** - .gitignore should already be set up by iteration 0
|
|
102
|
+
|
|
103
|
+
## Common Patterns
|
|
104
|
+
|
|
105
|
+
### Migrations
|
|
106
|
+
```sql
|
|
107
|
+
ALTER TABLE x ADD COLUMN IF NOT EXISTS y TEXT;
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### React Refs
|
|
111
|
+
```typescript
|
|
112
|
+
const ref = useRef<NodeJS.Timeout | null>(null);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Skipping Interactive Prompts
|
|
116
|
+
```bash
|
|
117
|
+
echo -e "\n\n\n" | npm run db:generate
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Browser Testing (Visual Verification)
|
|
121
|
+
|
|
122
|
+
When acceptance criteria mention UI verification, visual checks, or "verify at localhost":
|
|
123
|
+
|
|
124
|
+
### Take a Screenshot
|
|
125
|
+
```bash
|
|
126
|
+
cd /tmp && npm init -y && npm install playwright
|
|
127
|
+
node -e "
|
|
128
|
+
const { chromium } = require('playwright');
|
|
129
|
+
(async () => {
|
|
130
|
+
const browser = await chromium.launch();
|
|
131
|
+
const page = await browser.newPage();
|
|
132
|
+
await page.setViewportSize({ width: 1280, height: 900 });
|
|
133
|
+
await page.goto('http://localhost:3000/your-page');
|
|
134
|
+
await page.waitForLoadState('networkidle');
|
|
135
|
+
await page.screenshot({ path: '.wile/screenshots/verification.png', fullPage: true });
|
|
136
|
+
console.log('Screenshot saved');
|
|
137
|
+
await browser.close();
|
|
138
|
+
})();
|
|
139
|
+
"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Verify Element Exists
|
|
143
|
+
```bash
|
|
144
|
+
node -e "
|
|
145
|
+
const { chromium } = require('playwright');
|
|
146
|
+
(async () => {
|
|
147
|
+
const browser = await chromium.launch();
|
|
148
|
+
const page = await browser.newPage();
|
|
149
|
+
await page.goto('http://localhost:3000/login');
|
|
150
|
+
await page.waitForLoadState('networkidle');
|
|
151
|
+
const element = await page.\$('form#login');
|
|
152
|
+
console.log(element ? 'FOUND: form#login' : 'NOT FOUND: form#login');
|
|
153
|
+
await browser.close();
|
|
154
|
+
})();
|
|
155
|
+
"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Save screenshots to `.wile/screenshots/` with descriptive names.
|
|
159
|
+
|
|
160
|
+
## Error Recovery
|
|
161
|
+
|
|
162
|
+
If something goes wrong:
|
|
163
|
+
1. Read the error message carefully
|
|
164
|
+
2. Fix the issue
|
|
165
|
+
3. Continue with the current story
|
|
166
|
+
4. Document the issue in the progress log
|
|
167
|
+
|
|
168
|
+
Do NOT skip to the next story if the current one fails. Fix it first.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
run_case() {
|
|
5
|
+
LABEL="$1"
|
|
6
|
+
MODE="$2"
|
|
7
|
+
EXPECT_HEADER="$3"
|
|
8
|
+
|
|
9
|
+
TMP_DIR=$(mktemp -d /tmp/wile-additional-test-XXXXXX)
|
|
10
|
+
cleanup() {
|
|
11
|
+
rm -rf "$TMP_DIR"
|
|
12
|
+
}
|
|
13
|
+
trap cleanup EXIT INT TERM
|
|
14
|
+
|
|
15
|
+
SCRIPT_DIR="$TMP_DIR/agent"
|
|
16
|
+
BIN_DIR="$TMP_DIR/bin"
|
|
17
|
+
mkdir -p "$SCRIPT_DIR" "$BIN_DIR"
|
|
18
|
+
|
|
19
|
+
cp /Users/thiagoduarte/Projects/personal/wile/packages/agent/scripts/wile.sh "$SCRIPT_DIR/wile.sh"
|
|
20
|
+
chmod +x "$SCRIPT_DIR/wile.sh"
|
|
21
|
+
|
|
22
|
+
echo "BASE PROMPT" > "$SCRIPT_DIR/prompt.md"
|
|
23
|
+
|
|
24
|
+
ADDITIONAL="$TMP_DIR/additional.md"
|
|
25
|
+
if [ "$MODE" = "comment-only" ]; then
|
|
26
|
+
cat > "$ADDITIONAL" <<'EOF'
|
|
27
|
+
<!--
|
|
28
|
+
Use bullet points for additional instructions, e.g.
|
|
29
|
+
- You may run `supabase db reset --db-url "$SUPABASE_DB_URL"` when needed.
|
|
30
|
+
- Do not ask for permission before running it.
|
|
31
|
+
-->
|
|
32
|
+
EOF
|
|
33
|
+
else
|
|
34
|
+
cat > "$ADDITIONAL" <<'EOF'
|
|
35
|
+
<!--
|
|
36
|
+
Use bullet points for additional instructions, e.g.
|
|
37
|
+
- You may run `supabase db reset --db-url "$SUPABASE_DB_URL"` when needed.
|
|
38
|
+
- Do not ask for permission before running it.
|
|
39
|
+
-->
|
|
40
|
+
- Always reset the database before integration tests.
|
|
41
|
+
EOF
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
CAPTURE="$TMP_DIR/capture.txt"
|
|
45
|
+
|
|
46
|
+
cat > "$BIN_DIR/claude" <<'EOF'
|
|
47
|
+
#!/bin/sh
|
|
48
|
+
cat > "$CLAUDE_CAPTURE"
|
|
49
|
+
echo "<promise>COMPLETE</promise>"
|
|
50
|
+
EOF
|
|
51
|
+
chmod +x "$BIN_DIR/claude"
|
|
52
|
+
|
|
53
|
+
PATH="$BIN_DIR:$PATH" \
|
|
54
|
+
CLAUDE_CAPTURE="$CAPTURE" \
|
|
55
|
+
WILE_ADDITIONAL_INSTRUCTIONS="$ADDITIONAL" \
|
|
56
|
+
CC_CLAUDE_MODEL="sonnet" \
|
|
57
|
+
"$SCRIPT_DIR/wile.sh" 1 >/dev/null 2>&1
|
|
58
|
+
|
|
59
|
+
if ! grep -q "BASE PROMPT" "$CAPTURE"; then
|
|
60
|
+
echo "error: base prompt missing in $LABEL" >&2
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
if [ "$EXPECT_HEADER" = "yes" ]; then
|
|
65
|
+
grep -q "## Additional Instructions" "$CAPTURE"
|
|
66
|
+
grep -q "Always reset the database before integration tests." "$CAPTURE"
|
|
67
|
+
else
|
|
68
|
+
if grep -q "## Additional Instructions" "$CAPTURE"; then
|
|
69
|
+
echo "error: header appended unexpectedly in $LABEL" >&2
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
rm -rf "$TMP_DIR"
|
|
75
|
+
trap - EXIT INT TERM
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
run_case "comment-only" "comment-only" "no"
|
|
79
|
+
|
|
80
|
+
run_case "with-content" "with-content" "yes"
|
|
81
|
+
|
|
82
|
+
echo "test-additional-instructions: ok"
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Wile - Autonomous AI coding loop
|
|
4
|
+
# Pipes prompt to Claude Code repeatedly until all tasks complete
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
set -e
|
|
8
|
+
|
|
9
|
+
MAX_ITERATIONS=${1:-25}
|
|
10
|
+
CLAUDE_MODEL=${CC_CLAUDE_MODEL:-sonnet}
|
|
11
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
PROMPT_FILE="$SCRIPT_DIR/prompt.md"
|
|
13
|
+
SETUP_PROMPT_FILE="$SCRIPT_DIR/prompt-setup.md"
|
|
14
|
+
ADDITIONAL_PROMPT_FILE="${WILE_ADDITIONAL_INSTRUCTIONS:-}"
|
|
15
|
+
|
|
16
|
+
echo "══════════════════════════════════════════════════════"
|
|
17
|
+
echo " 🗡️ WILE - Autonomous Coding Agent"
|
|
18
|
+
echo "══════════════════════════════════════════════════════"
|
|
19
|
+
echo " Max iterations: $MAX_ITERATIONS"
|
|
20
|
+
echo " Model: $CLAUDE_MODEL"
|
|
21
|
+
echo " Prompt file: $PROMPT_FILE"
|
|
22
|
+
echo "══════════════════════════════════════════════════════"
|
|
23
|
+
echo ""
|
|
24
|
+
|
|
25
|
+
if [ ! -f "$PROMPT_FILE" ]; then
|
|
26
|
+
echo "ERROR: Prompt file not found: $PROMPT_FILE"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
if [ -n "$ADDITIONAL_PROMPT_FILE" ] && [ -f "$ADDITIONAL_PROMPT_FILE" ]; then
|
|
31
|
+
if [ -s "$ADDITIONAL_PROMPT_FILE" ]; then
|
|
32
|
+
STRIPPED_CONTENT=$(sed '/<!--/,/-->/d' "$ADDITIONAL_PROMPT_FILE" | tr -d '[:space:]')
|
|
33
|
+
if [ -n "$STRIPPED_CONTENT" ]; then
|
|
34
|
+
PROMPT_FILE="/tmp/wile-prompt.md"
|
|
35
|
+
cat "$SCRIPT_DIR/prompt.md" > "$PROMPT_FILE"
|
|
36
|
+
printf "\n\n## Additional Instructions\n\n" >> "$PROMPT_FILE"
|
|
37
|
+
cat "$ADDITIONAL_PROMPT_FILE" >> "$PROMPT_FILE"
|
|
38
|
+
fi
|
|
39
|
+
fi
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# ════════════════════════════════════════════════════════════
|
|
43
|
+
# ITERATION 0: Setup
|
|
44
|
+
# ════════════════════════════════════════════════════════════
|
|
45
|
+
echo ""
|
|
46
|
+
echo "══════════════════════════════════════════════════════"
|
|
47
|
+
echo " Iteration 0 - Setup"
|
|
48
|
+
echo "══════════════════════════════════════════════════════"
|
|
49
|
+
echo ""
|
|
50
|
+
|
|
51
|
+
if [ -f "$SETUP_PROMPT_FILE" ]; then
|
|
52
|
+
OUTPUT=$(cat "$SETUP_PROMPT_FILE" | claude --model "$CLAUDE_MODEL" --dangerously-skip-permissions 2>&1 | tee /dev/stderr) || true
|
|
53
|
+
|
|
54
|
+
# Check if setup failed critically
|
|
55
|
+
if echo "$OUTPUT" | grep -q "<promise>SETUP_FAILED</promise>"; then
|
|
56
|
+
echo ""
|
|
57
|
+
echo "══════════════════════════════════════════════════════"
|
|
58
|
+
echo " ❌ SETUP FAILED - Cannot continue"
|
|
59
|
+
echo "══════════════════════════════════════════════════════"
|
|
60
|
+
exit 2
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
echo ""
|
|
64
|
+
echo "Setup complete. Starting main loop..."
|
|
65
|
+
sleep 2
|
|
66
|
+
else
|
|
67
|
+
echo "No setup prompt found, skipping iteration 0..."
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# ════════════════════════════════════════════════════════════
|
|
71
|
+
# ITERATIONS 1-N: Main loop
|
|
72
|
+
# ════════════════════════════════════════════════════════════
|
|
73
|
+
for i in $(seq 1 $MAX_ITERATIONS); do
|
|
74
|
+
echo ""
|
|
75
|
+
echo "══════════════════════════════════════════════════════"
|
|
76
|
+
echo " Iteration $i of $MAX_ITERATIONS"
|
|
77
|
+
echo "══════════════════════════════════════════════════════"
|
|
78
|
+
echo ""
|
|
79
|
+
|
|
80
|
+
# Pipe prompt to Claude Code
|
|
81
|
+
# --dangerously-skip-permissions allows autonomous operation
|
|
82
|
+
# Capture output while also displaying it (tee to stderr)
|
|
83
|
+
OUTPUT=$(cat "$PROMPT_FILE" | claude --model "$CLAUDE_MODEL" --dangerously-skip-permissions 2>&1 | tee /dev/stderr) || true
|
|
84
|
+
|
|
85
|
+
# Check for completion signal
|
|
86
|
+
if echo "$OUTPUT" | grep -q "<promise>COMPLETE</promise>"; then
|
|
87
|
+
echo ""
|
|
88
|
+
echo "══════════════════════════════════════════════════════"
|
|
89
|
+
echo " ✅ ALL TASKS COMPLETE"
|
|
90
|
+
echo "══════════════════════════════════════════════════════"
|
|
91
|
+
exit 0
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
echo ""
|
|
95
|
+
echo "Iteration $i complete. Continuing..."
|
|
96
|
+
sleep 2
|
|
97
|
+
done
|
|
98
|
+
|
|
99
|
+
echo ""
|
|
100
|
+
echo "══════════════════════════════════════════════════════"
|
|
101
|
+
echo " ⚠️ MAX ITERATIONS REACHED ($MAX_ITERATIONS)"
|
|
102
|
+
echo " Some tasks may still be pending."
|
|
103
|
+
echo "══════════════════════════════════════════════════════"
|
|
104
|
+
exit 1
|
package/dist/cli.js
CHANGED
|
@@ -7904,6 +7904,10 @@ var resolveAgentDir = () => {
|
|
|
7904
7904
|
return foundFromCwd;
|
|
7905
7905
|
}
|
|
7906
7906
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
7907
|
+
const bundledAgentDir = join3(here, "agent");
|
|
7908
|
+
if (existsSync3(bundledAgentDir)) {
|
|
7909
|
+
return bundledAgentDir;
|
|
7910
|
+
}
|
|
7907
7911
|
const cliRoot = resolve(here, "..", "..", "..");
|
|
7908
7912
|
const agentDir = join3(cliRoot, "agent");
|
|
7909
7913
|
if (!existsSync3(agentDir)) {
|