wile 0.4.15 → 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 +1 -0
- package/dist/agent/entrypoint.sh +5 -4
- package/dist/agent/scripts/claude-stream.js +81 -0
- package/dist/agent/scripts/prompt.md +3 -0
- package/dist/agent/scripts/test-additional-instructions.sh +3 -2
- package/dist/agent/scripts/test-iteration-limit.sh +44 -0
- package/dist/agent/scripts/wile.sh +13 -3
- package/dist/cli.js +18 -8
- package/package.json +1 -1
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
|
|
package/dist/agent/entrypoint.sh
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
set -e
|
|
8
8
|
|
|
9
9
|
echo "══════════════════════════════════════════════════════"
|
|
10
|
-
echo "
|
|
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
|
-
|
|
108
|
-
|
|
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 "
|
|
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
|
+
});
|
|
@@ -100,6 +100,9 @@ After completing steps 1-8, check if ALL stories in `.wile/prd.json` have `passe
|
|
|
100
100
|
6. **No interactive prompts** - Use `echo -e "\n\n\n" |` if a command might prompt
|
|
101
101
|
7. **NEVER commit node_modules, dist, or build artifacts** - .gitignore should already be set up at the start of the run
|
|
102
102
|
8. **Use acceptance criteria as verification steps** - Run commands to confirm outputs or write tests that fail if the feature is removed
|
|
103
|
+
9. **Integration tests must validate real system behavior, not just the harness**
|
|
104
|
+
10. **If you discover reusable, module-specific guidance, add it to the nearest AGENTS.md**
|
|
105
|
+
Note: Never update .wile/AGENTS.md.
|
|
103
106
|
|
|
104
107
|
## Common Patterns
|
|
105
108
|
|
|
@@ -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
|
-
|
|
47
|
+
cat > "$BIN_DIR/claude" <<'EOF'
|
|
47
48
|
#!/bin/sh
|
|
48
49
|
cat > "$CLAUDE_CAPTURE"
|
|
49
|
-
|
|
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 "
|
|
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=$(
|
|
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=$(
|
|
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 ?? ""}`);
|
|
@@ -7745,6 +7755,7 @@ Use bullet points for additional instructions, e.g.
|
|
|
7745
7755
|
"Guidelines:",
|
|
7746
7756
|
"- Use outcome-focused acceptance criteria (observable results).",
|
|
7747
7757
|
'- Criteria should be hard to satisfy with "empty" tests.',
|
|
7758
|
+
"- For integration tests, write acceptance criteria that validate real system behavior (not just the harness).",
|
|
7748
7759
|
"- If verification is a command, state the expected result of that command.",
|
|
7749
7760
|
"- Use one behavior per bullet.",
|
|
7750
7761
|
"- Keep IDs stable and unique (e.g., US-123).",
|
|
@@ -7753,9 +7764,6 @@ Use bullet points for additional instructions, e.g.
|
|
|
7753
7764
|
"- Mark `passes: false` for work not done yet.",
|
|
7754
7765
|
"- Place the STOP HERE note only on the last story that requires human approval.",
|
|
7755
7766
|
"- Prefer concrete files/commands only when they reflect the real outcome.",
|
|
7756
|
-
"- Integration tests must validate real system behavior, not just the harness.",
|
|
7757
|
-
"- If you discover reusable, module-specific guidance, add it to the nearest AGENTS.md.",
|
|
7758
|
-
" Note: Never update .wile/AGENTS.md.",
|
|
7759
7767
|
"",
|
|
7760
7768
|
"Environment notes:",
|
|
7761
7769
|
"- Playwright (Chromium) is available in the agent container for UI checks.",
|
|
@@ -7834,6 +7842,7 @@ var readWileConfig = (options = {}) => {
|
|
|
7834
7842
|
branchName: env.BRANCH_NAME ?? "",
|
|
7835
7843
|
repoSource,
|
|
7836
7844
|
ccClaudeModel: env.CC_CLAUDE_MODEL,
|
|
7845
|
+
maxIterations: env.WILE_MAX_ITERATIONS,
|
|
7837
7846
|
ccClaudeCodeOauthToken: env.CC_CLAUDE_CODE_OAUTH_TOKEN,
|
|
7838
7847
|
ccAnthropicApiKey: env.CC_ANTHROPIC_API_KEY,
|
|
7839
7848
|
envProject
|
|
@@ -8020,8 +8029,9 @@ var runWile = async (options) => {
|
|
|
8020
8029
|
return;
|
|
8021
8030
|
}
|
|
8022
8031
|
const agentDir = resolveAgentDir();
|
|
8032
|
+
const resolvedIterations = options.maxIterations || config.maxIterations || "25";
|
|
8023
8033
|
buildAgentImage(agentDir);
|
|
8024
|
-
const dockerArgs = buildDockerArgs(options, config, paths, cwd);
|
|
8034
|
+
const dockerArgs = buildDockerArgs({ ...options, maxIterations: resolvedIterations }, config, paths, cwd);
|
|
8025
8035
|
const logsDir = join3(paths.wileDir, "logs");
|
|
8026
8036
|
mkdirSync(logsDir, { recursive: true });
|
|
8027
8037
|
const logPath = join3(logsDir, `run-${getTimestamp()}.log`);
|
|
@@ -8051,7 +8061,7 @@ program2.name("wile").description("Autonomous AI coding agent that ships feature
|
|
|
8051
8061
|
program2.command("config").description("Configure the current project for Wile").action(async () => {
|
|
8052
8062
|
await runConfig();
|
|
8053
8063
|
});
|
|
8054
|
-
program2.command("run").description("Run Wile on a repository").option("--repo <repo>", "Repository URL or local path").option("--max-iterations <count>", "Maximum iterations"
|
|
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) => {
|
|
8055
8065
|
await runWile(options);
|
|
8056
8066
|
});
|
|
8057
8067
|
program2.parse(process.argv);
|