wile 0.6.9 → 1.0.0
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/README.md +1 -1
- package/dist/agent/.env.example +17 -0
- package/dist/agent/Dockerfile +9 -7
- package/dist/agent/README.md +26 -22
- package/dist/agent/entrypoint.sh +76 -21
- package/dist/agent/scripts/claude-stream.ts +159 -0
- package/dist/agent/scripts/codex-stream.ts +167 -0
- package/dist/agent/scripts/gemini-stream.ts +97 -0
- package/dist/agent/scripts/mock-claude.sh +48 -21
- package/dist/agent/scripts/mock-codex.sh +126 -0
- package/dist/agent/scripts/mock-gemini.sh +48 -21
- package/dist/agent/scripts/opencode-stream.ts +93 -0
- package/dist/agent/scripts/prompt-compact.md +36 -34
- package/dist/agent/scripts/prompt-preflight.md +0 -7
- package/dist/agent/scripts/prompt.md +26 -24
- package/dist/agent/scripts/test-additional-instructions.sh +4 -2
- package/dist/agent/scripts/test-env-project-docker.sh +5 -4
- package/dist/agent/scripts/test-iteration-limit.sh +4 -2
- package/dist/agent/scripts/test-prd-validation-docker.sh +151 -0
- package/dist/agent/scripts/test-preflight-claude-docker.sh +5 -4
- package/dist/agent/scripts/test-preflight-docker.sh +16 -13
- package/dist/agent/scripts/validate-compact.ts +134 -0
- package/dist/agent/scripts/validate-prd.ts +280 -0
- package/dist/agent/scripts/wile-compact.sh +21 -5
- package/dist/agent/scripts/wile-preflight.sh +19 -3
- package/dist/agent/scripts/wile.sh +19 -3
- package/dist/cli.js +322 -65
- package/package.json +1 -1
- package/dist/agent/scripts/claude-stream.js +0 -88
- package/dist/agent/scripts/gemini-stream.js +0 -63
- package/dist/agent/scripts/opencode-stream.js +0 -52
- package/dist/agent/scripts/validate-compact.js +0 -135
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ This creates:
|
|
|
26
26
|
Set `WILE_REPO_SOURCE=local` in `.wile/secrets/.env` to run against the current directory without GitHub.
|
|
27
27
|
When `WILE_REPO_SOURCE=local`, GitHub credentials are optional.
|
|
28
28
|
Set `WILE_MAX_ITERATIONS` in `.wile/secrets/.env` to change the default loop limit (default: 25).
|
|
29
|
-
Set `CODING_AGENT=OC` to use OpenCode (OpenRouter), `CODING_AGENT=GC` to use Gemini CLI, otherwise `CODING_AGENT=CC` uses Claude Code.
|
|
29
|
+
Set `CODING_AGENT=CX` to use Codex CLI, `CODING_AGENT=OC` to use OpenCode (OpenRouter), `CODING_AGENT=GC` to use Gemini CLI, otherwise `CODING_AGENT=CC` uses Claude Code.
|
|
30
30
|
|
|
31
31
|
## Run Wile
|
|
32
32
|
|
package/dist/agent/.env.example
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
# - CC = Claude Code (default)
|
|
10
10
|
# - GC = Gemini CLI (Google account OAuth)
|
|
11
11
|
# - OC = OpenCode (OpenRouter)
|
|
12
|
+
# - CX = Codex CLI (ChatGPT subscription)
|
|
12
13
|
CODING_AGENT=CC
|
|
13
14
|
|
|
14
15
|
# =============================================================================
|
|
@@ -47,6 +48,19 @@ CC_CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-xxxxx
|
|
|
47
48
|
# Get one at: https://aistudio.google.com/app/apikey
|
|
48
49
|
# GEMINI_API_KEY=your-key
|
|
49
50
|
|
|
51
|
+
# =============================================================================
|
|
52
|
+
# REQUIRED - Codex CLI Authentication (CX only, choose ONE)
|
|
53
|
+
# =============================================================================
|
|
54
|
+
|
|
55
|
+
# OPTION 1: ChatGPT login (RECOMMENDED - uses your ChatGPT subscription)
|
|
56
|
+
# Run: codex login (or codex login --device-auth)
|
|
57
|
+
# Then base64 encode ~/.codex/auth.json and paste here
|
|
58
|
+
# CODEX_AUTH_JSON_B64=eyJ...
|
|
59
|
+
|
|
60
|
+
# OPTION 2: API key (uses API credits - pay per token)
|
|
61
|
+
# Get one at: https://platform.openai.com/api-keys
|
|
62
|
+
# CODEX_API_KEY=sk-openai-xxxxx
|
|
63
|
+
|
|
50
64
|
# =============================================================================
|
|
51
65
|
# GitHub Authentication (required when WILE_REPO_SOURCE=github)
|
|
52
66
|
# =============================================================================
|
|
@@ -83,6 +97,9 @@ CC_CLAUDE_MODEL=sonnet
|
|
|
83
97
|
# Gemini model name (default: auto-gemini-3)
|
|
84
98
|
# GEMINI_MODEL=auto-gemini-3
|
|
85
99
|
|
|
100
|
+
# Codex model name (defaults to Codex CLI default)
|
|
101
|
+
# CODEX_MODEL=gpt-5.1-codex
|
|
102
|
+
|
|
86
103
|
# =============================================================================
|
|
87
104
|
# PROJECT ENVIRONMENT VARIABLES
|
|
88
105
|
# =============================================================================
|
package/dist/agent/Dockerfile
CHANGED
|
@@ -34,7 +34,7 @@ JSON
|
|
|
34
34
|
RUN cd /opt/agent-browser \
|
|
35
35
|
&& bun add agent-browser \
|
|
36
36
|
&& node /opt/agent-browser/node_modules/agent-browser/scripts/postinstall.js \
|
|
37
|
-
&& ln -s /opt/agent-browser/node_modules/agent-browser/bin/agent-browser /usr/local/bin/agent-browser
|
|
37
|
+
&& ln -s /opt/agent-browser/node_modules/agent-browser/bin/agent-browser.js /usr/local/bin/agent-browser
|
|
38
38
|
|
|
39
39
|
# Create non-root user
|
|
40
40
|
RUN useradd -m -s /bin/bash wile \
|
|
@@ -63,11 +63,14 @@ RUN npm install -g opencode-ai@latest
|
|
|
63
63
|
# https://www.npmjs.com/package/@google/gemini-cli
|
|
64
64
|
RUN npm install -g @google/gemini-cli
|
|
65
65
|
|
|
66
|
+
# Install Codex CLI
|
|
67
|
+
# https://www.npmjs.com/package/@openai/codex
|
|
68
|
+
RUN npm install -g @openai/codex
|
|
69
|
+
|
|
66
70
|
# CC_ANTHROPIC_API_KEY is passed at runtime via environment variable (mapped to ANTHROPIC_API_KEY in entrypoint)
|
|
67
71
|
# Usage: claude --dangerously-skip-permissions
|
|
68
72
|
|
|
69
|
-
# Install
|
|
70
|
-
# Dependencies for headless Chromium
|
|
73
|
+
# Install Chromium dependencies for Playwright
|
|
71
74
|
RUN apt-get update && apt-get install -y \
|
|
72
75
|
libnss3 \
|
|
73
76
|
libnspr4 \
|
|
@@ -87,11 +90,10 @@ RUN apt-get update && apt-get install -y \
|
|
|
87
90
|
libatspi2.0-0 \
|
|
88
91
|
&& rm -rf /var/lib/apt/lists/*
|
|
89
92
|
|
|
90
|
-
#
|
|
93
|
+
# Download Chromium using the Playwright core version bundled with agent-browser
|
|
91
94
|
ENV PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-browsers
|
|
92
|
-
RUN
|
|
93
|
-
&&
|
|
94
|
-
&& npx playwright install chromium \
|
|
95
|
+
RUN mkdir -p /opt/playwright-browsers \
|
|
96
|
+
&& node /opt/agent-browser/node_modules/playwright-core/cli.js install chromium \
|
|
95
97
|
&& chmod -R 755 /opt/playwright-browsers
|
|
96
98
|
|
|
97
99
|
# Copy scripts
|
package/dist/agent/README.md
CHANGED
|
@@ -22,24 +22,24 @@ docker compose up --build
|
|
|
22
22
|
1. Container clones your repository
|
|
23
23
|
2. Checks out the specified branch
|
|
24
24
|
3. **Iteration 0 (Setup)**: Sets up .gitignore, initializes progress.txt
|
|
25
|
-
4. Reads `.wile/prd.json`
|
|
26
|
-
5. Loops through
|
|
27
|
-
- Picks
|
|
25
|
+
4. Reads `.wile/prd.json`
|
|
26
|
+
5. Loops through stories, implementing one per iteration:
|
|
27
|
+
- Picks first runnable story in array order where `status: "pending"` and dependencies are done
|
|
28
28
|
- Implements the feature/fix
|
|
29
29
|
- Runs tests and typecheck
|
|
30
|
-
-
|
|
31
|
-
- Marks story as complete
|
|
30
|
+
- Marks story as done
|
|
32
31
|
- Logs learnings
|
|
33
|
-
|
|
32
|
+
- Commits changes
|
|
33
|
+
6. Repeats until all stories are done or max iterations reached
|
|
34
34
|
|
|
35
35
|
## Project Structure
|
|
36
36
|
|
|
37
|
-
Your repository must have a `.wile
|
|
37
|
+
Your repository must have a `.wile/prd.json` file:
|
|
38
38
|
|
|
39
39
|
```
|
|
40
40
|
your-repo/
|
|
41
41
|
├── .wile/
|
|
42
|
-
│ ├── prd.json #
|
|
42
|
+
│ ├── prd.json # Stories backlog (required)
|
|
43
43
|
│ ├── progress.txt # Learnings log (created by agent)
|
|
44
44
|
│ └── screenshots/ # Visual verification (created by agent)
|
|
45
45
|
└── ... your code ...
|
|
@@ -49,18 +49,18 @@ your-repo/
|
|
|
49
49
|
|
|
50
50
|
```json
|
|
51
51
|
{
|
|
52
|
-
"
|
|
52
|
+
"stories": [
|
|
53
53
|
{
|
|
54
|
-
"id":
|
|
54
|
+
"id": 1,
|
|
55
55
|
"title": "Add login form",
|
|
56
|
+
"description": "Email/password form with validation",
|
|
56
57
|
"acceptanceCriteria": [
|
|
57
58
|
"Email/password fields exist",
|
|
58
59
|
"Form validates email format",
|
|
59
60
|
"typecheck passes"
|
|
60
61
|
],
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"notes": "Optional notes"
|
|
62
|
+
"dependsOn": [],
|
|
63
|
+
"status": "pending"
|
|
64
64
|
}
|
|
65
65
|
]
|
|
66
66
|
}
|
|
@@ -68,12 +68,12 @@ your-repo/
|
|
|
68
68
|
|
|
69
69
|
### Fields
|
|
70
70
|
|
|
71
|
-
- `id`: Unique identifier
|
|
71
|
+
- `id`: Unique numeric identifier
|
|
72
72
|
- `title`: Short description
|
|
73
|
+
- `description`: Additional context for implementation
|
|
73
74
|
- `acceptanceCriteria`: List of requirements (be specific!)
|
|
74
|
-
- `
|
|
75
|
-
- `
|
|
76
|
-
- `notes`: Optional context for the agent
|
|
75
|
+
- `dependsOn`: Array of prerequisite story IDs
|
|
76
|
+
- `status`: `pending` or `done`
|
|
77
77
|
|
|
78
78
|
## Tips for Success
|
|
79
79
|
|
|
@@ -110,13 +110,15 @@ For UI work, tell the agent how to verify:
|
|
|
110
110
|
|
|
111
111
|
| Variable | Required | Description |
|
|
112
112
|
|----------|----------|-------------|
|
|
113
|
-
| `CODING_AGENT` | No | `CC` (Claude Code, default), `GC` (Gemini CLI),
|
|
113
|
+
| `CODING_AGENT` | No | `CC` (Claude Code, default), `GC` (Gemini CLI), `OC` (OpenCode via OpenRouter), or `CX` (Codex CLI) |
|
|
114
114
|
| `CC_CLAUDE_CODE_OAUTH_TOKEN` | Yes* | OAuth token from `claude setup-token` (uses Pro/Max subscription) |
|
|
115
115
|
| `CC_ANTHROPIC_API_KEY` | Yes* | API key (uses API credits - alternative to OAuth) |
|
|
116
116
|
| `GEMINI_OAUTH_CREDS_B64` | Yes (GC)* | Base64 OAuth creds from `~/.gemini/oauth_creds.json` (uses Google account) |
|
|
117
117
|
| `GEMINI_API_KEY` | Yes (GC)* | Gemini API key (uses API credits - alternative to OAuth) |
|
|
118
118
|
| `OC_OPENROUTER_API_KEY` | Yes (OC) | OpenRouter API key for OpenCode |
|
|
119
119
|
| `OC_MODEL` | Yes (OC) | OpenRouter model id (set `glm-4.7` to target `openrouter/z-ai/glm-4.7`) |
|
|
120
|
+
| `CODEX_AUTH_JSON_B64` | Yes (CX)* | Base64 of `~/.codex/auth.json` from `codex login` (uses ChatGPT subscription) |
|
|
121
|
+
| `CODEX_API_KEY` | Yes (CX)* | OpenAI API key (uses API credits - alternative to auth.json) |
|
|
120
122
|
| `GEMINI_MODEL` | No (GC) | Gemini model name (default: `auto-gemini-3`) |
|
|
121
123
|
| `WILE_REPO_SOURCE` | No | `github` (default) or `local` |
|
|
122
124
|
| `GITHUB_TOKEN` | Yes (github) | GitHub PAT with repo access |
|
|
@@ -124,15 +126,17 @@ For UI work, tell the agent how to verify:
|
|
|
124
126
|
| `BRANCH_NAME` | Yes (github) | Branch to work on |
|
|
125
127
|
| `MAX_ITERATIONS` | No | Max loops (default: 25) |
|
|
126
128
|
| `CC_CLAUDE_MODEL` | No | Claude model alias/name (default: sonnet) |
|
|
129
|
+
| `CODEX_MODEL` | No (CX) | Codex model name (defaults to Codex CLI default) |
|
|
127
130
|
|
|
128
131
|
*Either `CC_CLAUDE_CODE_OAUTH_TOKEN` or `CC_ANTHROPIC_API_KEY` is required when `CODING_AGENT=CC`.
|
|
129
|
-
*Either `GEMINI_OAUTH_CREDS_B64` or `GEMINI_API_KEY` is required when `CODING_AGENT=GC`.
|
|
132
|
+
*Either `GEMINI_OAUTH_CREDS_B64` or `GEMINI_API_KEY` is required when `CODING_AGENT=GC`.
|
|
133
|
+
*Either `CODEX_AUTH_JSON_B64` or `CODEX_API_KEY` is required when `CODING_AGENT=CX`.
|
|
130
134
|
|
|
131
135
|
## Output Files
|
|
132
136
|
|
|
133
|
-
|
|
137
|
+
Tracked artifacts:
|
|
134
138
|
|
|
135
|
-
-
|
|
139
|
+
- `.wile/prd.json` - Product requirements stories backlog
|
|
136
140
|
- `progress.txt` - Log of completed work and learnings
|
|
137
141
|
- `screenshots/` - Visual verification screenshots (gitignored)
|
|
138
142
|
|
|
@@ -153,5 +157,5 @@ git show origin/your-branch:.wile/progress.txt
|
|
|
153
157
|
Check story status:
|
|
154
158
|
|
|
155
159
|
```bash
|
|
156
|
-
git show origin/your-branch:.wile/prd.json | jq '.
|
|
160
|
+
git show origin/your-branch:.wile/prd.json | jq '.stories[] | {id, title, status}'
|
|
157
161
|
```
|
package/dist/agent/entrypoint.sh
CHANGED
|
@@ -15,6 +15,8 @@ if command -v sysctl >/dev/null 2>&1; then
|
|
|
15
15
|
sysctl -w net.ipv6.conf.default.disable_ipv6=0 >/dev/null 2>&1 || true
|
|
16
16
|
fi
|
|
17
17
|
|
|
18
|
+
SCRIPT_DIR="/home/wile/scripts"
|
|
19
|
+
|
|
18
20
|
if [ "${WILE_TEST:-}" = "true" ]; then
|
|
19
21
|
TEST_REPO="${WILE_TEST_REPO_PATH:-/home/wile/workspace/repo}"
|
|
20
22
|
|
|
@@ -30,18 +32,19 @@ if [ "${WILE_TEST:-}" = "true" ]; then
|
|
|
30
32
|
exit 1
|
|
31
33
|
fi
|
|
32
34
|
|
|
35
|
+
bun "$SCRIPT_DIR/validate-prd.ts" --path .wile/prd.json >/dev/null
|
|
36
|
+
|
|
33
37
|
echo "TEST MODE: running mocked agent (no GitHub, no Claude)"
|
|
34
38
|
|
|
35
39
|
node - << 'NODE'
|
|
36
40
|
const fs = require('fs');
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const prdPath = path.join('.wile', 'prd.json');
|
|
40
|
-
const progressPath = path.join('.wile', 'progress.txt');
|
|
41
|
+
const prdPath = '.wile/prd.json';
|
|
42
|
+
const progressPath = '.wile/progress.txt';
|
|
41
43
|
|
|
42
44
|
const prd = JSON.parse(fs.readFileSync(prdPath, 'utf8'));
|
|
43
|
-
const stories = Array.isArray(prd.
|
|
44
|
-
const
|
|
45
|
+
const stories = Array.isArray(prd.stories) ? prd.stories : [];
|
|
46
|
+
const storyById = new Map(stories.map((story) => [story.id, story]));
|
|
47
|
+
const pending = stories.filter((story) => story.status === 'pending');
|
|
45
48
|
|
|
46
49
|
if (process.env.TEST_FORWARD) {
|
|
47
50
|
console.log(`Forwarded env: ${process.env.TEST_FORWARD}`);
|
|
@@ -52,14 +55,20 @@ if (pending.length === 0) {
|
|
|
52
55
|
process.exit(0);
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
const story =
|
|
59
|
+
stories.find(
|
|
60
|
+
(candidate) =>
|
|
61
|
+
candidate.status === 'pending' &&
|
|
62
|
+
Array.isArray(candidate.dependsOn) &&
|
|
63
|
+
candidate.dependsOn.every((depId) => storyById.get(depId)?.status === 'done')
|
|
64
|
+
) || null;
|
|
65
|
+
|
|
66
|
+
if (!story) {
|
|
67
|
+
console.error('No runnable pending stories in test mode.');
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
60
70
|
|
|
61
|
-
|
|
62
|
-
story.passes = true;
|
|
71
|
+
story.status = 'done';
|
|
63
72
|
fs.writeFileSync(prdPath, JSON.stringify(prd, null, 2) + '\n');
|
|
64
73
|
|
|
65
74
|
if (!fs.existsSync(progressPath)) {
|
|
@@ -115,6 +124,16 @@ elif [ "$CODING_AGENT" = "GC" ]; then
|
|
|
115
124
|
echo "Run 'gemini' locally and choose Login with Google to create ~/.gemini/oauth_creds.json."
|
|
116
125
|
exit 1
|
|
117
126
|
fi
|
|
127
|
+
elif [ "$CODING_AGENT" = "CX" ]; then
|
|
128
|
+
if [ -z "$CODEX_AUTH_JSON_B64" ] && [ -z "$CODEX_AUTH_JSON_PATH" ] && [ -z "$CODEX_API_KEY" ] && [ -z "$OPENAI_API_KEY" ]; then
|
|
129
|
+
echo "ERROR: Either CODEX_AUTH_JSON_B64 (or CODEX_AUTH_JSON_PATH) or CODEX_API_KEY is required"
|
|
130
|
+
echo ""
|
|
131
|
+
echo " CODEX_AUTH_JSON_B64 - Base64 of ~/.codex/auth.json from 'codex login' (recommended)"
|
|
132
|
+
echo " CODEX_API_KEY - OpenAI API key (pay per token)"
|
|
133
|
+
echo ""
|
|
134
|
+
echo "Run 'codex login' (or 'codex login --device-auth') locally, then base64 ~/.codex/auth.json."
|
|
135
|
+
exit 1
|
|
136
|
+
fi
|
|
118
137
|
else
|
|
119
138
|
if [ -z "$CC_CLAUDE_CODE_OAUTH_TOKEN" ] && [ -z "$CC_ANTHROPIC_API_KEY" ]; then
|
|
120
139
|
echo "ERROR: Either CC_CLAUDE_CODE_OAUTH_TOKEN or CC_ANTHROPIC_API_KEY is required"
|
|
@@ -128,7 +147,6 @@ else
|
|
|
128
147
|
fi
|
|
129
148
|
|
|
130
149
|
MAX_ITERATIONS=${MAX_ITERATIONS:-25}
|
|
131
|
-
SCRIPT_DIR="/home/wile/scripts"
|
|
132
150
|
WORKSPACE="/home/wile/workspace"
|
|
133
151
|
|
|
134
152
|
if [ "${WILE_MOCK_CLAUDE:-}" = "true" ] && [ "$CODING_AGENT" = "CC" ]; then
|
|
@@ -149,6 +167,15 @@ if [ "${WILE_MOCK_GEMINI:-}" = "true" ] && [ "$CODING_AGENT" = "GC" ]; then
|
|
|
149
167
|
export PATH="$MOCK_BIN:$PATH"
|
|
150
168
|
fi
|
|
151
169
|
|
|
170
|
+
if [ "${WILE_MOCK_CODEX:-}" = "true" ] && [ "$CODING_AGENT" = "CX" ]; then
|
|
171
|
+
echo " Codex: Mocked"
|
|
172
|
+
MOCK_BIN="/home/wile/mock-bin"
|
|
173
|
+
mkdir -p "$MOCK_BIN"
|
|
174
|
+
cp "$SCRIPT_DIR/mock-codex.sh" "$MOCK_BIN/codex"
|
|
175
|
+
chmod +x "$MOCK_BIN/codex"
|
|
176
|
+
export PATH="$MOCK_BIN:$PATH"
|
|
177
|
+
fi
|
|
178
|
+
|
|
152
179
|
if [ "$CODING_AGENT" = "OC" ]; then
|
|
153
180
|
if [ "$OC_PROVIDER" = "openrouter" ]; then
|
|
154
181
|
echo " Auth: OpenRouter (OpenCode)"
|
|
@@ -212,6 +239,30 @@ JSON
|
|
|
212
239
|
}
|
|
213
240
|
JSON
|
|
214
241
|
fi
|
|
242
|
+
elif [ "$CODING_AGENT" = "CX" ]; then
|
|
243
|
+
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
|
244
|
+
export CODEX_HOME
|
|
245
|
+
|
|
246
|
+
if [ -n "$CODEX_AUTH_JSON_B64" ] || [ -n "$CODEX_AUTH_JSON_PATH" ]; then
|
|
247
|
+
echo " Auth: ChatGPT (subscription)"
|
|
248
|
+
mkdir -p "$CODEX_HOME"
|
|
249
|
+
if [ -n "$CODEX_AUTH_JSON_B64" ]; then
|
|
250
|
+
echo "$CODEX_AUTH_JSON_B64" | base64 -d > "$CODEX_HOME/auth.json"
|
|
251
|
+
else
|
|
252
|
+
cp "$CODEX_AUTH_JSON_PATH" "$CODEX_HOME/auth.json"
|
|
253
|
+
fi
|
|
254
|
+
else
|
|
255
|
+
echo " Auth: API Key (OpenAI)"
|
|
256
|
+
CODEX_API_KEY_VALUE="${CODEX_API_KEY:-$OPENAI_API_KEY}"
|
|
257
|
+
mkdir -p "$CODEX_HOME"
|
|
258
|
+
cat > "$CODEX_HOME/auth.json" << EOF
|
|
259
|
+
{
|
|
260
|
+
"auth_mode": "apikey",
|
|
261
|
+
"OPENAI_API_KEY": "$CODEX_API_KEY_VALUE"
|
|
262
|
+
}
|
|
263
|
+
EOF
|
|
264
|
+
fi
|
|
265
|
+
chmod 600 "$CODEX_HOME/auth.json"
|
|
215
266
|
else
|
|
216
267
|
# Set up Claude Code authentication
|
|
217
268
|
if [ -n "$CC_CLAUDE_CODE_OAUTH_TOKEN" ]; then
|
|
@@ -314,24 +365,28 @@ echo "Checking for .wile/prd.json..."
|
|
|
314
365
|
if [ ! -f ".wile/prd.json" ]; then
|
|
315
366
|
echo "ERROR: .wile/prd.json not found!"
|
|
316
367
|
echo ""
|
|
317
|
-
echo "Your repository must have a .wile/prd.json file
|
|
318
|
-
echo "This file contains the
|
|
368
|
+
echo "Your repository must have a .wile/prd.json file."
|
|
369
|
+
echo "This file contains the stories for Wile to implement."
|
|
319
370
|
echo ""
|
|
320
371
|
echo "Example structure:"
|
|
321
372
|
echo ' {'
|
|
322
|
-
echo ' "
|
|
373
|
+
echo ' "stories": ['
|
|
323
374
|
echo ' {'
|
|
324
|
-
echo ' "id":
|
|
375
|
+
echo ' "id": 1,'
|
|
325
376
|
echo ' "title": "My feature",'
|
|
377
|
+
echo ' "description": "What this story delivers",'
|
|
326
378
|
echo ' "acceptanceCriteria": ["..."],'
|
|
327
|
-
echo ' "
|
|
328
|
-
echo ' "
|
|
379
|
+
echo ' "dependsOn": [],'
|
|
380
|
+
echo ' "status": "pending"'
|
|
329
381
|
echo ' }'
|
|
330
382
|
echo ' ]'
|
|
331
383
|
echo ' }'
|
|
332
384
|
exit 1
|
|
333
385
|
fi
|
|
334
386
|
|
|
387
|
+
echo "Validating .wile/prd.json..."
|
|
388
|
+
bun "$SCRIPT_DIR/validate-prd.ts" --path .wile/prd.json
|
|
389
|
+
|
|
335
390
|
# Set up .wile/screenshots directory
|
|
336
391
|
echo "Setting up .wile directory..."
|
|
337
392
|
mkdir -p .wile/screenshots
|
|
@@ -390,7 +445,7 @@ elif [ $EXIT_CODE -eq 1 ]; then
|
|
|
390
445
|
git commit -m "WIP: wile stopped at max iterations ($TIMESTAMP)
|
|
391
446
|
|
|
392
447
|
Partial work committed. Check .wile/progress.txt for details.
|
|
393
|
-
Some stories may still have
|
|
448
|
+
Some stories may still have status=\"pending\" in .wile/prd.json."
|
|
394
449
|
git push
|
|
395
450
|
fi
|
|
396
451
|
elif [ $EXIT_CODE -eq 2 ]; then
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { createInterface } from "node:readline";
|
|
3
|
+
|
|
4
|
+
type JsonObject = Record<string, unknown>;
|
|
5
|
+
|
|
6
|
+
const isObject = (value: unknown): value is JsonObject =>
|
|
7
|
+
typeof value === "object" && value !== null;
|
|
8
|
+
|
|
9
|
+
const getString = (obj: JsonObject, key: string): string | undefined => {
|
|
10
|
+
const value = obj[key];
|
|
11
|
+
return typeof value === "string" ? value : undefined;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const getObject = (obj: JsonObject, key: string): JsonObject | undefined => {
|
|
15
|
+
const value = obj[key];
|
|
16
|
+
return isObject(value) ? value : undefined;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const writeLine = (value: string): void => {
|
|
20
|
+
process.stdout.write(`${value}\n`);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const writeTextChunk = (text: string): void => {
|
|
24
|
+
process.stdout.write(text);
|
|
25
|
+
if (!text.endsWith("\n")) {
|
|
26
|
+
process.stdout.write("\n");
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const extractText = (content: unknown): void => {
|
|
31
|
+
if (!Array.isArray(content)) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
for (const rawChunk of content) {
|
|
36
|
+
if (!isObject(rawChunk)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const chunkType = getString(rawChunk, "type");
|
|
41
|
+
if (chunkType === "text") {
|
|
42
|
+
const text = getString(rawChunk, "text");
|
|
43
|
+
if (text) {
|
|
44
|
+
writeTextChunk(text);
|
|
45
|
+
}
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (chunkType === "thinking") {
|
|
50
|
+
const thinking = getString(rawChunk, "thinking");
|
|
51
|
+
if (thinking) {
|
|
52
|
+
writeLine(`[thinking] ${thinking}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const extractToolUse = (content: unknown): void => {
|
|
59
|
+
if (!Array.isArray(content)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const rawChunk of content) {
|
|
64
|
+
if (!isObject(rawChunk) || getString(rawChunk, "type") !== "tool_use") {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const toolName = getString(rawChunk, "name") ?? "tool";
|
|
69
|
+
const input = getObject(rawChunk, "input");
|
|
70
|
+
const description = input ? getString(input, "description") ?? "" : "";
|
|
71
|
+
const command = input ? getString(input, "command") ?? "" : "";
|
|
72
|
+
|
|
73
|
+
if (description && command) {
|
|
74
|
+
writeLine(`[tool] ${toolName}: ${description} (${command})`);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (command) {
|
|
78
|
+
writeLine(`[tool] ${toolName}: ${command}`);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (description) {
|
|
82
|
+
writeLine(`[tool] ${toolName}: ${description}`);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
writeLine(`[tool] ${toolName}`);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const rl = createInterface({
|
|
90
|
+
input: process.stdin,
|
|
91
|
+
crlfDelay: Infinity
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
rl.on("line", (line: string) => {
|
|
95
|
+
if (!line.trim()) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (process.env.WILE_STREAM_JSON === "true") {
|
|
100
|
+
writeLine(line);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let parsed: unknown;
|
|
105
|
+
try {
|
|
106
|
+
parsed = JSON.parse(line);
|
|
107
|
+
} catch {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!isObject(parsed)) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const payloadType = getString(parsed, "type");
|
|
116
|
+
|
|
117
|
+
if (payloadType === "system" && getString(parsed, "subtype") === "init") {
|
|
118
|
+
const model = getString(parsed, "model") ?? "unknown";
|
|
119
|
+
writeLine(`[system] model: ${model}`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (payloadType === "assistant") {
|
|
124
|
+
const message = getObject(parsed, "message");
|
|
125
|
+
if (!message) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const content = message["content"];
|
|
129
|
+
extractToolUse(content);
|
|
130
|
+
extractText(content);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (payloadType === "user") {
|
|
135
|
+
const message = getObject(parsed, "message");
|
|
136
|
+
const content = message ? message["content"] : undefined;
|
|
137
|
+
if (!Array.isArray(content)) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
for (const rawChunk of content) {
|
|
142
|
+
if (!isObject(rawChunk) || getString(rawChunk, "type") !== "tool_result") {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const toolUseId = getString(rawChunk, "tool_use_id") ?? "unknown";
|
|
147
|
+
const isError = rawChunk["is_error"] === true;
|
|
148
|
+
writeLine(`[tool-result] ${toolUseId} ${isError ? "error" : "ok"}`);
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (payloadType === "result") {
|
|
154
|
+
const result = getString(parsed, "result");
|
|
155
|
+
if (result) {
|
|
156
|
+
writeLine(result);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
});
|