wile 1.1.0 → 1.2.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 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,7 @@ 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
- 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.
42
+ 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
43
 
31
44
  ## Run Wile
32
45
 
@@ -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 (OpenRouter)
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
- # OpenRouter API key (used with OpenCode)
34
- # OC_OPENROUTER_API_KEY=sk-or-xxxxx
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: sonnet)
95
- CC_CLAUDE_MODEL=sonnet
92
+ # Claude model alias or full model name (default: opus)
93
+ CC_CLAUDE_MODEL=opus
96
94
 
97
- # Gemini model name (default: auto-gemini-3)
98
- # GEMINI_MODEL=auto-gemini-3
95
+ # Gemini model name (default: gemini-3-pro)
96
+ # GEMINI_MODEL=gemini-3-pro
99
97
 
100
- # Codex model name (defaults to Codex CLI default)
101
- # CODEX_MODEL=gpt-5.1-codex
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
@@ -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 via OpenRouter), or `CX` (Codex CLI) |
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
- | `OC_OPENROUTER_API_KEY` | Yes (OC) | OpenRouter API key for OpenCode |
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: `auto-gemini-3`) |
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: sonnet) |
129
- | `CODEX_MODEL` | No (CX) | Codex model name (defaults to Codex CLI default) |
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`.
@@ -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
- if [ "$OC_PROVIDER" = "openrouter" ]; then
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)"
@@ -6,16 +6,10 @@
6
6
  set -e
7
7
 
8
8
  CODING_AGENT=${CODING_AGENT:-CC}
9
- CLAUDE_MODEL=${CC_CLAUDE_MODEL:-sonnet}
10
- OC_PROVIDER=${OC_PROVIDER:-native}
11
- OC_MODEL=${OC_MODEL:-opencode/grok-code}
12
- GEMINI_MODEL=${GEMINI_MODEL:-auto-gemini-3}
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 "$model_arg" \
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:-sonnet}
10
- OC_PROVIDER=${OC_PROVIDER:-native}
11
- OC_MODEL=${OC_MODEL:-opencode/grok-code}
12
- GEMINI_MODEL=${GEMINI_MODEL:-auto-gemini-3}
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 "$model_arg" \
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:-sonnet}
12
- OC_PROVIDER=${OC_PROVIDER:-native}
13
- OC_MODEL=${OC_MODEL:-opencode/grok-code}
14
- GEMINI_MODEL=${GEMINI_MODEL:-auto-gemini-3}
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 "$model_arg" \
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,246 @@ 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: "Grok Code Fast 1 (recommended)", value: "opencode/grok-code" },
7557
- { title: "Big Pickle", value: "opencode/big-pickle" },
7558
- { title: "GLM-4.7", value: "opencode/glm-4.7-free" },
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("- `ccModel` (`sonnet` | `opus` | `haiku`, default `opus`)");
7778
+ lines.push("- `gcModel` (default `gemini-3-pro-preview`)");
7779
+ lines.push("- `cxModel` (default `gpt-5.3-codex`)");
7780
+ lines.push("");
7781
+ lines.push("Example (GitHub + Claude OAuth):");
7782
+ lines.push("```json");
7783
+ lines.push("{");
7784
+ lines.push(' "codingAgent": "CC",');
7785
+ lines.push(' "repoSource": "github",');
7786
+ lines.push(' "githubToken": "ghp_xxx",');
7787
+ lines.push(' "repoUrl": "https://github.com/owner/repo",');
7788
+ lines.push(' "branchName": "main",');
7789
+ lines.push(' "ccAuthMethod": "oauth",');
7790
+ lines.push(' "ccAuthValue": "claude_oauth_token",');
7791
+ lines.push(' "ccModel": "opus",');
7792
+ lines.push(' "maxIterations": 25');
7793
+ lines.push("}");
7794
+ lines.push("```");
7795
+ lines.push("");
7796
+ return lines.join(`
7797
+ `);
7798
+ };
7799
+ var parseNonInteractiveConfig = (raw) => {
7800
+ let parsed;
7801
+ try {
7802
+ parsed = JSON.parse(raw);
7803
+ } catch {
7804
+ return { errors: ["config is not valid JSON"] };
7805
+ }
7806
+ if (!isRecord(parsed)) {
7807
+ return { errors: ["config must be a JSON object"] };
7808
+ }
7809
+ const errors = [];
7810
+ for (const key of Object.keys(parsed)) {
7811
+ if (!nonInteractiveKeys.has(key)) {
7812
+ errors.push(`unknown key '${key}'`);
7813
+ }
7814
+ }
7815
+ const codingAgentRaw = parsed.codingAgent;
7816
+ const codingAgent = codingAgentRaw === "CC" || codingAgentRaw === "OC" || codingAgentRaw === "GC" || codingAgentRaw === "CX" ? codingAgentRaw : undefined;
7817
+ if (!codingAgent) {
7818
+ errors.push("codingAgent must be one of: CC, OC, GC, CX");
7819
+ }
7820
+ const repoSourceRaw = parsed.repoSource;
7821
+ const repoSource = repoSourceRaw === "github" || repoSourceRaw === "local" ? repoSourceRaw : undefined;
7822
+ if (!repoSource) {
7823
+ errors.push("repoSource must be one of: github, local");
7824
+ }
7825
+ const branchName = asNonEmptyString(parsed.branchName);
7826
+ const envProjectPath = asNonEmptyString(parsed.envProjectPath);
7827
+ const githubToken = asNonEmptyString(parsed.githubToken);
7828
+ const repoUrl = asNonEmptyString(parsed.repoUrl);
7829
+ const ccAuthValue = asNonEmptyString(parsed.ccAuthValue);
7830
+ const ocModel = asNonEmptyString(parsed.ocModel);
7831
+ const gcOauthPath = asNonEmptyString(parsed.gcOauthPath);
7832
+ const gcApiKey = asNonEmptyString(parsed.gcApiKey);
7833
+ const gcModel = asNonEmptyString(parsed.gcModel);
7834
+ const cxAuthJsonPath = asNonEmptyString(parsed.cxAuthJsonPath);
7835
+ const cxApiKey = asNonEmptyString(parsed.cxApiKey);
7836
+ const cxModel = asNonEmptyString(parsed.cxModel);
7837
+ if (repoSource === "github") {
7838
+ if (!githubToken) {
7839
+ errors.push("githubToken is required when repoSource=github");
7840
+ }
7841
+ if (!repoUrl) {
7842
+ errors.push("repoUrl is required when repoSource=github");
7843
+ }
7844
+ }
7845
+ const ccAuthMethodRaw = parsed.ccAuthMethod;
7846
+ const ccAuthMethod = ccAuthMethodRaw === "oauth" || ccAuthMethodRaw === "apiKey" ? ccAuthMethodRaw : undefined;
7847
+ const ccModelRaw = parsed.ccModel;
7848
+ const ccModel = ccModelRaw === "sonnet" || ccModelRaw === "opus" || ccModelRaw === "haiku" ? ccModelRaw : undefined;
7849
+ const gcAuthMethodRaw = parsed.gcAuthMethod;
7850
+ const gcAuthMethod = gcAuthMethodRaw === "oauth" || gcAuthMethodRaw === "apiKey" ? gcAuthMethodRaw : undefined;
7851
+ const cxAuthMethodRaw = parsed.cxAuthMethod;
7852
+ const cxAuthMethod = cxAuthMethodRaw === "oauth" || cxAuthMethodRaw === "apiKey" ? cxAuthMethodRaw : undefined;
7853
+ if (codingAgent === "CC") {
7854
+ if (!ccAuthMethod) {
7855
+ errors.push("ccAuthMethod is required when codingAgent=CC");
7856
+ }
7857
+ if (!ccAuthValue) {
7858
+ errors.push("ccAuthValue is required when codingAgent=CC");
7859
+ }
7860
+ if (parsed.ccModel !== undefined && !ccModel) {
7861
+ errors.push("ccModel must be one of: sonnet, opus, haiku");
7862
+ }
7863
+ }
7864
+ if (codingAgent === "OC" && !ocModel) {
7865
+ errors.push("ocModel is required when codingAgent=OC");
7866
+ }
7867
+ if (codingAgent === "GC") {
7868
+ if (!gcAuthMethod) {
7869
+ errors.push("gcAuthMethod is required when codingAgent=GC");
7870
+ } else if (gcAuthMethod === "oauth" && !gcOauthPath) {
7871
+ errors.push("gcOauthPath is required when codingAgent=GC and gcAuthMethod=oauth");
7872
+ } else if (gcAuthMethod === "apiKey" && !gcApiKey) {
7873
+ errors.push("gcApiKey is required when codingAgent=GC and gcAuthMethod=apiKey");
7874
+ }
7875
+ }
7876
+ if (codingAgent === "CX") {
7877
+ if (!cxAuthMethod) {
7878
+ errors.push("cxAuthMethod is required when codingAgent=CX");
7879
+ } else if (cxAuthMethod === "oauth" && !cxAuthJsonPath) {
7880
+ errors.push("cxAuthJsonPath is required when codingAgent=CX and cxAuthMethod=oauth");
7881
+ } else if (cxAuthMethod === "apiKey" && !cxApiKey) {
7882
+ errors.push("cxApiKey is required when codingAgent=CX and cxAuthMethod=apiKey");
7883
+ }
7884
+ }
7885
+ const maxIterationsRaw = parsed.maxIterations;
7886
+ let maxIterations;
7887
+ if (maxIterationsRaw !== undefined) {
7888
+ if (typeof maxIterationsRaw !== "number" || !Number.isFinite(maxIterationsRaw)) {
7889
+ errors.push("maxIterations must be a number");
7890
+ } else if (maxIterationsRaw <= 0) {
7891
+ errors.push("maxIterations must be greater than 0");
7892
+ } else {
7893
+ maxIterations = Math.floor(maxIterationsRaw);
7894
+ }
7895
+ }
7896
+ if (errors.length > 0 || !codingAgent || !repoSource) {
7897
+ return { errors };
7898
+ }
7899
+ return {
7900
+ config: {
7901
+ codingAgent,
7902
+ repoSource,
7903
+ branchName,
7904
+ envProjectPath,
7905
+ maxIterations,
7906
+ githubToken,
7907
+ repoUrl,
7908
+ ccAuthMethod,
7909
+ ccAuthValue,
7910
+ ccModel,
7911
+ ocModel,
7912
+ gcAuthMethod,
7913
+ gcOauthPath,
7914
+ gcApiKey,
7915
+ gcModel,
7916
+ cxAuthMethod,
7917
+ cxAuthJsonPath,
7918
+ cxApiKey,
7919
+ cxModel
7920
+ },
7921
+ errors: []
7922
+ };
7923
+ };
7924
+ var toPromptInjectValues = (config) => {
7925
+ const values = [config.codingAgent];
7926
+ if (config.codingAgent === "CC") {
7927
+ values.push(config.ccAuthMethod, config.ccAuthValue, config.ccModel ?? "opus");
7928
+ } else if (config.codingAgent === "OC") {
7929
+ values.push(config.ocModel ?? "opencode/kimi-k2.5-free");
7930
+ } else if (config.codingAgent === "GC") {
7931
+ values.push(config.gcAuthMethod);
7932
+ if (config.gcAuthMethod === "oauth") {
7933
+ values.push(config.gcOauthPath);
7934
+ } else {
7935
+ values.push(config.gcApiKey);
7936
+ }
7937
+ } else {
7938
+ values.push(config.cxAuthMethod);
7939
+ if (config.cxAuthMethod === "oauth") {
7940
+ values.push(config.cxAuthJsonPath);
7941
+ } else {
7942
+ values.push(config.cxApiKey);
7943
+ }
7944
+ values.push(config.cxModel ?? "gpt-5.3-codex");
7945
+ }
7946
+ values.push(config.repoSource);
7947
+ if (config.repoSource === "github") {
7948
+ values.push(config.githubToken, config.repoUrl);
7949
+ }
7950
+ values.push(config.branchName ?? "main");
7951
+ values.push(config.envProjectPath ?? ".wile/.env.project");
7952
+ values.push(config.maxIterations ?? 25);
7953
+ return values;
7954
+ };
7561
7955
  var readEnvFile = async (path) => {
7562
7956
  if (!existsSync(path)) {
7563
7957
  return {};
@@ -7628,36 +8022,6 @@ var setIfDefined = (env, key, value) => {
7628
8022
  }
7629
8023
  env[key] = value;
7630
8024
  };
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
8025
  var resolvePath = (cwd, input) => {
7662
8026
  const expanded = input.startsWith("~") ? join(homedir(), input.slice(1)) : input;
7663
8027
  return isAbsolute(expanded) ? expanded : resolve(cwd, expanded);
@@ -7666,7 +8030,28 @@ var readBase64File = async (path) => {
7666
8030
  const contents = await readFile(path);
7667
8031
  return Buffer.from(contents).toString("base64");
7668
8032
  };
7669
- var runConfig = async () => {
8033
+ var runConfig = async (options = {}) => {
8034
+ const nonInteractiveInput = options.nonInteractive;
8035
+ if (nonInteractiveInput === true) {
8036
+ process.stdout.write(renderNonInteractiveConfigHelp());
8037
+ return true;
8038
+ }
8039
+ let nonInteractiveConfig;
8040
+ if (typeof nonInteractiveInput === "string") {
8041
+ const parsed = parseNonInteractiveConfig(nonInteractiveInput);
8042
+ if (!parsed.config) {
8043
+ process.stdout.write(renderNonInteractiveConfigHelp());
8044
+ process.stdout.write(`
8045
+ `);
8046
+ console.error("Error: invalid non-interactive config.");
8047
+ for (const error of parsed.errors) {
8048
+ console.error(`- ${error}`);
8049
+ }
8050
+ return false;
8051
+ }
8052
+ nonInteractiveConfig = parsed.config;
8053
+ import_prompts.default.inject(toPromptInjectValues(parsed.config));
8054
+ }
7670
8055
  const cwd = process.cwd();
7671
8056
  const wileDir = join(cwd, ".wile");
7672
8057
  const secretsDir = join(wileDir, "secrets");
@@ -7679,8 +8064,10 @@ var runConfig = async () => {
7679
8064
  const preflightPath = join(wileDir, "preflight.md");
7680
8065
  const agentsPath = join(wileDir, "AGENTS.md");
7681
8066
  await mkdir(secretsDir, { recursive: true });
7682
- maybeInject();
7683
- const existingEnv = await readEnvFile(envPath);
8067
+ if (!nonInteractiveConfig) {
8068
+ maybeInject();
8069
+ }
8070
+ const { known: existingEnv, extra: extraEnv } = splitEnv(await readEnvFile(envPath));
7684
8071
  const codingAgentResponse = await prompt({
7685
8072
  type: "select",
7686
8073
  name: "codingAgent",
@@ -7699,10 +8086,7 @@ var runConfig = async () => {
7699
8086
  let codexAuthMethod = null;
7700
8087
  let authValueResponse = {};
7701
8088
  let defaultModelResponse = {};
7702
- let ocProviderResponse = {};
7703
- let ocKeyResponse = {};
7704
8089
  let ocModelResponse = {};
7705
- let ocNativeModelResponse = {};
7706
8090
  let geminiOauthPathResponse = {};
7707
8091
  let geminiApiKeyResponse = {};
7708
8092
  let codexAuthJsonPathResponse = {};
@@ -7742,45 +8126,14 @@ var runConfig = async () => {
7742
8126
  initial: existingEnv.CC_CLAUDE_MODEL === "sonnet" ? 0 : existingEnv.CC_CLAUDE_MODEL === "haiku" ? 2 : 1
7743
8127
  });
7744
8128
  } else if (codingAgent === "OC") {
7745
- const providerDefault = existingEnv.OC_PROVIDER === "openrouter" ? "openrouter" : "native";
7746
- ocProviderResponse = await prompt({
8129
+ const existingNativeIdx = nativeOcModels.findIndex((m) => m.value === existingEnv.OC_MODEL);
8130
+ ocModelResponse = await prompt({
7747
8131
  type: "select",
7748
- name: "ocProvider",
7749
- message: "OpenCode provider",
7750
- choices: [
7751
- { title: "Native free models (no API key)", value: "native" },
7752
- { title: "OpenRouter (pay per token)", value: "openrouter" }
7753
- ],
7754
- initial: providerDefault === "openrouter" ? 1 : 0
8132
+ name: "ocModel",
8133
+ message: "OpenCode model (free)",
8134
+ choices: nativeOcModels,
8135
+ initial: existingNativeIdx >= 0 ? existingNativeIdx : 0
7755
8136
  });
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
8137
  } else if (codingAgent === "GC") {
7785
8138
  const geminiAuthDefault = existingEnv.GEMINI_OAUTH_CREDS_B64 ? "oauth" : existingEnv.GEMINI_API_KEY ? "apiKey" : "oauth";
7786
8139
  const geminiAuthResponse = await prompt({
@@ -7849,7 +8202,7 @@ var runConfig = async () => {
7849
8202
  type: "text",
7850
8203
  name: "codexModel",
7851
8204
  message: "Codex model (press enter to keep existing)",
7852
- initial: existingEnv.CODEX_MODEL ?? ""
8205
+ initial: existingEnv.CODEX_MODEL ?? "gpt-5.3-codex"
7853
8206
  });
7854
8207
  }
7855
8208
  const repoSourceResponse = await prompt({
@@ -7902,10 +8255,9 @@ var runConfig = async () => {
7902
8255
  const maxIterations = Number.isFinite(iterationsResponse.maxIterations) && iterationsResponse.maxIterations > 0 ? iterationsResponse.maxIterations : fallbackIterations;
7903
8256
  const authFallback = authMethod === "oauth" ? existingEnv.CC_CLAUDE_CODE_OAUTH_TOKEN : existingEnv.CC_ANTHROPIC_API_KEY;
7904
8257
  const authValue = codingAgent === "CC" ? coalesceValue(authValueResponse.authValue, authFallback) : undefined;
7905
- const ocProvider = codingAgent === "OC" ? ocProviderResponse.ocProvider ?? "native" : undefined;
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;
8258
+ const ocModel = codingAgent === "OC" ? coalesceValue(ocModelResponse.ocModel, existingEnv.OC_MODEL ?? "opencode/kimi-k2.5-free") : undefined;
7908
8259
  const geminiApiKey = codingAgent === "GC" ? coalesceValue(geminiApiKeyResponse.geminiApiKey, existingEnv.GEMINI_API_KEY) : undefined;
8260
+ const geminiModel = codingAgent === "GC" ? coalesceValue(nonInteractiveConfig?.gcModel, existingEnv.GEMINI_MODEL ?? "gemini-3-pro-preview") : undefined;
7909
8261
  let geminiOauthCredsB64;
7910
8262
  if (codingAgent === "GC" && geminiAuthMethod === "oauth") {
7911
8263
  const configuredPath = coalesceValue(geminiOauthPathResponse.geminiOauthPath);
@@ -7935,7 +8287,7 @@ var runConfig = async () => {
7935
8287
  codexAuthJsonB64 = existingEnv.CODEX_AUTH_JSON_B64;
7936
8288
  }
7937
8289
  }
7938
- const codexModel = codingAgent === "CX" ? coalesceValue(codexModelResponse.codexModel, existingEnv.CODEX_MODEL) : undefined;
8290
+ const codexModel = codingAgent === "CX" ? coalesceValue(codexModelResponse.codexModel, existingEnv.CODEX_MODEL ?? "gpt-5.3-codex") : undefined;
7939
8291
  const githubToken = repoSource === "github" ? coalesceValue(githubTokenResponse.githubToken, existingEnv.GITHUB_TOKEN) : existingEnv.GITHUB_TOKEN;
7940
8292
  const repoUrl = repoSource === "github" ? coalesceValue(repoResponse.repoUrl, existingEnv.GITHUB_REPO_URL) : existingEnv.GITHUB_REPO_URL;
7941
8293
  const envProjectPathValue = coalesceValue(envProjectPathResponse.envProjectPath, existingEnv.WILE_ENV_PROJECT_PATH ?? ".wile/.env.project") ?? ".wile/.env.project";
@@ -7956,17 +8308,14 @@ var runConfig = async () => {
7956
8308
  setIfDefined(envOut, "CC_ANTHROPIC_API_KEY", authValue);
7957
8309
  }
7958
8310
  } else if (codingAgent === "OC") {
7959
- setIfDefined(envOut, "OC_PROVIDER", ocProvider ?? "native");
7960
- setIfDefined(envOut, "OC_MODEL", ocModel ?? "opencode/grok-code");
7961
- if (ocProvider === "openrouter") {
7962
- setIfDefined(envOut, "OC_OPENROUTER_API_KEY", ocKey);
7963
- }
8311
+ setIfDefined(envOut, "OC_MODEL", ocModel ?? "opencode/kimi-k2.5-free");
7964
8312
  } else if (codingAgent === "GC") {
7965
8313
  if (geminiAuthMethod === "apiKey") {
7966
8314
  setIfDefined(envOut, "GEMINI_API_KEY", geminiApiKey);
7967
8315
  } else {
7968
8316
  setIfDefined(envOut, "GEMINI_OAUTH_CREDS_B64", geminiOauthCredsB64);
7969
8317
  }
8318
+ setIfDefined(envOut, "GEMINI_MODEL", geminiModel);
7970
8319
  } else if (codingAgent === "CX") {
7971
8320
  if (codexAuthMethod === "apiKey") {
7972
8321
  setIfDefined(envOut, "CODEX_API_KEY", codexApiKey);
@@ -7975,7 +8324,7 @@ var runConfig = async () => {
7975
8324
  }
7976
8325
  setIfDefined(envOut, "CODEX_MODEL", codexModel);
7977
8326
  }
7978
- await writeFile(envPath, toEnvString(envOut));
8327
+ await writeFile(envPath, toEnvString(envOut, extraEnv));
7979
8328
  await ensureGitignore(gitignorePath);
7980
8329
  const envProjectTarget = envProjectPathValue === ".wile/.env.project" ? envProjectPath : envProjectPathValue;
7981
8330
  const envProjectResolved = envProjectTarget.startsWith("/") ? envProjectTarget : join(cwd, envProjectTarget);
@@ -8046,6 +8395,7 @@ Wile config complete.`);
8046
8395
  if (!hadPreflight) {
8047
8396
  console.log("Created .wile/preflight.md for preflight checks (optional).");
8048
8397
  }
8398
+ return true;
8049
8399
  };
8050
8400
 
8051
8401
  // src/commands/compact.ts
@@ -8100,7 +8450,7 @@ var readWileConfig = (options = {}) => {
8100
8450
  if (validate) {
8101
8451
  ensureRequired(existsSync2(paths.envPath), "Missing .wile/secrets/.env. Run 'bunx wile config' first.");
8102
8452
  }
8103
- const env = parseEnvFile(paths.envPath);
8453
+ const { known: env } = splitEnv(parseEnvFile(paths.envPath));
8104
8454
  const envProjectPath = resolveEnvProjectPath(options.cwd ?? process.cwd(), env.WILE_ENV_PROJECT_PATH);
8105
8455
  const envProject = parseEnvFile(envProjectPath);
8106
8456
  const repoSource = env.WILE_REPO_SOURCE || "github";
@@ -8115,10 +8465,6 @@ var readWileConfig = (options = {}) => {
8115
8465
  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
8466
  }
8117
8467
  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
8468
  ensureRequired(Boolean(env.OC_MODEL), "OC_MODEL is required in .wile/secrets/.env for OpenCode.");
8123
8469
  }
8124
8470
  if (env.CODING_AGENT === "GC") {
@@ -8143,11 +8489,10 @@ var readWileConfig = (options = {}) => {
8143
8489
  maxIterations: env.WILE_MAX_ITERATIONS,
8144
8490
  ccClaudeCodeOauthToken: env.CC_CLAUDE_CODE_OAUTH_TOKEN,
8145
8491
  ccAnthropicApiKey: env.CC_ANTHROPIC_API_KEY,
8146
- ocProvider: env.OC_PROVIDER ?? "native",
8147
- ocOpenrouterApiKey: env.OC_OPENROUTER_API_KEY,
8148
8492
  ocModel: env.OC_MODEL,
8149
8493
  geminiOauthCredsB64: env.GEMINI_OAUTH_CREDS_B64,
8150
8494
  geminiApiKey: env.GEMINI_API_KEY,
8495
+ geminiModel: env.GEMINI_MODEL,
8151
8496
  codexAuthJsonB64: env.CODEX_AUTH_JSON_B64,
8152
8497
  codexAuthJsonPath: env.CODEX_AUTH_JSON_PATH,
8153
8498
  codexApiKey: env.CODEX_API_KEY ?? env.OPENAI_API_KEY,
@@ -8670,8 +9015,11 @@ var packageJsonPath = resolve4(new URL("../package.json", import.meta.url).pathn
8670
9015
  var packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
8671
9016
  var program2 = new Command;
8672
9017
  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();
9018
+ 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) => {
9019
+ const ok = await runConfig({ nonInteractive: options.nonInteractive });
9020
+ if (!ok) {
9021
+ process.exitCode = 1;
9022
+ }
8675
9023
  });
8676
9024
  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
9025
  await runWile(options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wile",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Autonomous AI coding agent that ships features while you sleep",
5
5
  "type": "module",
6
6
  "bin": {