wile 0.8.0 → 1.0.1
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/dist/agent/Dockerfile +1 -1
- package/dist/agent/README.md +20 -20
- package/dist/agent/entrypoint.sh +33 -21
- package/dist/agent/scripts/claude-stream.ts +159 -0
- package/dist/agent/scripts/codex-stream.ts +167 -0
- package/dist/agent/scripts/gemini-stream.ts +97 -0
- package/dist/agent/scripts/mock-claude.sh +48 -21
- package/dist/agent/scripts/mock-codex.sh +48 -21
- package/dist/agent/scripts/mock-gemini.sh +48 -21
- package/dist/agent/scripts/opencode-stream.ts +93 -0
- package/dist/agent/scripts/prompt-compact.md +36 -34
- package/dist/agent/scripts/prompt-preflight.md +0 -7
- package/dist/agent/scripts/prompt.md +26 -24
- package/dist/agent/scripts/test-additional-instructions.sh +4 -2
- package/dist/agent/scripts/test-env-project-docker.sh +5 -4
- package/dist/agent/scripts/test-iteration-limit.sh +4 -2
- package/dist/agent/scripts/test-prd-validation-docker.sh +151 -0
- package/dist/agent/scripts/test-preflight-claude-docker.sh +5 -4
- package/dist/agent/scripts/test-preflight-docker.sh +16 -13
- package/dist/agent/scripts/validate-compact.ts +134 -0
- package/dist/agent/scripts/validate-prd.ts +280 -0
- package/dist/agent/scripts/wile-compact.sh +6 -6
- package/dist/agent/scripts/wile-preflight.sh +4 -4
- package/dist/agent/scripts/wile.sh +4 -4
- package/dist/cli.js +236 -58
- package/package.json +10 -1
- package/dist/agent/scripts/claude-stream.js +0 -88
- package/dist/agent/scripts/codex-stream.js +0 -125
- package/dist/agent/scripts/gemini-stream.js +0 -63
- package/dist/agent/scripts/opencode-stream.js +0 -52
- package/dist/agent/scripts/validate-compact.js +0 -135
|
@@ -8,26 +8,53 @@ const fs = require("fs");
|
|
|
8
8
|
const prdPath = ".wile/prd.json";
|
|
9
9
|
const progressPath = ".wile/progress.txt";
|
|
10
10
|
const prd = JSON.parse(fs.readFileSync(prdPath, "utf8"));
|
|
11
|
-
const stories = Array.isArray(prd.
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
11
|
+
const stories = Array.isArray(prd.stories) ? prd.stories : [];
|
|
12
|
+
const pendingStories = stories.filter((story) => story.status === "pending");
|
|
13
|
+
const doneStories = stories.filter((story) => story.status === "done");
|
|
14
|
+
const requiredDoneIds = new Set();
|
|
15
|
+
for (const story of pendingStories) {
|
|
16
|
+
const deps = Array.isArray(story.dependsOn) ? story.dependsOn : [];
|
|
17
|
+
for (const depId of deps) {
|
|
18
|
+
requiredDoneIds.add(depId);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const retainedDoneStories = doneStories.filter((story) => requiredDoneIds.has(story.id));
|
|
22
|
+
const compactableDoneStories = doneStories.filter((story) => !requiredDoneIds.has(story.id));
|
|
23
|
+
const reservedIds = new Set(stories.map((story) => story.id));
|
|
24
|
+
for (const story of stories) {
|
|
25
|
+
const priorCompacted = Array.isArray(story.compactedFrom) ? story.compactedFrom : [];
|
|
26
|
+
for (const compactedId of priorCompacted) {
|
|
27
|
+
reservedIds.add(compactedId);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const summaryId = Math.max(0, ...reservedIds) + 1;
|
|
31
|
+
|
|
32
|
+
const nextStories = [...pendingStories, ...retainedDoneStories];
|
|
33
|
+
if (compactableDoneStories.length > 0) {
|
|
34
|
+
const compactedFrom = [
|
|
35
|
+
...new Set(
|
|
36
|
+
compactableDoneStories.flatMap((story) => {
|
|
37
|
+
const priorCompacted = Array.isArray(story.compactedFrom) ? story.compactedFrom : [];
|
|
38
|
+
return [story.id, ...priorCompacted];
|
|
39
|
+
})
|
|
40
|
+
)
|
|
41
|
+
].sort((a, b) => a - b);
|
|
42
|
+
|
|
43
|
+
nextStories.push({
|
|
44
|
+
id: summaryId,
|
|
45
|
+
title: "[COMPACT] Completed stories summary",
|
|
46
|
+
description: "Compacted completed stories into a high-level summary entry.",
|
|
47
|
+
acceptanceCriteria: [
|
|
48
|
+
"Historical summary for compacted completed work.",
|
|
49
|
+
"Pending stories were preserved unchanged."
|
|
50
|
+
],
|
|
51
|
+
dependsOn: [],
|
|
52
|
+
compactedFrom,
|
|
53
|
+
status: "done"
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fs.writeFileSync(prdPath, JSON.stringify({ stories: nextStories }, null, 2) + "\n");
|
|
31
58
|
|
|
32
59
|
const progressLines = [
|
|
33
60
|
"# Wile Progress Log",
|
|
@@ -46,7 +73,7 @@ fs.writeFileSync(progressPath, progressLines.join("\n"));
|
|
|
46
73
|
NODE
|
|
47
74
|
|
|
48
75
|
cat <<'JSON'
|
|
49
|
-
{"type":"assistant","message":{"content":[{"type":"text","text":"{\"
|
|
76
|
+
{"type":"assistant","message":{"content":[{"type":"text","text":"{\"summaryStoryId\":999,\"summaryTitle\":\"[COMPACT] Completed stories summary\"}\n"}]}}
|
|
50
77
|
JSON
|
|
51
78
|
exit 0
|
|
52
79
|
fi
|
|
@@ -8,26 +8,53 @@ const fs = require("fs");
|
|
|
8
8
|
const prdPath = ".wile/prd.json";
|
|
9
9
|
const progressPath = ".wile/progress.txt";
|
|
10
10
|
const prd = JSON.parse(fs.readFileSync(prdPath, "utf8"));
|
|
11
|
-
const stories = Array.isArray(prd.
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
11
|
+
const stories = Array.isArray(prd.stories) ? prd.stories : [];
|
|
12
|
+
const pendingStories = stories.filter((story) => story.status === "pending");
|
|
13
|
+
const doneStories = stories.filter((story) => story.status === "done");
|
|
14
|
+
const requiredDoneIds = new Set();
|
|
15
|
+
for (const story of pendingStories) {
|
|
16
|
+
const deps = Array.isArray(story.dependsOn) ? story.dependsOn : [];
|
|
17
|
+
for (const depId of deps) {
|
|
18
|
+
requiredDoneIds.add(depId);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const retainedDoneStories = doneStories.filter((story) => requiredDoneIds.has(story.id));
|
|
22
|
+
const compactableDoneStories = doneStories.filter((story) => !requiredDoneIds.has(story.id));
|
|
23
|
+
const reservedIds = new Set(stories.map((story) => story.id));
|
|
24
|
+
for (const story of stories) {
|
|
25
|
+
const priorCompacted = Array.isArray(story.compactedFrom) ? story.compactedFrom : [];
|
|
26
|
+
for (const compactedId of priorCompacted) {
|
|
27
|
+
reservedIds.add(compactedId);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const summaryId = Math.max(0, ...reservedIds) + 1;
|
|
31
|
+
|
|
32
|
+
const nextStories = [...pendingStories, ...retainedDoneStories];
|
|
33
|
+
if (compactableDoneStories.length > 0) {
|
|
34
|
+
const compactedFrom = [
|
|
35
|
+
...new Set(
|
|
36
|
+
compactableDoneStories.flatMap((story) => {
|
|
37
|
+
const priorCompacted = Array.isArray(story.compactedFrom) ? story.compactedFrom : [];
|
|
38
|
+
return [story.id, ...priorCompacted];
|
|
39
|
+
})
|
|
40
|
+
)
|
|
41
|
+
].sort((a, b) => a - b);
|
|
42
|
+
|
|
43
|
+
nextStories.push({
|
|
44
|
+
id: summaryId,
|
|
45
|
+
title: "[COMPACT] Completed stories summary",
|
|
46
|
+
description: "Compacted completed stories into a high-level summary entry.",
|
|
47
|
+
acceptanceCriteria: [
|
|
48
|
+
"Historical summary for compacted completed work.",
|
|
49
|
+
"Pending stories were preserved unchanged."
|
|
50
|
+
],
|
|
51
|
+
dependsOn: [],
|
|
52
|
+
compactedFrom,
|
|
53
|
+
status: "done"
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fs.writeFileSync(prdPath, JSON.stringify({ stories: nextStories }, null, 2) + "\n");
|
|
31
58
|
|
|
32
59
|
const progressLines = [
|
|
33
60
|
"# Wile Progress Log",
|
|
@@ -46,7 +73,7 @@ fs.writeFileSync(progressPath, progressLines.join("\n"));
|
|
|
46
73
|
NODE
|
|
47
74
|
|
|
48
75
|
cat <<'JSON'
|
|
49
|
-
{"type":"item.completed","item":{"id":"item_1","type":"agent_message","text":"{\"
|
|
76
|
+
{"type":"item.completed","item":{"id":"item_1","type":"agent_message","text":"{\"summaryStoryId\":999,\"summaryTitle\":\"[COMPACT] Completed stories summary\"}\n"}}
|
|
50
77
|
JSON
|
|
51
78
|
exit 0
|
|
52
79
|
fi
|
|
@@ -8,26 +8,53 @@ const fs = require("fs");
|
|
|
8
8
|
const prdPath = ".wile/prd.json";
|
|
9
9
|
const progressPath = ".wile/progress.txt";
|
|
10
10
|
const prd = JSON.parse(fs.readFileSync(prdPath, "utf8"));
|
|
11
|
-
const stories = Array.isArray(prd.
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
11
|
+
const stories = Array.isArray(prd.stories) ? prd.stories : [];
|
|
12
|
+
const pendingStories = stories.filter((story) => story.status === "pending");
|
|
13
|
+
const doneStories = stories.filter((story) => story.status === "done");
|
|
14
|
+
const requiredDoneIds = new Set();
|
|
15
|
+
for (const story of pendingStories) {
|
|
16
|
+
const deps = Array.isArray(story.dependsOn) ? story.dependsOn : [];
|
|
17
|
+
for (const depId of deps) {
|
|
18
|
+
requiredDoneIds.add(depId);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const retainedDoneStories = doneStories.filter((story) => requiredDoneIds.has(story.id));
|
|
22
|
+
const compactableDoneStories = doneStories.filter((story) => !requiredDoneIds.has(story.id));
|
|
23
|
+
const reservedIds = new Set(stories.map((story) => story.id));
|
|
24
|
+
for (const story of stories) {
|
|
25
|
+
const priorCompacted = Array.isArray(story.compactedFrom) ? story.compactedFrom : [];
|
|
26
|
+
for (const compactedId of priorCompacted) {
|
|
27
|
+
reservedIds.add(compactedId);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const summaryId = Math.max(0, ...reservedIds) + 1;
|
|
31
|
+
|
|
32
|
+
const nextStories = [...pendingStories, ...retainedDoneStories];
|
|
33
|
+
if (compactableDoneStories.length > 0) {
|
|
34
|
+
const compactedFrom = [
|
|
35
|
+
...new Set(
|
|
36
|
+
compactableDoneStories.flatMap((story) => {
|
|
37
|
+
const priorCompacted = Array.isArray(story.compactedFrom) ? story.compactedFrom : [];
|
|
38
|
+
return [story.id, ...priorCompacted];
|
|
39
|
+
})
|
|
40
|
+
)
|
|
41
|
+
].sort((a, b) => a - b);
|
|
42
|
+
|
|
43
|
+
nextStories.push({
|
|
44
|
+
id: summaryId,
|
|
45
|
+
title: "[COMPACT] Completed stories summary",
|
|
46
|
+
description: "Compacted completed stories into a high-level summary entry.",
|
|
47
|
+
acceptanceCriteria: [
|
|
48
|
+
"Historical summary for compacted completed work.",
|
|
49
|
+
"Pending stories were preserved unchanged."
|
|
50
|
+
],
|
|
51
|
+
dependsOn: [],
|
|
52
|
+
compactedFrom,
|
|
53
|
+
status: "done"
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fs.writeFileSync(prdPath, JSON.stringify({ stories: nextStories }, null, 2) + "\n");
|
|
31
58
|
|
|
32
59
|
const progressLines = [
|
|
33
60
|
"# Wile Progress Log",
|
|
@@ -46,7 +73,7 @@ fs.writeFileSync(progressPath, progressLines.join("\n"));
|
|
|
46
73
|
NODE
|
|
47
74
|
|
|
48
75
|
cat <<'JSON'
|
|
49
|
-
{"type":"message","role":"assistant","content":"{\"
|
|
76
|
+
{"type":"message","role":"assistant","content":"{\"summaryStoryId\":999,\"summaryTitle\":\"[COMPACT] Completed stories summary\"}\n"}
|
|
50
77
|
JSON
|
|
51
78
|
exit 0
|
|
52
79
|
fi
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { createInterface } from "node:readline";
|
|
3
|
+
|
|
4
|
+
type JsonObject = Record<string, unknown>;
|
|
5
|
+
|
|
6
|
+
const isObject = (value: unknown): value is JsonObject =>
|
|
7
|
+
typeof value === "object" && value !== null;
|
|
8
|
+
|
|
9
|
+
const getString = (obj: JsonObject, key: string): string | undefined => {
|
|
10
|
+
const value = obj[key];
|
|
11
|
+
return typeof value === "string" ? value : undefined;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const getObject = (obj: JsonObject, key: string): JsonObject | undefined => {
|
|
15
|
+
const value = obj[key];
|
|
16
|
+
return isObject(value) ? value : undefined;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const writeLine = (value: string): void => {
|
|
20
|
+
process.stdout.write(`${value}\n`);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const formatToolLine = (tool: string, title: string, input: JsonObject): string => {
|
|
24
|
+
if (title) {
|
|
25
|
+
return `[tool] ${tool}: ${title}`;
|
|
26
|
+
}
|
|
27
|
+
if (Object.keys(input).length > 0) {
|
|
28
|
+
return `[tool] ${tool}: ${JSON.stringify(input)}`;
|
|
29
|
+
}
|
|
30
|
+
return `[tool] ${tool}`;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const rl = createInterface({
|
|
34
|
+
input: process.stdin,
|
|
35
|
+
crlfDelay: Infinity
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
rl.on("line", (line: string) => {
|
|
39
|
+
if (!line.trim()) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let parsed: unknown;
|
|
44
|
+
try {
|
|
45
|
+
parsed = JSON.parse(line);
|
|
46
|
+
} catch {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!isObject(parsed)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const payloadType = getString(parsed, "type");
|
|
55
|
+
|
|
56
|
+
if (payloadType === "text") {
|
|
57
|
+
const part = getObject(parsed, "part");
|
|
58
|
+
const text = part ? getString(part, "text") : undefined;
|
|
59
|
+
if (text) {
|
|
60
|
+
process.stdout.write(text);
|
|
61
|
+
if (!text.endsWith("\n")) {
|
|
62
|
+
process.stdout.write("\n");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (payloadType === "tool_use") {
|
|
69
|
+
const part = getObject(parsed, "part");
|
|
70
|
+
if (!part) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const tool = getString(part, "tool") ?? "tool";
|
|
75
|
+
const state = getObject(part, "state");
|
|
76
|
+
const title = state ? getString(state, "title") ?? "" : "";
|
|
77
|
+
const input = state ? getObject(state, "input") ?? {} : {};
|
|
78
|
+
writeLine(formatToolLine(tool, title, input));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (payloadType === "error") {
|
|
83
|
+
const error = getObject(parsed, "error");
|
|
84
|
+
if (!error) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const name = getString(error, "name") ?? "error";
|
|
88
|
+
const data = getObject(error, "data");
|
|
89
|
+
const message =
|
|
90
|
+
(data ? getString(data, "message") : undefined) ?? getString(error, "message") ?? "";
|
|
91
|
+
writeLine(`[error] ${name}${message ? `: ${message}` : ""}`);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
@@ -1,44 +1,46 @@
|
|
|
1
1
|
You are running a one-shot compaction task.
|
|
2
2
|
|
|
3
|
-
Goal: compact both `.wile/prd.json` and `.wile/progress.txt` while preserving
|
|
3
|
+
Goal: compact both `.wile/prd.json` and `.wile/progress.txt` while preserving correctness.
|
|
4
|
+
|
|
5
|
+
Rules:
|
|
6
|
+
- Final `.wile/prd.json` must stay valid under the current schema (`stories`, numeric `id`, `dependsOn`, `status`).
|
|
7
|
+
- Never reuse a compacted story ID. Any ID listed in any `compactedFrom` is permanently reserved.
|
|
8
|
+
- Keep every pending story exactly as-is.
|
|
9
|
+
- Do not create missing dependencies.
|
|
4
10
|
|
|
5
11
|
Steps:
|
|
6
12
|
1. Read `.wile/prd.json` and `.wile/progress.txt`.
|
|
7
|
-
2.
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
|
|
13
|
+
2. Compute:
|
|
14
|
+
- `pendingStories`: all stories where `status` is `"pending"`
|
|
15
|
+
- `doneStories`: all stories where `status` is `"done"`
|
|
16
|
+
- `requiredDoneIds`: every done-story id referenced by any pending story `dependsOn`
|
|
17
|
+
3. Build a compacted `stories` array:
|
|
18
|
+
- Keep all `pendingStories` exactly as-is.
|
|
19
|
+
- Keep all done stories whose id is in `requiredDoneIds` exactly as-is.
|
|
20
|
+
- For remaining done stories, replace them with one summary done story:
|
|
21
|
+
- `id`: next available integer id greater than every story `id` and every value in every `compactedFrom` array
|
|
22
|
+
- `title`: `[COMPACT] Completed stories summary`
|
|
23
|
+
- `description`: concise summary of shipped work
|
|
24
|
+
- `acceptanceCriteria`: a short list (1-3 bullets) describing what was completed
|
|
25
|
+
- `dependsOn`: []
|
|
26
|
+
- `compactedFrom`: sorted unique list of all compacted story IDs
|
|
27
|
+
- Include each replaced done story `id`
|
|
28
|
+
- If a replaced story already has `compactedFrom`, include those IDs too (preserve tombstones transitively)
|
|
29
|
+
- `status`: `"done"`
|
|
30
|
+
- Preserve stable ordering as much as possible: keep retained stories in original order and append the summary story at the end.
|
|
31
|
+
4. Update `.wile/progress.txt`:
|
|
32
|
+
- Keep header `# Wile Progress Log` and `## Codebase Patterns`.
|
|
33
|
+
- Add useful high-level bullets under `## Codebase Patterns`.
|
|
34
|
+
- Replace verbose history with a concise high-level summary.
|
|
35
|
+
- Ignore preflight-failure noise.
|
|
36
|
+
5. When committing changes, only `.wile/prd.json` and `.wile/progress.txt` should be committed.
|
|
27
37
|
|
|
28
38
|
Response format:
|
|
29
|
-
- Your response must end with a single-line JSON object and nothing after it
|
|
30
|
-
- The final non-empty line
|
|
39
|
+
- Your response must end with a single-line JSON object and nothing after it.
|
|
40
|
+
- The final non-empty line must be valid JSON with this shape:
|
|
31
41
|
{
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"tasks": [
|
|
35
|
-
"High level of what was accomplished here",
|
|
36
|
-
"Should NOT have all tasks in here, should be very summarized"
|
|
37
|
-
],
|
|
38
|
-
"taskIds": "From TASK-001 to TASK-029",
|
|
39
|
-
"priority": 1,
|
|
40
|
-
"passes": true,
|
|
41
|
-
"notes": "Don't repeat task ids when starting the next one."
|
|
42
|
+
"summaryStoryId": 123,
|
|
43
|
+
"summaryTitle": "[COMPACT] Completed stories summary"
|
|
42
44
|
}
|
|
43
|
-
- Do not wrap
|
|
45
|
+
- Do not wrap JSON in code fences.
|
|
44
46
|
- Ensure nothing appears after the JSON line.
|
|
@@ -39,13 +39,6 @@ cat .wile/prd.json
|
|
|
39
39
|
- Under **Checks run**, include the exact command(s) you executed (verbatim).
|
|
40
40
|
- Under **Failures**, include the specific missing file names or failing commands.
|
|
41
41
|
|
|
42
|
-
- If GitHub is configured (`WILE_REPO_SOURCE=github` or `GITHUB_REPO_URL` is set), commit and push the progress update:
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
git add .wile/progress.txt
|
|
46
|
-
git commit -m "chore: preflight failed"
|
|
47
|
-
git push
|
|
48
|
-
```
|
|
49
42
|
- Respond with exactly:
|
|
50
43
|
|
|
51
44
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Wile Agent Instructions
|
|
2
2
|
|
|
3
|
-
You are an autonomous coding agent running in a loop. Each iteration, you complete ONE
|
|
3
|
+
You are an autonomous coding agent running in a loop. Each iteration, you complete ONE story from the backlog, then exit. The loop will call you again for the next story.
|
|
4
4
|
|
|
5
5
|
## Your Task (Execute in Order)
|
|
6
6
|
|
|
@@ -8,7 +8,11 @@ You are an autonomous coding agent running in a loop. Each iteration, you comple
|
|
|
8
8
|
```bash
|
|
9
9
|
cat .wile/prd.json
|
|
10
10
|
```
|
|
11
|
-
Parse the `
|
|
11
|
+
Parse the `stories` array. Pick the first story (array order) where:
|
|
12
|
+
- `status` is `"pending"`
|
|
13
|
+
- every ID in `dependsOn` points to a story whose `status` is `"done"`
|
|
14
|
+
|
|
15
|
+
If no pending stories remain, respond with `<promise>ALL_STORIES_COMPLETED</promise>`.
|
|
12
16
|
|
|
13
17
|
### 2. Read Progress Log
|
|
14
18
|
```bash
|
|
@@ -24,7 +28,7 @@ git status
|
|
|
24
28
|
Ensure you're on the correct branch specified by the `BRANCH_NAME` environment variable.
|
|
25
29
|
|
|
26
30
|
### 4. Implement the Story
|
|
27
|
-
|
|
31
|
+
Implement the selected runnable pending story completely.
|
|
28
32
|
|
|
29
33
|
- Read and understand all acceptance criteria
|
|
30
34
|
- Implement the feature/fix
|
|
@@ -41,10 +45,10 @@ npm test || npm run test
|
|
|
41
45
|
|
|
42
46
|
If tests or typecheck fail, fix the issues before proceeding.
|
|
43
47
|
|
|
44
|
-
### 6. Update prd.json
|
|
45
|
-
Set `
|
|
48
|
+
### 6. Update .wile/prd.json
|
|
49
|
+
Set `status: "done"` for the completed story:
|
|
46
50
|
```bash
|
|
47
|
-
# Edit .wile/prd.json to mark the story as
|
|
51
|
+
# Edit .wile/prd.json to mark the story as done
|
|
48
52
|
```
|
|
49
53
|
|
|
50
54
|
### 7. Log Your Progress
|
|
@@ -73,42 +77,40 @@ Set `passes: true` for the completed story:
|
|
|
73
77
|
|
|
74
78
|
If you discovered important patterns, also add them to the **Codebase Patterns** section at the TOP of progress.txt.
|
|
75
79
|
|
|
76
|
-
### 8. Commit
|
|
80
|
+
### 8. Commit
|
|
77
81
|
```bash
|
|
78
82
|
git add -A
|
|
79
83
|
git commit -m "feat: [STORY-ID] - [Story Title]"
|
|
80
|
-
git push
|
|
81
84
|
```
|
|
82
85
|
|
|
83
86
|
Use the exact story ID and title from `.wile/prd.json`.
|
|
84
87
|
|
|
85
88
|
## Stop Condition
|
|
86
89
|
|
|
87
|
-
After completing steps 1-8, check if ALL stories in `.wile/prd.json` have `
|
|
90
|
+
After completing steps 1-8, check if ALL stories in `.wile/prd.json` have `status: "done"`.
|
|
88
91
|
|
|
89
|
-
**If ALL stories
|
|
92
|
+
**If ALL stories are done**, respond with exactly:
|
|
90
93
|
```
|
|
91
94
|
<promise>ALL_STORIES_COMPLETED</promise>
|
|
92
95
|
```
|
|
93
96
|
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.
|
|
94
97
|
|
|
95
|
-
**If there are still stories with `
|
|
98
|
+
**If there are still stories with `status: "pending"`**, end your response normally. The loop will call you again for the next story.
|
|
96
99
|
|
|
97
100
|
## Important Rules
|
|
98
101
|
|
|
99
102
|
1. **ONE story per iteration** - Do not implement multiple stories
|
|
100
|
-
2. **
|
|
101
|
-
3. **
|
|
102
|
-
4. **
|
|
103
|
-
5. **
|
|
104
|
-
6. **
|
|
105
|
-
7. **
|
|
106
|
-
8. **
|
|
107
|
-
9. **
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
11. **
|
|
111
|
-
12. **No verification section means no pass** - If the progress entry lacks a **Verification** section, do not mark `passes: true`.
|
|
103
|
+
2. **One commit per feature** - Include the implementation, .wile/prd.json update, and progress log in a single commit
|
|
104
|
+
3. **Fix related files** - If typecheck requires changes in other files, make them (this is not scope creep)
|
|
105
|
+
4. **Be idempotent** - Use `IF NOT EXISTS` for migrations, check before creating files
|
|
106
|
+
5. **No interactive prompts** - Use `echo -e "\n\n\n" |` if a command might prompt
|
|
107
|
+
6. **NEVER commit node_modules, dist, or build artifacts** - .gitignore should already be set up at the start of the run
|
|
108
|
+
7. **Use acceptance criteria as verification steps** - Run commands to confirm outputs or write tests that fail if the feature is removed
|
|
109
|
+
8. **Integration tests must validate real system behavior, not just the harness**
|
|
110
|
+
9. **If you discover reusable, module-specific guidance, add it to the nearest AGENTS.md**
|
|
111
|
+
Note: Never update .wile/AGENTS.md.
|
|
112
|
+
10. **Definition of done** - Set `status: "done"` only when every acceptance criterion has concrete verification and all verifications passed.
|
|
113
|
+
11. **No verification section means not done** - If the progress entry lacks a **Verification** section, do not mark the story as done.
|
|
112
114
|
|
|
113
115
|
## Common Patterns
|
|
114
116
|
|
|
@@ -158,7 +160,7 @@ const { chromium } = require('playwright');
|
|
|
158
160
|
const page = await browser.newPage();
|
|
159
161
|
await page.goto('http://localhost:3000/login');
|
|
160
162
|
await page.waitForLoadState('networkidle');
|
|
161
|
-
const element = await page
|
|
163
|
+
const element = await page.$('form#login');
|
|
162
164
|
console.log(element ? 'FOUND: form#login' : 'NOT FOUND: form#login');
|
|
163
165
|
await browser.close();
|
|
164
166
|
})();
|
|
@@ -16,8 +16,9 @@ run_case() {
|
|
|
16
16
|
BIN_DIR="$TMP_DIR/bin"
|
|
17
17
|
mkdir -p "$SCRIPT_DIR" "$BIN_DIR"
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
cp /
|
|
19
|
+
REPO_SCRIPTS="$(cd "$(dirname "$0")" && pwd)"
|
|
20
|
+
cp "$REPO_SCRIPTS/wile.sh" "$SCRIPT_DIR/wile.sh"
|
|
21
|
+
cp "$REPO_SCRIPTS/claude-stream.ts" "$SCRIPT_DIR/claude-stream.ts"
|
|
21
22
|
chmod +x "$SCRIPT_DIR/wile.sh"
|
|
22
23
|
|
|
23
24
|
echo "BASE PROMPT" > "$SCRIPT_DIR/prompt.md"
|
|
@@ -52,6 +53,7 @@ EOF
|
|
|
52
53
|
chmod +x "$BIN_DIR/claude"
|
|
53
54
|
|
|
54
55
|
PATH="$BIN_DIR:$PATH" \
|
|
56
|
+
CODING_AGENT="CC" \
|
|
55
57
|
CLAUDE_CAPTURE="$CAPTURE" \
|
|
56
58
|
WILE_ADDITIONAL_INSTRUCTIONS="$ADDITIONAL" \
|
|
57
59
|
CC_CLAUDE_MODEL="sonnet" \
|
|
@@ -15,13 +15,14 @@ trap cleanup EXIT INT TERM
|
|
|
15
15
|
mkdir -p "$TMP_DIR/.wile/secrets"
|
|
16
16
|
cat > "$TMP_DIR/.wile/prd.json" <<'JSON'
|
|
17
17
|
{
|
|
18
|
-
"
|
|
18
|
+
"stories": [
|
|
19
19
|
{
|
|
20
|
-
"id":
|
|
20
|
+
"id": 1,
|
|
21
21
|
"title": "Env project forward test",
|
|
22
|
+
"description": "Forward custom env file into container",
|
|
22
23
|
"acceptanceCriteria": ["n/a"],
|
|
23
|
-
"
|
|
24
|
-
"
|
|
24
|
+
"dependsOn": [],
|
|
25
|
+
"status": "pending"
|
|
25
26
|
}
|
|
26
27
|
]
|
|
27
28
|
}
|
|
@@ -11,8 +11,9 @@ SCRIPT_DIR="$TMP_DIR/agent"
|
|
|
11
11
|
BIN_DIR="$TMP_DIR/bin"
|
|
12
12
|
mkdir -p "$SCRIPT_DIR" "$BIN_DIR"
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
cp /
|
|
14
|
+
REPO_SCRIPTS="$(cd "$(dirname "$0")" && pwd)"
|
|
15
|
+
cp "$REPO_SCRIPTS/wile.sh" "$SCRIPT_DIR/wile.sh"
|
|
16
|
+
cp "$REPO_SCRIPTS/claude-stream.ts" "$SCRIPT_DIR/claude-stream.ts"
|
|
16
17
|
chmod +x "$SCRIPT_DIR/wile.sh"
|
|
17
18
|
|
|
18
19
|
echo "BASE PROMPT" > "$SCRIPT_DIR/prompt.md"
|
|
@@ -27,6 +28,7 @@ chmod +x "$BIN_DIR/claude"
|
|
|
27
28
|
|
|
28
29
|
set +e
|
|
29
30
|
PATH="$BIN_DIR:$PATH" \
|
|
31
|
+
CODING_AGENT="CC" \
|
|
30
32
|
CC_CLAUDE_MODEL="sonnet" \
|
|
31
33
|
"$SCRIPT_DIR/wile.sh" 3 > "$OUTPUT_FILE" 2>&1
|
|
32
34
|
EXIT_CODE=$?
|