wile 0.4.16 → 0.4.17

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
@@ -25,6 +25,7 @@ This creates:
25
25
 
26
26
  Set `WILE_REPO_SOURCE=local` in `.wile/secrets/.env` to run against the current directory without GitHub.
27
27
  When `WILE_REPO_SOURCE=local`, GitHub credentials are optional.
28
+ Set `WILE_MAX_ITERATIONS` in `.wile/secrets/.env` to change the default loop limit (default: 25).
28
29
 
29
30
  ## Run Wile
30
31
 
@@ -7,7 +7,7 @@
7
7
  set -e
8
8
 
9
9
  echo "══════════════════════════════════════════════════════"
10
- echo " 🗡️ WILE - Container Startup"
10
+ echo " 🌵 WILE - Container Startup"
11
11
  echo "══════════════════════════════════════════════════════"
12
12
 
13
13
  if [ "${WILE_TEST:-}" = "true" ]; then
@@ -104,8 +104,9 @@ if [ "${WILE_MOCK_CLAUDE:-}" = "true" ]; then
104
104
  mkdir -p "$MOCK_BIN"
105
105
  cat > "$MOCK_BIN/claude" << 'MOCK'
106
106
  #!/bin/sh
107
- echo "ANSWER: 2"
108
- echo "<promise>COMPLETE</promise>"
107
+ cat <<'JSON'
108
+ {"type":"assistant","message":{"content":[{"type":"text","text":"ANSWER: 2\n<promise>COMPLETE</promise>\n"}]}}
109
+ JSON
109
110
  MOCK
110
111
  chmod +x "$MOCK_BIN/claude"
111
112
  export PATH="$MOCK_BIN:$PATH"
@@ -161,7 +162,7 @@ echo ""
161
162
 
162
163
  # Configure git
163
164
  echo "Configuring git..."
164
- git config --global user.name "Wile Bot"
165
+ git config --global user.name "wile"
165
166
  git config --global user.email "wile@bot.local"
166
167
  git config --global credential.helper store
167
168
 
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+ const readline = require("node:readline");
3
+
4
+ const rl = readline.createInterface({
5
+ input: process.stdin,
6
+ crlfDelay: Infinity
7
+ });
8
+
9
+ const writeLine = (value) => {
10
+ process.stdout.write(`${value}\n`);
11
+ };
12
+
13
+ const extractText = (content) => {
14
+ if (!Array.isArray(content)) return;
15
+ for (const chunk of content) {
16
+ if (!chunk || typeof chunk !== "object") continue;
17
+ if (chunk.type === "text" && typeof chunk.text === "string") {
18
+ process.stdout.write(chunk.text);
19
+ continue;
20
+ }
21
+ if (chunk.type === "thinking" && typeof chunk.thinking === "string") {
22
+ writeLine(`[thinking] ${chunk.thinking}`);
23
+ }
24
+ }
25
+ };
26
+
27
+ const extractToolUse = (content) => {
28
+ if (!Array.isArray(content)) return;
29
+ for (const chunk of content) {
30
+ if (!chunk || typeof chunk !== "object") continue;
31
+ if (chunk.type === "tool_use") {
32
+ const toolName = chunk.name ?? "tool";
33
+ const description = chunk.input?.description ?? "";
34
+ const command = chunk.input?.command ?? "";
35
+ if (description && command) {
36
+ writeLine(`[tool] ${toolName}: ${description} (${command})`);
37
+ } else if (command) {
38
+ writeLine(`[tool] ${toolName}: ${command}`);
39
+ } else if (description) {
40
+ writeLine(`[tool] ${toolName}: ${description}`);
41
+ } else {
42
+ writeLine(`[tool] ${toolName}`);
43
+ }
44
+ }
45
+ }
46
+ };
47
+
48
+ rl.on("line", (line) => {
49
+ if (!line.trim()) return;
50
+ let payload;
51
+ try {
52
+ payload = JSON.parse(line);
53
+ } catch {
54
+ return;
55
+ }
56
+
57
+ if (payload.type === "system" && payload.subtype === "init") {
58
+ const model = payload.model ?? "unknown";
59
+ writeLine(`[system] model: ${model}`);
60
+ return;
61
+ }
62
+
63
+ if (payload.type === "assistant" && payload.message) {
64
+ extractToolUse(payload.message.content);
65
+ extractText(payload.message.content);
66
+ return;
67
+ }
68
+
69
+ if (payload.type === "user" && payload.message?.content) {
70
+ for (const chunk of payload.message.content) {
71
+ if (chunk?.type === "tool_result") {
72
+ writeLine(`[tool-result] ${chunk.tool_use_id ?? "unknown"} ${chunk.is_error ? "error" : "ok"}`);
73
+ }
74
+ }
75
+ return;
76
+ }
77
+
78
+ if (payload.type === "result" && payload.result) {
79
+ writeLine(payload.result);
80
+ }
81
+ });
@@ -17,6 +17,7 @@ run_case() {
17
17
  mkdir -p "$SCRIPT_DIR" "$BIN_DIR"
18
18
 
19
19
  cp /Users/thiagoduarte/Projects/personal/wile/packages/agent/scripts/wile.sh "$SCRIPT_DIR/wile.sh"
20
+ cp /Users/thiagoduarte/Projects/personal/wile/packages/agent/scripts/claude-stream.js "$SCRIPT_DIR/claude-stream.js"
20
21
  chmod +x "$SCRIPT_DIR/wile.sh"
21
22
 
22
23
  echo "BASE PROMPT" > "$SCRIPT_DIR/prompt.md"
@@ -43,10 +44,10 @@ EOF
43
44
 
44
45
  CAPTURE="$TMP_DIR/capture.txt"
45
46
 
46
- cat > "$BIN_DIR/claude" <<'EOF'
47
+ cat > "$BIN_DIR/claude" <<'EOF'
47
48
  #!/bin/sh
48
49
  cat > "$CLAUDE_CAPTURE"
49
- echo "<promise>COMPLETE</promise>"
50
+ printf '%s\n' '{"type":"assistant","message":{"content":[{"type":"text","text":"<promise>COMPLETE</promise>\\n"}]}}'
50
51
  EOF
51
52
  chmod +x "$BIN_DIR/claude"
52
53
 
@@ -0,0 +1,44 @@
1
+ #!/bin/sh
2
+ set -euo pipefail
3
+
4
+ TMP_DIR=$(mktemp -d /tmp/wile-iteration-limit-XXXXXX)
5
+ cleanup() {
6
+ rm -rf "$TMP_DIR"
7
+ }
8
+ trap cleanup EXIT INT TERM
9
+
10
+ SCRIPT_DIR="$TMP_DIR/agent"
11
+ BIN_DIR="$TMP_DIR/bin"
12
+ mkdir -p "$SCRIPT_DIR" "$BIN_DIR"
13
+
14
+ cp /Users/thiagoduarte/Projects/personal/wile/packages/agent/scripts/wile.sh "$SCRIPT_DIR/wile.sh"
15
+ cp /Users/thiagoduarte/Projects/personal/wile/packages/agent/scripts/claude-stream.js "$SCRIPT_DIR/claude-stream.js"
16
+ chmod +x "$SCRIPT_DIR/wile.sh"
17
+
18
+ echo "BASE PROMPT" > "$SCRIPT_DIR/prompt.md"
19
+
20
+ OUTPUT_FILE="$TMP_DIR/output.txt"
21
+
22
+ cat > "$BIN_DIR/claude" <<'EOF'
23
+ #!/bin/sh
24
+ printf '%s\n' '{"type":"assistant","message":{"content":[{"type":"text","text":"working...\n"}]}}'
25
+ EOF
26
+ chmod +x "$BIN_DIR/claude"
27
+
28
+ set +e
29
+ PATH="$BIN_DIR:$PATH" \
30
+ CC_CLAUDE_MODEL="sonnet" \
31
+ "$SCRIPT_DIR/wile.sh" 3 > "$OUTPUT_FILE" 2>&1
32
+ EXIT_CODE=$?
33
+ set -e
34
+
35
+ if [ "$EXIT_CODE" -eq 0 ]; then
36
+ echo "error: expected non-zero exit when max iterations reached" >&2
37
+ exit 1
38
+ fi
39
+
40
+ grep -q "MAX ITERATIONS REACHED (3)" "$OUTPUT_FILE"
41
+ grep -q "Iteration 1 of 3" "$OUTPUT_FILE"
42
+ grep -q "Iteration 3 of 3" "$OUTPUT_FILE"
43
+
44
+ echo "test-iteration-limit: ok"
@@ -14,7 +14,7 @@ SETUP_PROMPT_FILE="$SCRIPT_DIR/prompt-setup.md"
14
14
  ADDITIONAL_PROMPT_FILE="${WILE_ADDITIONAL_INSTRUCTIONS:-}"
15
15
 
16
16
  echo "══════════════════════════════════════════════════════"
17
- echo " 🗡️ WILE - Autonomous Coding Agent"
17
+ echo " 🌵 WILE - Autonomous Coding Agent"
18
18
  echo "══════════════════════════════════════════════════════"
19
19
  echo " Max iterations: $MAX_ITERATIONS"
20
20
  echo " Model: $CLAUDE_MODEL"
@@ -49,7 +49,12 @@ echo "════════════════════════
49
49
  echo ""
50
50
 
51
51
  if [ -f "$SETUP_PROMPT_FILE" ]; then
52
- OUTPUT=$(cat "$SETUP_PROMPT_FILE" | claude --model "$CLAUDE_MODEL" --verbose --dangerously-skip-permissions 2>&1 | tee /dev/stderr) || true
52
+ OUTPUT=$(
53
+ cat "$SETUP_PROMPT_FILE" \
54
+ | claude --model "$CLAUDE_MODEL" --print --output-format stream-json --verbose --dangerously-skip-permissions \
55
+ | node "$SCRIPT_DIR/claude-stream.js" \
56
+ | tee /dev/stderr
57
+ ) || true
53
58
 
54
59
  # Check if setup failed critically
55
60
  if echo "$OUTPUT" | grep -q "<promise>SETUP_FAILED</promise>"; then
@@ -80,7 +85,12 @@ for i in $(seq 1 $MAX_ITERATIONS); do
80
85
  # Pipe prompt to Claude Code
81
86
  # --dangerously-skip-permissions allows autonomous operation
82
87
  # Capture output while also displaying it (tee to stderr)
83
- OUTPUT=$(cat "$PROMPT_FILE" | claude --model "$CLAUDE_MODEL" --verbose --dangerously-skip-permissions 2>&1 | tee /dev/stderr) || true
88
+ OUTPUT=$(
89
+ cat "$PROMPT_FILE" \
90
+ | claude --model "$CLAUDE_MODEL" --print --output-format stream-json --verbose --dangerously-skip-permissions \
91
+ | node "$SCRIPT_DIR/claude-stream.js" \
92
+ | tee /dev/stderr
93
+ ) || true
84
94
 
85
95
  # Check for completion signal
86
96
  if echo "$OUTPUT" | grep -q "<promise>COMPLETE</promise>"; then
package/dist/cli.js CHANGED
@@ -7658,9 +7658,10 @@ var runConfig = async () => {
7658
7658
  message: "Default Claude model",
7659
7659
  choices: [
7660
7660
  { title: "sonnet", value: "sonnet" },
7661
- { title: "opus", value: "opus" }
7661
+ { title: "opus", value: "opus" },
7662
+ { title: "haiku", value: "haiku" }
7662
7663
  ],
7663
- initial: existingEnv.CC_CLAUDE_MODEL === "opus" ? 1 : 0
7664
+ initial: existingEnv.CC_CLAUDE_MODEL === "opus" ? 1 : existingEnv.CC_CLAUDE_MODEL === "haiku" ? 2 : 0
7664
7665
  });
7665
7666
  const repoSourceResponse = await prompt({
7666
7667
  type: "select",
@@ -7696,6 +7697,14 @@ var runConfig = async () => {
7696
7697
  message: "Default branch name",
7697
7698
  initial: existingEnv.BRANCH_NAME ?? "main"
7698
7699
  });
7700
+ const iterationsResponse = await prompt({
7701
+ type: "number",
7702
+ name: "maxIterations",
7703
+ message: "Default max iterations",
7704
+ initial: existingEnv.WILE_MAX_ITERATIONS ? Number(existingEnv.WILE_MAX_ITERATIONS) : 25
7705
+ });
7706
+ const fallbackIterations = existingEnv.WILE_MAX_ITERATIONS ? Number(existingEnv.WILE_MAX_ITERATIONS) : 25;
7707
+ const maxIterations = Number.isFinite(iterationsResponse.maxIterations) && iterationsResponse.maxIterations > 0 ? iterationsResponse.maxIterations : fallbackIterations;
7699
7708
  const authFallback = authMethod === "oauth" ? existingEnv.CC_CLAUDE_CODE_OAUTH_TOKEN : existingEnv.CC_ANTHROPIC_API_KEY;
7700
7709
  const authValue = coalesceValue(authValueResponse.authValue, authFallback);
7701
7710
  const githubToken = repoSource === "github" ? coalesceValue(githubTokenResponse.githubToken, existingEnv.GITHUB_TOKEN) : existingEnv.GITHUB_TOKEN;
@@ -7707,7 +7716,8 @@ var runConfig = async () => {
7707
7716
  `GITHUB_TOKEN=${githubToken ?? ""}`,
7708
7717
  `GITHUB_REPO_URL=${repoUrl ?? ""}`,
7709
7718
  `BRANCH_NAME=${branchName ?? "main"}`,
7710
- `CC_CLAUDE_MODEL=${defaultModelResponse.model}`
7719
+ `CC_CLAUDE_MODEL=${defaultModelResponse.model}`,
7720
+ `WILE_MAX_ITERATIONS=${maxIterations}`
7711
7721
  ];
7712
7722
  if (authMethod === "oauth") {
7713
7723
  envLines.push(`CC_CLAUDE_CODE_OAUTH_TOKEN=${authValue ?? ""}`);
@@ -7832,6 +7842,7 @@ var readWileConfig = (options = {}) => {
7832
7842
  branchName: env.BRANCH_NAME ?? "",
7833
7843
  repoSource,
7834
7844
  ccClaudeModel: env.CC_CLAUDE_MODEL,
7845
+ maxIterations: env.WILE_MAX_ITERATIONS,
7835
7846
  ccClaudeCodeOauthToken: env.CC_CLAUDE_CODE_OAUTH_TOKEN,
7836
7847
  ccAnthropicApiKey: env.CC_ANTHROPIC_API_KEY,
7837
7848
  envProject
@@ -8018,8 +8029,9 @@ var runWile = async (options) => {
8018
8029
  return;
8019
8030
  }
8020
8031
  const agentDir = resolveAgentDir();
8032
+ const resolvedIterations = options.maxIterations || config.maxIterations || "25";
8021
8033
  buildAgentImage(agentDir);
8022
- const dockerArgs = buildDockerArgs(options, config, paths, cwd);
8034
+ const dockerArgs = buildDockerArgs({ ...options, maxIterations: resolvedIterations }, config, paths, cwd);
8023
8035
  const logsDir = join3(paths.wileDir, "logs");
8024
8036
  mkdirSync(logsDir, { recursive: true });
8025
8037
  const logPath = join3(logsDir, `run-${getTimestamp()}.log`);
@@ -8049,7 +8061,7 @@ program2.name("wile").description("Autonomous AI coding agent that ships feature
8049
8061
  program2.command("config").description("Configure the current project for Wile").action(async () => {
8050
8062
  await runConfig();
8051
8063
  });
8052
- program2.command("run").description("Run Wile on a repository").option("--repo <repo>", "Repository URL or local path").option("--max-iterations <count>", "Maximum iterations", "25").option("--test", "Run in test mode").option("--debug", "Print debug info before running").action(async (options) => {
8064
+ 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) => {
8053
8065
  await runWile(options);
8054
8066
  });
8055
8067
  program2.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wile",
3
- "version": "0.4.16",
3
+ "version": "0.4.17",
4
4
  "description": "Autonomous AI coding agent that ships features while you sleep",
5
5
  "type": "module",
6
6
  "bin": {