wile 1.1.0 → 1.3.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 +15 -1
- package/dist/agent/.env.example +9 -11
- package/dist/agent/README.md +5 -6
- package/dist/agent/entrypoint.sh +1 -22
- package/dist/agent/scripts/test-compact-custom-dockerfile.sh +56 -0
- package/dist/agent/scripts/test-custom-dockerfile.sh +59 -0
- package/dist/agent/scripts/test-run-no-custom-dockerfile.sh +56 -0
- package/dist/agent/scripts/wile-compact.sh +5 -16
- package/dist/agent/scripts/wile-preflight.sh +5 -16
- package/dist/agent/scripts/wile.sh +5 -16
- package/dist/cli.js +516 -106
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,6 +16,19 @@ From your project repo:
|
|
|
16
16
|
bunx wile config
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
For non-interactive setup docs (useful for agents creating `.wile/secrets/.env` directly):
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
bunx wile config --non-interactive
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Apply non-interactive config (agent mode):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
export WILE_PROMPTS_INJECT='{"codingAgent":"OC","repoSource":"local","ocModel":"opencode/kimi-k2.5-free","branchName":"main","envProjectPath":".wile/.env.project","maxIterations":25}'
|
|
29
|
+
bunx wile config --non-interactive "$WILE_PROMPTS_INJECT"
|
|
30
|
+
```
|
|
31
|
+
|
|
19
32
|
This creates:
|
|
20
33
|
|
|
21
34
|
- `.wile/secrets/.env` for required credentials
|
|
@@ -26,7 +39,8 @@ This creates:
|
|
|
26
39
|
Set `WILE_REPO_SOURCE=local` in `.wile/secrets/.env` to run against the current directory without GitHub.
|
|
27
40
|
When `WILE_REPO_SOURCE=local`, GitHub credentials are optional.
|
|
28
41
|
Set `WILE_MAX_ITERATIONS` in `.wile/secrets/.env` to change the default loop limit (default: 25).
|
|
29
|
-
|
|
42
|
+
Optionally customize `.wile/Dockerfile` (created by `wile config`) to extend `wile-agent:base`.
|
|
43
|
+
Set `CODING_AGENT=CX` to use Codex CLI, `CODING_AGENT=OC` to use OpenCode (free native models), `CODING_AGENT=GC` to use Gemini CLI, otherwise `CODING_AGENT=CC` uses Claude Code.
|
|
30
44
|
|
|
31
45
|
## Run Wile
|
|
32
46
|
|
package/dist/agent/.env.example
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# Choose which coding agent to run:
|
|
9
9
|
# - CC = Claude Code (default)
|
|
10
10
|
# - GC = Gemini CLI (Google account OAuth)
|
|
11
|
-
# - OC = OpenCode (
|
|
11
|
+
# - OC = OpenCode (free native models)
|
|
12
12
|
# - CX = Codex CLI (ChatGPT subscription)
|
|
13
13
|
CODING_AGENT=CC
|
|
14
14
|
|
|
@@ -30,10 +30,8 @@ CC_CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-xxxxx
|
|
|
30
30
|
# REQUIRED - OpenCode Authentication (OC only)
|
|
31
31
|
# =============================================================================
|
|
32
32
|
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
# OpenRouter model id (Wile maps `glm-4.7` to `openrouter/z-ai/glm-4.7`)
|
|
36
|
-
# OC_MODEL=glm-4.7
|
|
33
|
+
# Native model id (default: opencode/kimi-k2.5-free)
|
|
34
|
+
# OC_MODEL=opencode/kimi-k2.5-free
|
|
37
35
|
|
|
38
36
|
# =============================================================================
|
|
39
37
|
# REQUIRED - Gemini CLI Authentication (GC only, choose ONE)
|
|
@@ -91,14 +89,14 @@ BRANCH_NAME=feature/my-feature
|
|
|
91
89
|
# Maximum loop iterations before stopping (default: 25)
|
|
92
90
|
MAX_ITERATIONS=25
|
|
93
91
|
|
|
94
|
-
# Claude model alias or full model name (default:
|
|
95
|
-
CC_CLAUDE_MODEL=
|
|
92
|
+
# Claude model alias or full model name (default: opus)
|
|
93
|
+
CC_CLAUDE_MODEL=opus
|
|
96
94
|
|
|
97
|
-
# Gemini model name (default:
|
|
98
|
-
# GEMINI_MODEL=
|
|
95
|
+
# Gemini model name (default: gemini-3-pro)
|
|
96
|
+
# GEMINI_MODEL=gemini-3-pro
|
|
99
97
|
|
|
100
|
-
# Codex model name (
|
|
101
|
-
# CODEX_MODEL=gpt-5.
|
|
98
|
+
# Codex model name (default: gpt-5.3-codex)
|
|
99
|
+
# CODEX_MODEL=gpt-5.3-codex
|
|
102
100
|
|
|
103
101
|
# =============================================================================
|
|
104
102
|
# PROJECT ENVIRONMENT VARIABLES
|
package/dist/agent/README.md
CHANGED
|
@@ -110,23 +110,22 @@ 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), `OC` (OpenCode
|
|
113
|
+
| `CODING_AGENT` | No | `CC` (Claude Code, default), `GC` (Gemini CLI), `OC` (OpenCode native/free models), 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
|
-
| `
|
|
119
|
-
| `OC_MODEL` | Yes (OC) | OpenRouter model id (set `glm-4.7` to target `openrouter/z-ai/glm-4.7`) |
|
|
118
|
+
| `OC_MODEL` | Yes (OC) | OpenCode native model id (default: `opencode/kimi-k2.5-free`) |
|
|
120
119
|
| `CODEX_AUTH_JSON_B64` | Yes (CX)* | Base64 of `~/.codex/auth.json` from `codex login` (uses ChatGPT subscription) |
|
|
121
120
|
| `CODEX_API_KEY` | Yes (CX)* | OpenAI API key (uses API credits - alternative to auth.json) |
|
|
122
|
-
| `GEMINI_MODEL` | No (GC) | Gemini model name (default: `
|
|
121
|
+
| `GEMINI_MODEL` | No (GC) | Gemini model name (default: `gemini-3-pro-preview`) |
|
|
123
122
|
| `WILE_REPO_SOURCE` | No | `github` (default) or `local` |
|
|
124
123
|
| `GITHUB_TOKEN` | Yes (github) | GitHub PAT with repo access |
|
|
125
124
|
| `GITHUB_REPO_URL` | Yes (github) | HTTPS URL to repository |
|
|
126
125
|
| `BRANCH_NAME` | Yes (github) | Branch to work on |
|
|
127
126
|
| `MAX_ITERATIONS` | No | Max loops (default: 25) |
|
|
128
|
-
| `CC_CLAUDE_MODEL` | No | Claude model alias/name (default:
|
|
129
|
-
| `CODEX_MODEL` | No (CX) | Codex model name (
|
|
127
|
+
| `CC_CLAUDE_MODEL` | No | Claude model alias/name (default: `opus`) |
|
|
128
|
+
| `CODEX_MODEL` | No (CX) | Codex model name (default: `gpt-5.3-codex`) |
|
|
130
129
|
|
|
131
130
|
*Either `CC_CLAUDE_CODE_OAUTH_TOKEN` or `CC_ANTHROPIC_API_KEY` is required when `CODING_AGENT=CC`.
|
|
132
131
|
*Either `GEMINI_OAUTH_CREDS_B64` or `GEMINI_API_KEY` is required when `CODING_AGENT=GC`.
|
package/dist/agent/entrypoint.sh
CHANGED
|
@@ -105,11 +105,6 @@ fi
|
|
|
105
105
|
|
|
106
106
|
# Authentication for selected coding agent
|
|
107
107
|
if [ "$CODING_AGENT" = "OC" ]; then
|
|
108
|
-
OC_PROVIDER="${OC_PROVIDER:-native}"
|
|
109
|
-
if [ "$OC_PROVIDER" = "openrouter" ] && [ -z "$OC_OPENROUTER_API_KEY" ]; then
|
|
110
|
-
echo "ERROR: OC_OPENROUTER_API_KEY is required for OpenCode with OpenRouter provider"
|
|
111
|
-
exit 1
|
|
112
|
-
fi
|
|
113
108
|
if [ -z "$OC_MODEL" ]; then
|
|
114
109
|
echo "ERROR: OC_MODEL is required for OpenCode"
|
|
115
110
|
exit 1
|
|
@@ -177,23 +172,7 @@ if [ "${WILE_MOCK_CODEX:-}" = "true" ] && [ "$CODING_AGENT" = "CX" ]; then
|
|
|
177
172
|
fi
|
|
178
173
|
|
|
179
174
|
if [ "$CODING_AGENT" = "OC" ]; then
|
|
180
|
-
|
|
181
|
-
echo " Auth: OpenRouter (OpenCode)"
|
|
182
|
-
OPENCODE_DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/opencode"
|
|
183
|
-
mkdir -p "$OPENCODE_DATA_DIR"
|
|
184
|
-
cat > "$OPENCODE_DATA_DIR/auth.json" << OPENCODEAUTH
|
|
185
|
-
{
|
|
186
|
-
"openrouter": {
|
|
187
|
-
"type": "api",
|
|
188
|
-
"key": "$OC_OPENROUTER_API_KEY"
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
OPENCODEAUTH
|
|
192
|
-
chmod 600 "$OPENCODE_DATA_DIR/auth.json"
|
|
193
|
-
export OPENROUTER_API_KEY="$OC_OPENROUTER_API_KEY"
|
|
194
|
-
else
|
|
195
|
-
echo " Auth: Native (free models)"
|
|
196
|
-
fi
|
|
175
|
+
echo " Auth: Native (free models)"
|
|
197
176
|
elif [ "$CODING_AGENT" = "GC" ]; then
|
|
198
177
|
if [ -n "$GEMINI_OAUTH_CREDS_B64" ] || [ -n "$GEMINI_OAUTH_CREDS_PATH" ]; then
|
|
199
178
|
echo " Auth: OAuth (Google account)"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT_DIR=$(cd "$(dirname "$0")/../../.." && pwd)
|
|
5
|
+
AGENT_DIR="$ROOT_DIR/packages/agent"
|
|
6
|
+
TMP_DIR=$(mktemp -d /tmp/wile-compact-custom-dockerfile-XXXXXX)
|
|
7
|
+
|
|
8
|
+
cleanup() {
|
|
9
|
+
rm -rf "$TMP_DIR"
|
|
10
|
+
}
|
|
11
|
+
trap cleanup EXIT INT TERM
|
|
12
|
+
|
|
13
|
+
mkdir -p "$TMP_DIR/.wile/secrets"
|
|
14
|
+
printf "secrets/\nscreenshots/\nlogs/\n" > "$TMP_DIR/.wile/.gitignore"
|
|
15
|
+
cp "$ROOT_DIR/scripts/fixtures/compact-prd.json" "$TMP_DIR/.wile/prd.json"
|
|
16
|
+
cp "$ROOT_DIR/scripts/fixtures/compact-progress.txt" "$TMP_DIR/.wile/progress.txt"
|
|
17
|
+
|
|
18
|
+
git -C "$TMP_DIR" init >/dev/null
|
|
19
|
+
git -C "$TMP_DIR" config user.email "test@local"
|
|
20
|
+
git -C "$TMP_DIR" config user.name "Wile Test"
|
|
21
|
+
git -C "$TMP_DIR" add .wile
|
|
22
|
+
git -C "$TMP_DIR" commit -m "seed compact custom dockerfile test" >/dev/null
|
|
23
|
+
|
|
24
|
+
cat > "$TMP_DIR/.wile/secrets/.env" <<'ENV'
|
|
25
|
+
CODING_AGENT=CC
|
|
26
|
+
CC_ANTHROPIC_API_KEY=dummy-key
|
|
27
|
+
CC_CLAUDE_MODEL=sonnet
|
|
28
|
+
WILE_REPO_SOURCE=local
|
|
29
|
+
WILE_MOCK_CLAUDE=true
|
|
30
|
+
ENV
|
|
31
|
+
|
|
32
|
+
cat > "$TMP_DIR/.wile/Dockerfile" <<'DOCKERFILE'
|
|
33
|
+
ARG WILE_BASE_IMAGE=wile-agent:base
|
|
34
|
+
FROM ${WILE_BASE_IMAGE}
|
|
35
|
+
|
|
36
|
+
USER root
|
|
37
|
+
RUN apt-get update \
|
|
38
|
+
&& apt-get install -y --no-install-recommends jq \
|
|
39
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
40
|
+
USER wile
|
|
41
|
+
DOCKERFILE
|
|
42
|
+
|
|
43
|
+
export WILE_AGENT_DIR="$AGENT_DIR"
|
|
44
|
+
cd "$TMP_DIR"
|
|
45
|
+
node "$ROOT_DIR/packages/cli/dist/cli.js" compact --max-iterations 1
|
|
46
|
+
|
|
47
|
+
LOG_FILE=$(ls "$TMP_DIR/.wile/logs"/compact-*.log | head -n 1)
|
|
48
|
+
if [ -z "$LOG_FILE" ]; then
|
|
49
|
+
echo "error: expected compact log file" >&2
|
|
50
|
+
exit 1
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
grep -q "WILE - Compact PRD & Progress" "$LOG_FILE"
|
|
54
|
+
docker run --rm --entrypoint sh wile-agent:local -lc "command -v jq >/dev/null"
|
|
55
|
+
|
|
56
|
+
echo "test-compact-custom-dockerfile: ok"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT_DIR=$(cd "$(dirname "$0")/../../.." && pwd)
|
|
5
|
+
AGENT_DIR="$ROOT_DIR/packages/agent"
|
|
6
|
+
TMP_DIR=$(mktemp -d /tmp/wile-custom-dockerfile-XXXXXX)
|
|
7
|
+
|
|
8
|
+
cleanup() {
|
|
9
|
+
rm -rf "$TMP_DIR"
|
|
10
|
+
}
|
|
11
|
+
trap cleanup EXIT INT TERM
|
|
12
|
+
|
|
13
|
+
mkdir -p "$TMP_DIR/.wile/secrets"
|
|
14
|
+
printf "secrets/\nscreenshots/\nlogs/\n" > "$TMP_DIR/.wile/.gitignore"
|
|
15
|
+
|
|
16
|
+
cat > "$TMP_DIR/.wile/prd.json" <<'JSON'
|
|
17
|
+
{
|
|
18
|
+
"stories": [
|
|
19
|
+
{
|
|
20
|
+
"id": 1,
|
|
21
|
+
"title": "Custom dockerfile test",
|
|
22
|
+
"description": "Verify project Dockerfile extends Wile base image.",
|
|
23
|
+
"acceptanceCriteria": ["n/a"],
|
|
24
|
+
"dependsOn": [],
|
|
25
|
+
"status": "pending"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
JSON
|
|
30
|
+
|
|
31
|
+
cat > "$TMP_DIR/.wile/secrets/.env" <<'ENV'
|
|
32
|
+
CODING_AGENT=CC
|
|
33
|
+
ENV
|
|
34
|
+
|
|
35
|
+
cat > "$TMP_DIR/.wile/Dockerfile" <<'DOCKERFILE'
|
|
36
|
+
ARG WILE_BASE_IMAGE=wile-agent:base
|
|
37
|
+
FROM ${WILE_BASE_IMAGE}
|
|
38
|
+
|
|
39
|
+
USER root
|
|
40
|
+
RUN apt-get update \
|
|
41
|
+
&& apt-get install -y --no-install-recommends jq \
|
|
42
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
43
|
+
USER wile
|
|
44
|
+
DOCKERFILE
|
|
45
|
+
|
|
46
|
+
export WILE_AGENT_DIR="$AGENT_DIR"
|
|
47
|
+
cd "$TMP_DIR"
|
|
48
|
+
node "$ROOT_DIR/packages/cli/dist/cli.js" run --test --max-iterations 1
|
|
49
|
+
|
|
50
|
+
LOG_FILE=$(ls "$TMP_DIR/.wile/logs"/run-*.log | head -n 1)
|
|
51
|
+
if [ -z "$LOG_FILE" ]; then
|
|
52
|
+
echo "error: expected run log file" >&2
|
|
53
|
+
exit 1
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
grep -q "TEST MODE" "$LOG_FILE"
|
|
57
|
+
docker run --rm --entrypoint sh wile-agent:local -lc "command -v jq >/dev/null"
|
|
58
|
+
|
|
59
|
+
echo "test-custom-dockerfile: ok"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT_DIR=$(cd "$(dirname "$0")/../../.." && pwd)
|
|
5
|
+
AGENT_DIR="$ROOT_DIR/packages/agent"
|
|
6
|
+
TMP_DIR=$(mktemp -d /tmp/wile-run-no-custom-XXXXXX)
|
|
7
|
+
|
|
8
|
+
cleanup() {
|
|
9
|
+
rm -rf "$TMP_DIR"
|
|
10
|
+
}
|
|
11
|
+
trap cleanup EXIT INT TERM
|
|
12
|
+
|
|
13
|
+
mkdir -p "$TMP_DIR/.wile/secrets"
|
|
14
|
+
printf "secrets/\nscreenshots/\nlogs/\n" > "$TMP_DIR/.wile/.gitignore"
|
|
15
|
+
|
|
16
|
+
cat > "$TMP_DIR/.wile/prd.json" <<'JSON'
|
|
17
|
+
{
|
|
18
|
+
"stories": [
|
|
19
|
+
{
|
|
20
|
+
"id": 1,
|
|
21
|
+
"title": "Default dockerfile fallback test",
|
|
22
|
+
"description": "Ensure run still works without .wile/Dockerfile",
|
|
23
|
+
"acceptanceCriteria": ["n/a"],
|
|
24
|
+
"dependsOn": [],
|
|
25
|
+
"status": "pending"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
JSON
|
|
30
|
+
|
|
31
|
+
cat > "$TMP_DIR/.wile/secrets/.env" <<'ENV'
|
|
32
|
+
CODING_AGENT=CC
|
|
33
|
+
ENV
|
|
34
|
+
|
|
35
|
+
if [ -f "$TMP_DIR/.wile/Dockerfile" ]; then
|
|
36
|
+
echo "error: .wile/Dockerfile should not exist for fallback test" >&2
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
export WILE_AGENT_DIR="$AGENT_DIR"
|
|
41
|
+
cd "$TMP_DIR"
|
|
42
|
+
OUTPUT_FILE="$TMP_DIR/output.txt"
|
|
43
|
+
node "$ROOT_DIR/packages/cli/dist/cli.js" run --test --max-iterations 1 --debug 2>&1 | tee "$OUTPUT_FILE"
|
|
44
|
+
|
|
45
|
+
grep -q "customDockerfile: (unset)" "$OUTPUT_FILE"
|
|
46
|
+
|
|
47
|
+
LOG_FILE=$(ls "$TMP_DIR/.wile/logs"/run-*.log | head -n 1)
|
|
48
|
+
if [ -z "$LOG_FILE" ]; then
|
|
49
|
+
echo "error: expected run log file" >&2
|
|
50
|
+
exit 1
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
grep -q "TEST MODE" "$LOG_FILE"
|
|
54
|
+
grep -q "\"status\": \"done\"" "$TMP_DIR/.wile/prd.json"
|
|
55
|
+
|
|
56
|
+
echo "test-run-no-custom-dockerfile: ok"
|
|
@@ -6,16 +6,10 @@
|
|
|
6
6
|
set -e
|
|
7
7
|
|
|
8
8
|
CODING_AGENT=${CODING_AGENT:-CC}
|
|
9
|
-
CLAUDE_MODEL=${CC_CLAUDE_MODEL:-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
CODEX_MODEL=${CODEX_MODEL:-}
|
|
14
|
-
|
|
15
|
-
# For openrouter provider, prepend vendor prefix if missing
|
|
16
|
-
if [ "$OC_PROVIDER" = "openrouter" ] && [[ "$OC_MODEL" != */* ]]; then
|
|
17
|
-
OC_MODEL="z-ai/$OC_MODEL"
|
|
18
|
-
fi
|
|
9
|
+
CLAUDE_MODEL=${CC_CLAUDE_MODEL:-opus}
|
|
10
|
+
OC_MODEL=${OC_MODEL:-opencode/kimi-k2.5-free}
|
|
11
|
+
GEMINI_MODEL=${GEMINI_MODEL:-gemini-3-pro-preview}
|
|
12
|
+
CODEX_MODEL=${CODEX_MODEL:-gpt-5.3-codex}
|
|
19
13
|
|
|
20
14
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
15
|
PROMPT_FILE="$SCRIPT_DIR/prompt-compact.md"
|
|
@@ -30,7 +24,6 @@ echo " 🌵 WILE - Compact PRD & Progress"
|
|
|
30
24
|
echo "══════════════════════════════════════════════════════"
|
|
31
25
|
echo " Agent: $CODING_AGENT"
|
|
32
26
|
if [ "$CODING_AGENT" = "OC" ]; then
|
|
33
|
-
echo " Provider: $OC_PROVIDER"
|
|
34
27
|
echo " Model: $OC_MODEL"
|
|
35
28
|
elif [ "$CODING_AGENT" = "GC" ]; then
|
|
36
29
|
echo " Model: ${GEMINI_MODEL:-auto}"
|
|
@@ -67,12 +60,8 @@ run_claude() {
|
|
|
67
60
|
|
|
68
61
|
run_opencode() {
|
|
69
62
|
local prompt_path="$1"
|
|
70
|
-
local model_arg="$OC_MODEL"
|
|
71
|
-
if [ "$OC_PROVIDER" = "openrouter" ]; then
|
|
72
|
-
model_arg="openrouter/$OC_MODEL"
|
|
73
|
-
fi
|
|
74
63
|
cat "$prompt_path" \
|
|
75
|
-
| opencode run --format json --model "$
|
|
64
|
+
| opencode run --format json --model "$OC_MODEL" \
|
|
76
65
|
| bun "$SCRIPT_DIR/opencode-stream.ts"
|
|
77
66
|
}
|
|
78
67
|
|
|
@@ -6,16 +6,10 @@
|
|
|
6
6
|
set -e
|
|
7
7
|
|
|
8
8
|
CODING_AGENT=${CODING_AGENT:-CC}
|
|
9
|
-
CLAUDE_MODEL=${CC_CLAUDE_MODEL:-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
CODEX_MODEL=${CODEX_MODEL:-}
|
|
14
|
-
|
|
15
|
-
# For openrouter provider, prepend vendor prefix if missing
|
|
16
|
-
if [ "$OC_PROVIDER" = "openrouter" ] && [[ "$OC_MODEL" != */* ]]; then
|
|
17
|
-
OC_MODEL="z-ai/$OC_MODEL"
|
|
18
|
-
fi
|
|
9
|
+
CLAUDE_MODEL=${CC_CLAUDE_MODEL:-opus}
|
|
10
|
+
OC_MODEL=${OC_MODEL:-opencode/kimi-k2.5-free}
|
|
11
|
+
GEMINI_MODEL=${GEMINI_MODEL:-gemini-3-pro-preview}
|
|
12
|
+
CODEX_MODEL=${CODEX_MODEL:-gpt-5.3-codex}
|
|
19
13
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
14
|
PREFLIGHT_PROMPT_FILE="$SCRIPT_DIR/prompt-preflight.md"
|
|
21
15
|
PROGRESS_PATH="${WILE_PROGRESS_PATH:-.wile/progress.txt}"
|
|
@@ -29,7 +23,6 @@ echo " 🌵 WILE - Preflight"
|
|
|
29
23
|
echo "══════════════════════════════════════════════════════"
|
|
30
24
|
echo " Agent: $CODING_AGENT"
|
|
31
25
|
if [ "$CODING_AGENT" = "OC" ]; then
|
|
32
|
-
echo " Provider: $OC_PROVIDER"
|
|
33
26
|
echo " Model: $OC_MODEL"
|
|
34
27
|
elif [ "$CODING_AGENT" = "GC" ]; then
|
|
35
28
|
echo " Model: ${GEMINI_MODEL:-auto}"
|
|
@@ -56,12 +49,8 @@ run_claude() {
|
|
|
56
49
|
|
|
57
50
|
run_opencode() {
|
|
58
51
|
local prompt_path="$1"
|
|
59
|
-
local model_arg="$OC_MODEL"
|
|
60
|
-
if [ "$OC_PROVIDER" = "openrouter" ]; then
|
|
61
|
-
model_arg="openrouter/$OC_MODEL"
|
|
62
|
-
fi
|
|
63
52
|
cat "$prompt_path" \
|
|
64
|
-
| opencode run --format json --model "$
|
|
53
|
+
| opencode run --format json --model "$OC_MODEL" \
|
|
65
54
|
| bun "$SCRIPT_DIR/opencode-stream.ts"
|
|
66
55
|
}
|
|
67
56
|
|
|
@@ -8,16 +8,10 @@ set -e
|
|
|
8
8
|
|
|
9
9
|
MAX_ITERATIONS=${1:-25}
|
|
10
10
|
CODING_AGENT=${CODING_AGENT:-CC}
|
|
11
|
-
CLAUDE_MODEL=${CC_CLAUDE_MODEL:-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
CODEX_MODEL=${CODEX_MODEL:-}
|
|
16
|
-
|
|
17
|
-
# For openrouter provider, prepend vendor prefix if missing
|
|
18
|
-
if [ "$OC_PROVIDER" = "openrouter" ] && [[ "$OC_MODEL" != */* ]]; then
|
|
19
|
-
OC_MODEL="z-ai/$OC_MODEL"
|
|
20
|
-
fi
|
|
11
|
+
CLAUDE_MODEL=${CC_CLAUDE_MODEL:-opus}
|
|
12
|
+
OC_MODEL=${OC_MODEL:-opencode/kimi-k2.5-free}
|
|
13
|
+
GEMINI_MODEL=${GEMINI_MODEL:-gemini-3-pro-preview}
|
|
14
|
+
CODEX_MODEL=${CODEX_MODEL:-gpt-5.3-codex}
|
|
21
15
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
22
16
|
PROMPT_FILE="$SCRIPT_DIR/prompt.md"
|
|
23
17
|
PREFLIGHT_PROMPT_FILE="$SCRIPT_DIR/prompt-preflight.md"
|
|
@@ -34,7 +28,6 @@ echo "════════════════════════
|
|
|
34
28
|
echo " Agent: $CODING_AGENT"
|
|
35
29
|
echo " Max iterations: $MAX_ITERATIONS"
|
|
36
30
|
if [ "$CODING_AGENT" = "OC" ]; then
|
|
37
|
-
echo " Provider: $OC_PROVIDER"
|
|
38
31
|
echo " Model: $OC_MODEL"
|
|
39
32
|
elif [ "$CODING_AGENT" = "GC" ]; then
|
|
40
33
|
echo " Model: ${GEMINI_MODEL:-auto}"
|
|
@@ -73,12 +66,8 @@ run_claude() {
|
|
|
73
66
|
|
|
74
67
|
run_opencode() {
|
|
75
68
|
local prompt_path="$1"
|
|
76
|
-
local model_arg="$OC_MODEL"
|
|
77
|
-
if [ "$OC_PROVIDER" = "openrouter" ]; then
|
|
78
|
-
model_arg="openrouter/$OC_MODEL"
|
|
79
|
-
fi
|
|
80
69
|
cat "$prompt_path" \
|
|
81
|
-
| opencode run --format json --model "$
|
|
70
|
+
| opencode run --format json --model "$OC_MODEL" \
|
|
82
71
|
| bun "$SCRIPT_DIR/opencode-stream.ts"
|
|
83
72
|
}
|
|
84
73
|
|
package/dist/cli.js
CHANGED
|
@@ -7451,6 +7451,166 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
7451
7451
|
import { existsSync } from "node:fs";
|
|
7452
7452
|
import { join, resolve, isAbsolute } from "node:path";
|
|
7453
7453
|
import { homedir } from "node:os";
|
|
7454
|
+
|
|
7455
|
+
// src/lib/env-schema.ts
|
|
7456
|
+
var ENV_VAR_DOC = [
|
|
7457
|
+
{
|
|
7458
|
+
key: "CODING_AGENT",
|
|
7459
|
+
section: "core",
|
|
7460
|
+
description: "Agent to run: CC (Claude), GC (Gemini), OC (OpenCode), CX (Codex).",
|
|
7461
|
+
requiredWhen: "Always.",
|
|
7462
|
+
defaultValue: "CC"
|
|
7463
|
+
},
|
|
7464
|
+
{
|
|
7465
|
+
key: "WILE_REPO_SOURCE",
|
|
7466
|
+
section: "core",
|
|
7467
|
+
description: "Where to get the repo from.",
|
|
7468
|
+
requiredWhen: "Always.",
|
|
7469
|
+
defaultValue: "github"
|
|
7470
|
+
},
|
|
7471
|
+
{
|
|
7472
|
+
key: "WILE_ENV_PROJECT_PATH",
|
|
7473
|
+
section: "core",
|
|
7474
|
+
description: "Path to project env file forwarded into Docker.",
|
|
7475
|
+
requiredWhen: "Optional.",
|
|
7476
|
+
defaultValue: ".wile/.env.project"
|
|
7477
|
+
},
|
|
7478
|
+
{
|
|
7479
|
+
key: "WILE_MAX_ITERATIONS",
|
|
7480
|
+
section: "core",
|
|
7481
|
+
description: "Default max loop iterations for `wile run`.",
|
|
7482
|
+
requiredWhen: "Optional.",
|
|
7483
|
+
defaultValue: "25"
|
|
7484
|
+
},
|
|
7485
|
+
{
|
|
7486
|
+
key: "GITHUB_TOKEN",
|
|
7487
|
+
section: "github",
|
|
7488
|
+
description: "GitHub token used for clone/push in GitHub repo mode.",
|
|
7489
|
+
requiredWhen: "Required when `WILE_REPO_SOURCE=github`.",
|
|
7490
|
+
secret: true
|
|
7491
|
+
},
|
|
7492
|
+
{
|
|
7493
|
+
key: "GITHUB_REPO_URL",
|
|
7494
|
+
section: "github",
|
|
7495
|
+
description: "Repository URL to clone in GitHub repo mode.",
|
|
7496
|
+
requiredWhen: "Required when `WILE_REPO_SOURCE=github`."
|
|
7497
|
+
},
|
|
7498
|
+
{
|
|
7499
|
+
key: "BRANCH_NAME",
|
|
7500
|
+
section: "github",
|
|
7501
|
+
description: "Branch Wile checks out and pushes to.",
|
|
7502
|
+
requiredWhen: "Required when `WILE_REPO_SOURCE=github`.",
|
|
7503
|
+
defaultValue: "main"
|
|
7504
|
+
},
|
|
7505
|
+
{
|
|
7506
|
+
key: "CC_CLAUDE_MODEL",
|
|
7507
|
+
section: "claude",
|
|
7508
|
+
description: "Claude model (`sonnet`, `opus`, `haiku`).",
|
|
7509
|
+
requiredWhen: "When `CODING_AGENT=CC`.",
|
|
7510
|
+
defaultValue: "opus"
|
|
7511
|
+
},
|
|
7512
|
+
{
|
|
7513
|
+
key: "CC_CLAUDE_CODE_OAUTH_TOKEN",
|
|
7514
|
+
section: "claude",
|
|
7515
|
+
description: "Claude Code OAuth token (subscription auth).",
|
|
7516
|
+
requiredWhen: "One of this or `CC_ANTHROPIC_API_KEY` when `CODING_AGENT=CC`.",
|
|
7517
|
+
secret: true
|
|
7518
|
+
},
|
|
7519
|
+
{
|
|
7520
|
+
key: "CC_ANTHROPIC_API_KEY",
|
|
7521
|
+
section: "claude",
|
|
7522
|
+
description: "Anthropic API key (pay-per-token auth).",
|
|
7523
|
+
requiredWhen: "One of this or `CC_CLAUDE_CODE_OAUTH_TOKEN` when `CODING_AGENT=CC`.",
|
|
7524
|
+
secret: true
|
|
7525
|
+
},
|
|
7526
|
+
{
|
|
7527
|
+
key: "OC_MODEL",
|
|
7528
|
+
section: "opencode",
|
|
7529
|
+
description: "OpenCode model id (native/free model).",
|
|
7530
|
+
requiredWhen: "Required when `CODING_AGENT=OC`.",
|
|
7531
|
+
defaultValue: "opencode/kimi-k2.5-free"
|
|
7532
|
+
},
|
|
7533
|
+
{
|
|
7534
|
+
key: "GEMINI_OAUTH_CREDS_B64",
|
|
7535
|
+
section: "gemini",
|
|
7536
|
+
description: "Base64-encoded `~/.gemini/oauth_creds.json`.",
|
|
7537
|
+
requiredWhen: "One of this or `GEMINI_API_KEY` when `CODING_AGENT=GC`.",
|
|
7538
|
+
secret: true
|
|
7539
|
+
},
|
|
7540
|
+
{
|
|
7541
|
+
key: "GEMINI_API_KEY",
|
|
7542
|
+
section: "gemini",
|
|
7543
|
+
description: "Gemini API key.",
|
|
7544
|
+
requiredWhen: "One of this or `GEMINI_OAUTH_CREDS_B64` when `CODING_AGENT=GC`.",
|
|
7545
|
+
secret: true
|
|
7546
|
+
},
|
|
7547
|
+
{
|
|
7548
|
+
key: "GEMINI_MODEL",
|
|
7549
|
+
section: "gemini",
|
|
7550
|
+
description: "Gemini model id.",
|
|
7551
|
+
requiredWhen: "When `CODING_AGENT=GC`.",
|
|
7552
|
+
defaultValue: "gemini-3-pro-preview"
|
|
7553
|
+
},
|
|
7554
|
+
{
|
|
7555
|
+
key: "CODEX_AUTH_JSON_B64",
|
|
7556
|
+
section: "codex",
|
|
7557
|
+
description: "Base64-encoded `~/.codex/auth.json` (subscription auth).",
|
|
7558
|
+
requiredWhen: "One of this, `CODEX_AUTH_JSON_PATH`, `CODEX_API_KEY`, or `OPENAI_API_KEY` when `CODING_AGENT=CX`.",
|
|
7559
|
+
secret: true
|
|
7560
|
+
},
|
|
7561
|
+
{
|
|
7562
|
+
key: "CODEX_AUTH_JSON_PATH",
|
|
7563
|
+
section: "codex",
|
|
7564
|
+
description: "Path to `~/.codex/auth.json` (used by `wile config`, then encoded).",
|
|
7565
|
+
requiredWhen: "Optional. Can be used instead of `CODEX_AUTH_JSON_B64` when generating config interactively."
|
|
7566
|
+
},
|
|
7567
|
+
{
|
|
7568
|
+
key: "CODEX_API_KEY",
|
|
7569
|
+
section: "codex",
|
|
7570
|
+
description: "OpenAI API key for Codex API-key auth.",
|
|
7571
|
+
requiredWhen: "One of this, `OPENAI_API_KEY`, `CODEX_AUTH_JSON_B64`, or `CODEX_AUTH_JSON_PATH` when `CODING_AGENT=CX`.",
|
|
7572
|
+
secret: true
|
|
7573
|
+
},
|
|
7574
|
+
{
|
|
7575
|
+
key: "OPENAI_API_KEY",
|
|
7576
|
+
section: "codex",
|
|
7577
|
+
description: "Alias fallback for `CODEX_API_KEY`.",
|
|
7578
|
+
requiredWhen: "Optional alias; counts as Codex API key when `CODING_AGENT=CX`.",
|
|
7579
|
+
secret: true
|
|
7580
|
+
},
|
|
7581
|
+
{
|
|
7582
|
+
key: "CODEX_MODEL",
|
|
7583
|
+
section: "codex",
|
|
7584
|
+
description: "Codex model override.",
|
|
7585
|
+
requiredWhen: "When `CODING_AGENT=CX`.",
|
|
7586
|
+
defaultValue: "gpt-5.3-codex"
|
|
7587
|
+
}
|
|
7588
|
+
];
|
|
7589
|
+
var ENV_FILE_KEY_ORDER = ENV_VAR_DOC.map((entry) => entry.key);
|
|
7590
|
+
var knownKeySet = new Set(ENV_FILE_KEY_ORDER);
|
|
7591
|
+
var isEnvFileKey = (key) => knownKeySet.has(key);
|
|
7592
|
+
var splitEnv = (raw) => {
|
|
7593
|
+
const known = {};
|
|
7594
|
+
const extra = {};
|
|
7595
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
7596
|
+
if (isEnvFileKey(key)) {
|
|
7597
|
+
known[key] = value;
|
|
7598
|
+
continue;
|
|
7599
|
+
}
|
|
7600
|
+
extra[key] = value;
|
|
7601
|
+
}
|
|
7602
|
+
return { known, extra };
|
|
7603
|
+
};
|
|
7604
|
+
var toEnvString = (known, extra = {}) => {
|
|
7605
|
+
const ordered = ENV_FILE_KEY_ORDER.filter((key) => known[key] !== undefined);
|
|
7606
|
+
const rest = Object.keys(extra).sort();
|
|
7607
|
+
const lines = [...ordered, ...rest].map((key) => `${key}=${known[key] ?? extra[key]}`);
|
|
7608
|
+
return lines.join(`
|
|
7609
|
+
`) + `
|
|
7610
|
+
`;
|
|
7611
|
+
};
|
|
7612
|
+
|
|
7613
|
+
// src/commands/config.ts
|
|
7454
7614
|
var prdExample = {
|
|
7455
7615
|
stories: [
|
|
7456
7616
|
{
|
|
@@ -7545,7 +7705,6 @@ var prdExample = {
|
|
|
7545
7705
|
var tips = {
|
|
7546
7706
|
oauth: "Tip: run 'claude setup-token' on your machine to generate an OAuth token (uses Pro/Max subscription).",
|
|
7547
7707
|
apiKey: "Tip: create an Anthropic API key in the console (uses API credits).",
|
|
7548
|
-
openrouter: "Tip: create an OpenRouter API key at https://openrouter.ai/keys (pay per token).",
|
|
7549
7708
|
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
7709
|
geminiOauth: "Tip: run 'gemini' locally and choose Login with Google to create ~/.gemini/oauth_creds.json.",
|
|
7551
7710
|
geminiApiKey: "Tip: create a Gemini API key at https://aistudio.google.com/app/apikey (pay per token).",
|
|
@@ -7553,11 +7712,247 @@ var tips = {
|
|
|
7553
7712
|
codexApiKey: "Tip: create an OpenAI API key at https://platform.openai.com/api-keys (pay per token)."
|
|
7554
7713
|
};
|
|
7555
7714
|
var nativeOcModels = [
|
|
7556
|
-
{ title: "
|
|
7557
|
-
{ title: "
|
|
7558
|
-
{ title: "
|
|
7559
|
-
{ title: "MiniMax M2.1", value: "opencode/minimax-m2.1-free" }
|
|
7715
|
+
{ title: "Kimi K2.5 Free (recommended)", value: "opencode/kimi-k2.5-free" },
|
|
7716
|
+
{ title: "MiniMax 2.5", value: "opencode/minimax-m2.5-free" },
|
|
7717
|
+
{ title: "Big Pickle", value: "opencode/big-pickle" }
|
|
7560
7718
|
];
|
|
7719
|
+
var nonInteractiveKeys = new Set([
|
|
7720
|
+
"codingAgent",
|
|
7721
|
+
"repoSource",
|
|
7722
|
+
"branchName",
|
|
7723
|
+
"envProjectPath",
|
|
7724
|
+
"maxIterations",
|
|
7725
|
+
"githubToken",
|
|
7726
|
+
"repoUrl",
|
|
7727
|
+
"ccAuthMethod",
|
|
7728
|
+
"ccAuthValue",
|
|
7729
|
+
"ccModel",
|
|
7730
|
+
"ocModel",
|
|
7731
|
+
"gcAuthMethod",
|
|
7732
|
+
"gcOauthPath",
|
|
7733
|
+
"gcApiKey",
|
|
7734
|
+
"gcModel",
|
|
7735
|
+
"cxAuthMethod",
|
|
7736
|
+
"cxAuthJsonPath",
|
|
7737
|
+
"cxApiKey",
|
|
7738
|
+
"cxModel"
|
|
7739
|
+
]);
|
|
7740
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7741
|
+
var asNonEmptyString = (value) => {
|
|
7742
|
+
if (typeof value !== "string") {
|
|
7743
|
+
return;
|
|
7744
|
+
}
|
|
7745
|
+
const trimmed = value.trim();
|
|
7746
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
7747
|
+
};
|
|
7748
|
+
var renderNonInteractiveConfigHelp = () => {
|
|
7749
|
+
const lines = [];
|
|
7750
|
+
lines.push("# Wile non-interactive config");
|
|
7751
|
+
lines.push("");
|
|
7752
|
+
lines.push("Use one flag for both docs and config application:");
|
|
7753
|
+
lines.push("- `bunx wile config --non-interactive` prints this help.");
|
|
7754
|
+
lines.push("- `bunx wile config --non-interactive '<json>'` validates and applies config.");
|
|
7755
|
+
lines.push("- Direct edits to `.wile/secrets/.env` are discouraged; prefer this command so validation runs before writing.");
|
|
7756
|
+
lines.push("");
|
|
7757
|
+
lines.push("The JSON payload is ideal to pass via `WILE_PROMPTS_INJECT`:");
|
|
7758
|
+
lines.push("```bash");
|
|
7759
|
+
lines.push(`export WILE_PROMPTS_INJECT='{"codingAgent":"OC","repoSource":"local","ocModel":"opencode/kimi-k2.5-free","branchName":"main","envProjectPath":".wile/.env.project","maxIterations":25}'`);
|
|
7760
|
+
lines.push('bunx wile config --non-interactive "$WILE_PROMPTS_INJECT"');
|
|
7761
|
+
lines.push("```");
|
|
7762
|
+
lines.push("");
|
|
7763
|
+
lines.push("Required fields:");
|
|
7764
|
+
lines.push("- Always: `codingAgent`, `repoSource`.");
|
|
7765
|
+
lines.push("- If `repoSource=github`: `githubToken`, `repoUrl`.");
|
|
7766
|
+
lines.push("- If `codingAgent=CC`: `ccAuthMethod`, `ccAuthValue`.");
|
|
7767
|
+
lines.push("- If `codingAgent=OC`: `ocModel`.");
|
|
7768
|
+
lines.push("- If `codingAgent=GC` and `gcAuthMethod=oauth`: `gcOauthPath`.");
|
|
7769
|
+
lines.push("- If `codingAgent=GC` and `gcAuthMethod=apiKey`: `gcApiKey`.");
|
|
7770
|
+
lines.push("- If `codingAgent=CX` and `cxAuthMethod=oauth`: `cxAuthJsonPath`.");
|
|
7771
|
+
lines.push("- If `codingAgent=CX` and `cxAuthMethod=apiKey`: `cxApiKey`.");
|
|
7772
|
+
lines.push("");
|
|
7773
|
+
lines.push("Optional fields:");
|
|
7774
|
+
lines.push("- `branchName` (default `main`)");
|
|
7775
|
+
lines.push("- `envProjectPath` (default `.wile/.env.project`)");
|
|
7776
|
+
lines.push("- `maxIterations` (default `25`)");
|
|
7777
|
+
lines.push("- Optional custom Dockerfile: edit `.wile/Dockerfile` (created automatically by `wile config`)");
|
|
7778
|
+
lines.push("- `ccModel` (`sonnet` | `opus` | `haiku`, default `opus`)");
|
|
7779
|
+
lines.push("- `gcModel` (default `gemini-3-pro-preview`)");
|
|
7780
|
+
lines.push("- `cxModel` (default `gpt-5.3-codex`)");
|
|
7781
|
+
lines.push("");
|
|
7782
|
+
lines.push("Example (GitHub + Claude OAuth):");
|
|
7783
|
+
lines.push("```json");
|
|
7784
|
+
lines.push("{");
|
|
7785
|
+
lines.push(' "codingAgent": "CC",');
|
|
7786
|
+
lines.push(' "repoSource": "github",');
|
|
7787
|
+
lines.push(' "githubToken": "ghp_xxx",');
|
|
7788
|
+
lines.push(' "repoUrl": "https://github.com/owner/repo",');
|
|
7789
|
+
lines.push(' "branchName": "main",');
|
|
7790
|
+
lines.push(' "ccAuthMethod": "oauth",');
|
|
7791
|
+
lines.push(' "ccAuthValue": "claude_oauth_token",');
|
|
7792
|
+
lines.push(' "ccModel": "opus",');
|
|
7793
|
+
lines.push(' "maxIterations": 25');
|
|
7794
|
+
lines.push("}");
|
|
7795
|
+
lines.push("```");
|
|
7796
|
+
lines.push("");
|
|
7797
|
+
return lines.join(`
|
|
7798
|
+
`);
|
|
7799
|
+
};
|
|
7800
|
+
var parseNonInteractiveConfig = (raw) => {
|
|
7801
|
+
let parsed;
|
|
7802
|
+
try {
|
|
7803
|
+
parsed = JSON.parse(raw);
|
|
7804
|
+
} catch {
|
|
7805
|
+
return { errors: ["config is not valid JSON"] };
|
|
7806
|
+
}
|
|
7807
|
+
if (!isRecord(parsed)) {
|
|
7808
|
+
return { errors: ["config must be a JSON object"] };
|
|
7809
|
+
}
|
|
7810
|
+
const errors = [];
|
|
7811
|
+
for (const key of Object.keys(parsed)) {
|
|
7812
|
+
if (!nonInteractiveKeys.has(key)) {
|
|
7813
|
+
errors.push(`unknown key '${key}'`);
|
|
7814
|
+
}
|
|
7815
|
+
}
|
|
7816
|
+
const codingAgentRaw = parsed.codingAgent;
|
|
7817
|
+
const codingAgent = codingAgentRaw === "CC" || codingAgentRaw === "OC" || codingAgentRaw === "GC" || codingAgentRaw === "CX" ? codingAgentRaw : undefined;
|
|
7818
|
+
if (!codingAgent) {
|
|
7819
|
+
errors.push("codingAgent must be one of: CC, OC, GC, CX");
|
|
7820
|
+
}
|
|
7821
|
+
const repoSourceRaw = parsed.repoSource;
|
|
7822
|
+
const repoSource = repoSourceRaw === "github" || repoSourceRaw === "local" ? repoSourceRaw : undefined;
|
|
7823
|
+
if (!repoSource) {
|
|
7824
|
+
errors.push("repoSource must be one of: github, local");
|
|
7825
|
+
}
|
|
7826
|
+
const branchName = asNonEmptyString(parsed.branchName);
|
|
7827
|
+
const envProjectPath = asNonEmptyString(parsed.envProjectPath);
|
|
7828
|
+
const githubToken = asNonEmptyString(parsed.githubToken);
|
|
7829
|
+
const repoUrl = asNonEmptyString(parsed.repoUrl);
|
|
7830
|
+
const ccAuthValue = asNonEmptyString(parsed.ccAuthValue);
|
|
7831
|
+
const ocModel = asNonEmptyString(parsed.ocModel);
|
|
7832
|
+
const gcOauthPath = asNonEmptyString(parsed.gcOauthPath);
|
|
7833
|
+
const gcApiKey = asNonEmptyString(parsed.gcApiKey);
|
|
7834
|
+
const gcModel = asNonEmptyString(parsed.gcModel);
|
|
7835
|
+
const cxAuthJsonPath = asNonEmptyString(parsed.cxAuthJsonPath);
|
|
7836
|
+
const cxApiKey = asNonEmptyString(parsed.cxApiKey);
|
|
7837
|
+
const cxModel = asNonEmptyString(parsed.cxModel);
|
|
7838
|
+
if (repoSource === "github") {
|
|
7839
|
+
if (!githubToken) {
|
|
7840
|
+
errors.push("githubToken is required when repoSource=github");
|
|
7841
|
+
}
|
|
7842
|
+
if (!repoUrl) {
|
|
7843
|
+
errors.push("repoUrl is required when repoSource=github");
|
|
7844
|
+
}
|
|
7845
|
+
}
|
|
7846
|
+
const ccAuthMethodRaw = parsed.ccAuthMethod;
|
|
7847
|
+
const ccAuthMethod = ccAuthMethodRaw === "oauth" || ccAuthMethodRaw === "apiKey" ? ccAuthMethodRaw : undefined;
|
|
7848
|
+
const ccModelRaw = parsed.ccModel;
|
|
7849
|
+
const ccModel = ccModelRaw === "sonnet" || ccModelRaw === "opus" || ccModelRaw === "haiku" ? ccModelRaw : undefined;
|
|
7850
|
+
const gcAuthMethodRaw = parsed.gcAuthMethod;
|
|
7851
|
+
const gcAuthMethod = gcAuthMethodRaw === "oauth" || gcAuthMethodRaw === "apiKey" ? gcAuthMethodRaw : undefined;
|
|
7852
|
+
const cxAuthMethodRaw = parsed.cxAuthMethod;
|
|
7853
|
+
const cxAuthMethod = cxAuthMethodRaw === "oauth" || cxAuthMethodRaw === "apiKey" ? cxAuthMethodRaw : undefined;
|
|
7854
|
+
if (codingAgent === "CC") {
|
|
7855
|
+
if (!ccAuthMethod) {
|
|
7856
|
+
errors.push("ccAuthMethod is required when codingAgent=CC");
|
|
7857
|
+
}
|
|
7858
|
+
if (!ccAuthValue) {
|
|
7859
|
+
errors.push("ccAuthValue is required when codingAgent=CC");
|
|
7860
|
+
}
|
|
7861
|
+
if (parsed.ccModel !== undefined && !ccModel) {
|
|
7862
|
+
errors.push("ccModel must be one of: sonnet, opus, haiku");
|
|
7863
|
+
}
|
|
7864
|
+
}
|
|
7865
|
+
if (codingAgent === "OC" && !ocModel) {
|
|
7866
|
+
errors.push("ocModel is required when codingAgent=OC");
|
|
7867
|
+
}
|
|
7868
|
+
if (codingAgent === "GC") {
|
|
7869
|
+
if (!gcAuthMethod) {
|
|
7870
|
+
errors.push("gcAuthMethod is required when codingAgent=GC");
|
|
7871
|
+
} else if (gcAuthMethod === "oauth" && !gcOauthPath) {
|
|
7872
|
+
errors.push("gcOauthPath is required when codingAgent=GC and gcAuthMethod=oauth");
|
|
7873
|
+
} else if (gcAuthMethod === "apiKey" && !gcApiKey) {
|
|
7874
|
+
errors.push("gcApiKey is required when codingAgent=GC and gcAuthMethod=apiKey");
|
|
7875
|
+
}
|
|
7876
|
+
}
|
|
7877
|
+
if (codingAgent === "CX") {
|
|
7878
|
+
if (!cxAuthMethod) {
|
|
7879
|
+
errors.push("cxAuthMethod is required when codingAgent=CX");
|
|
7880
|
+
} else if (cxAuthMethod === "oauth" && !cxAuthJsonPath) {
|
|
7881
|
+
errors.push("cxAuthJsonPath is required when codingAgent=CX and cxAuthMethod=oauth");
|
|
7882
|
+
} else if (cxAuthMethod === "apiKey" && !cxApiKey) {
|
|
7883
|
+
errors.push("cxApiKey is required when codingAgent=CX and cxAuthMethod=apiKey");
|
|
7884
|
+
}
|
|
7885
|
+
}
|
|
7886
|
+
const maxIterationsRaw = parsed.maxIterations;
|
|
7887
|
+
let maxIterations;
|
|
7888
|
+
if (maxIterationsRaw !== undefined) {
|
|
7889
|
+
if (typeof maxIterationsRaw !== "number" || !Number.isFinite(maxIterationsRaw)) {
|
|
7890
|
+
errors.push("maxIterations must be a number");
|
|
7891
|
+
} else if (maxIterationsRaw <= 0) {
|
|
7892
|
+
errors.push("maxIterations must be greater than 0");
|
|
7893
|
+
} else {
|
|
7894
|
+
maxIterations = Math.floor(maxIterationsRaw);
|
|
7895
|
+
}
|
|
7896
|
+
}
|
|
7897
|
+
if (errors.length > 0 || !codingAgent || !repoSource) {
|
|
7898
|
+
return { errors };
|
|
7899
|
+
}
|
|
7900
|
+
return {
|
|
7901
|
+
config: {
|
|
7902
|
+
codingAgent,
|
|
7903
|
+
repoSource,
|
|
7904
|
+
branchName,
|
|
7905
|
+
envProjectPath,
|
|
7906
|
+
maxIterations,
|
|
7907
|
+
githubToken,
|
|
7908
|
+
repoUrl,
|
|
7909
|
+
ccAuthMethod,
|
|
7910
|
+
ccAuthValue,
|
|
7911
|
+
ccModel,
|
|
7912
|
+
ocModel,
|
|
7913
|
+
gcAuthMethod,
|
|
7914
|
+
gcOauthPath,
|
|
7915
|
+
gcApiKey,
|
|
7916
|
+
gcModel,
|
|
7917
|
+
cxAuthMethod,
|
|
7918
|
+
cxAuthJsonPath,
|
|
7919
|
+
cxApiKey,
|
|
7920
|
+
cxModel
|
|
7921
|
+
},
|
|
7922
|
+
errors: []
|
|
7923
|
+
};
|
|
7924
|
+
};
|
|
7925
|
+
var toPromptInjectValues = (config) => {
|
|
7926
|
+
const values = [config.codingAgent];
|
|
7927
|
+
if (config.codingAgent === "CC") {
|
|
7928
|
+
values.push(config.ccAuthMethod, config.ccAuthValue, config.ccModel ?? "opus");
|
|
7929
|
+
} else if (config.codingAgent === "OC") {
|
|
7930
|
+
values.push(config.ocModel ?? "opencode/kimi-k2.5-free");
|
|
7931
|
+
} else if (config.codingAgent === "GC") {
|
|
7932
|
+
values.push(config.gcAuthMethod);
|
|
7933
|
+
if (config.gcAuthMethod === "oauth") {
|
|
7934
|
+
values.push(config.gcOauthPath);
|
|
7935
|
+
} else {
|
|
7936
|
+
values.push(config.gcApiKey);
|
|
7937
|
+
}
|
|
7938
|
+
} else {
|
|
7939
|
+
values.push(config.cxAuthMethod);
|
|
7940
|
+
if (config.cxAuthMethod === "oauth") {
|
|
7941
|
+
values.push(config.cxAuthJsonPath);
|
|
7942
|
+
} else {
|
|
7943
|
+
values.push(config.cxApiKey);
|
|
7944
|
+
}
|
|
7945
|
+
values.push(config.cxModel ?? "gpt-5.3-codex");
|
|
7946
|
+
}
|
|
7947
|
+
values.push(config.repoSource);
|
|
7948
|
+
if (config.repoSource === "github") {
|
|
7949
|
+
values.push(config.githubToken, config.repoUrl);
|
|
7950
|
+
}
|
|
7951
|
+
values.push(config.branchName ?? "main");
|
|
7952
|
+
values.push(config.envProjectPath ?? ".wile/.env.project");
|
|
7953
|
+
values.push(config.maxIterations ?? 25);
|
|
7954
|
+
return values;
|
|
7955
|
+
};
|
|
7561
7956
|
var readEnvFile = async (path) => {
|
|
7562
7957
|
if (!existsSync(path)) {
|
|
7563
7958
|
return {};
|
|
@@ -7628,36 +8023,6 @@ var setIfDefined = (env, key, value) => {
|
|
|
7628
8023
|
}
|
|
7629
8024
|
env[key] = value;
|
|
7630
8025
|
};
|
|
7631
|
-
var toEnvString = (env) => {
|
|
7632
|
-
const orderedKeys = [
|
|
7633
|
-
"CODING_AGENT",
|
|
7634
|
-
"WILE_REPO_SOURCE",
|
|
7635
|
-
"WILE_ENV_PROJECT_PATH",
|
|
7636
|
-
"GITHUB_TOKEN",
|
|
7637
|
-
"GITHUB_REPO_URL",
|
|
7638
|
-
"BRANCH_NAME",
|
|
7639
|
-
"WILE_MAX_ITERATIONS",
|
|
7640
|
-
"CC_CLAUDE_MODEL",
|
|
7641
|
-
"CC_CLAUDE_CODE_OAUTH_TOKEN",
|
|
7642
|
-
"CC_ANTHROPIC_API_KEY",
|
|
7643
|
-
"OC_PROVIDER",
|
|
7644
|
-
"OC_MODEL",
|
|
7645
|
-
"OC_OPENROUTER_API_KEY",
|
|
7646
|
-
"GEMINI_OAUTH_CREDS_B64",
|
|
7647
|
-
"GEMINI_API_KEY",
|
|
7648
|
-
"CODEX_AUTH_JSON_B64",
|
|
7649
|
-
"CODEX_AUTH_JSON_PATH",
|
|
7650
|
-
"CODEX_API_KEY",
|
|
7651
|
-
"CODEX_MODEL"
|
|
7652
|
-
];
|
|
7653
|
-
const orderedSet = new Set(orderedKeys);
|
|
7654
|
-
const ordered = orderedKeys.filter((key) => (key in env));
|
|
7655
|
-
const rest = Object.keys(env).filter((key) => !orderedSet.has(key)).sort();
|
|
7656
|
-
const lines = [...ordered, ...rest].map((key) => `${key}=${env[key]}`);
|
|
7657
|
-
return lines.join(`
|
|
7658
|
-
`) + `
|
|
7659
|
-
`;
|
|
7660
|
-
};
|
|
7661
8026
|
var resolvePath = (cwd, input) => {
|
|
7662
8027
|
const expanded = input.startsWith("~") ? join(homedir(), input.slice(1)) : input;
|
|
7663
8028
|
return isAbsolute(expanded) ? expanded : resolve(cwd, expanded);
|
|
@@ -7666,7 +8031,28 @@ var readBase64File = async (path) => {
|
|
|
7666
8031
|
const contents = await readFile(path);
|
|
7667
8032
|
return Buffer.from(contents).toString("base64");
|
|
7668
8033
|
};
|
|
7669
|
-
var runConfig = async () => {
|
|
8034
|
+
var runConfig = async (options = {}) => {
|
|
8035
|
+
const nonInteractiveInput = options.nonInteractive;
|
|
8036
|
+
if (nonInteractiveInput === true) {
|
|
8037
|
+
process.stdout.write(renderNonInteractiveConfigHelp());
|
|
8038
|
+
return true;
|
|
8039
|
+
}
|
|
8040
|
+
let nonInteractiveConfig;
|
|
8041
|
+
if (typeof nonInteractiveInput === "string") {
|
|
8042
|
+
const parsed = parseNonInteractiveConfig(nonInteractiveInput);
|
|
8043
|
+
if (!parsed.config) {
|
|
8044
|
+
process.stdout.write(renderNonInteractiveConfigHelp());
|
|
8045
|
+
process.stdout.write(`
|
|
8046
|
+
`);
|
|
8047
|
+
console.error("Error: invalid non-interactive config.");
|
|
8048
|
+
for (const error of parsed.errors) {
|
|
8049
|
+
console.error(`- ${error}`);
|
|
8050
|
+
}
|
|
8051
|
+
return false;
|
|
8052
|
+
}
|
|
8053
|
+
nonInteractiveConfig = parsed.config;
|
|
8054
|
+
import_prompts.default.inject(toPromptInjectValues(parsed.config));
|
|
8055
|
+
}
|
|
7670
8056
|
const cwd = process.cwd();
|
|
7671
8057
|
const wileDir = join(cwd, ".wile");
|
|
7672
8058
|
const secretsDir = join(wileDir, "secrets");
|
|
@@ -7678,9 +8064,12 @@ var runConfig = async () => {
|
|
|
7678
8064
|
const additionalInstructionsPath = join(wileDir, "additional-instructions.md");
|
|
7679
8065
|
const preflightPath = join(wileDir, "preflight.md");
|
|
7680
8066
|
const agentsPath = join(wileDir, "AGENTS.md");
|
|
8067
|
+
const dockerfilePath = join(wileDir, "Dockerfile");
|
|
7681
8068
|
await mkdir(secretsDir, { recursive: true });
|
|
7682
|
-
|
|
7683
|
-
|
|
8069
|
+
if (!nonInteractiveConfig) {
|
|
8070
|
+
maybeInject();
|
|
8071
|
+
}
|
|
8072
|
+
const { known: existingEnv, extra: extraEnv } = splitEnv(await readEnvFile(envPath));
|
|
7684
8073
|
const codingAgentResponse = await prompt({
|
|
7685
8074
|
type: "select",
|
|
7686
8075
|
name: "codingAgent",
|
|
@@ -7699,10 +8088,7 @@ var runConfig = async () => {
|
|
|
7699
8088
|
let codexAuthMethod = null;
|
|
7700
8089
|
let authValueResponse = {};
|
|
7701
8090
|
let defaultModelResponse = {};
|
|
7702
|
-
let ocProviderResponse = {};
|
|
7703
|
-
let ocKeyResponse = {};
|
|
7704
8091
|
let ocModelResponse = {};
|
|
7705
|
-
let ocNativeModelResponse = {};
|
|
7706
8092
|
let geminiOauthPathResponse = {};
|
|
7707
8093
|
let geminiApiKeyResponse = {};
|
|
7708
8094
|
let codexAuthJsonPathResponse = {};
|
|
@@ -7742,45 +8128,14 @@ var runConfig = async () => {
|
|
|
7742
8128
|
initial: existingEnv.CC_CLAUDE_MODEL === "sonnet" ? 0 : existingEnv.CC_CLAUDE_MODEL === "haiku" ? 2 : 1
|
|
7743
8129
|
});
|
|
7744
8130
|
} else if (codingAgent === "OC") {
|
|
7745
|
-
const
|
|
7746
|
-
|
|
8131
|
+
const existingNativeIdx = nativeOcModels.findIndex((m) => m.value === existingEnv.OC_MODEL);
|
|
8132
|
+
ocModelResponse = await prompt({
|
|
7747
8133
|
type: "select",
|
|
7748
|
-
name: "
|
|
7749
|
-
message: "OpenCode
|
|
7750
|
-
choices:
|
|
7751
|
-
|
|
7752
|
-
{ title: "OpenRouter (pay per token)", value: "openrouter" }
|
|
7753
|
-
],
|
|
7754
|
-
initial: providerDefault === "openrouter" ? 1 : 0
|
|
8134
|
+
name: "ocModel",
|
|
8135
|
+
message: "OpenCode model (free)",
|
|
8136
|
+
choices: nativeOcModels,
|
|
8137
|
+
initial: existingNativeIdx >= 0 ? existingNativeIdx : 0
|
|
7755
8138
|
});
|
|
7756
|
-
const ocProvider2 = ocProviderResponse.ocProvider;
|
|
7757
|
-
if (ocProvider2 === "openrouter") {
|
|
7758
|
-
console.log("");
|
|
7759
|
-
console.log(tips.openrouter);
|
|
7760
|
-
console.log("");
|
|
7761
|
-
ocKeyResponse = await prompt({
|
|
7762
|
-
type: "password",
|
|
7763
|
-
name: "ocKey",
|
|
7764
|
-
message: "OpenRouter API key (press enter to keep existing)",
|
|
7765
|
-
initial: existingEnv.OC_OPENROUTER_API_KEY ?? ""
|
|
7766
|
-
});
|
|
7767
|
-
ocModelResponse = await prompt({
|
|
7768
|
-
type: "select",
|
|
7769
|
-
name: "ocModel",
|
|
7770
|
-
message: "OpenCode model (OpenRouter)",
|
|
7771
|
-
choices: [{ title: "glm-4.7", value: "glm-4.7" }],
|
|
7772
|
-
initial: 0
|
|
7773
|
-
});
|
|
7774
|
-
} else {
|
|
7775
|
-
const existingNativeIdx = nativeOcModels.findIndex((m) => m.value === existingEnv.OC_MODEL);
|
|
7776
|
-
ocNativeModelResponse = await prompt({
|
|
7777
|
-
type: "select",
|
|
7778
|
-
name: "ocNativeModel",
|
|
7779
|
-
message: "OpenCode model (free)",
|
|
7780
|
-
choices: nativeOcModels,
|
|
7781
|
-
initial: existingNativeIdx >= 0 ? existingNativeIdx : 0
|
|
7782
|
-
});
|
|
7783
|
-
}
|
|
7784
8139
|
} else if (codingAgent === "GC") {
|
|
7785
8140
|
const geminiAuthDefault = existingEnv.GEMINI_OAUTH_CREDS_B64 ? "oauth" : existingEnv.GEMINI_API_KEY ? "apiKey" : "oauth";
|
|
7786
8141
|
const geminiAuthResponse = await prompt({
|
|
@@ -7849,7 +8204,7 @@ var runConfig = async () => {
|
|
|
7849
8204
|
type: "text",
|
|
7850
8205
|
name: "codexModel",
|
|
7851
8206
|
message: "Codex model (press enter to keep existing)",
|
|
7852
|
-
initial: existingEnv.CODEX_MODEL ?? ""
|
|
8207
|
+
initial: existingEnv.CODEX_MODEL ?? "gpt-5.3-codex"
|
|
7853
8208
|
});
|
|
7854
8209
|
}
|
|
7855
8210
|
const repoSourceResponse = await prompt({
|
|
@@ -7902,10 +8257,9 @@ var runConfig = async () => {
|
|
|
7902
8257
|
const maxIterations = Number.isFinite(iterationsResponse.maxIterations) && iterationsResponse.maxIterations > 0 ? iterationsResponse.maxIterations : fallbackIterations;
|
|
7903
8258
|
const authFallback = authMethod === "oauth" ? existingEnv.CC_CLAUDE_CODE_OAUTH_TOKEN : existingEnv.CC_ANTHROPIC_API_KEY;
|
|
7904
8259
|
const authValue = codingAgent === "CC" ? coalesceValue(authValueResponse.authValue, authFallback) : undefined;
|
|
7905
|
-
const
|
|
7906
|
-
const ocKey = codingAgent === "OC" && ocProvider === "openrouter" ? coalesceValue(ocKeyResponse.ocKey, existingEnv.OC_OPENROUTER_API_KEY) : undefined;
|
|
7907
|
-
const ocModel = codingAgent === "OC" ? ocProvider === "openrouter" ? coalesceValue(ocModelResponse.ocModel, existingEnv.OC_MODEL ?? "glm-4.7") : coalesceValue(ocNativeModelResponse.ocNativeModel, existingEnv.OC_MODEL ?? "opencode/grok-code") : undefined;
|
|
8260
|
+
const ocModel = codingAgent === "OC" ? coalesceValue(ocModelResponse.ocModel, existingEnv.OC_MODEL ?? "opencode/kimi-k2.5-free") : undefined;
|
|
7908
8261
|
const geminiApiKey = codingAgent === "GC" ? coalesceValue(geminiApiKeyResponse.geminiApiKey, existingEnv.GEMINI_API_KEY) : undefined;
|
|
8262
|
+
const geminiModel = codingAgent === "GC" ? coalesceValue(nonInteractiveConfig?.gcModel, existingEnv.GEMINI_MODEL ?? "gemini-3-pro-preview") : undefined;
|
|
7909
8263
|
let geminiOauthCredsB64;
|
|
7910
8264
|
if (codingAgent === "GC" && geminiAuthMethod === "oauth") {
|
|
7911
8265
|
const configuredPath = coalesceValue(geminiOauthPathResponse.geminiOauthPath);
|
|
@@ -7935,7 +8289,7 @@ var runConfig = async () => {
|
|
|
7935
8289
|
codexAuthJsonB64 = existingEnv.CODEX_AUTH_JSON_B64;
|
|
7936
8290
|
}
|
|
7937
8291
|
}
|
|
7938
|
-
const codexModel = codingAgent === "CX" ? coalesceValue(codexModelResponse.codexModel, existingEnv.CODEX_MODEL) : undefined;
|
|
8292
|
+
const codexModel = codingAgent === "CX" ? coalesceValue(codexModelResponse.codexModel, existingEnv.CODEX_MODEL ?? "gpt-5.3-codex") : undefined;
|
|
7939
8293
|
const githubToken = repoSource === "github" ? coalesceValue(githubTokenResponse.githubToken, existingEnv.GITHUB_TOKEN) : existingEnv.GITHUB_TOKEN;
|
|
7940
8294
|
const repoUrl = repoSource === "github" ? coalesceValue(repoResponse.repoUrl, existingEnv.GITHUB_REPO_URL) : existingEnv.GITHUB_REPO_URL;
|
|
7941
8295
|
const envProjectPathValue = coalesceValue(envProjectPathResponse.envProjectPath, existingEnv.WILE_ENV_PROJECT_PATH ?? ".wile/.env.project") ?? ".wile/.env.project";
|
|
@@ -7956,17 +8310,14 @@ var runConfig = async () => {
|
|
|
7956
8310
|
setIfDefined(envOut, "CC_ANTHROPIC_API_KEY", authValue);
|
|
7957
8311
|
}
|
|
7958
8312
|
} else if (codingAgent === "OC") {
|
|
7959
|
-
setIfDefined(envOut, "
|
|
7960
|
-
setIfDefined(envOut, "OC_MODEL", ocModel ?? "opencode/grok-code");
|
|
7961
|
-
if (ocProvider === "openrouter") {
|
|
7962
|
-
setIfDefined(envOut, "OC_OPENROUTER_API_KEY", ocKey);
|
|
7963
|
-
}
|
|
8313
|
+
setIfDefined(envOut, "OC_MODEL", ocModel ?? "opencode/kimi-k2.5-free");
|
|
7964
8314
|
} else if (codingAgent === "GC") {
|
|
7965
8315
|
if (geminiAuthMethod === "apiKey") {
|
|
7966
8316
|
setIfDefined(envOut, "GEMINI_API_KEY", geminiApiKey);
|
|
7967
8317
|
} else {
|
|
7968
8318
|
setIfDefined(envOut, "GEMINI_OAUTH_CREDS_B64", geminiOauthCredsB64);
|
|
7969
8319
|
}
|
|
8320
|
+
setIfDefined(envOut, "GEMINI_MODEL", geminiModel);
|
|
7970
8321
|
} else if (codingAgent === "CX") {
|
|
7971
8322
|
if (codexAuthMethod === "apiKey") {
|
|
7972
8323
|
setIfDefined(envOut, "CODEX_API_KEY", codexApiKey);
|
|
@@ -7975,7 +8326,7 @@ var runConfig = async () => {
|
|
|
7975
8326
|
}
|
|
7976
8327
|
setIfDefined(envOut, "CODEX_MODEL", codexModel);
|
|
7977
8328
|
}
|
|
7978
|
-
await writeFile(envPath, toEnvString(envOut));
|
|
8329
|
+
await writeFile(envPath, toEnvString(envOut, extraEnv));
|
|
7979
8330
|
await ensureGitignore(gitignorePath);
|
|
7980
8331
|
const envProjectTarget = envProjectPathValue === ".wile/.env.project" ? envProjectPath : envProjectPathValue;
|
|
7981
8332
|
const envProjectResolved = envProjectTarget.startsWith("/") ? envProjectTarget : join(cwd, envProjectTarget);
|
|
@@ -8004,6 +8355,18 @@ Use bullet points for preflight checks, e.g.
|
|
|
8004
8355
|
- Run \`supabase db reset --db-url "$SUPABASE_DB_URL"\`.
|
|
8005
8356
|
-->
|
|
8006
8357
|
`);
|
|
8358
|
+
await writeIfMissing(dockerfilePath, [
|
|
8359
|
+
"ARG WILE_BASE_IMAGE=wile-agent:base",
|
|
8360
|
+
"FROM ${WILE_BASE_IMAGE}",
|
|
8361
|
+
"",
|
|
8362
|
+
"# Optional: install additional system dependencies for your project.",
|
|
8363
|
+
"# Example:",
|
|
8364
|
+
"# USER root",
|
|
8365
|
+
"# RUN apt-get update && apt-get install -y --no-install-recommends jq && rm -rf /var/lib/apt/lists/*",
|
|
8366
|
+
"# USER wile",
|
|
8367
|
+
""
|
|
8368
|
+
].join(`
|
|
8369
|
+
`));
|
|
8007
8370
|
await writeIfMissing(agentsPath, [
|
|
8008
8371
|
"# PRD authoring guidance for Wile",
|
|
8009
8372
|
"",
|
|
@@ -8046,6 +8409,7 @@ Wile config complete.`);
|
|
|
8046
8409
|
if (!hadPreflight) {
|
|
8047
8410
|
console.log("Created .wile/preflight.md for preflight checks (optional).");
|
|
8048
8411
|
}
|
|
8412
|
+
return true;
|
|
8049
8413
|
};
|
|
8050
8414
|
|
|
8051
8415
|
// src/commands/compact.ts
|
|
@@ -8082,6 +8446,13 @@ var resolveEnvProjectPath = (cwd, configured) => {
|
|
|
8082
8446
|
}
|
|
8083
8447
|
return defaultPath;
|
|
8084
8448
|
};
|
|
8449
|
+
var resolveAgentDockerfilePath = (cwd) => {
|
|
8450
|
+
const defaultPath = join2(cwd, ".wile", "Dockerfile");
|
|
8451
|
+
if (existsSync2(defaultPath)) {
|
|
8452
|
+
return defaultPath;
|
|
8453
|
+
}
|
|
8454
|
+
return;
|
|
8455
|
+
};
|
|
8085
8456
|
var parseEnvFile = (path) => {
|
|
8086
8457
|
if (!existsSync2(path)) {
|
|
8087
8458
|
return {};
|
|
@@ -8100,8 +8471,9 @@ var readWileConfig = (options = {}) => {
|
|
|
8100
8471
|
if (validate) {
|
|
8101
8472
|
ensureRequired(existsSync2(paths.envPath), "Missing .wile/secrets/.env. Run 'bunx wile config' first.");
|
|
8102
8473
|
}
|
|
8103
|
-
const env = parseEnvFile(paths.envPath);
|
|
8474
|
+
const { known: env } = splitEnv(parseEnvFile(paths.envPath));
|
|
8104
8475
|
const envProjectPath = resolveEnvProjectPath(options.cwd ?? process.cwd(), env.WILE_ENV_PROJECT_PATH);
|
|
8476
|
+
const agentDockerfile = resolveAgentDockerfilePath(options.cwd ?? process.cwd());
|
|
8105
8477
|
const envProject = parseEnvFile(envProjectPath);
|
|
8106
8478
|
const repoSource = env.WILE_REPO_SOURCE || "github";
|
|
8107
8479
|
if (validate) {
|
|
@@ -8115,10 +8487,6 @@ var readWileConfig = (options = {}) => {
|
|
|
8115
8487
|
ensureRequired(Boolean(env.CC_CLAUDE_CODE_OAUTH_TOKEN || env.CC_ANTHROPIC_API_KEY), "Either CC_CLAUDE_CODE_OAUTH_TOKEN or CC_ANTHROPIC_API_KEY is required in .wile/secrets/.env.");
|
|
8116
8488
|
}
|
|
8117
8489
|
if (env.CODING_AGENT === "OC") {
|
|
8118
|
-
const ocProvider = env.OC_PROVIDER || "native";
|
|
8119
|
-
if (ocProvider === "openrouter") {
|
|
8120
|
-
ensureRequired(Boolean(env.OC_OPENROUTER_API_KEY), "OC_OPENROUTER_API_KEY is required in .wile/secrets/.env for OpenCode with OpenRouter provider.");
|
|
8121
|
-
}
|
|
8122
8490
|
ensureRequired(Boolean(env.OC_MODEL), "OC_MODEL is required in .wile/secrets/.env for OpenCode.");
|
|
8123
8491
|
}
|
|
8124
8492
|
if (env.CODING_AGENT === "GC") {
|
|
@@ -8139,15 +8507,15 @@ var readWileConfig = (options = {}) => {
|
|
|
8139
8507
|
githubRepoUrl: env.GITHUB_REPO_URL ?? "",
|
|
8140
8508
|
branchName: env.BRANCH_NAME ?? "",
|
|
8141
8509
|
repoSource,
|
|
8510
|
+
agentDockerfile,
|
|
8142
8511
|
ccClaudeModel: env.CC_CLAUDE_MODEL,
|
|
8143
8512
|
maxIterations: env.WILE_MAX_ITERATIONS,
|
|
8144
8513
|
ccClaudeCodeOauthToken: env.CC_CLAUDE_CODE_OAUTH_TOKEN,
|
|
8145
8514
|
ccAnthropicApiKey: env.CC_ANTHROPIC_API_KEY,
|
|
8146
|
-
ocProvider: env.OC_PROVIDER ?? "native",
|
|
8147
|
-
ocOpenrouterApiKey: env.OC_OPENROUTER_API_KEY,
|
|
8148
8515
|
ocModel: env.OC_MODEL,
|
|
8149
8516
|
geminiOauthCredsB64: env.GEMINI_OAUTH_CREDS_B64,
|
|
8150
8517
|
geminiApiKey: env.GEMINI_API_KEY,
|
|
8518
|
+
geminiModel: env.GEMINI_MODEL,
|
|
8151
8519
|
codexAuthJsonB64: env.CODEX_AUTH_JSON_B64,
|
|
8152
8520
|
codexAuthJsonPath: env.CODEX_AUTH_JSON_PATH,
|
|
8153
8521
|
codexApiKey: env.CODEX_API_KEY ?? env.OPENAI_API_KEY,
|
|
@@ -8404,6 +8772,8 @@ import { existsSync as existsSync3, readFileSync as readFileSync3, mkdirSync, cr
|
|
|
8404
8772
|
import { spawn, spawnSync } from "node:child_process";
|
|
8405
8773
|
import { resolve as resolve3, join as join3, dirname } from "node:path";
|
|
8406
8774
|
import { fileURLToPath } from "node:url";
|
|
8775
|
+
var LOCAL_AGENT_IMAGE = "wile-agent:local";
|
|
8776
|
+
var BASE_AGENT_IMAGE = "wile-agent:base";
|
|
8407
8777
|
var findAgentDir = (startDir) => {
|
|
8408
8778
|
let current = startDir;
|
|
8409
8779
|
while (true) {
|
|
@@ -8439,13 +8809,42 @@ var validatePrdLocation = (paths) => {
|
|
|
8439
8809
|
throw new Error("Missing .wile/prd.json. Run 'bunx wile config'.");
|
|
8440
8810
|
}
|
|
8441
8811
|
};
|
|
8442
|
-
var
|
|
8443
|
-
const result = spawnSync("docker",
|
|
8444
|
-
stdio: "inherit"
|
|
8445
|
-
});
|
|
8812
|
+
var runDockerBuild = (args, errorMessage) => {
|
|
8813
|
+
const result = spawnSync("docker", args, { stdio: "inherit" });
|
|
8446
8814
|
if (result.status !== 0) {
|
|
8447
|
-
throw new Error(
|
|
8815
|
+
throw new Error(errorMessage);
|
|
8816
|
+
}
|
|
8817
|
+
};
|
|
8818
|
+
var validateCustomDockerfile = (dockerfilePath) => {
|
|
8819
|
+
const contents = readFileSync3(dockerfilePath, "utf8");
|
|
8820
|
+
if (contents.includes("WILE_BASE_IMAGE") || contents.includes(BASE_AGENT_IMAGE)) {
|
|
8821
|
+
return;
|
|
8822
|
+
}
|
|
8823
|
+
throw new Error([
|
|
8824
|
+
`Custom Dockerfile must extend ${BASE_AGENT_IMAGE}.`,
|
|
8825
|
+
`Update ${dockerfilePath} to include either:`,
|
|
8826
|
+
`- ARG WILE_BASE_IMAGE=${BASE_AGENT_IMAGE} + FROM \${WILE_BASE_IMAGE}`,
|
|
8827
|
+
`- FROM ${BASE_AGENT_IMAGE}`
|
|
8828
|
+
].join(`
|
|
8829
|
+
`));
|
|
8830
|
+
};
|
|
8831
|
+
var buildAgentImage = (agentDir, options) => {
|
|
8832
|
+
if (!options.customDockerfilePath) {
|
|
8833
|
+
runDockerBuild(["build", "-t", LOCAL_AGENT_IMAGE, agentDir], "Docker build failed.");
|
|
8834
|
+
return;
|
|
8448
8835
|
}
|
|
8836
|
+
validateCustomDockerfile(options.customDockerfilePath);
|
|
8837
|
+
runDockerBuild(["build", "-t", BASE_AGENT_IMAGE, agentDir], "Docker base image build failed.");
|
|
8838
|
+
runDockerBuild([
|
|
8839
|
+
"build",
|
|
8840
|
+
"-t",
|
|
8841
|
+
LOCAL_AGENT_IMAGE,
|
|
8842
|
+
"--build-arg",
|
|
8843
|
+
`WILE_BASE_IMAGE=${BASE_AGENT_IMAGE}`,
|
|
8844
|
+
"-f",
|
|
8845
|
+
options.customDockerfilePath,
|
|
8846
|
+
options.projectDir
|
|
8847
|
+
], "Custom Docker build failed.");
|
|
8449
8848
|
};
|
|
8450
8849
|
var resolveAgentDir = () => {
|
|
8451
8850
|
const override = process.env.WILE_AGENT_DIR;
|
|
@@ -8535,7 +8934,7 @@ var buildDockerArgs = (options, config, paths, cwd, extraEnv = []) => {
|
|
|
8535
8934
|
dockerArgs.push("-e", `WILE_ADDITIONAL_INSTRUCTIONS=${additionalInstructionsPath}`);
|
|
8536
8935
|
dockerArgs.push("-v", `${additionalInstructionsPath}:${additionalInstructionsPath}`);
|
|
8537
8936
|
}
|
|
8538
|
-
dockerArgs.push(
|
|
8937
|
+
dockerArgs.push(LOCAL_AGENT_IMAGE);
|
|
8539
8938
|
return dockerArgs;
|
|
8540
8939
|
};
|
|
8541
8940
|
var runWile = async (options) => {
|
|
@@ -8558,6 +8957,7 @@ var runWile = async (options) => {
|
|
|
8558
8957
|
console.log(`- WILE_AGENT_DIR: ${process.env.WILE_AGENT_DIR ?? "(unset)"}`);
|
|
8559
8958
|
console.log(`- codingAgent: ${config.codingAgent}`);
|
|
8560
8959
|
console.log(`- repoSource: ${config.repoSource}`);
|
|
8960
|
+
console.log(`- customDockerfile: ${config.agentDockerfile ?? "(unset)"}`);
|
|
8561
8961
|
console.log(`- githubRepoUrl: ${config.githubRepoUrl || "(empty)"}`);
|
|
8562
8962
|
console.log(`- branchName: ${config.branchName || "(empty)"}`);
|
|
8563
8963
|
}
|
|
@@ -8584,7 +8984,10 @@ var runWile = async (options) => {
|
|
|
8584
8984
|
}
|
|
8585
8985
|
const agentDir = resolveAgentDir();
|
|
8586
8986
|
const resolvedIterations = options.maxIterations || config.maxIterations || "25";
|
|
8587
|
-
buildAgentImage(agentDir
|
|
8987
|
+
buildAgentImage(agentDir, {
|
|
8988
|
+
projectDir: cwd,
|
|
8989
|
+
customDockerfilePath: config.agentDockerfile
|
|
8990
|
+
});
|
|
8588
8991
|
const dockerArgs = buildDockerArgs({ ...options, maxIterations: resolvedIterations }, config, paths, cwd);
|
|
8589
8992
|
const logsDir = join3(paths.wileDir, "logs");
|
|
8590
8993
|
mkdirSync(logsDir, { recursive: true });
|
|
@@ -8628,6 +9031,7 @@ var runCompact = async (options) => {
|
|
|
8628
9031
|
console.log(`- WILE_AGENT_DIR: ${process.env.WILE_AGENT_DIR ?? "(unset)"}`);
|
|
8629
9032
|
console.log(`- codingAgent: ${config.codingAgent}`);
|
|
8630
9033
|
console.log(`- repoSource: ${config.repoSource}`);
|
|
9034
|
+
console.log(`- customDockerfile: ${config.agentDockerfile ?? "(unset)"}`);
|
|
8631
9035
|
console.log(`- githubRepoUrl: ${config.githubRepoUrl || "(empty)"}`);
|
|
8632
9036
|
console.log(`- branchName: ${config.branchName || "(empty)"}`);
|
|
8633
9037
|
}
|
|
@@ -8651,7 +9055,10 @@ var runCompact = async (options) => {
|
|
|
8651
9055
|
}
|
|
8652
9056
|
const agentDir = resolveAgentDir();
|
|
8653
9057
|
const resolvedIterations = options.maxIterations || "1";
|
|
8654
|
-
buildAgentImage(agentDir
|
|
9058
|
+
buildAgentImage(agentDir, {
|
|
9059
|
+
projectDir: cwd,
|
|
9060
|
+
customDockerfilePath: config.agentDockerfile
|
|
9061
|
+
});
|
|
8655
9062
|
const dockerArgs = buildDockerArgs({ ...options, maxIterations: resolvedIterations }, config, paths, cwd, ["WILE_MODE=compact"]);
|
|
8656
9063
|
const logsDir = join4(paths.wileDir, "logs");
|
|
8657
9064
|
mkdirSync2(logsDir, { recursive: true });
|
|
@@ -8670,8 +9077,11 @@ var packageJsonPath = resolve4(new URL("../package.json", import.meta.url).pathn
|
|
|
8670
9077
|
var packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
|
|
8671
9078
|
var program2 = new Command;
|
|
8672
9079
|
program2.name("wile").description("Autonomous AI coding agent that ships features while you sleep").version(packageJson.version);
|
|
8673
|
-
program2.command("config").description("Configure the current project for Wile").action(async () => {
|
|
8674
|
-
await runConfig();
|
|
9080
|
+
program2.command("config").description("Configure the current project for Wile").option("--non-interactive [config]", "No value: print agent config docs. With JSON value: validate and apply non-interactive config.").action(async (options) => {
|
|
9081
|
+
const ok = await runConfig({ nonInteractive: options.nonInteractive });
|
|
9082
|
+
if (!ok) {
|
|
9083
|
+
process.exitCode = 1;
|
|
9084
|
+
}
|
|
8675
9085
|
});
|
|
8676
9086
|
program2.command("run").description("Run Wile on a repository").option("--repo <repo>", "Repository URL or local path").option("--max-iterations <count>", "Maximum iterations").option("--test", "Run in test mode").option("--debug", "Print debug info before running").action(async (options) => {
|
|
8677
9087
|
await runWile(options);
|