wile 1.2.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 CHANGED
@@ -39,6 +39,7 @@ This creates:
39
39
  Set `WILE_REPO_SOURCE=local` in `.wile/secrets/.env` to run against the current directory without GitHub.
40
40
  When `WILE_REPO_SOURCE=local`, GitHub credentials are optional.
41
41
  Set `WILE_MAX_ITERATIONS` in `.wile/secrets/.env` to change the default loop limit (default: 25).
42
+ Optionally customize `.wile/Dockerfile` (created by `wile config`) to extend `wile-agent:base`.
42
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.
43
44
 
44
45
  ## Run Wile
@@ -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"
package/dist/cli.js CHANGED
@@ -7774,6 +7774,7 @@ var renderNonInteractiveConfigHelp = () => {
7774
7774
  lines.push("- `branchName` (default `main`)");
7775
7775
  lines.push("- `envProjectPath` (default `.wile/.env.project`)");
7776
7776
  lines.push("- `maxIterations` (default `25`)");
7777
+ lines.push("- Optional custom Dockerfile: edit `.wile/Dockerfile` (created automatically by `wile config`)");
7777
7778
  lines.push("- `ccModel` (`sonnet` | `opus` | `haiku`, default `opus`)");
7778
7779
  lines.push("- `gcModel` (default `gemini-3-pro-preview`)");
7779
7780
  lines.push("- `cxModel` (default `gpt-5.3-codex`)");
@@ -8063,6 +8064,7 @@ var runConfig = async (options = {}) => {
8063
8064
  const additionalInstructionsPath = join(wileDir, "additional-instructions.md");
8064
8065
  const preflightPath = join(wileDir, "preflight.md");
8065
8066
  const agentsPath = join(wileDir, "AGENTS.md");
8067
+ const dockerfilePath = join(wileDir, "Dockerfile");
8066
8068
  await mkdir(secretsDir, { recursive: true });
8067
8069
  if (!nonInteractiveConfig) {
8068
8070
  maybeInject();
@@ -8353,6 +8355,18 @@ Use bullet points for preflight checks, e.g.
8353
8355
  - Run \`supabase db reset --db-url "$SUPABASE_DB_URL"\`.
8354
8356
  -->
8355
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
+ `));
8356
8370
  await writeIfMissing(agentsPath, [
8357
8371
  "# PRD authoring guidance for Wile",
8358
8372
  "",
@@ -8432,6 +8446,13 @@ var resolveEnvProjectPath = (cwd, configured) => {
8432
8446
  }
8433
8447
  return defaultPath;
8434
8448
  };
8449
+ var resolveAgentDockerfilePath = (cwd) => {
8450
+ const defaultPath = join2(cwd, ".wile", "Dockerfile");
8451
+ if (existsSync2(defaultPath)) {
8452
+ return defaultPath;
8453
+ }
8454
+ return;
8455
+ };
8435
8456
  var parseEnvFile = (path) => {
8436
8457
  if (!existsSync2(path)) {
8437
8458
  return {};
@@ -8452,6 +8473,7 @@ var readWileConfig = (options = {}) => {
8452
8473
  }
8453
8474
  const { known: env } = splitEnv(parseEnvFile(paths.envPath));
8454
8475
  const envProjectPath = resolveEnvProjectPath(options.cwd ?? process.cwd(), env.WILE_ENV_PROJECT_PATH);
8476
+ const agentDockerfile = resolveAgentDockerfilePath(options.cwd ?? process.cwd());
8455
8477
  const envProject = parseEnvFile(envProjectPath);
8456
8478
  const repoSource = env.WILE_REPO_SOURCE || "github";
8457
8479
  if (validate) {
@@ -8485,6 +8507,7 @@ var readWileConfig = (options = {}) => {
8485
8507
  githubRepoUrl: env.GITHUB_REPO_URL ?? "",
8486
8508
  branchName: env.BRANCH_NAME ?? "",
8487
8509
  repoSource,
8510
+ agentDockerfile,
8488
8511
  ccClaudeModel: env.CC_CLAUDE_MODEL,
8489
8512
  maxIterations: env.WILE_MAX_ITERATIONS,
8490
8513
  ccClaudeCodeOauthToken: env.CC_CLAUDE_CODE_OAUTH_TOKEN,
@@ -8749,6 +8772,8 @@ import { existsSync as existsSync3, readFileSync as readFileSync3, mkdirSync, cr
8749
8772
  import { spawn, spawnSync } from "node:child_process";
8750
8773
  import { resolve as resolve3, join as join3, dirname } from "node:path";
8751
8774
  import { fileURLToPath } from "node:url";
8775
+ var LOCAL_AGENT_IMAGE = "wile-agent:local";
8776
+ var BASE_AGENT_IMAGE = "wile-agent:base";
8752
8777
  var findAgentDir = (startDir) => {
8753
8778
  let current = startDir;
8754
8779
  while (true) {
@@ -8784,14 +8809,43 @@ var validatePrdLocation = (paths) => {
8784
8809
  throw new Error("Missing .wile/prd.json. Run 'bunx wile config'.");
8785
8810
  }
8786
8811
  };
8787
- var buildAgentImage = (agentDir) => {
8788
- const result = spawnSync("docker", ["build", "-t", "wile-agent:local", agentDir], {
8789
- stdio: "inherit"
8790
- });
8812
+ var runDockerBuild = (args, errorMessage) => {
8813
+ const result = spawnSync("docker", args, { stdio: "inherit" });
8791
8814
  if (result.status !== 0) {
8792
- throw new Error("Docker build failed.");
8815
+ throw new Error(errorMessage);
8793
8816
  }
8794
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;
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.");
8848
+ };
8795
8849
  var resolveAgentDir = () => {
8796
8850
  const override = process.env.WILE_AGENT_DIR;
8797
8851
  if (override && existsSync3(override)) {
@@ -8880,7 +8934,7 @@ var buildDockerArgs = (options, config, paths, cwd, extraEnv = []) => {
8880
8934
  dockerArgs.push("-e", `WILE_ADDITIONAL_INSTRUCTIONS=${additionalInstructionsPath}`);
8881
8935
  dockerArgs.push("-v", `${additionalInstructionsPath}:${additionalInstructionsPath}`);
8882
8936
  }
8883
- dockerArgs.push("wile-agent:local");
8937
+ dockerArgs.push(LOCAL_AGENT_IMAGE);
8884
8938
  return dockerArgs;
8885
8939
  };
8886
8940
  var runWile = async (options) => {
@@ -8903,6 +8957,7 @@ var runWile = async (options) => {
8903
8957
  console.log(`- WILE_AGENT_DIR: ${process.env.WILE_AGENT_DIR ?? "(unset)"}`);
8904
8958
  console.log(`- codingAgent: ${config.codingAgent}`);
8905
8959
  console.log(`- repoSource: ${config.repoSource}`);
8960
+ console.log(`- customDockerfile: ${config.agentDockerfile ?? "(unset)"}`);
8906
8961
  console.log(`- githubRepoUrl: ${config.githubRepoUrl || "(empty)"}`);
8907
8962
  console.log(`- branchName: ${config.branchName || "(empty)"}`);
8908
8963
  }
@@ -8929,7 +8984,10 @@ var runWile = async (options) => {
8929
8984
  }
8930
8985
  const agentDir = resolveAgentDir();
8931
8986
  const resolvedIterations = options.maxIterations || config.maxIterations || "25";
8932
- buildAgentImage(agentDir);
8987
+ buildAgentImage(agentDir, {
8988
+ projectDir: cwd,
8989
+ customDockerfilePath: config.agentDockerfile
8990
+ });
8933
8991
  const dockerArgs = buildDockerArgs({ ...options, maxIterations: resolvedIterations }, config, paths, cwd);
8934
8992
  const logsDir = join3(paths.wileDir, "logs");
8935
8993
  mkdirSync(logsDir, { recursive: true });
@@ -8973,6 +9031,7 @@ var runCompact = async (options) => {
8973
9031
  console.log(`- WILE_AGENT_DIR: ${process.env.WILE_AGENT_DIR ?? "(unset)"}`);
8974
9032
  console.log(`- codingAgent: ${config.codingAgent}`);
8975
9033
  console.log(`- repoSource: ${config.repoSource}`);
9034
+ console.log(`- customDockerfile: ${config.agentDockerfile ?? "(unset)"}`);
8976
9035
  console.log(`- githubRepoUrl: ${config.githubRepoUrl || "(empty)"}`);
8977
9036
  console.log(`- branchName: ${config.branchName || "(empty)"}`);
8978
9037
  }
@@ -8996,7 +9055,10 @@ var runCompact = async (options) => {
8996
9055
  }
8997
9056
  const agentDir = resolveAgentDir();
8998
9057
  const resolvedIterations = options.maxIterations || "1";
8999
- buildAgentImage(agentDir);
9058
+ buildAgentImage(agentDir, {
9059
+ projectDir: cwd,
9060
+ customDockerfilePath: config.agentDockerfile
9061
+ });
9000
9062
  const dockerArgs = buildDockerArgs({ ...options, maxIterations: resolvedIterations }, config, paths, cwd, ["WILE_MODE=compact"]);
9001
9063
  const logsDir = join4(paths.wileDir, "logs");
9002
9064
  mkdirSync2(logsDir, { recursive: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wile",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Autonomous AI coding agent that ships features while you sleep",
5
5
  "type": "module",
6
6
  "bin": {