wile 0.4.18 → 0.4.20

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
@@ -19,7 +19,7 @@ bunx wile config
19
19
  This creates:
20
20
 
21
21
  - `.wile/secrets/.env` for required credentials
22
- - `.wile/secrets/.env.project` for env vars forwarded into the container
22
+ - `.wile/.env.project` for env vars forwarded into the container (or set `WILE_ENV_PROJECT_PATH`)
23
23
  - `.wile/.gitignore` to ignore `secrets/`, `screenshots/`, and `logs/`
24
24
  - `.wile/prd.json` (empty) and `.wile/prd.json.example`
25
25
 
@@ -10,6 +10,11 @@ echo "════════════════════════
10
10
  echo " 🌵 WILE - Container Startup"
11
11
  echo "══════════════════════════════════════════════════════"
12
12
 
13
+ if command -v sysctl >/dev/null 2>&1; then
14
+ sysctl -w net.ipv6.conf.all.disable_ipv6=0 >/dev/null 2>&1 || true
15
+ sysctl -w net.ipv6.conf.default.disable_ipv6=0 >/dev/null 2>&1 || true
16
+ fi
17
+
13
18
  if [ "${WILE_TEST:-}" = "true" ]; then
14
19
  TEST_REPO="${WILE_TEST_REPO_PATH:-/home/wile/workspace/repo}"
15
20
 
@@ -38,6 +43,10 @@ const prd = JSON.parse(fs.readFileSync(prdPath, 'utf8'));
38
43
  const stories = Array.isArray(prd.userStories) ? prd.userStories : [];
39
44
  const pending = stories.filter((story) => story.passes === false);
40
45
 
46
+ if (process.env.TEST_FORWARD) {
47
+ console.log(`Forwarded env: ${process.env.TEST_FORWARD}`);
48
+ }
49
+
41
50
  if (pending.length === 0) {
42
51
  console.log('No pending stories to complete in test mode.');
43
52
  process.exit(0);
@@ -10,6 +10,34 @@ COUNT=$(cat "$COUNT_FILE")
10
10
  NEXT_COUNT=$((COUNT + 1))
11
11
  echo "$NEXT_COUNT" > "$COUNT_FILE"
12
12
 
13
+ if [ "${WILE_MOCK_MODE:-}" = "preflight_fail" ]; then
14
+ PROGRESS_PATH=".wile/progress.txt"
15
+ if [ -f "$PROGRESS_PATH" ]; then
16
+ :
17
+ else
18
+ printf '%s\n' "# Wile Progress Log" "" "## Codebase Patterns" "" "---" > "$PROGRESS_PATH"
19
+ fi
20
+
21
+ DATE=$(date +%Y-%m-%d)
22
+ cat >> "$PROGRESS_PATH" <<EOF
23
+
24
+ ---
25
+
26
+ ## ${DATE} - PREFLIGHT FAILED
27
+
28
+ **Checks run:**
29
+ - Mocked preflight check
30
+
31
+ **Failures:**
32
+ - Mocked preflight failure (WILE_MOCK_MODE=preflight_fail)
33
+ EOF
34
+
35
+ cat <<'JSON'
36
+ {"type":"assistant","message":{"content":[{"type":"text","text":"<promise>PREFLIGHT_FAILED</promise>\n"}]}}
37
+ JSON
38
+ exit 0
39
+ fi
40
+
13
41
  if [ "$COUNT" -eq 0 ]; then
14
42
  cat <<'JSON'
15
43
  {"type":"assistant","message":{"content":[{"type":"text","text":"ANSWER: 2\n"}]}}
@@ -0,0 +1,56 @@
1
+ # Wile Preflight (Iteration 0)
2
+
3
+ You are running the preflight phase for a Wile autonomous coding session.
4
+
5
+ ## Tasks
6
+
7
+ 1. Verify `.wile/prd.json` exists and is valid JSON:
8
+
9
+ ```bash
10
+ cat .wile/prd.json
11
+ ```
12
+
13
+ 2. If `.wile/preflight.md` does **not** exist, do nothing else. Do not modify files and do not print extra output.
14
+
15
+ 3. If `.wile/preflight.md` exists, read it and follow the checks exactly, in order. Run any commands listed in code blocks. If it describes a check without a command, perform the check and note the result.
16
+
17
+ 4. If **any** check fails or cannot be completed:
18
+ - Append a new entry to `.wile/progress.txt` describing what failed and why, using this format:
19
+
20
+ ```markdown
21
+ ---
22
+
23
+ ## [DATE] - PREFLIGHT FAILED
24
+
25
+ **Checks run:**
26
+ - ...
27
+
28
+ **Failures:**
29
+ - ...
30
+ ```
31
+
32
+ - If GitHub is configured (`WILE_REPO_SOURCE=github` or `GITHUB_REPO_URL` is set), commit and push the progress update:
33
+
34
+ ```bash
35
+ git add .wile/progress.txt
36
+ git commit -m "chore: preflight failed"
37
+ git push
38
+ ```
39
+ - Respond with exactly:
40
+
41
+ ```
42
+ <promise>PREFLIGHT_FAILED</promise>
43
+ ```
44
+
45
+ The entire response must be exactly that single line. No other text before or after. No extra lines. No markdown. No backticks. No code blocks.
46
+
47
+ 5. If all checks pass, respond with exactly:
48
+ ```
49
+ <promise>COMPLETE</promise>
50
+ ```
51
+ The entire response must be exactly that single line. No other text before or after. No extra lines. No markdown. No backticks. No code blocks.
52
+
53
+ ## Notes
54
+
55
+ - Preflight may have side effects if the checks require them.
56
+ - Do not change any files unless a failure must be recorded in `.wile/progress.txt`.
@@ -35,8 +35,8 @@ Pick the highest priority story where `passes: false` and implement it completel
35
35
  Run the project's tests and type checking:
36
36
  ```bash
37
37
  # Try common commands (adapt to the project)
38
- npm run typecheck || npm run tsc || npx tsc --noEmit || true
39
- npm test || npm run test || true
38
+ npm run typecheck || npm run tsc || npx tsc --noEmit
39
+ npm test || npm run test
40
40
  ```
41
41
 
42
42
  If tests or typecheck fail, fix the issues before proceeding.
@@ -58,6 +58,9 @@ Set `passes: true` for the completed story:
58
58
  **Implemented:**
59
59
  - What was done
60
60
 
61
+ **Verification:**
62
+ - Command(s) run and pass/fail status
63
+
61
64
  **Files changed:**
62
65
  - file1.ts
63
66
  - file2.ts
@@ -104,6 +107,8 @@ The entire response must be exactly that single line. No other text before or af
104
107
  9. **Integration tests must validate real system behavior, not just the harness**
105
108
  10. **If you discover reusable, module-specific guidance, add it to the nearest AGENTS.md**
106
109
  Note: Never update .wile/AGENTS.md.
110
+ 11. **Definition of done** - Do not set `passes: true` unless each acceptance criterion has a concrete verification and all verifications passed. If any verification fails or can’t run, leave `passes: false` and explain why in `.wile/progress.txt`.
111
+ 12. **No verification section means no pass** - If the progress entry lacks a **Verification** section, do not mark `passes: true`.
107
112
 
108
113
  ## Common Patterns
109
114
 
@@ -0,0 +1,52 @@
1
+ #!/bin/sh
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR=$(cd "$(dirname "$0")/../../.." && pwd)
5
+ AGENT_DIR="$ROOT_DIR/packages/agent"
6
+
7
+ docker build -t wile-agent:local "$AGENT_DIR" >/dev/null
8
+
9
+ TMP_DIR=$(mktemp -d /tmp/wile-env-project-XXXXXX)
10
+ cleanup() {
11
+ rm -rf "$TMP_DIR"
12
+ }
13
+ trap cleanup EXIT INT TERM
14
+
15
+ mkdir -p "$TMP_DIR/.wile/secrets"
16
+ cat > "$TMP_DIR/.wile/prd.json" <<'JSON'
17
+ {
18
+ "userStories": [
19
+ {
20
+ "id": "US-TEST-ENV-001",
21
+ "title": "Env project forward test",
22
+ "acceptanceCriteria": ["n/a"],
23
+ "priority": 1,
24
+ "passes": false
25
+ }
26
+ ]
27
+ }
28
+ JSON
29
+
30
+ printf "secrets/\nscreenshots/\nlogs/\n" > "$TMP_DIR/.wile/.gitignore"
31
+
32
+ printf "TEST_FORWARD=ok\n" > "$TMP_DIR/.env.custom"
33
+
34
+ cat > "$TMP_DIR/.wile/secrets/.env" <<'ENV'
35
+ CODING_AGENT=CC
36
+ WILE_ENV_PROJECT_PATH=.env.custom
37
+ ENV
38
+
39
+ export WILE_AGENT_DIR="$AGENT_DIR"
40
+
41
+ cd "$TMP_DIR"
42
+ node "$ROOT_DIR/packages/cli/dist/cli.js" run --test --max-iterations 1 >/dev/null 2>&1
43
+
44
+ LOG_FILE=$(ls "$TMP_DIR/.wile/logs"/run-*.log | head -n 1)
45
+ if [ -z "$LOG_FILE" ]; then
46
+ echo "error: expected run log file" >&2
47
+ exit 1
48
+ fi
49
+
50
+ grep -q "Forwarded env: ok" "$LOG_FILE"
51
+
52
+ echo "test-env-project-docker: ok"
@@ -0,0 +1,134 @@
1
+ #!/bin/sh
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR=$(cd "$(dirname "$0")/../../.." && pwd)
5
+ AGENT_DIR="$ROOT_DIR/packages/agent"
6
+
7
+ docker build -t wile-agent:local "$AGENT_DIR" >/dev/null
8
+
9
+ run_failure_case() {
10
+ TMP_DIR=$(mktemp -d /tmp/wile-preflight-fail-XXXXXX)
11
+ cleanup() {
12
+ rm -rf "$TMP_DIR"
13
+ }
14
+ trap cleanup EXIT INT TERM
15
+
16
+ mkdir -p "$TMP_DIR/.wile"
17
+ cat > "$TMP_DIR/.wile/prd.json" <<'JSON'
18
+ {
19
+ "userStories": [
20
+ {
21
+ "id": "US-TEST-001",
22
+ "title": "Preflight fail test",
23
+ "acceptanceCriteria": ["n/a"],
24
+ "priority": 1,
25
+ "passes": false
26
+ }
27
+ ]
28
+ }
29
+ JSON
30
+
31
+ cat > "$TMP_DIR/.wile/progress.txt" <<'TXT'
32
+ # Wile Progress Log
33
+
34
+ ## Codebase Patterns
35
+
36
+ ---
37
+ TXT
38
+
39
+ cat > "$TMP_DIR/.wile/preflight.md" <<'MD'
40
+ <!--
41
+ Use bullet points for preflight checks, e.g.
42
+ - Confirm SUPABASE_DB_URL is set.
43
+ - Run `supabase db reset --db-url "$SUPABASE_DB_URL"`.
44
+ -->
45
+ MD
46
+
47
+ OUTPUT_FILE="$TMP_DIR/output.txt"
48
+
49
+ set +e
50
+ docker run --rm \
51
+ -e CODING_AGENT=CC \
52
+ -e CC_ANTHROPIC_API_KEY=dummy-key \
53
+ -e WILE_REPO_SOURCE=local \
54
+ -e WILE_LOCAL_REPO_PATH=/home/wile/workspace/repo \
55
+ -e MAX_ITERATIONS=1 \
56
+ -e WILE_MOCK_CLAUDE=true \
57
+ -e WILE_MOCK_MODE=preflight_fail \
58
+ -v "$TMP_DIR:/home/wile/workspace/repo" \
59
+ wile-agent:local > "$OUTPUT_FILE" 2>&1
60
+ EXIT_CODE=$?
61
+ set -e
62
+
63
+ if [ "$EXIT_CODE" -eq 0 ]; then
64
+ echo "error: expected non-zero exit code for preflight failure" >&2
65
+ exit 1
66
+ fi
67
+
68
+ grep -q "PREFLIGHT FAILED - Cannot continue" "$OUTPUT_FILE"
69
+ grep -q "PREFLIGHT FAILED" "$TMP_DIR/.wile/progress.txt"
70
+
71
+ rm -rf "$TMP_DIR"
72
+ trap - EXIT INT TERM
73
+ }
74
+
75
+ run_success_case() {
76
+ TMP_DIR=$(mktemp -d /tmp/wile-preflight-success-XXXXXX)
77
+ cleanup() {
78
+ rm -rf "$TMP_DIR"
79
+ }
80
+ trap cleanup EXIT INT TERM
81
+
82
+ mkdir -p "$TMP_DIR/.wile"
83
+ cat > "$TMP_DIR/.wile/prd.json" <<'JSON'
84
+ {
85
+ "userStories": [
86
+ {
87
+ "id": "US-TEST-002",
88
+ "title": "Preflight success test",
89
+ "acceptanceCriteria": ["n/a"],
90
+ "priority": 1,
91
+ "passes": false
92
+ }
93
+ ]
94
+ }
95
+ JSON
96
+
97
+ cat > "$TMP_DIR/.wile/preflight.md" <<'MD'
98
+ <!--
99
+ Use bullet points for preflight checks, e.g.
100
+ - Confirm SUPABASE_DB_URL is set.
101
+ - Run `supabase db reset --db-url "$SUPABASE_DB_URL"`.
102
+ -->
103
+ MD
104
+
105
+ OUTPUT_FILE="$TMP_DIR/output.txt"
106
+
107
+ set +e
108
+ docker run --rm \
109
+ -e CODING_AGENT=CC \
110
+ -e CC_ANTHROPIC_API_KEY=dummy-key \
111
+ -e WILE_REPO_SOURCE=local \
112
+ -e WILE_LOCAL_REPO_PATH=/home/wile/workspace/repo \
113
+ -e MAX_ITERATIONS=1 \
114
+ -e WILE_MOCK_CLAUDE=true \
115
+ -v "$TMP_DIR:/home/wile/workspace/repo" \
116
+ wile-agent:local > "$OUTPUT_FILE" 2>&1
117
+ EXIT_CODE=$?
118
+ set -e
119
+
120
+ if [ "$EXIT_CODE" -ne 0 ]; then
121
+ echo "error: expected zero exit code for preflight success" >&2
122
+ exit 1
123
+ fi
124
+
125
+ grep -q "Preflight complete. Starting main loop..." "$OUTPUT_FILE"
126
+
127
+ rm -rf "$TMP_DIR"
128
+ trap - EXIT INT TERM
129
+ }
130
+
131
+ run_failure_case
132
+ run_success_case
133
+
134
+ echo "test-preflight-docker: ok"
@@ -0,0 +1,81 @@
1
+ #!/bin/bash
2
+ #
3
+ # Wile - Preflight check (single run)
4
+ #
5
+
6
+ set -e
7
+
8
+ CODING_AGENT=${CODING_AGENT:-CC}
9
+ CLAUDE_MODEL=${CC_CLAUDE_MODEL:-sonnet}
10
+ OC_MODEL=${OC_MODEL:-glm-4.7}
11
+ if [[ "$OC_MODEL" != */* ]]; then
12
+ OC_MODEL="z-ai/$OC_MODEL"
13
+ fi
14
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ PREFLIGHT_PROMPT_FILE="$SCRIPT_DIR/prompt-preflight.md"
16
+ TEE_TARGET="${WILE_TEE_TARGET:-/dev/stderr}"
17
+ if ! ( : > "$TEE_TARGET" ) 2>/dev/null; then
18
+ TEE_TARGET="/dev/null"
19
+ fi
20
+
21
+ echo "══════════════════════════════════════════════════════"
22
+ echo " 🌵 WILE - Preflight"
23
+ echo "══════════════════════════════════════════════════════"
24
+ echo " Agent: $CODING_AGENT"
25
+ if [ "$CODING_AGENT" = "OC" ]; then
26
+ echo " Model: $OC_MODEL"
27
+ else
28
+ echo " Model: $CLAUDE_MODEL"
29
+ fi
30
+ echo " Prompt file: $PREFLIGHT_PROMPT_FILE"
31
+ echo "══════════════════════════════════════════════════════"
32
+ echo ""
33
+
34
+ if [ ! -f "$PREFLIGHT_PROMPT_FILE" ]; then
35
+ echo "ERROR: Preflight prompt file not found: $PREFLIGHT_PROMPT_FILE"
36
+ exit 1
37
+ fi
38
+
39
+ run_claude() {
40
+ local prompt_path="$1"
41
+ cat "$prompt_path" \
42
+ | claude --model "$CLAUDE_MODEL" --print --output-format stream-json --verbose --dangerously-skip-permissions \
43
+ | node "$SCRIPT_DIR/claude-stream.js"
44
+ }
45
+
46
+ run_opencode() {
47
+ local prompt_path="$1"
48
+ cat "$prompt_path" \
49
+ | opencode run --format json --model "openrouter/$OC_MODEL" \
50
+ | node "$SCRIPT_DIR/opencode-stream.js"
51
+ }
52
+
53
+ run_agent() {
54
+ local prompt_path="$1"
55
+ if [ "$CODING_AGENT" = "OC" ]; then
56
+ run_opencode "$prompt_path"
57
+ else
58
+ run_claude "$prompt_path"
59
+ fi
60
+ }
61
+
62
+ OUTPUT=$(run_agent "$PREFLIGHT_PROMPT_FILE" | tee "$TEE_TARGET") || true
63
+ CLEAN_OUTPUT=$(printf '%s' "$OUTPUT" | tr -d '\r' | sed -e 's/[[:space:]]*$//')
64
+ if printf '%s\n' "$CLEAN_OUTPUT" | grep -q -E '^[[:space:]]*<promise>PREFLIGHT_FAILED</promise>[[:space:]]*$'; then
65
+ if printf '%s' "$CLEAN_OUTPUT" | grep -F '```' >/dev/null 2>&1; then
66
+ :
67
+ elif printf '%s' "$CLEAN_OUTPUT" | grep -F '`<promise>PREFLIGHT_FAILED</promise>`' >/dev/null 2>&1; then
68
+ :
69
+ else
70
+ echo ""
71
+ echo "══════════════════════════════════════════════════════"
72
+ echo " ❌ PREFLIGHT FAILED"
73
+ echo "══════════════════════════════════════════════════════"
74
+ exit 2
75
+ fi
76
+ fi
77
+
78
+ echo ""
79
+ echo "══════════════════════════════════════════════════════"
80
+ echo " ✅ PREFLIGHT COMPLETE"
81
+ echo "══════════════════════════════════════════════════════"
@@ -15,6 +15,7 @@ if [[ "$OC_MODEL" != */* ]]; then
15
15
  fi
16
16
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17
17
  PROMPT_FILE="$SCRIPT_DIR/prompt.md"
18
+ PREFLIGHT_PROMPT_FILE="$SCRIPT_DIR/prompt-preflight.md"
18
19
  SETUP_PROMPT_FILE="$SCRIPT_DIR/prompt-setup.md"
19
20
  ADDITIONAL_PROMPT_FILE="${WILE_ADDITIONAL_INSTRUCTIONS:-}"
20
21
  TEE_TARGET="${WILE_TEE_TARGET:-/dev/stderr}"
@@ -77,15 +78,37 @@ run_agent() {
77
78
  }
78
79
 
79
80
  # ════════════════════════════════════════════════════════════
80
- # ITERATION 0: Setup
81
+ # ITERATION 0: Preflight / Setup
81
82
  # ════════════════════════════════════════════════════════════
82
83
  echo ""
83
84
  echo "══════════════════════════════════════════════════════"
84
- echo " Iteration 0 - Setup"
85
+ echo " Iteration 0 - Preflight"
85
86
  echo "══════════════════════════════════════════════════════"
86
87
  echo ""
87
88
 
88
- if [ -f "$SETUP_PROMPT_FILE" ]; then
89
+ if [ -f "$PREFLIGHT_PROMPT_FILE" ]; then
90
+ OUTPUT=$(run_agent "$PREFLIGHT_PROMPT_FILE" | tee "$TEE_TARGET") || true
91
+
92
+ # Check if preflight failed critically (tag must be on its own line; reject backticks/code fences)
93
+ CLEAN_OUTPUT=$(printf '%s' "$OUTPUT" | tr -d '\r' | sed -e 's/[[:space:]]*$//')
94
+ if printf '%s\n' "$CLEAN_OUTPUT" | grep -q -E '^[[:space:]]*<promise>PREFLIGHT_FAILED</promise>[[:space:]]*$'; then
95
+ if printf '%s' "$CLEAN_OUTPUT" | grep -F '```' >/dev/null 2>&1; then
96
+ :
97
+ elif printf '%s' "$CLEAN_OUTPUT" | grep -F '`<promise>PREFLIGHT_FAILED</promise>`' >/dev/null 2>&1; then
98
+ :
99
+ else
100
+ echo ""
101
+ echo "══════════════════════════════════════════════════════"
102
+ echo " ❌ PREFLIGHT FAILED - Cannot continue"
103
+ echo "══════════════════════════════════════════════════════"
104
+ exit 2
105
+ fi
106
+ fi
107
+
108
+ echo ""
109
+ echo "Preflight complete. Starting main loop..."
110
+ sleep 2
111
+ elif [ -f "$SETUP_PROMPT_FILE" ]; then
89
112
  OUTPUT=$(run_agent "$SETUP_PROMPT_FILE" | tee "$TEE_TARGET") || true
90
113
 
91
114
  # Check if setup failed critically
package/dist/cli.js CHANGED
@@ -7442,7 +7442,7 @@ var {
7442
7442
 
7443
7443
  // src/cli.ts
7444
7444
  import { readFileSync as readFileSync3 } from "node:fs";
7445
- import { resolve as resolve2 } from "node:path";
7445
+ import { resolve as resolve3 } from "node:path";
7446
7446
 
7447
7447
  // src/commands/config.ts
7448
7448
  var import_prompts = __toESM(require_prompts3(), 1);
@@ -7555,7 +7555,7 @@ var readEnvFile = async (path) => {
7555
7555
  return import_dotenv.default.parse(contents);
7556
7556
  };
7557
7557
  var ensureGitignore = async (path) => {
7558
- const entries = ["secrets/", "screenshots/", "logs/"];
7558
+ const entries = ["secrets/", "screenshots/", "logs/", ".env.project"];
7559
7559
  if (!existsSync(path)) {
7560
7560
  await writeFile(path, entries.join(`
7561
7561
  `) + `
@@ -7617,11 +7617,12 @@ var runConfig = async () => {
7617
7617
  const wileDir = join(cwd, ".wile");
7618
7618
  const secretsDir = join(wileDir, "secrets");
7619
7619
  const envPath = join(secretsDir, ".env");
7620
- const envProjectPath = join(secretsDir, ".env.project");
7620
+ const envProjectPath = join(wileDir, ".env.project");
7621
7621
  const gitignorePath = join(wileDir, ".gitignore");
7622
7622
  const prdPath = join(wileDir, "prd.json");
7623
7623
  const prdExamplePath = join(wileDir, "prd.json.example");
7624
7624
  const additionalInstructionsPath = join(wileDir, "additional-instructions.md");
7625
+ const preflightPath = join(wileDir, "preflight.md");
7625
7626
  const agentsPath = join(wileDir, "AGENTS.md");
7626
7627
  await mkdir(secretsDir, { recursive: true });
7627
7628
  const existingEnv = await readEnvFile(envPath);
@@ -7723,9 +7724,15 @@ var runConfig = async () => {
7723
7724
  const branchResponse = await prompt({
7724
7725
  type: "text",
7725
7726
  name: "branchName",
7726
- message: "Default branch name",
7727
+ message: "Default GitHub branch name",
7727
7728
  initial: existingEnv.BRANCH_NAME ?? "main"
7728
7729
  });
7730
+ const envProjectPathResponse = await prompt({
7731
+ type: "text",
7732
+ name: "envProjectPath",
7733
+ message: "Project env file path to forward into the container",
7734
+ initial: existingEnv.WILE_ENV_PROJECT_PATH ?? ".wile/.env.project"
7735
+ });
7729
7736
  const iterationsResponse = await prompt({
7730
7737
  type: "number",
7731
7738
  name: "maxIterations",
@@ -7740,10 +7747,12 @@ var runConfig = async () => {
7740
7747
  const ocModel = codingAgent === "OC" ? coalesceValue(ocModelResponse.ocModel, existingEnv.OC_MODEL ?? "glm-4.7") : undefined;
7741
7748
  const githubToken = repoSource === "github" ? coalesceValue(githubTokenResponse.githubToken, existingEnv.GITHUB_TOKEN) : existingEnv.GITHUB_TOKEN;
7742
7749
  const repoUrl = repoSource === "github" ? coalesceValue(repoResponse.repoUrl, existingEnv.GITHUB_REPO_URL) : existingEnv.GITHUB_REPO_URL;
7750
+ const envProjectPathValue = coalesceValue(envProjectPathResponse.envProjectPath, existingEnv.WILE_ENV_PROJECT_PATH ?? ".wile/.env.project") ?? ".wile/.env.project";
7743
7751
  const branchName = coalesceValue(branchResponse.branchName, existingEnv.BRANCH_NAME ?? "main");
7744
7752
  const envLines = [
7745
7753
  `CODING_AGENT=${codingAgent}`,
7746
7754
  `WILE_REPO_SOURCE=${repoSource}`,
7755
+ `WILE_ENV_PROJECT_PATH=${envProjectPathValue}`,
7747
7756
  `GITHUB_TOKEN=${githubToken ?? ""}`,
7748
7757
  `GITHUB_REPO_URL=${repoUrl ?? ""}`,
7749
7758
  `BRANCH_NAME=${branchName ?? "main"}`,
@@ -7764,8 +7773,12 @@ var runConfig = async () => {
7764
7773
  `) + `
7765
7774
  `);
7766
7775
  await ensureGitignore(gitignorePath);
7767
- await writeIfMissing(envProjectPath, `# Add env vars here to forward into the container
7776
+ const envProjectTarget = envProjectPathValue === ".wile/.env.project" ? envProjectPath : envProjectPathValue;
7777
+ const envProjectResolved = envProjectTarget.startsWith("/") ? envProjectTarget : join(cwd, envProjectTarget);
7778
+ if (envProjectResolved.startsWith(wileDir)) {
7779
+ await writeIfMissing(envProjectResolved, `# Add env vars here to forward into the container
7768
7780
  `);
7781
+ }
7769
7782
  if (!existsSync(prdPath)) {
7770
7783
  const prdContents = JSON.stringify({ userStories: [] }, null, 2);
7771
7784
  await writeFile(prdPath, prdContents + `
@@ -7774,11 +7787,18 @@ var runConfig = async () => {
7774
7787
  await writeIfMissing(prdExamplePath, JSON.stringify(prdExample, null, 2) + `
7775
7788
  `);
7776
7789
  const hadAdditionalInstructions = existsSync(additionalInstructionsPath);
7790
+ const hadPreflight = existsSync(preflightPath);
7777
7791
  await writeIfMissing(additionalInstructionsPath, `<!--
7778
7792
  Use bullet points for additional instructions, e.g.
7779
7793
  - You may run \`supabase db reset --db-url "$SUPABASE_DB_URL"\` when needed.
7780
7794
  - Do not ask for permission before running it.
7781
7795
  -->
7796
+ `);
7797
+ await writeIfMissing(preflightPath, `<!--
7798
+ Use bullet points for preflight checks, e.g.
7799
+ - Confirm SUPABASE_DB_URL is set.
7800
+ - Run \`supabase db reset --db-url "$SUPABASE_DB_URL"\`.
7801
+ -->
7782
7802
  `);
7783
7803
  await writeIfMissing(agentsPath, [
7784
7804
  "# PRD authoring guidance for Wile",
@@ -7803,7 +7823,7 @@ Use bullet points for additional instructions, e.g.
7803
7823
  "",
7804
7824
  "Environment notes:",
7805
7825
  "- Playwright (Chromium) is available in the agent container for UI checks.",
7806
- "- Project env vars can be passed via `.wile/secrets/.env.project`.",
7826
+ "- Project env vars can be passed via `.wile/.env.project` (or override with WILE_ENV_PROJECT_PATH).",
7807
7827
  "- Optional extra guidance can be added in `.wile/additional-instructions.md`.",
7808
7828
  "- The container has outbound internet access by default.",
7809
7829
  ""
@@ -7811,22 +7831,25 @@ Use bullet points for additional instructions, e.g.
7811
7831
  `));
7812
7832
  console.log(`
7813
7833
  Wile config complete.`);
7814
- console.log("Add project env vars to .wile/secrets/.env.project when needed.");
7834
+ console.log("Add project env vars to .wile/.env.project when needed.");
7815
7835
  if (!hadAdditionalInstructions) {
7816
7836
  console.log("Created .wile/additional-instructions.md for extra agent guidance (optional).");
7817
7837
  }
7838
+ if (!hadPreflight) {
7839
+ console.log("Created .wile/preflight.md for preflight checks (optional).");
7840
+ }
7818
7841
  };
7819
7842
 
7820
7843
  // src/commands/run.ts
7821
7844
  import { existsSync as existsSync3, readFileSync as readFileSync2, mkdirSync, createWriteStream, writeFileSync } from "node:fs";
7822
7845
  import { spawn, spawnSync } from "node:child_process";
7823
- import { resolve, join as join3, dirname } from "node:path";
7846
+ import { resolve as resolve2, join as join3, dirname } from "node:path";
7824
7847
  import { fileURLToPath } from "node:url";
7825
7848
 
7826
7849
  // src/lib/config.ts
7827
7850
  var import_dotenv2 = __toESM(require_main(), 1);
7828
7851
  import { existsSync as existsSync2, readFileSync } from "node:fs";
7829
- import { join as join2 } from "node:path";
7852
+ import { join as join2, resolve, isAbsolute } from "node:path";
7830
7853
  var getWilePaths = (cwd = process.cwd()) => {
7831
7854
  const wileDir = join2(cwd, ".wile");
7832
7855
  const secretsDir = join2(wileDir, "secrets");
@@ -7834,11 +7857,25 @@ var getWilePaths = (cwd = process.cwd()) => {
7834
7857
  wileDir,
7835
7858
  secretsDir,
7836
7859
  envPath: join2(secretsDir, ".env"),
7837
- envProjectPath: join2(secretsDir, ".env.project"),
7860
+ envProjectPath: join2(wileDir, ".env.project"),
7838
7861
  gitignorePath: join2(wileDir, ".gitignore"),
7839
7862
  prdPath: join2(wileDir, "prd.json")
7840
7863
  };
7841
7864
  };
7865
+ var resolveEnvProjectPath = (cwd, configured) => {
7866
+ const defaultPath = join2(cwd, ".wile", ".env.project");
7867
+ const legacyPath = join2(cwd, ".wile", "secrets", ".env.project");
7868
+ if (configured && configured.trim().length > 0) {
7869
+ return isAbsolute(configured) ? configured : resolve(cwd, configured);
7870
+ }
7871
+ if (existsSync2(defaultPath)) {
7872
+ return defaultPath;
7873
+ }
7874
+ if (existsSync2(legacyPath)) {
7875
+ return legacyPath;
7876
+ }
7877
+ return defaultPath;
7878
+ };
7842
7879
  var parseEnvFile = (path) => {
7843
7880
  if (!existsSync2(path)) {
7844
7881
  return {};
@@ -7858,7 +7895,8 @@ var readWileConfig = (options = {}) => {
7858
7895
  ensureRequired(existsSync2(paths.envPath), "Missing .wile/secrets/.env. Run 'bunx wile config' first.");
7859
7896
  }
7860
7897
  const env = parseEnvFile(paths.envPath);
7861
- const envProject = parseEnvFile(paths.envProjectPath);
7898
+ const envProjectPath = resolveEnvProjectPath(options.cwd ?? process.cwd(), env.WILE_ENV_PROJECT_PATH);
7899
+ const envProject = parseEnvFile(envProjectPath);
7862
7900
  const repoSource = env.WILE_REPO_SOURCE || "github";
7863
7901
  if (validate) {
7864
7902
  ensureRequired(env.CODING_AGENT === "CC" || env.CODING_AGENT === "OC", "CODING_AGENT must be set to CC or OC in .wile/secrets/.env. Run 'bunx wile config'.");
@@ -7876,7 +7914,10 @@ var readWileConfig = (options = {}) => {
7876
7914
  }
7877
7915
  }
7878
7916
  return {
7879
- paths,
7917
+ paths: {
7918
+ ...paths,
7919
+ envProjectPath
7920
+ },
7880
7921
  config: {
7881
7922
  codingAgent: env.CODING_AGENT ?? "CC",
7882
7923
  githubToken: env.GITHUB_TOKEN ?? "",
@@ -7968,7 +8009,7 @@ var resolveAgentDir = () => {
7968
8009
  if (existsSync3(bundledAgentDir)) {
7969
8010
  return bundledAgentDir;
7970
8011
  }
7971
- const cliRoot = resolve(here, "..", "..", "..");
8012
+ const cliRoot = resolve2(here, "..", "..", "..");
7972
8013
  const agentDir = join3(cliRoot, "agent");
7973
8014
  if (!existsSync3(agentDir)) {
7974
8015
  throw new Error("Unable to locate packages/agent. Run from the monorepo.");
@@ -8099,7 +8140,7 @@ var runWile = async (options) => {
8099
8140
  };
8100
8141
 
8101
8142
  // src/cli.ts
8102
- var packageJsonPath = resolve2(new URL("../package.json", import.meta.url).pathname);
8143
+ var packageJsonPath = resolve3(new URL("../package.json", import.meta.url).pathname);
8103
8144
  var packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
8104
8145
  var program2 = new Command;
8105
8146
  program2.name("wile").description("Autonomous AI coding agent that ships features while you sleep").version(packageJson.version);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wile",
3
- "version": "0.4.18",
3
+ "version": "0.4.20",
4
4
  "description": "Autonomous AI coding agent that ships features while you sleep",
5
5
  "type": "module",
6
6
  "bin": {