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.
Files changed (31) hide show
  1. package/dist/agent/Dockerfile +1 -1
  2. package/dist/agent/README.md +20 -20
  3. package/dist/agent/entrypoint.sh +33 -21
  4. package/dist/agent/scripts/claude-stream.ts +159 -0
  5. package/dist/agent/scripts/codex-stream.ts +167 -0
  6. package/dist/agent/scripts/gemini-stream.ts +97 -0
  7. package/dist/agent/scripts/mock-claude.sh +48 -21
  8. package/dist/agent/scripts/mock-codex.sh +48 -21
  9. package/dist/agent/scripts/mock-gemini.sh +48 -21
  10. package/dist/agent/scripts/opencode-stream.ts +93 -0
  11. package/dist/agent/scripts/prompt-compact.md +36 -34
  12. package/dist/agent/scripts/prompt-preflight.md +0 -7
  13. package/dist/agent/scripts/prompt.md +26 -24
  14. package/dist/agent/scripts/test-additional-instructions.sh +4 -2
  15. package/dist/agent/scripts/test-env-project-docker.sh +5 -4
  16. package/dist/agent/scripts/test-iteration-limit.sh +4 -2
  17. package/dist/agent/scripts/test-prd-validation-docker.sh +151 -0
  18. package/dist/agent/scripts/test-preflight-claude-docker.sh +5 -4
  19. package/dist/agent/scripts/test-preflight-docker.sh +16 -13
  20. package/dist/agent/scripts/validate-compact.ts +134 -0
  21. package/dist/agent/scripts/validate-prd.ts +280 -0
  22. package/dist/agent/scripts/wile-compact.sh +6 -6
  23. package/dist/agent/scripts/wile-preflight.sh +4 -4
  24. package/dist/agent/scripts/wile.sh +4 -4
  25. package/dist/cli.js +236 -58
  26. package/package.json +10 -1
  27. package/dist/agent/scripts/claude-stream.js +0 -88
  28. package/dist/agent/scripts/codex-stream.js +0 -125
  29. package/dist/agent/scripts/gemini-stream.js +0 -63
  30. package/dist/agent/scripts/opencode-stream.js +0 -52
  31. package/dist/agent/scripts/validate-compact.js +0 -135
@@ -51,7 +51,7 @@ run_claude() {
51
51
  local prompt_path="$1"
52
52
  cat "$prompt_path" \
53
53
  | claude --model "$CLAUDE_MODEL" --print --output-format stream-json --verbose --dangerously-skip-permissions \
54
- | node "$SCRIPT_DIR/claude-stream.js"
54
+ | bun "$SCRIPT_DIR/claude-stream.ts"
55
55
  }
56
56
 
57
57
  run_opencode() {
@@ -62,7 +62,7 @@ run_opencode() {
62
62
  fi
63
63
  cat "$prompt_path" \
64
64
  | opencode run --format json --model "$model_arg" \
65
- | node "$SCRIPT_DIR/opencode-stream.js"
65
+ | bun "$SCRIPT_DIR/opencode-stream.ts"
66
66
  }
67
67
 
68
68
  run_gemini() {
@@ -74,7 +74,7 @@ run_gemini() {
74
74
  model_args=(--model "$GEMINI_MODEL")
75
75
  fi
76
76
  gemini --output-format stream-json --yolo "${model_args[@]}" "$prompt_text" \
77
- | node "$SCRIPT_DIR/gemini-stream.js"
77
+ | bun "$SCRIPT_DIR/gemini-stream.ts"
78
78
  }
79
79
 
80
80
  run_codex() {
@@ -85,7 +85,7 @@ run_codex() {
85
85
  fi
86
86
  cat "$prompt_path" \
87
87
  | codex exec --json --dangerously-bypass-approvals-and-sandbox "${model_args[@]}" - \
88
- | node "$SCRIPT_DIR/codex-stream.js"
88
+ | bun "$SCRIPT_DIR/codex-stream.ts"
89
89
  }
90
90
 
91
91
  run_agent() {
@@ -68,7 +68,7 @@ run_claude() {
68
68
  local prompt_path="$1"
69
69
  cat "$prompt_path" \
70
70
  | claude --model "$CLAUDE_MODEL" --print --output-format stream-json --verbose --dangerously-skip-permissions \
71
- | node "$SCRIPT_DIR/claude-stream.js"
71
+ | bun "$SCRIPT_DIR/claude-stream.ts"
72
72
  }
73
73
 
74
74
  run_opencode() {
@@ -79,7 +79,7 @@ run_opencode() {
79
79
  fi
80
80
  cat "$prompt_path" \
81
81
  | opencode run --format json --model "$model_arg" \
82
- | node "$SCRIPT_DIR/opencode-stream.js"
82
+ | bun "$SCRIPT_DIR/opencode-stream.ts"
83
83
  }
84
84
 
85
85
  run_gemini() {
@@ -91,7 +91,7 @@ run_gemini() {
91
91
  model_args=(--model "$GEMINI_MODEL")
92
92
  fi
93
93
  gemini --output-format stream-json --yolo "${model_args[@]}" "$prompt_text" \
94
- | node "$SCRIPT_DIR/gemini-stream.js"
94
+ | bun "$SCRIPT_DIR/gemini-stream.ts"
95
95
  }
96
96
 
97
97
  run_codex() {
@@ -102,7 +102,7 @@ run_codex() {
102
102
  fi
103
103
  cat "$prompt_path" \
104
104
  | codex exec --json --dangerously-bypass-approvals-and-sandbox "${model_args[@]}" - \
105
- | node "$SCRIPT_DIR/codex-stream.js"
105
+ | bun "$SCRIPT_DIR/codex-stream.ts"
106
106
  }
107
107
 
108
108
  run_agent() {
package/dist/cli.js CHANGED
@@ -7441,7 +7441,7 @@ var {
7441
7441
  } = import__.default;
7442
7442
 
7443
7443
  // src/cli.ts
7444
- import { readFileSync as readFileSync3 } from "node:fs";
7444
+ import { readFileSync as readFileSync4 } from "node:fs";
7445
7445
  import { resolve as resolve4 } from "node:path";
7446
7446
 
7447
7447
  // src/commands/config.ts
@@ -7452,10 +7452,11 @@ import { existsSync } from "node:fs";
7452
7452
  import { join, resolve, isAbsolute } from "node:path";
7453
7453
  import { homedir } from "node:os";
7454
7454
  var prdExample = {
7455
- userStories: [
7455
+ stories: [
7456
7456
  {
7457
- id: "US-001",
7457
+ id: 1,
7458
7458
  title: "Initialize project with Vite and React",
7459
+ description: "Set up a minimal React + Vite + TypeScript project.",
7459
7460
  acceptanceCriteria: [
7460
7461
  "package.json exists with vite, react, react-dom dependencies",
7461
7462
  "vite.config.ts configured for React",
@@ -7464,13 +7465,13 @@ var prdExample = {
7464
7465
  "npm run dev starts dev server on port 5173",
7465
7466
  "npm run build produces dist/ folder"
7466
7467
  ],
7467
- priority: 1,
7468
- passes: false,
7469
- notes: "Use TypeScript. Keep it minimal."
7468
+ dependsOn: [],
7469
+ status: "pending"
7470
7470
  },
7471
7471
  {
7472
- id: "US-002",
7472
+ id: 2,
7473
7473
  title: "Create 3x3 game board component",
7474
+ description: "Render a centered 3x3 visual board with clickable cells.",
7474
7475
  acceptanceCriteria: [
7475
7476
  "src/components/Board.tsx exists",
7476
7477
  "Renders 3x3 grid of clickable squares",
@@ -7479,13 +7480,13 @@ var prdExample = {
7479
7480
  "Board is centered on page",
7480
7481
  "Squares have visible borders"
7481
7482
  ],
7482
- priority: 2,
7483
- passes: false,
7484
- notes: "Just the visual grid, no game logic yet"
7483
+ dependsOn: [1],
7484
+ status: "pending"
7485
7485
  },
7486
7486
  {
7487
- id: "US-003",
7487
+ id: 3,
7488
7488
  title: "Implement alternating X and O turns",
7489
+ description: "Track turns and prevent overwriting existing moves.",
7489
7490
  acceptanceCriteria: [
7490
7491
  "First click places X",
7491
7492
  "Second click places O",
@@ -7494,13 +7495,13 @@ var prdExample = {
7494
7495
  "O displayed in red color",
7495
7496
  "Cannot click already-filled square"
7496
7497
  ],
7497
- priority: 3,
7498
- passes: false,
7499
- notes: "Use React state for board and current player"
7498
+ dependsOn: [2],
7499
+ status: "pending"
7500
7500
  },
7501
7501
  {
7502
- id: "US-004",
7502
+ id: 4,
7503
7503
  title: "Detect win condition",
7504
+ description: "Determine winner across rows, columns, and diagonals.",
7504
7505
  acceptanceCriteria: [
7505
7506
  "Detects horizontal wins (3 rows)",
7506
7507
  "Detects vertical wins (3 columns)",
@@ -7509,13 +7510,13 @@ var prdExample = {
7509
7510
  "Winning message appears above the board",
7510
7511
  "No more moves allowed after win"
7511
7512
  ],
7512
- priority: 4,
7513
- passes: false,
7514
- notes: "Check all 8 possible winning combinations"
7513
+ dependsOn: [3],
7514
+ status: "pending"
7515
7515
  },
7516
7516
  {
7517
- id: "US-005",
7517
+ id: 5,
7518
7518
  title: "Detect draw and add reset button",
7519
+ description: "Handle full-board draw and allow restarting the game.",
7519
7520
  acceptanceCriteria: [
7520
7521
  "Shows 'Draw!' when all 9 squares filled with no winner",
7521
7522
  "Reset button appears below the board",
@@ -7523,22 +7524,21 @@ var prdExample = {
7523
7524
  "Reset sets turn back to X",
7524
7525
  "Reset clears any win/draw message"
7525
7526
  ],
7526
- priority: 5,
7527
- passes: false,
7528
- notes: "Complete the game loop"
7527
+ dependsOn: [4],
7528
+ status: "pending"
7529
7529
  },
7530
7530
  {
7531
- id: "US-006",
7531
+ id: 6,
7532
7532
  title: "Add current turn indicator",
7533
+ description: "Show whose turn it is while the game is still active.",
7533
7534
  acceptanceCriteria: [
7534
7535
  "Shows 'Current turn: X' or 'Current turn: O' above board",
7535
7536
  "Updates after each move",
7536
7537
  "Hidden when game is won or drawn",
7537
7538
  "X indicator in blue, O indicator in red"
7538
7539
  ],
7539
- priority: 6,
7540
- passes: false,
7541
- notes: "Polish the UX"
7540
+ dependsOn: [3],
7541
+ status: "pending"
7542
7542
  }
7543
7543
  ]
7544
7544
  };
@@ -7667,7 +7667,6 @@ var readBase64File = async (path) => {
7667
7667
  return Buffer.from(contents).toString("base64");
7668
7668
  };
7669
7669
  var runConfig = async () => {
7670
- maybeInject();
7671
7670
  const cwd = process.cwd();
7672
7671
  const wileDir = join(cwd, ".wile");
7673
7672
  const secretsDir = join(wileDir, "secrets");
@@ -7680,6 +7679,7 @@ var runConfig = async () => {
7680
7679
  const preflightPath = join(wileDir, "preflight.md");
7681
7680
  const agentsPath = join(wileDir, "AGENTS.md");
7682
7681
  await mkdir(secretsDir, { recursive: true });
7682
+ maybeInject();
7683
7683
  const existingEnv = await readEnvFile(envPath);
7684
7684
  const codingAgentResponse = await prompt({
7685
7685
  type: "select",
@@ -7984,7 +7984,7 @@ var runConfig = async () => {
7984
7984
  `);
7985
7985
  }
7986
7986
  if (!existsSync(prdPath)) {
7987
- const prdContents = JSON.stringify({ userStories: [] }, null, 2);
7987
+ const prdContents = JSON.stringify({ stories: [] }, null, 2);
7988
7988
  await writeFile(prdPath, prdContents + `
7989
7989
  `);
7990
7990
  }
@@ -8007,9 +8007,9 @@ Use bullet points for preflight checks, e.g.
8007
8007
  await writeIfMissing(agentsPath, [
8008
8008
  "# PRD authoring guidance for Wile",
8009
8009
  "",
8010
- "Wile reads `.wile/prd.json` each iteration, picks the highest-priority story",
8011
- "with `passes: false`, implements exactly one story, marks it complete, logs",
8012
- "progress, and repeats until all stories pass. The PRD should be written so",
8010
+ "Wile reads `.wile/prd.json` each iteration, picks the first runnable story in `stories`",
8011
+ 'where `status: "pending"`, implements exactly one story, marks it `done`, logs',
8012
+ "progress, and repeats until all stories are done. The PRD should be written so",
8013
8013
  "each story is independently actionable and verifiable.",
8014
8014
  "",
8015
8015
  "Guidelines:",
@@ -8018,10 +8018,14 @@ Use bullet points for preflight checks, e.g.
8018
8018
  "- For integration tests, write acceptance criteria that validate real system behavior (not just the harness).",
8019
8019
  "- If verification is a command, state the expected result of that command.",
8020
8020
  "- Use one behavior per bullet.",
8021
- "- Keep IDs stable and unique (e.g., US-123).",
8021
+ "- Keep IDs stable, unique, and numeric (e.g., 1, 2, 3).",
8022
+ "- Never reuse IDs listed in any story's `compactedFrom`.",
8022
8023
  '- Avoid vague terms like "should" or "nice".',
8023
8024
  "- Keep stories small enough to finish in one iteration.",
8024
- "- Mark `passes: false` for work not done yet.",
8025
+ '- Use `status: "pending"` for work not done yet and `status: "done"` only after all acceptance criteria are verified.',
8026
+ "- Use `dependsOn` to model prerequisites by story ID.",
8027
+ "- `dependsOn` must reference active story IDs only; never reference compacted IDs.",
8028
+ "- When compacting completed stories, add `compactedFrom: [oldId1, oldId2, ...]` to the summary done story.",
8025
8029
  "- Prefer concrete files/commands only when they reflect the real outcome.",
8026
8030
  "",
8027
8031
  "Environment notes:",
@@ -8034,6 +8038,7 @@ Use bullet points for preflight checks, e.g.
8034
8038
  `));
8035
8039
  console.log(`
8036
8040
  Wile config complete.`);
8041
+ console.log("Created PRD files in .wile/: prd.json and prd.json.example.");
8037
8042
  console.log("Add project env vars to .wile/.env.project when needed.");
8038
8043
  if (!hadAdditionalInstructions) {
8039
8044
  console.log("Created .wile/additional-instructions.md for extra agent guidance (optional).");
@@ -8152,8 +8157,181 @@ var readWileConfig = (options = {}) => {
8152
8157
  };
8153
8158
  };
8154
8159
 
8160
+ // src/lib/prd.ts
8161
+ import { readFileSync as readFileSync2 } from "node:fs";
8162
+ var toObject = (value, message) => {
8163
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
8164
+ throw new Error(message);
8165
+ }
8166
+ return value;
8167
+ };
8168
+ var toNonEmptyString = (value, message) => {
8169
+ if (typeof value !== "string" || value.trim().length === 0) {
8170
+ throw new Error(message);
8171
+ }
8172
+ return value;
8173
+ };
8174
+ var toInteger = (value, message) => {
8175
+ if (typeof value !== "number" || !Number.isInteger(value)) {
8176
+ throw new Error(message);
8177
+ }
8178
+ return value;
8179
+ };
8180
+ var toStringArray = (value, message) => {
8181
+ if (!Array.isArray(value)) {
8182
+ throw new Error(message);
8183
+ }
8184
+ if (!value.every((item) => typeof item === "string" && item.trim().length > 0)) {
8185
+ throw new Error(message);
8186
+ }
8187
+ return value;
8188
+ };
8189
+ var toIntegerArray = (value, message) => {
8190
+ if (!Array.isArray(value)) {
8191
+ throw new Error(message);
8192
+ }
8193
+ if (!value.every((item) => typeof item === "number" && Number.isInteger(item))) {
8194
+ throw new Error(message);
8195
+ }
8196
+ return value;
8197
+ };
8198
+ var parsePrdStory = (storyRaw, index) => {
8199
+ const label = `stories[${index}]`;
8200
+ const story = toObject(storyRaw, `${label} must be an object.`);
8201
+ const id = toInteger(story.id, `${label}.id must be an integer number.`);
8202
+ const title = toNonEmptyString(story.title, `${label}.title must be a non-empty string.`);
8203
+ const description = toNonEmptyString(story.description, `${label}.description must be a non-empty string.`);
8204
+ const acceptanceCriteria = toStringArray(story.acceptanceCriteria, `${label}.acceptanceCriteria must be an array of non-empty strings.`);
8205
+ const dependsOn = toIntegerArray(story.dependsOn, `${label}.dependsOn must be an array of integer story IDs.`);
8206
+ const status = story.status;
8207
+ if (status !== "pending" && status !== "done") {
8208
+ throw new Error(`${label}.status must be "pending" or "done".`);
8209
+ }
8210
+ let compactedFrom;
8211
+ if (story.compactedFrom !== undefined) {
8212
+ compactedFrom = toIntegerArray(story.compactedFrom, `${label}.compactedFrom must be an array of integer story IDs.`);
8213
+ if (new Set(compactedFrom).size !== compactedFrom.length) {
8214
+ throw new Error(`${label}.compactedFrom must not contain duplicate IDs.`);
8215
+ }
8216
+ if (status !== "done") {
8217
+ throw new Error(`${label}.compactedFrom is only allowed when status is "done".`);
8218
+ }
8219
+ }
8220
+ return {
8221
+ id,
8222
+ title,
8223
+ description,
8224
+ acceptanceCriteria,
8225
+ dependsOn,
8226
+ compactedFrom,
8227
+ status
8228
+ };
8229
+ };
8230
+ var findDependencyCycle = (stories) => {
8231
+ const edges = new Map;
8232
+ for (const story of stories) {
8233
+ edges.set(story.id, story.dependsOn);
8234
+ }
8235
+ const visiting = new Set;
8236
+ const visited = new Set;
8237
+ const path = [];
8238
+ const dfs = (id) => {
8239
+ if (visiting.has(id)) {
8240
+ const start = path.indexOf(id);
8241
+ return start === -1 ? [id] : [...path.slice(start), id];
8242
+ }
8243
+ if (visited.has(id)) {
8244
+ return null;
8245
+ }
8246
+ visiting.add(id);
8247
+ path.push(id);
8248
+ const deps = edges.get(id) ?? [];
8249
+ for (const dep of deps) {
8250
+ const cycle = dfs(dep);
8251
+ if (cycle) {
8252
+ return cycle;
8253
+ }
8254
+ }
8255
+ path.pop();
8256
+ visiting.delete(id);
8257
+ visited.add(id);
8258
+ return null;
8259
+ };
8260
+ for (const story of stories) {
8261
+ const cycle = dfs(story.id);
8262
+ if (cycle) {
8263
+ return cycle;
8264
+ }
8265
+ }
8266
+ return null;
8267
+ };
8268
+ var validatePrd = (raw) => {
8269
+ const payload = toObject(raw, "prd.json must be a JSON object.");
8270
+ if (!Array.isArray(payload.stories)) {
8271
+ throw new Error('prd.json must contain a top-level "stories" array.');
8272
+ }
8273
+ const stories = payload.stories.map((item, idx) => parsePrdStory(item, idx));
8274
+ const storyById = new Map;
8275
+ for (const story of stories) {
8276
+ if (storyById.has(story.id)) {
8277
+ throw new Error(`Duplicate story id detected: ${story.id}.`);
8278
+ }
8279
+ storyById.set(story.id, story);
8280
+ }
8281
+ const compactedById = new Map;
8282
+ for (const story of stories) {
8283
+ for (const compactedId of story.compactedFrom ?? []) {
8284
+ if (compactedById.has(compactedId)) {
8285
+ throw new Error(`Compacted story id ${compactedId} is listed multiple times (stories ${compactedById.get(compactedId)} and ${story.id}).`);
8286
+ }
8287
+ compactedById.set(compactedId, story.id);
8288
+ }
8289
+ }
8290
+ for (const [compactedId, ownerStoryId] of compactedById) {
8291
+ if (storyById.has(compactedId)) {
8292
+ throw new Error(`Story id ${compactedId} is reserved by compactedFrom in story ${ownerStoryId}.`);
8293
+ }
8294
+ }
8295
+ for (const story of stories) {
8296
+ for (const depId of story.dependsOn) {
8297
+ const compactedOwner = compactedById.get(depId);
8298
+ if (compactedOwner !== undefined) {
8299
+ throw new Error(`Story ${story.id} depends on compacted story id ${depId} (compacted in story ${compactedOwner}).`);
8300
+ }
8301
+ if (!storyById.has(depId)) {
8302
+ throw new Error(`Story ${story.id} depends on missing story id ${depId}.`);
8303
+ }
8304
+ }
8305
+ }
8306
+ const cycle = findDependencyCycle(stories);
8307
+ if (cycle) {
8308
+ throw new Error(`Dependency cycle detected: ${cycle.join(" -> ")}.`);
8309
+ }
8310
+ const pendingStories = stories.filter((story) => story.status === "pending");
8311
+ const runnableStory = stories.find((story) => story.status === "pending" && story.dependsOn.every((depId) => storyById.get(depId)?.status === "done")) ?? null;
8312
+ if (pendingStories.length > 0 && !runnableStory) {
8313
+ const blockedIds = pendingStories.map((story) => story.id).join(", ");
8314
+ throw new Error(`No runnable pending stories in prd.json. Pending stories are blocked: ${blockedIds}.`);
8315
+ }
8316
+ return {
8317
+ prd: { stories },
8318
+ pendingStories,
8319
+ runnableStory,
8320
+ allDone: pendingStories.length === 0
8321
+ };
8322
+ };
8323
+ var readAndValidatePrd = (path) => {
8324
+ let raw;
8325
+ try {
8326
+ raw = JSON.parse(readFileSync2(path, "utf8"));
8327
+ } catch {
8328
+ throw new Error(`Failed to read ${path}. Ensure it is valid JSON.`);
8329
+ }
8330
+ return validatePrd(raw);
8331
+ };
8332
+
8155
8333
  // src/commands/run.ts
8156
- import { existsSync as existsSync3, readFileSync as readFileSync2, mkdirSync, createWriteStream, writeFileSync } from "node:fs";
8334
+ import { existsSync as existsSync3, readFileSync as readFileSync3, mkdirSync, createWriteStream, writeFileSync } from "node:fs";
8157
8335
  import { spawn, spawnSync } from "node:child_process";
8158
8336
  import { resolve as resolve3, join as join3, dirname } from "node:path";
8159
8337
  import { fileURLToPath } from "node:url";
@@ -8171,19 +8349,11 @@ var findAgentDir = (startDir) => {
8171
8349
  current = parent;
8172
8350
  }
8173
8351
  };
8174
- var readJson = (path) => {
8175
- try {
8176
- const contents = readFileSync2(path, "utf8");
8177
- return JSON.parse(contents);
8178
- } catch {
8179
- throw new Error(`Failed to read ${path}. Ensure it is valid JSON.`);
8180
- }
8181
- };
8182
8352
  var validateGitignore = (path) => {
8183
8353
  if (!existsSync3(path)) {
8184
8354
  throw new Error("Missing .wile/.gitignore. Run 'bunx wile config'.");
8185
8355
  }
8186
- const contents = readFileSync2(path, "utf8");
8356
+ const contents = readFileSync3(path, "utf8");
8187
8357
  const missing = [];
8188
8358
  if (!contents.includes("secrets/"))
8189
8359
  missing.push("secrets/");
@@ -8195,11 +8365,10 @@ var validateGitignore = (path) => {
8195
8365
  throw new Error(`Missing ${missing.join(", ")} in .wile/.gitignore. Run 'bunx wile config'.`);
8196
8366
  }
8197
8367
  };
8198
- var hasPendingStories = (prd) => {
8199
- if (!Array.isArray(prd.userStories)) {
8200
- return false;
8368
+ var validatePrdLocation = (paths) => {
8369
+ if (!existsSync3(paths.prdPath)) {
8370
+ throw new Error("Missing .wile/prd.json. Run 'bunx wile config'.");
8201
8371
  }
8202
- return prd.userStories.some((story) => story.passes === false);
8203
8372
  };
8204
8373
  var buildAgentImage = (agentDir) => {
8205
8374
  const result = spawnSync("docker", ["build", "-t", "wile-agent:local", agentDir], {
@@ -8329,14 +8498,20 @@ var runWile = async (options) => {
8329
8498
  console.error(error.message);
8330
8499
  process.exit(1);
8331
8500
  }
8332
- if (!existsSync3(paths.prdPath)) {
8333
- console.error("Missing .wile/prd.json. Run 'bunx wile config'.");
8334
- process.exit(1);
8335
- }
8336
- const prd = readJson(paths.prdPath);
8337
- if (!hasPendingStories(prd)) {
8338
- console.log("No pending stories in .wile/prd.json. Add a story to run Wile.");
8339
- return;
8501
+ const repoSource = options.test || options.repo ? "github" : config.repoSource ?? "github";
8502
+ const shouldValidateLocalPrd = options.test || repoSource === "local";
8503
+ if (shouldValidateLocalPrd) {
8504
+ try {
8505
+ validatePrdLocation(paths);
8506
+ const validation = readAndValidatePrd(paths.prdPath);
8507
+ if (validation.allDone) {
8508
+ console.log("No pending stories in .wile/prd.json. Add a pending story to run Wile.");
8509
+ return;
8510
+ }
8511
+ } catch (error) {
8512
+ console.error(error.message);
8513
+ process.exit(1);
8514
+ }
8340
8515
  }
8341
8516
  const agentDir = resolveAgentDir();
8342
8517
  const resolvedIterations = options.maxIterations || config.maxIterations || "25";
@@ -8393,8 +8568,11 @@ var runCompact = async (options) => {
8393
8568
  console.error(error.message);
8394
8569
  process.exit(1);
8395
8570
  }
8396
- if (!existsSync4(paths.prdPath)) {
8397
- console.error("Missing .wile/prd.json. Run 'bunx wile config'.");
8571
+ try {
8572
+ validatePrdLocation(paths);
8573
+ readAndValidatePrd(paths.prdPath);
8574
+ } catch (error) {
8575
+ console.error(error.message);
8398
8576
  process.exit(1);
8399
8577
  }
8400
8578
  const progressPath = join4(paths.wileDir, "progress.txt");
@@ -8420,7 +8598,7 @@ var runCompact = async (options) => {
8420
8598
 
8421
8599
  // src/cli.ts
8422
8600
  var packageJsonPath = resolve4(new URL("../package.json", import.meta.url).pathname);
8423
- var packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
8601
+ var packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
8424
8602
  var program2 = new Command;
8425
8603
  program2.name("wile").description("Autonomous AI coding agent that ships features while you sleep").version(packageJson.version);
8426
8604
  program2.command("config").description("Configure the current project for Wile").action(async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wile",
3
- "version": "0.8.0",
3
+ "version": "1.0.1",
4
4
  "description": "Autonomous AI coding agent that ships features while you sleep",
5
5
  "type": "module",
6
6
  "bin": {
@@ -31,6 +31,15 @@
31
31
  "agent",
32
32
  "claude"
33
33
  ],
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/dooart/wile.git",
37
+ "directory": "packages/cli"
38
+ },
39
+ "homepage": "https://github.com/dooart/wile/tree/main/packages/cli#readme",
40
+ "bugs": {
41
+ "url": "https://github.com/dooart/wile/issues"
42
+ },
34
43
  "license": "MIT",
35
44
  "devDependencies": {
36
45
  "@types/prompts": "^2.4.9",
@@ -1,88 +0,0 @@
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
- if (!chunk.text.endsWith("\n")) {
20
- process.stdout.write("\n");
21
- }
22
- continue;
23
- }
24
- if (chunk.type === "thinking" && typeof chunk.thinking === "string") {
25
- writeLine(`[thinking] ${chunk.thinking}`);
26
- }
27
- }
28
- };
29
-
30
- const extractToolUse = (content) => {
31
- if (!Array.isArray(content)) return;
32
- for (const chunk of content) {
33
- if (!chunk || typeof chunk !== "object") continue;
34
- if (chunk.type === "tool_use") {
35
- const toolName = chunk.name ?? "tool";
36
- const description = chunk.input?.description ?? "";
37
- const command = chunk.input?.command ?? "";
38
- if (description && command) {
39
- writeLine(`[tool] ${toolName}: ${description} (${command})`);
40
- } else if (command) {
41
- writeLine(`[tool] ${toolName}: ${command}`);
42
- } else if (description) {
43
- writeLine(`[tool] ${toolName}: ${description}`);
44
- } else {
45
- writeLine(`[tool] ${toolName}`);
46
- }
47
- }
48
- }
49
- };
50
-
51
- rl.on("line", (line) => {
52
- if (!line.trim()) return;
53
- if (process.env.WILE_STREAM_JSON === "true") {
54
- writeLine(line);
55
- return;
56
- }
57
- let payload;
58
- try {
59
- payload = JSON.parse(line);
60
- } catch {
61
- return;
62
- }
63
-
64
- if (payload.type === "system" && payload.subtype === "init") {
65
- const model = payload.model ?? "unknown";
66
- writeLine(`[system] model: ${model}`);
67
- return;
68
- }
69
-
70
- if (payload.type === "assistant" && payload.message) {
71
- extractToolUse(payload.message.content);
72
- extractText(payload.message.content);
73
- return;
74
- }
75
-
76
- if (payload.type === "user" && payload.message?.content) {
77
- for (const chunk of payload.message.content) {
78
- if (chunk?.type === "tool_result") {
79
- writeLine(`[tool-result] ${chunk.tool_use_id ?? "unknown"} ${chunk.is_error ? "error" : "ok"}`);
80
- }
81
- }
82
- return;
83
- }
84
-
85
- if (payload.type === "result" && payload.result) {
86
- writeLine(payload.result);
87
- }
88
- });