wile 0.6.8 → 0.8.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 +8 -6
- package/dist/agent/README.md +6 -2
- package/dist/agent/entrypoint.sh +43 -0
- package/dist/agent/scripts/codex-stream.js +125 -0
- package/dist/agent/scripts/mock-claude.sh +18 -3
- package/dist/agent/scripts/mock-codex.sh +99 -0
- package/dist/agent/scripts/test-preflight-docker.sh +62 -1
- package/dist/agent/scripts/wile-compact.sh +16 -0
- package/dist/agent/scripts/wile-preflight.sh +39 -2
- package/dist/agent/scripts/wile.sh +42 -7
- package/dist/cli.js +86 -7
- package/package.json +1 -1
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
|
@@ -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
|
@@ -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,9 +126,11 @@ 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
|
|
package/dist/agent/entrypoint.sh
CHANGED
|
@@ -115,6 +115,16 @@ elif [ "$CODING_AGENT" = "GC" ]; then
|
|
|
115
115
|
echo "Run 'gemini' locally and choose Login with Google to create ~/.gemini/oauth_creds.json."
|
|
116
116
|
exit 1
|
|
117
117
|
fi
|
|
118
|
+
elif [ "$CODING_AGENT" = "CX" ]; then
|
|
119
|
+
if [ -z "$CODEX_AUTH_JSON_B64" ] && [ -z "$CODEX_AUTH_JSON_PATH" ] && [ -z "$CODEX_API_KEY" ] && [ -z "$OPENAI_API_KEY" ]; then
|
|
120
|
+
echo "ERROR: Either CODEX_AUTH_JSON_B64 (or CODEX_AUTH_JSON_PATH) or CODEX_API_KEY is required"
|
|
121
|
+
echo ""
|
|
122
|
+
echo " CODEX_AUTH_JSON_B64 - Base64 of ~/.codex/auth.json from 'codex login' (recommended)"
|
|
123
|
+
echo " CODEX_API_KEY - OpenAI API key (pay per token)"
|
|
124
|
+
echo ""
|
|
125
|
+
echo "Run 'codex login' (or 'codex login --device-auth') locally, then base64 ~/.codex/auth.json."
|
|
126
|
+
exit 1
|
|
127
|
+
fi
|
|
118
128
|
else
|
|
119
129
|
if [ -z "$CC_CLAUDE_CODE_OAUTH_TOKEN" ] && [ -z "$CC_ANTHROPIC_API_KEY" ]; then
|
|
120
130
|
echo "ERROR: Either CC_CLAUDE_CODE_OAUTH_TOKEN or CC_ANTHROPIC_API_KEY is required"
|
|
@@ -149,6 +159,15 @@ if [ "${WILE_MOCK_GEMINI:-}" = "true" ] && [ "$CODING_AGENT" = "GC" ]; then
|
|
|
149
159
|
export PATH="$MOCK_BIN:$PATH"
|
|
150
160
|
fi
|
|
151
161
|
|
|
162
|
+
if [ "${WILE_MOCK_CODEX:-}" = "true" ] && [ "$CODING_AGENT" = "CX" ]; then
|
|
163
|
+
echo " Codex: Mocked"
|
|
164
|
+
MOCK_BIN="/home/wile/mock-bin"
|
|
165
|
+
mkdir -p "$MOCK_BIN"
|
|
166
|
+
cp "$SCRIPT_DIR/mock-codex.sh" "$MOCK_BIN/codex"
|
|
167
|
+
chmod +x "$MOCK_BIN/codex"
|
|
168
|
+
export PATH="$MOCK_BIN:$PATH"
|
|
169
|
+
fi
|
|
170
|
+
|
|
152
171
|
if [ "$CODING_AGENT" = "OC" ]; then
|
|
153
172
|
if [ "$OC_PROVIDER" = "openrouter" ]; then
|
|
154
173
|
echo " Auth: OpenRouter (OpenCode)"
|
|
@@ -212,6 +231,30 @@ JSON
|
|
|
212
231
|
}
|
|
213
232
|
JSON
|
|
214
233
|
fi
|
|
234
|
+
elif [ "$CODING_AGENT" = "CX" ]; then
|
|
235
|
+
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
|
236
|
+
export CODEX_HOME
|
|
237
|
+
|
|
238
|
+
if [ -n "$CODEX_AUTH_JSON_B64" ] || [ -n "$CODEX_AUTH_JSON_PATH" ]; then
|
|
239
|
+
echo " Auth: ChatGPT (subscription)"
|
|
240
|
+
mkdir -p "$CODEX_HOME"
|
|
241
|
+
if [ -n "$CODEX_AUTH_JSON_B64" ]; then
|
|
242
|
+
echo "$CODEX_AUTH_JSON_B64" | base64 -d > "$CODEX_HOME/auth.json"
|
|
243
|
+
else
|
|
244
|
+
cp "$CODEX_AUTH_JSON_PATH" "$CODEX_HOME/auth.json"
|
|
245
|
+
fi
|
|
246
|
+
else
|
|
247
|
+
echo " Auth: API Key (OpenAI)"
|
|
248
|
+
CODEX_API_KEY_VALUE="${CODEX_API_KEY:-$OPENAI_API_KEY}"
|
|
249
|
+
mkdir -p "$CODEX_HOME"
|
|
250
|
+
cat > "$CODEX_HOME/auth.json" << EOF
|
|
251
|
+
{
|
|
252
|
+
"auth_mode": "apikey",
|
|
253
|
+
"OPENAI_API_KEY": "$CODEX_API_KEY_VALUE"
|
|
254
|
+
}
|
|
255
|
+
EOF
|
|
256
|
+
fi
|
|
257
|
+
chmod 600 "$CODEX_HOME/auth.json"
|
|
215
258
|
else
|
|
216
259
|
# Set up Claude Code authentication
|
|
217
260
|
if [ -n "$CC_CLAUDE_CODE_OAUTH_TOKEN" ]; then
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const readline = require("node:readline");
|
|
3
|
+
|
|
4
|
+
const rl = readline.createInterface({
|
|
5
|
+
input: process.stdin,
|
|
6
|
+
crlfDelay: Infinity
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const writeLine = (value) => {
|
|
10
|
+
process.stdout.write(`${value}\n`);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const writeText = (text) => {
|
|
14
|
+
if (typeof text !== "string") return;
|
|
15
|
+
process.stdout.write(text);
|
|
16
|
+
if (!text.endsWith("\n")) {
|
|
17
|
+
process.stdout.write("\n");
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const formatChanges = (changes) => {
|
|
22
|
+
if (!Array.isArray(changes) || changes.length === 0) return "";
|
|
23
|
+
return changes
|
|
24
|
+
.map((change) => `${change.kind ?? "update"} ${change.path ?? "unknown"}`)
|
|
25
|
+
.join(", ");
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
rl.on("line", (line) => {
|
|
29
|
+
if (!line.trim()) return;
|
|
30
|
+
if (process.env.WILE_STREAM_JSON === "true") {
|
|
31
|
+
writeLine(line);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let payload;
|
|
36
|
+
try {
|
|
37
|
+
payload = JSON.parse(line);
|
|
38
|
+
} catch {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const type = payload.type;
|
|
43
|
+
|
|
44
|
+
if (type === "thread.started") {
|
|
45
|
+
if (payload.thread_id) {
|
|
46
|
+
writeLine(`[system] thread: ${payload.thread_id}`);
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (type === "turn.failed") {
|
|
52
|
+
const message = payload.error?.message ?? "Unknown error";
|
|
53
|
+
writeLine(`[error] ${message}`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (type === "error") {
|
|
58
|
+
const message = payload.message ?? "Unknown error";
|
|
59
|
+
writeLine(`[error] ${message}`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!payload.item) return;
|
|
64
|
+
|
|
65
|
+
const itemType = payload.item.type;
|
|
66
|
+
|
|
67
|
+
if (type === "item.completed" && itemType === "agent_message") {
|
|
68
|
+
writeText(payload.item.text);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (type === "item.completed" && itemType === "reasoning") {
|
|
73
|
+
writeLine(`[thinking] ${payload.item.text ?? ""}`);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (itemType === "command_execution") {
|
|
78
|
+
const command = payload.item.command ?? "command";
|
|
79
|
+
if (type === "item.started") {
|
|
80
|
+
writeLine(`[tool] shell: ${command}`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (type === "item.completed") {
|
|
84
|
+
const status = payload.item.status ?? "completed";
|
|
85
|
+
const exitCode =
|
|
86
|
+
payload.item.exit_code !== undefined && payload.item.exit_code !== null
|
|
87
|
+
? ` (exit ${payload.item.exit_code})`
|
|
88
|
+
: "";
|
|
89
|
+
writeLine(`[tool-result] shell ${status}${exitCode}`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (type === "item.completed" && itemType === "file_change") {
|
|
95
|
+
const detail = formatChanges(payload.item.changes);
|
|
96
|
+
writeLine(`[tool] file_change${detail ? `: ${detail}` : ""}`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (itemType === "mcp_tool_call") {
|
|
101
|
+
const server = payload.item.server ?? "mcp";
|
|
102
|
+
const tool = payload.item.tool ?? "tool";
|
|
103
|
+
if (type === "item.started") {
|
|
104
|
+
writeLine(`[tool] mcp ${server}/${tool}`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (type === "item.completed") {
|
|
108
|
+
const status = payload.item.status ?? "completed";
|
|
109
|
+
writeLine(`[tool-result] mcp ${server}/${tool} ${status}`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (itemType === "web_search") {
|
|
115
|
+
const query = payload.item.query ?? "";
|
|
116
|
+
if (type === "item.started") {
|
|
117
|
+
writeLine(`[tool] web_search${query ? `: ${query}` : ""}`);
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (type === "item.completed" && itemType === "error") {
|
|
123
|
+
writeLine(`[error] ${payload.item.message ?? "Unknown error"}`);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
@@ -60,7 +60,11 @@ COUNT=$(cat "$COUNT_FILE")
|
|
|
60
60
|
NEXT_COUNT=$((COUNT + 1))
|
|
61
61
|
echo "$NEXT_COUNT" > "$COUNT_FILE"
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
MODE=${WILE_MOCK_MODE:-}
|
|
64
|
+
if [ -n "$MODE" ] && { [ "$MODE" = "preflight_fail" ] || [ "$MODE" = "preflight_fail_split" ] || [ "$MODE" = "preflight_fail_trailing" ]; }; then
|
|
65
|
+
if [ "$MODE" = "preflight_fail_trailing" ] && [ "$COUNT" -ne 0 ]; then
|
|
66
|
+
:
|
|
67
|
+
else
|
|
64
68
|
PROGRESS_PATH=".wile/progress.txt"
|
|
65
69
|
if [ -f "$PROGRESS_PATH" ]; then
|
|
66
70
|
:
|
|
@@ -82,10 +86,21 @@ if [ "${WILE_MOCK_MODE:-}" = "preflight_fail" ]; then
|
|
|
82
86
|
- Mocked preflight failure (WILE_MOCK_MODE=preflight_fail)
|
|
83
87
|
EOF
|
|
84
88
|
|
|
85
|
-
|
|
89
|
+
if [ "$MODE" = "preflight_fail_split" ]; then
|
|
90
|
+
cat <<'JSON'
|
|
91
|
+
{"type":"assistant","message":{"content":[{"type":"text","text":"<\npromise>PREFLIGHT_FAILED</promise>\n"}]}}
|
|
92
|
+
JSON
|
|
93
|
+
elif [ "$MODE" = "preflight_fail_trailing" ]; then
|
|
94
|
+
cat <<'JSON'
|
|
95
|
+
{"type":"assistant","message":{"content":[{"type":"text","text":"<promise>PREFLIGHT_FAILED</promise> trailing\n"}]}}
|
|
96
|
+
JSON
|
|
97
|
+
else
|
|
98
|
+
cat <<'JSON'
|
|
86
99
|
{"type":"assistant","message":{"content":[{"type":"text","text":"<promise>PREFLIGHT_FAILED</promise>\n"}]}}
|
|
87
100
|
JSON
|
|
88
|
-
|
|
101
|
+
fi
|
|
102
|
+
exit 0
|
|
103
|
+
fi
|
|
89
104
|
fi
|
|
90
105
|
|
|
91
106
|
if [ "$COUNT" -eq 0 ]; then
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
if [ "${WILE_MODE:-}" = "compact" ]; then
|
|
5
|
+
node - <<'NODE'
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
|
|
8
|
+
const prdPath = ".wile/prd.json";
|
|
9
|
+
const progressPath = ".wile/progress.txt";
|
|
10
|
+
const prd = JSON.parse(fs.readFileSync(prdPath, "utf8"));
|
|
11
|
+
const stories = Array.isArray(prd.userStories) ? prd.userStories : [];
|
|
12
|
+
const firstStory = stories[0] || {};
|
|
13
|
+
const lastStory = stories[stories.length - 1] || {};
|
|
14
|
+
const priority = typeof firstStory.priority === "number" ? firstStory.priority : 1;
|
|
15
|
+
const taskIds = `From ${firstStory.id || "TASK-001"} to ${lastStory.id || "TASK-001"}`;
|
|
16
|
+
|
|
17
|
+
const compactStory = {
|
|
18
|
+
id: "GROUP-001",
|
|
19
|
+
title: "summary of everything done here",
|
|
20
|
+
tasks: [
|
|
21
|
+
"High level of what was accomplished here",
|
|
22
|
+
"Should NOT have all tasks in here, should be very summarized"
|
|
23
|
+
],
|
|
24
|
+
taskIds,
|
|
25
|
+
priority,
|
|
26
|
+
passes: true,
|
|
27
|
+
notes: "Don't repeat task ids when starting the next one."
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
fs.writeFileSync(prdPath, JSON.stringify({ userStories: [compactStory] }, null, 2) + "\n");
|
|
31
|
+
|
|
32
|
+
const progressLines = [
|
|
33
|
+
"# Wile Progress Log",
|
|
34
|
+
"",
|
|
35
|
+
"## Codebase Patterns",
|
|
36
|
+
"- Summarized key learnings for future agents.",
|
|
37
|
+
"",
|
|
38
|
+
"---",
|
|
39
|
+
"",
|
|
40
|
+
"Compacted the progress log to highlight the most important work and patterns.",
|
|
41
|
+
"Kept only high-level details to preserve context without noise.",
|
|
42
|
+
""
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
fs.writeFileSync(progressPath, progressLines.join("\n"));
|
|
46
|
+
NODE
|
|
47
|
+
|
|
48
|
+
cat <<'JSON'
|
|
49
|
+
{"type":"item.completed","item":{"id":"item_1","type":"agent_message","text":"{\"id\":\"GROUP-001\",\"title\":\"summary of everything done here\",\"tasks\":[\"High level of what was accomplished here\",\"Should NOT have all tasks in here, should be very summarized\"],\"taskIds\":\"From TASK-001 to TASK-029\",\"priority\":1,\"passes\":true,\"notes\":\"Don't repeat task ids when starting the next one.\"}\n"}}
|
|
50
|
+
JSON
|
|
51
|
+
exit 0
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
COUNT_FILE="/tmp/wile-codex-mock-count"
|
|
55
|
+
if [ ! -f "$COUNT_FILE" ]; then
|
|
56
|
+
echo "0" > "$COUNT_FILE"
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
COUNT=$(cat "$COUNT_FILE")
|
|
60
|
+
NEXT_COUNT=$((COUNT + 1))
|
|
61
|
+
echo "$NEXT_COUNT" > "$COUNT_FILE"
|
|
62
|
+
|
|
63
|
+
if [ "${WILE_MOCK_MODE:-}" = "preflight_fail" ]; then
|
|
64
|
+
PROGRESS_PATH=".wile/progress.txt"
|
|
65
|
+
if [ -f "$PROGRESS_PATH" ]; then
|
|
66
|
+
:
|
|
67
|
+
else
|
|
68
|
+
printf '%s\n' "# Wile Progress Log" "" "## Codebase Patterns" "" "---" > "$PROGRESS_PATH"
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
DATE=$(date +%Y-%m-%d)
|
|
72
|
+
cat >> "$PROGRESS_PATH" <<EOF
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## ${DATE} - PREFLIGHT FAILED
|
|
77
|
+
|
|
78
|
+
**Checks run:**
|
|
79
|
+
- Mocked preflight check
|
|
80
|
+
|
|
81
|
+
**Failures:**
|
|
82
|
+
- Mocked preflight failure (WILE_MOCK_MODE=preflight_fail)
|
|
83
|
+
EOF
|
|
84
|
+
|
|
85
|
+
cat <<'JSON'
|
|
86
|
+
{"type":"item.completed","item":{"id":"item_1","type":"agent_message","text":"<promise>PREFLIGHT_FAILED</promise>\n"}}
|
|
87
|
+
JSON
|
|
88
|
+
exit 0
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
if [ "$COUNT" -eq 0 ]; then
|
|
92
|
+
cat <<'JSON'
|
|
93
|
+
{"type":"item.completed","item":{"id":"item_1","type":"agent_message","text":"ANSWER: 2\n"}}
|
|
94
|
+
JSON
|
|
95
|
+
else
|
|
96
|
+
cat <<'JSON'
|
|
97
|
+
{"type":"item.completed","item":{"id":"item_1","type":"agent_message","text":"<promise>ALL_STORIES_COMPLETED</promise>\n"}}
|
|
98
|
+
JSON
|
|
99
|
+
fi
|
|
@@ -7,6 +7,7 @@ AGENT_DIR="$ROOT_DIR/packages/agent"
|
|
|
7
7
|
docker build -t wile-agent:local "$AGENT_DIR" >/dev/null
|
|
8
8
|
|
|
9
9
|
run_failure_case() {
|
|
10
|
+
local mock_mode="${1:-preflight_fail}"
|
|
10
11
|
TMP_DIR=$(mktemp -d /tmp/wile-preflight-fail-XXXXXX)
|
|
11
12
|
cleanup() {
|
|
12
13
|
rm -rf "$TMP_DIR"
|
|
@@ -54,7 +55,7 @@ MD
|
|
|
54
55
|
-e WILE_LOCAL_REPO_PATH=/home/wile/workspace/repo \
|
|
55
56
|
-e MAX_ITERATIONS=1 \
|
|
56
57
|
-e WILE_MOCK_CLAUDE=true \
|
|
57
|
-
-e WILE_MOCK_MODE=
|
|
58
|
+
-e WILE_MOCK_MODE="$mock_mode" \
|
|
58
59
|
-v "$TMP_DIR:/home/wile/workspace/repo" \
|
|
59
60
|
wile-agent:local 2>&1 | tee "$OUTPUT_FILE"
|
|
60
61
|
EXIT_CODE=$?
|
|
@@ -76,6 +77,64 @@ MD
|
|
|
76
77
|
trap - EXIT INT TERM
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
run_trailing_case() {
|
|
81
|
+
TMP_DIR=$(mktemp -d /tmp/wile-preflight-trailing-XXXXXX)
|
|
82
|
+
cleanup() {
|
|
83
|
+
rm -rf "$TMP_DIR"
|
|
84
|
+
}
|
|
85
|
+
trap cleanup EXIT INT TERM
|
|
86
|
+
|
|
87
|
+
mkdir -p "$TMP_DIR/.wile"
|
|
88
|
+
cat > "$TMP_DIR/.wile/prd.json" <<'JSON'
|
|
89
|
+
{
|
|
90
|
+
"userStories": [
|
|
91
|
+
{
|
|
92
|
+
"id": "US-TEST-003",
|
|
93
|
+
"title": "Preflight trailing marker test",
|
|
94
|
+
"acceptanceCriteria": ["n/a"],
|
|
95
|
+
"priority": 1,
|
|
96
|
+
"passes": false
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
JSON
|
|
101
|
+
|
|
102
|
+
cat > "$TMP_DIR/.wile/preflight.md" <<'MD'
|
|
103
|
+
<!--
|
|
104
|
+
Use bullet points for preflight checks, e.g.
|
|
105
|
+
- Confirm SUPABASE_DB_URL is set.
|
|
106
|
+
- Run `supabase db reset --db-url "$SUPABASE_DB_URL"`.
|
|
107
|
+
-->
|
|
108
|
+
MD
|
|
109
|
+
|
|
110
|
+
OUTPUT_FILE="$TMP_DIR/output.txt"
|
|
111
|
+
|
|
112
|
+
set +e
|
|
113
|
+
docker run --rm \
|
|
114
|
+
-e CODING_AGENT=CC \
|
|
115
|
+
-e CC_ANTHROPIC_API_KEY=dummy-key \
|
|
116
|
+
-e WILE_REPO_SOURCE=local \
|
|
117
|
+
-e WILE_LOCAL_REPO_PATH=/home/wile/workspace/repo \
|
|
118
|
+
-e MAX_ITERATIONS=1 \
|
|
119
|
+
-e WILE_MOCK_CLAUDE=true \
|
|
120
|
+
-e WILE_MOCK_MODE=preflight_fail_trailing \
|
|
121
|
+
-v "$TMP_DIR:/home/wile/workspace/repo" \
|
|
122
|
+
wile-agent:local 2>&1 | tee "$OUTPUT_FILE"
|
|
123
|
+
EXIT_CODE=$?
|
|
124
|
+
set -e
|
|
125
|
+
|
|
126
|
+
if [ "$EXIT_CODE" -ne 0 ]; then
|
|
127
|
+
echo "error: expected zero exit code for trailing marker case" >&2
|
|
128
|
+
exit 1
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
grep -q "Preflight complete. Starting main loop..." "$OUTPUT_FILE"
|
|
132
|
+
grep -q "✅ ALL TASKS COMPLETE" "$OUTPUT_FILE"
|
|
133
|
+
|
|
134
|
+
rm -rf "$TMP_DIR"
|
|
135
|
+
trap - EXIT INT TERM
|
|
136
|
+
}
|
|
137
|
+
|
|
79
138
|
run_success_case() {
|
|
80
139
|
TMP_DIR=$(mktemp -d /tmp/wile-preflight-success-XXXXXX)
|
|
81
140
|
cleanup() {
|
|
@@ -133,6 +192,8 @@ MD
|
|
|
133
192
|
}
|
|
134
193
|
|
|
135
194
|
run_failure_case
|
|
195
|
+
run_failure_case preflight_fail_split
|
|
196
|
+
run_trailing_case
|
|
136
197
|
run_success_case
|
|
137
198
|
|
|
138
199
|
echo "test-preflight-docker: ok"
|
|
@@ -10,6 +10,7 @@ CLAUDE_MODEL=${CC_CLAUDE_MODEL:-sonnet}
|
|
|
10
10
|
OC_PROVIDER=${OC_PROVIDER:-native}
|
|
11
11
|
OC_MODEL=${OC_MODEL:-opencode/grok-code}
|
|
12
12
|
GEMINI_MODEL=${GEMINI_MODEL:-auto-gemini-3}
|
|
13
|
+
CODEX_MODEL=${CODEX_MODEL:-}
|
|
13
14
|
|
|
14
15
|
# For openrouter provider, prepend vendor prefix if missing
|
|
15
16
|
if [ "$OC_PROVIDER" = "openrouter" ] && [[ "$OC_MODEL" != */* ]]; then
|
|
@@ -33,6 +34,8 @@ if [ "$CODING_AGENT" = "OC" ]; then
|
|
|
33
34
|
echo " Model: $OC_MODEL"
|
|
34
35
|
elif [ "$CODING_AGENT" = "GC" ]; then
|
|
35
36
|
echo " Model: ${GEMINI_MODEL:-auto}"
|
|
37
|
+
elif [ "$CODING_AGENT" = "CX" ]; then
|
|
38
|
+
echo " Model: ${CODEX_MODEL:-default}"
|
|
36
39
|
else
|
|
37
40
|
echo " Model: $CLAUDE_MODEL"
|
|
38
41
|
fi
|
|
@@ -85,12 +88,25 @@ run_gemini() {
|
|
|
85
88
|
| node "$SCRIPT_DIR/gemini-stream.js"
|
|
86
89
|
}
|
|
87
90
|
|
|
91
|
+
run_codex() {
|
|
92
|
+
local prompt_path="$1"
|
|
93
|
+
local model_args=()
|
|
94
|
+
if [ -n "$CODEX_MODEL" ]; then
|
|
95
|
+
model_args=(--model "$CODEX_MODEL")
|
|
96
|
+
fi
|
|
97
|
+
cat "$prompt_path" \
|
|
98
|
+
| codex exec --json --dangerously-bypass-approvals-and-sandbox "${model_args[@]}" - \
|
|
99
|
+
| node "$SCRIPT_DIR/codex-stream.js"
|
|
100
|
+
}
|
|
101
|
+
|
|
88
102
|
run_agent() {
|
|
89
103
|
local prompt_path="$1"
|
|
90
104
|
if [ "$CODING_AGENT" = "OC" ]; then
|
|
91
105
|
run_opencode "$prompt_path"
|
|
92
106
|
elif [ "$CODING_AGENT" = "GC" ]; then
|
|
93
107
|
run_gemini "$prompt_path"
|
|
108
|
+
elif [ "$CODING_AGENT" = "CX" ]; then
|
|
109
|
+
run_codex "$prompt_path"
|
|
94
110
|
else
|
|
95
111
|
run_claude "$prompt_path"
|
|
96
112
|
fi
|
|
@@ -10,6 +10,7 @@ CLAUDE_MODEL=${CC_CLAUDE_MODEL:-sonnet}
|
|
|
10
10
|
OC_PROVIDER=${OC_PROVIDER:-native}
|
|
11
11
|
OC_MODEL=${OC_MODEL:-opencode/grok-code}
|
|
12
12
|
GEMINI_MODEL=${GEMINI_MODEL:-auto-gemini-3}
|
|
13
|
+
CODEX_MODEL=${CODEX_MODEL:-}
|
|
13
14
|
|
|
14
15
|
# For openrouter provider, prepend vendor prefix if missing
|
|
15
16
|
if [ "$OC_PROVIDER" = "openrouter" ] && [[ "$OC_MODEL" != */* ]]; then
|
|
@@ -17,6 +18,7 @@ if [ "$OC_PROVIDER" = "openrouter" ] && [[ "$OC_MODEL" != */* ]]; then
|
|
|
17
18
|
fi
|
|
18
19
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
19
20
|
PREFLIGHT_PROMPT_FILE="$SCRIPT_DIR/prompt-preflight.md"
|
|
21
|
+
PROGRESS_PATH="${WILE_PROGRESS_PATH:-.wile/progress.txt}"
|
|
20
22
|
TEE_TARGET="${WILE_TEE_TARGET:-/dev/stderr}"
|
|
21
23
|
if ! ( : > "$TEE_TARGET" ) 2>/dev/null; then
|
|
22
24
|
TEE_TARGET="/dev/null"
|
|
@@ -31,6 +33,8 @@ if [ "$CODING_AGENT" = "OC" ]; then
|
|
|
31
33
|
echo " Model: $OC_MODEL"
|
|
32
34
|
elif [ "$CODING_AGENT" = "GC" ]; then
|
|
33
35
|
echo " Model: ${GEMINI_MODEL:-auto}"
|
|
36
|
+
elif [ "$CODING_AGENT" = "CX" ]; then
|
|
37
|
+
echo " Model: ${CODEX_MODEL:-default}"
|
|
34
38
|
else
|
|
35
39
|
echo " Model: $CLAUDE_MODEL"
|
|
36
40
|
fi
|
|
@@ -73,25 +77,58 @@ run_gemini() {
|
|
|
73
77
|
| node "$SCRIPT_DIR/gemini-stream.js"
|
|
74
78
|
}
|
|
75
79
|
|
|
80
|
+
run_codex() {
|
|
81
|
+
local prompt_path="$1"
|
|
82
|
+
local model_args=()
|
|
83
|
+
if [ -n "$CODEX_MODEL" ]; then
|
|
84
|
+
model_args=(--model "$CODEX_MODEL")
|
|
85
|
+
fi
|
|
86
|
+
cat "$prompt_path" \
|
|
87
|
+
| codex exec --json --dangerously-bypass-approvals-and-sandbox "${model_args[@]}" - \
|
|
88
|
+
| node "$SCRIPT_DIR/codex-stream.js"
|
|
89
|
+
}
|
|
90
|
+
|
|
76
91
|
run_agent() {
|
|
77
92
|
local prompt_path="$1"
|
|
78
93
|
if [ "$CODING_AGENT" = "OC" ]; then
|
|
79
94
|
run_opencode "$prompt_path"
|
|
80
95
|
elif [ "$CODING_AGENT" = "GC" ]; then
|
|
81
96
|
run_gemini "$prompt_path"
|
|
97
|
+
elif [ "$CODING_AGENT" = "CX" ]; then
|
|
98
|
+
run_codex "$prompt_path"
|
|
82
99
|
else
|
|
83
100
|
run_claude "$prompt_path"
|
|
84
101
|
fi
|
|
85
102
|
}
|
|
86
103
|
|
|
104
|
+
has_terminal_promise() {
|
|
105
|
+
local output="$1"
|
|
106
|
+
local marker="$2"
|
|
107
|
+
local clean_output normalized_output marker_re
|
|
108
|
+
|
|
109
|
+
clean_output=$(printf '%s' "$output" | tr -d '\r')
|
|
110
|
+
normalized_output=$(printf '%s' "$clean_output" | tr '\n' ' ' | sed -e 's/[[:space:]]*$//')
|
|
111
|
+
marker_re=${marker//_/[[:space:]]*_[[:space:]]*}
|
|
112
|
+
|
|
113
|
+
printf '%s' "$normalized_output" | grep -q -E "<[[:space:]]*promise>[[:space:]]*${marker_re}[[:space:]]*</[[:space:]]*promise>[[:space:]]*$"
|
|
114
|
+
}
|
|
115
|
+
|
|
87
116
|
OUTPUT=$(run_agent "$PREFLIGHT_PROMPT_FILE" | tee "$TEE_TARGET") || true
|
|
88
|
-
CLEAN_OUTPUT=$(printf '%s' "$OUTPUT" | tr -d '\r'
|
|
89
|
-
if
|
|
117
|
+
CLEAN_OUTPUT=$(printf '%s' "$OUTPUT" | tr -d '\r')
|
|
118
|
+
if has_terminal_promise "$OUTPUT" "PREFLIGHT_FAILED"; then
|
|
90
119
|
if printf '%s' "$CLEAN_OUTPUT" | grep -F '```' >/dev/null 2>&1; then
|
|
91
120
|
:
|
|
92
121
|
elif printf '%s' "$CLEAN_OUTPUT" | grep -F '`<promise>PREFLIGHT_FAILED</promise>`' >/dev/null 2>&1; then
|
|
93
122
|
:
|
|
94
123
|
else
|
|
124
|
+
if [ -f "$PROGRESS_PATH" ]; then
|
|
125
|
+
TAIL_LINES="${PREFLIGHT_LOG_TAIL:-80}"
|
|
126
|
+
echo ""
|
|
127
|
+
echo "══════════════════════════════════════════════════════"
|
|
128
|
+
echo " Preflight log tail (${TAIL_LINES} lines)"
|
|
129
|
+
echo "══════════════════════════════════════════════════════"
|
|
130
|
+
tail -n "$TAIL_LINES" "$PROGRESS_PATH" || true
|
|
131
|
+
fi
|
|
95
132
|
echo ""
|
|
96
133
|
echo "══════════════════════════════════════════════════════"
|
|
97
134
|
echo " ❌ PREFLIGHT FAILED"
|
|
@@ -12,6 +12,7 @@ CLAUDE_MODEL=${CC_CLAUDE_MODEL:-sonnet}
|
|
|
12
12
|
OC_PROVIDER=${OC_PROVIDER:-native}
|
|
13
13
|
OC_MODEL=${OC_MODEL:-opencode/grok-code}
|
|
14
14
|
GEMINI_MODEL=${GEMINI_MODEL:-auto-gemini-3}
|
|
15
|
+
CODEX_MODEL=${CODEX_MODEL:-}
|
|
15
16
|
|
|
16
17
|
# For openrouter provider, prepend vendor prefix if missing
|
|
17
18
|
if [ "$OC_PROVIDER" = "openrouter" ] && [[ "$OC_MODEL" != */* ]]; then
|
|
@@ -20,6 +21,7 @@ fi
|
|
|
20
21
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
22
|
PROMPT_FILE="$SCRIPT_DIR/prompt.md"
|
|
22
23
|
PREFLIGHT_PROMPT_FILE="$SCRIPT_DIR/prompt-preflight.md"
|
|
24
|
+
PROGRESS_PATH="${WILE_PROGRESS_PATH:-.wile/progress.txt}"
|
|
23
25
|
ADDITIONAL_PROMPT_FILE="${WILE_ADDITIONAL_INSTRUCTIONS:-}"
|
|
24
26
|
TEE_TARGET="${WILE_TEE_TARGET:-/dev/stderr}"
|
|
25
27
|
if ! ( : > "$TEE_TARGET" ) 2>/dev/null; then
|
|
@@ -36,6 +38,8 @@ if [ "$CODING_AGENT" = "OC" ]; then
|
|
|
36
38
|
echo " Model: $OC_MODEL"
|
|
37
39
|
elif [ "$CODING_AGENT" = "GC" ]; then
|
|
38
40
|
echo " Model: ${GEMINI_MODEL:-auto}"
|
|
41
|
+
elif [ "$CODING_AGENT" = "CX" ]; then
|
|
42
|
+
echo " Model: ${CODEX_MODEL:-default}"
|
|
39
43
|
else
|
|
40
44
|
echo " Model: $CLAUDE_MODEL"
|
|
41
45
|
fi
|
|
@@ -90,17 +94,42 @@ run_gemini() {
|
|
|
90
94
|
| node "$SCRIPT_DIR/gemini-stream.js"
|
|
91
95
|
}
|
|
92
96
|
|
|
97
|
+
run_codex() {
|
|
98
|
+
local prompt_path="$1"
|
|
99
|
+
local model_args=()
|
|
100
|
+
if [ -n "$CODEX_MODEL" ]; then
|
|
101
|
+
model_args=(--model "$CODEX_MODEL")
|
|
102
|
+
fi
|
|
103
|
+
cat "$prompt_path" \
|
|
104
|
+
| codex exec --json --dangerously-bypass-approvals-and-sandbox "${model_args[@]}" - \
|
|
105
|
+
| node "$SCRIPT_DIR/codex-stream.js"
|
|
106
|
+
}
|
|
107
|
+
|
|
93
108
|
run_agent() {
|
|
94
109
|
local prompt_path="$1"
|
|
95
110
|
if [ "$CODING_AGENT" = "OC" ]; then
|
|
96
111
|
run_opencode "$prompt_path"
|
|
97
112
|
elif [ "$CODING_AGENT" = "GC" ]; then
|
|
98
113
|
run_gemini "$prompt_path"
|
|
114
|
+
elif [ "$CODING_AGENT" = "CX" ]; then
|
|
115
|
+
run_codex "$prompt_path"
|
|
99
116
|
else
|
|
100
117
|
run_claude "$prompt_path"
|
|
101
118
|
fi
|
|
102
119
|
}
|
|
103
120
|
|
|
121
|
+
has_terminal_promise() {
|
|
122
|
+
local output="$1"
|
|
123
|
+
local marker="$2"
|
|
124
|
+
local clean_output normalized_output marker_re
|
|
125
|
+
|
|
126
|
+
clean_output=$(printf '%s' "$output" | tr -d '\r')
|
|
127
|
+
normalized_output=$(printf '%s' "$clean_output" | tr '\n' ' ' | sed -e 's/[[:space:]]*$//')
|
|
128
|
+
marker_re=${marker//_/[[:space:]]*_[[:space:]]*}
|
|
129
|
+
|
|
130
|
+
printf '%s' "$normalized_output" | grep -q -E "<[[:space:]]*promise>[[:space:]]*${marker_re}[[:space:]]*</[[:space:]]*promise>[[:space:]]*$"
|
|
131
|
+
}
|
|
132
|
+
|
|
104
133
|
# ════════════════════════════════════════════════════════════
|
|
105
134
|
# ITERATION 0: Preflight / Setup
|
|
106
135
|
# ════════════════════════════════════════════════════════════
|
|
@@ -113,14 +142,22 @@ echo ""
|
|
|
113
142
|
if [ -f "$PREFLIGHT_PROMPT_FILE" ]; then
|
|
114
143
|
OUTPUT=$(run_agent "$PREFLIGHT_PROMPT_FILE" | tee "$TEE_TARGET") || true
|
|
115
144
|
|
|
116
|
-
# Check if preflight failed critically (tag must be
|
|
117
|
-
CLEAN_OUTPUT=$(printf '%s' "$OUTPUT" | tr -d '\r'
|
|
118
|
-
if
|
|
145
|
+
# Check if preflight failed critically (tag must be terminal; reject backticks/code fences)
|
|
146
|
+
CLEAN_OUTPUT=$(printf '%s' "$OUTPUT" | tr -d '\r')
|
|
147
|
+
if has_terminal_promise "$OUTPUT" "PREFLIGHT_FAILED"; then
|
|
119
148
|
if printf '%s' "$CLEAN_OUTPUT" | grep -F '```' >/dev/null 2>&1; then
|
|
120
149
|
:
|
|
121
150
|
elif printf '%s' "$CLEAN_OUTPUT" | grep -F '`<promise>PREFLIGHT_FAILED</promise>`' >/dev/null 2>&1; then
|
|
122
151
|
:
|
|
123
152
|
else
|
|
153
|
+
if [ -f "$PROGRESS_PATH" ]; then
|
|
154
|
+
TAIL_LINES="${PREFLIGHT_LOG_TAIL:-80}"
|
|
155
|
+
echo ""
|
|
156
|
+
echo "══════════════════════════════════════════════════════"
|
|
157
|
+
echo " Preflight log tail (${TAIL_LINES} lines)"
|
|
158
|
+
echo "══════════════════════════════════════════════════════"
|
|
159
|
+
tail -n "$TAIL_LINES" "$PROGRESS_PATH" || true
|
|
160
|
+
fi
|
|
124
161
|
echo ""
|
|
125
162
|
echo "══════════════════════════════════════════════════════"
|
|
126
163
|
echo " ❌ PREFLIGHT FAILED - Cannot continue"
|
|
@@ -151,10 +188,8 @@ for i in $(seq 1 $MAX_ITERATIONS); do
|
|
|
151
188
|
# Capture output while also displaying it (tee to stderr)
|
|
152
189
|
OUTPUT=$(run_agent "$PROMPT_FILE" | tee "$TEE_TARGET") || true
|
|
153
190
|
|
|
154
|
-
# Check for completion signal (tag must be
|
|
155
|
-
|
|
156
|
-
FINAL_LINE=$(printf '%s\n' "$CLEAN_OUTPUT" | awk 'NF { last=$0 } END { print last }')
|
|
157
|
-
if printf '%s\n' "$FINAL_LINE" | grep -q -E '^[[:space:]]*<promise>ALL_STORIES_COMPLETED</promise>[[:space:]]*$'; then
|
|
191
|
+
# Check for completion signal (tag must be terminal)
|
|
192
|
+
if has_terminal_promise "$OUTPUT" "ALL_STORIES_COMPLETED"; then
|
|
158
193
|
echo ""
|
|
159
194
|
echo "══════════════════════════════════════════════════════"
|
|
160
195
|
echo " ✅ ALL TASKS COMPLETE"
|
package/dist/cli.js
CHANGED
|
@@ -7548,7 +7548,9 @@ var tips = {
|
|
|
7548
7548
|
openrouter: "Tip: create an OpenRouter API key at https://openrouter.ai/keys (pay per token).",
|
|
7549
7549
|
github: "Tip: use a GitHub Personal Access Token (fine-grained recommended). Create at https://github.com/settings/tokens?type=beta with Contents (read/write) and Metadata (read).",
|
|
7550
7550
|
geminiOauth: "Tip: run 'gemini' locally and choose Login with Google to create ~/.gemini/oauth_creds.json.",
|
|
7551
|
-
geminiApiKey: "Tip: create a Gemini API key at https://aistudio.google.com/app/apikey (pay per token)."
|
|
7551
|
+
geminiApiKey: "Tip: create a Gemini API key at https://aistudio.google.com/app/apikey (pay per token).",
|
|
7552
|
+
codexOauth: "Tip: run 'codex login' (or 'codex login --device-auth') locally to create ~/.codex/auth.json.",
|
|
7553
|
+
codexApiKey: "Tip: create an OpenAI API key at https://platform.openai.com/api-keys (pay per token)."
|
|
7552
7554
|
};
|
|
7553
7555
|
var nativeOcModels = [
|
|
7554
7556
|
{ title: "Grok Code Fast 1 (recommended)", value: "opencode/grok-code" },
|
|
@@ -7642,7 +7644,11 @@ var toEnvString = (env) => {
|
|
|
7642
7644
|
"OC_MODEL",
|
|
7643
7645
|
"OC_OPENROUTER_API_KEY",
|
|
7644
7646
|
"GEMINI_OAUTH_CREDS_B64",
|
|
7645
|
-
"GEMINI_API_KEY"
|
|
7647
|
+
"GEMINI_API_KEY",
|
|
7648
|
+
"CODEX_AUTH_JSON_B64",
|
|
7649
|
+
"CODEX_AUTH_JSON_PATH",
|
|
7650
|
+
"CODEX_API_KEY",
|
|
7651
|
+
"CODEX_MODEL"
|
|
7646
7652
|
];
|
|
7647
7653
|
const orderedSet = new Set(orderedKeys);
|
|
7648
7654
|
const ordered = orderedKeys.filter((key) => (key in env));
|
|
@@ -7682,13 +7688,15 @@ var runConfig = async () => {
|
|
|
7682
7688
|
choices: [
|
|
7683
7689
|
{ title: "Claude Code (CC)", value: "CC" },
|
|
7684
7690
|
{ title: "Gemini CLI (GC)", value: "GC" },
|
|
7685
|
-
{ title: "OpenCode (OC)", value: "OC" }
|
|
7691
|
+
{ title: "OpenCode (OC)", value: "OC" },
|
|
7692
|
+
{ title: "Codex CLI (CX)", value: "CX" }
|
|
7686
7693
|
],
|
|
7687
|
-
initial: existingEnv.CODING_AGENT === "OC" ? 2 : existingEnv.CODING_AGENT === "GC" ? 1 : 0
|
|
7694
|
+
initial: existingEnv.CODING_AGENT === "OC" ? 2 : existingEnv.CODING_AGENT === "GC" ? 1 : existingEnv.CODING_AGENT === "CX" ? 3 : 0
|
|
7688
7695
|
});
|
|
7689
7696
|
const codingAgent = codingAgentResponse.codingAgent;
|
|
7690
7697
|
let authMethod = null;
|
|
7691
7698
|
let geminiAuthMethod = null;
|
|
7699
|
+
let codexAuthMethod = null;
|
|
7692
7700
|
let authValueResponse = {};
|
|
7693
7701
|
let defaultModelResponse = {};
|
|
7694
7702
|
let ocProviderResponse = {};
|
|
@@ -7697,6 +7705,9 @@ var runConfig = async () => {
|
|
|
7697
7705
|
let ocNativeModelResponse = {};
|
|
7698
7706
|
let geminiOauthPathResponse = {};
|
|
7699
7707
|
let geminiApiKeyResponse = {};
|
|
7708
|
+
let codexAuthJsonPathResponse = {};
|
|
7709
|
+
let codexApiKeyResponse = {};
|
|
7710
|
+
let codexModelResponse = {};
|
|
7700
7711
|
if (codingAgent === "CC") {
|
|
7701
7712
|
const authDefault = existingEnv.CC_CLAUDE_CODE_OAUTH_TOKEN ? "oauth" : existingEnv.CC_ANTHROPIC_API_KEY ? "apiKey" : "oauth";
|
|
7702
7713
|
const authResponse = await prompt({
|
|
@@ -7770,7 +7781,7 @@ var runConfig = async () => {
|
|
|
7770
7781
|
initial: existingNativeIdx >= 0 ? existingNativeIdx : 0
|
|
7771
7782
|
});
|
|
7772
7783
|
}
|
|
7773
|
-
} else {
|
|
7784
|
+
} else if (codingAgent === "GC") {
|
|
7774
7785
|
const geminiAuthDefault = existingEnv.GEMINI_OAUTH_CREDS_B64 ? "oauth" : existingEnv.GEMINI_API_KEY ? "apiKey" : "oauth";
|
|
7775
7786
|
const geminiAuthResponse = await prompt({
|
|
7776
7787
|
type: "select",
|
|
@@ -7802,6 +7813,44 @@ var runConfig = async () => {
|
|
|
7802
7813
|
initial: existingEnv.GEMINI_API_KEY ?? ""
|
|
7803
7814
|
});
|
|
7804
7815
|
}
|
|
7816
|
+
} else {
|
|
7817
|
+
const codexAuthDefault = existingEnv.CODEX_AUTH_JSON_B64 ? "oauth" : existingEnv.CODEX_API_KEY || existingEnv.OPENAI_API_KEY ? "apiKey" : "oauth";
|
|
7818
|
+
const codexAuthResponse = await prompt({
|
|
7819
|
+
type: "select",
|
|
7820
|
+
name: "codexAuthMethod",
|
|
7821
|
+
message: "Codex CLI authentication",
|
|
7822
|
+
choices: [
|
|
7823
|
+
{ title: "ChatGPT login (subscription)", value: "oauth" },
|
|
7824
|
+
{ title: "API key (OpenAI API)", value: "apiKey" }
|
|
7825
|
+
],
|
|
7826
|
+
initial: codexAuthDefault === "apiKey" ? 1 : 0
|
|
7827
|
+
});
|
|
7828
|
+
codexAuthMethod = codexAuthResponse.codexAuthMethod;
|
|
7829
|
+
console.log("");
|
|
7830
|
+
console.log(codexAuthMethod === "oauth" ? tips.codexOauth : tips.codexApiKey);
|
|
7831
|
+
console.log("");
|
|
7832
|
+
if (codexAuthMethod === "oauth") {
|
|
7833
|
+
const defaultAuthPath = "~/.codex/auth.json";
|
|
7834
|
+
codexAuthJsonPathResponse = await prompt({
|
|
7835
|
+
type: "text",
|
|
7836
|
+
name: "codexAuthJsonPath",
|
|
7837
|
+
message: "Codex auth.json path (press enter to keep existing)",
|
|
7838
|
+
initial: defaultAuthPath
|
|
7839
|
+
});
|
|
7840
|
+
} else {
|
|
7841
|
+
codexApiKeyResponse = await prompt({
|
|
7842
|
+
type: "password",
|
|
7843
|
+
name: "codexApiKey",
|
|
7844
|
+
message: "OpenAI API key (press enter to keep existing)",
|
|
7845
|
+
initial: existingEnv.CODEX_API_KEY ?? existingEnv.OPENAI_API_KEY ?? ""
|
|
7846
|
+
});
|
|
7847
|
+
}
|
|
7848
|
+
codexModelResponse = await prompt({
|
|
7849
|
+
type: "text",
|
|
7850
|
+
name: "codexModel",
|
|
7851
|
+
message: "Codex model (press enter to keep existing)",
|
|
7852
|
+
initial: existingEnv.CODEX_MODEL ?? ""
|
|
7853
|
+
});
|
|
7805
7854
|
}
|
|
7806
7855
|
const repoSourceResponse = await prompt({
|
|
7807
7856
|
type: "select",
|
|
@@ -7871,6 +7920,22 @@ var runConfig = async () => {
|
|
|
7871
7920
|
geminiOauthCredsB64 = existingEnv.GEMINI_OAUTH_CREDS_B64;
|
|
7872
7921
|
}
|
|
7873
7922
|
}
|
|
7923
|
+
const codexApiKey = codingAgent === "CX" ? coalesceValue(codexApiKeyResponse.codexApiKey, existingEnv.CODEX_API_KEY ?? existingEnv.OPENAI_API_KEY) : undefined;
|
|
7924
|
+
let codexAuthJsonB64;
|
|
7925
|
+
if (codingAgent === "CX" && codexAuthMethod === "oauth") {
|
|
7926
|
+
const configuredPath = coalesceValue(codexAuthJsonPathResponse.codexAuthJsonPath);
|
|
7927
|
+
if (configuredPath) {
|
|
7928
|
+
const resolvedPath = resolvePath(cwd, configuredPath);
|
|
7929
|
+
try {
|
|
7930
|
+
codexAuthJsonB64 = await readBase64File(resolvedPath);
|
|
7931
|
+
} catch {
|
|
7932
|
+
throw new Error(`Failed to read Codex auth.json file: ${resolvedPath}`);
|
|
7933
|
+
}
|
|
7934
|
+
} else {
|
|
7935
|
+
codexAuthJsonB64 = existingEnv.CODEX_AUTH_JSON_B64;
|
|
7936
|
+
}
|
|
7937
|
+
}
|
|
7938
|
+
const codexModel = codingAgent === "CX" ? coalesceValue(codexModelResponse.codexModel, existingEnv.CODEX_MODEL) : undefined;
|
|
7874
7939
|
const githubToken = repoSource === "github" ? coalesceValue(githubTokenResponse.githubToken, existingEnv.GITHUB_TOKEN) : existingEnv.GITHUB_TOKEN;
|
|
7875
7940
|
const repoUrl = repoSource === "github" ? coalesceValue(repoResponse.repoUrl, existingEnv.GITHUB_REPO_URL) : existingEnv.GITHUB_REPO_URL;
|
|
7876
7941
|
const envProjectPathValue = coalesceValue(envProjectPathResponse.envProjectPath, existingEnv.WILE_ENV_PROJECT_PATH ?? ".wile/.env.project") ?? ".wile/.env.project";
|
|
@@ -7896,12 +7961,19 @@ var runConfig = async () => {
|
|
|
7896
7961
|
if (ocProvider === "openrouter") {
|
|
7897
7962
|
setIfDefined(envOut, "OC_OPENROUTER_API_KEY", ocKey);
|
|
7898
7963
|
}
|
|
7899
|
-
} else {
|
|
7964
|
+
} else if (codingAgent === "GC") {
|
|
7900
7965
|
if (geminiAuthMethod === "apiKey") {
|
|
7901
7966
|
setIfDefined(envOut, "GEMINI_API_KEY", geminiApiKey);
|
|
7902
7967
|
} else {
|
|
7903
7968
|
setIfDefined(envOut, "GEMINI_OAUTH_CREDS_B64", geminiOauthCredsB64);
|
|
7904
7969
|
}
|
|
7970
|
+
} else if (codingAgent === "CX") {
|
|
7971
|
+
if (codexAuthMethod === "apiKey") {
|
|
7972
|
+
setIfDefined(envOut, "CODEX_API_KEY", codexApiKey);
|
|
7973
|
+
} else {
|
|
7974
|
+
setIfDefined(envOut, "CODEX_AUTH_JSON_B64", codexAuthJsonB64);
|
|
7975
|
+
}
|
|
7976
|
+
setIfDefined(envOut, "CODEX_MODEL", codexModel);
|
|
7905
7977
|
}
|
|
7906
7978
|
await writeFile(envPath, toEnvString(envOut));
|
|
7907
7979
|
await ensureGitignore(gitignorePath);
|
|
@@ -8028,7 +8100,7 @@ var readWileConfig = (options = {}) => {
|
|
|
8028
8100
|
const envProject = parseEnvFile(envProjectPath);
|
|
8029
8101
|
const repoSource = env.WILE_REPO_SOURCE || "github";
|
|
8030
8102
|
if (validate) {
|
|
8031
|
-
ensureRequired(env.CODING_AGENT === "CC" || env.CODING_AGENT === "OC" || env.CODING_AGENT === "GC", "CODING_AGENT must be set to CC, OC, or
|
|
8103
|
+
ensureRequired(env.CODING_AGENT === "CC" || env.CODING_AGENT === "OC" || env.CODING_AGENT === "GC" || env.CODING_AGENT === "CX", "CODING_AGENT must be set to CC, OC, GC, or CX in .wile/secrets/.env. Run 'bunx wile config'.");
|
|
8032
8104
|
if (repoSource === "github") {
|
|
8033
8105
|
ensureRequired(Boolean(env.GITHUB_TOKEN), "GITHUB_TOKEN is required in .wile/secrets/.env. Run 'bunx wile config'.");
|
|
8034
8106
|
ensureRequired(Boolean(env.GITHUB_REPO_URL), "GITHUB_REPO_URL is required in .wile/secrets/.env. Run 'bunx wile config'.");
|
|
@@ -8047,6 +8119,9 @@ var readWileConfig = (options = {}) => {
|
|
|
8047
8119
|
if (env.CODING_AGENT === "GC") {
|
|
8048
8120
|
ensureRequired(Boolean(env.GEMINI_OAUTH_CREDS_B64 || env.GEMINI_API_KEY), "Either GEMINI_OAUTH_CREDS_B64 or GEMINI_API_KEY is required in .wile/secrets/.env for Gemini CLI.");
|
|
8049
8121
|
}
|
|
8122
|
+
if (env.CODING_AGENT === "CX") {
|
|
8123
|
+
ensureRequired(Boolean(env.CODEX_AUTH_JSON_B64 || env.CODEX_AUTH_JSON_PATH || env.CODEX_API_KEY || env.OPENAI_API_KEY), "Either CODEX_AUTH_JSON_B64 (or CODEX_AUTH_JSON_PATH) or CODEX_API_KEY is required in .wile/secrets/.env for Codex CLI.");
|
|
8124
|
+
}
|
|
8050
8125
|
}
|
|
8051
8126
|
return {
|
|
8052
8127
|
paths: {
|
|
@@ -8068,6 +8143,10 @@ var readWileConfig = (options = {}) => {
|
|
|
8068
8143
|
ocModel: env.OC_MODEL,
|
|
8069
8144
|
geminiOauthCredsB64: env.GEMINI_OAUTH_CREDS_B64,
|
|
8070
8145
|
geminiApiKey: env.GEMINI_API_KEY,
|
|
8146
|
+
codexAuthJsonB64: env.CODEX_AUTH_JSON_B64,
|
|
8147
|
+
codexAuthJsonPath: env.CODEX_AUTH_JSON_PATH,
|
|
8148
|
+
codexApiKey: env.CODEX_API_KEY ?? env.OPENAI_API_KEY,
|
|
8149
|
+
codexModel: env.CODEX_MODEL,
|
|
8071
8150
|
envProject
|
|
8072
8151
|
}
|
|
8073
8152
|
};
|