chiefwiggum 1.2.8 → 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/dist/cli.cjs ADDED
@@ -0,0 +1,904 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // node_modules/tsup/assets/cjs_shims.js
27
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
28
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
29
+
30
+ // src/cli.ts
31
+ var import_commander = require("commander");
32
+ var import_picocolors9 = __toESM(require("picocolors"), 1);
33
+ var import_node_fs5 = require("fs");
34
+ var import_node_path3 = require("path");
35
+ var import_node_url2 = require("url");
36
+
37
+ // src/utils/banner.ts
38
+ var import_picocolors = __toESM(require("picocolors"), 1);
39
+ var import_node_child_process = require("child_process");
40
+ var BANNER = `
41
+ ${import_picocolors.default.yellow("\u2591\u2588\u2580\u2580\u2591\u2588\u2591\u2588\u2591\u2580\u2588\u2580\u2591\u2588\u2580\u2580\u2591\u2588\u2580\u2580\u2591\u2591\u2591\u2588\u2591\u2588\u2591\u2580\u2588\u2580\u2591\u2588\u2580\u2580\u2591\u2588\u2580\u2580\u2591\u2588\u2591\u2588\u2591\u2588\u2584\u2588")}
42
+ ${import_picocolors.default.yellow("\u2591\u2588\u2591\u2591\u2591\u2588\u2580\u2588\u2591\u2591\u2588\u2591\u2591\u2588\u2580\u2580\u2591\u2588\u2580\u2580\u2591\u2591\u2591\u2588\u2584\u2588\u2591\u2591\u2588\u2591\u2591\u2588\u2591\u2588\u2591\u2588\u2591\u2588\u2591\u2588\u2591\u2588\u2591\u2588\u2591\u2588")}
43
+ ${import_picocolors.default.yellow("\u2591\u2580\u2580\u2580\u2591\u2580\u2591\u2580\u2591\u2580\u2580\u2580\u2591\u2580\u2580\u2580\u2591\u2580\u2591\u2591\u2591\u2591\u2591\u2580\u2591\u2580\u2591\u2580\u2580\u2580\u2591\u2580\u2580\u2580\u2591\u2580\u2580\u2580\u2591\u2580\u2580\u2580\u2591\u2580\u2591\u2580")}`;
44
+ async function checkForUpdates(currentVersion) {
45
+ try {
46
+ const result = (0, import_node_child_process.execSync)("npm view chiefwiggum version", {
47
+ timeout: 2e3,
48
+ encoding: "utf-8",
49
+ stdio: ["pipe", "pipe", "pipe"]
50
+ }).trim();
51
+ if (result && result !== currentVersion) {
52
+ console.log(import_picocolors.default.cyan(` \u2728 Update available: v${currentVersion} \u2192 v${result}`));
53
+ console.log(import_picocolors.default.cyan(" Run: npx chiefwiggum@latest"));
54
+ console.log();
55
+ }
56
+ } catch {
57
+ }
58
+ }
59
+ async function showBanner(version2) {
60
+ console.log(BANNER);
61
+ console.log(import_picocolors.default.yellow(` Autonomous Coding Agent v${version2}`));
62
+ console.log();
63
+ await checkForUpdates(version2);
64
+ }
65
+
66
+ // src/lib/process.ts
67
+ var import_picocolors2 = __toESM(require("picocolors"), 1);
68
+ var import_node_child_process2 = require("child_process");
69
+ var cleanupRan = false;
70
+ function cleanup() {
71
+ if (cleanupRan) return;
72
+ cleanupRan = true;
73
+ process.stdout.write("\x1B[?25h");
74
+ }
75
+ function setupSignalHandlers() {
76
+ process.on("SIGINT", () => {
77
+ console.log();
78
+ console.log(import_picocolors2.default.yellow("Interrupted."));
79
+ cleanup();
80
+ process.exit(130);
81
+ });
82
+ process.on("SIGTERM", () => {
83
+ cleanup();
84
+ process.exit(143);
85
+ });
86
+ process.on("uncaughtException", (err) => {
87
+ console.error(import_picocolors2.default.red("Uncaught exception:"), err.message);
88
+ cleanup();
89
+ process.exit(1);
90
+ });
91
+ process.on("unhandledRejection", (reason) => {
92
+ console.error(import_picocolors2.default.red("Unhandled rejection:"), reason);
93
+ cleanup();
94
+ process.exit(1);
95
+ });
96
+ process.on("exit", cleanup);
97
+ }
98
+ function cleanupStaleProcesses() {
99
+ try {
100
+ if (process.platform !== "win32") {
101
+ (0, import_node_child_process2.execSync)("pkill -f playwright 2>/dev/null || true", { stdio: "pipe" });
102
+ (0, import_node_child_process2.execSync)("pkill -f chromium 2>/dev/null || true", { stdio: "pipe" });
103
+ (0, import_node_child_process2.execSync)("lsof -ti:3000 | xargs kill -9 2>/dev/null || true", { stdio: "pipe" });
104
+ } else {
105
+ (0, import_node_child_process2.execSync)('taskkill /F /IM "playwright*" 2>nul || exit 0', { stdio: "pipe", shell: "cmd" });
106
+ (0, import_node_child_process2.execSync)('taskkill /F /IM "chromium*" 2>nul || exit 0', { stdio: "pipe", shell: "cmd" });
107
+ }
108
+ } catch {
109
+ }
110
+ }
111
+ function sleep(ms) {
112
+ return new Promise((resolve) => setTimeout(resolve, ms));
113
+ }
114
+
115
+ // src/commands/status.ts
116
+ var import_node_fs = require("fs");
117
+ var import_node_child_process3 = require("child_process");
118
+ var import_picocolors3 = __toESM(require("picocolors"), 1);
119
+
120
+ // src/lib/config.ts
121
+ var import_node_os = require("os");
122
+ var import_node_path = require("path");
123
+ var config = {
124
+ // Paths
125
+ chiefwiggumHome: process.env.CHIEFWIGGUM_HOME || (0, import_node_path.join)((0, import_node_os.homedir)(), ".chiefwiggum"),
126
+ get templatesDir() {
127
+ return (0, import_node_path.join)(this.chiefwiggumHome, "templates");
128
+ },
129
+ todoFile: process.env.TODO_FILE || "TODO.md",
130
+ // Timing
131
+ cooldownSeconds: parseInt(process.env.COOLDOWN_SECONDS || "5", 10),
132
+ iterationTimeoutMinutes: parseInt(process.env.ITERATION_TIMEOUT_MINUTES || "60", 10),
133
+ // Guardrails
134
+ maxConsecutiveFailures: parseInt(process.env.MAX_CONSECUTIVE_FAILURES || "3", 10),
135
+ maxNoCommitCycles: parseInt(process.env.MAX_NO_COMMIT_CYCLES || "5", 10),
136
+ maxClaudeAttempts: parseInt(process.env.MAX_CLAUDE_ATTEMPTS || "3", 10)
137
+ };
138
+ process.env.CLAUDE_CODE_EXIT_AFTER_STOP_DELAY = process.env.CLAUDE_CODE_EXIT_AFTER_STOP_DELAY || "30000";
139
+
140
+ // src/commands/status.ts
141
+ async function cmdStatus() {
142
+ console.log(import_picocolors3.default.bold("Project Status"));
143
+ console.log();
144
+ const hasTodo = (0, import_node_fs.existsSync)(config.todoFile);
145
+ const hasSpecs = (0, import_node_fs.existsSync)("specs");
146
+ const hasClaudeMd = (0, import_node_fs.existsSync)("CLAUDE.md");
147
+ const check = (exists) => exists ? import_picocolors3.default.green("\u2713") : import_picocolors3.default.red("\u2717");
148
+ console.log(` TODO.md: ${check(hasTodo)}`);
149
+ console.log(` specs/: ${check(hasSpecs)}`);
150
+ console.log(` CLAUDE.md: ${check(hasClaudeMd)}`);
151
+ console.log();
152
+ if (hasTodo) {
153
+ try {
154
+ const content = (0, import_node_fs.readFileSync)(config.todoFile, "utf-8");
155
+ const lines = content.split("\n");
156
+ let total = 0;
157
+ let done = 0;
158
+ for (const line of lines) {
159
+ if (/^\s*-\s*\[.\]/.test(line)) {
160
+ total++;
161
+ if (/^\s*-\s*\[x\]/i.test(line)) {
162
+ done++;
163
+ }
164
+ }
165
+ }
166
+ const remaining = total - done;
167
+ console.log(import_picocolors3.default.bold("Tasks"));
168
+ console.log(` Total: ${total}`);
169
+ console.log(` Completed: ${import_picocolors3.default.green(String(done))}`);
170
+ console.log(` Remaining: ${import_picocolors3.default.yellow(String(remaining))}`);
171
+ console.log();
172
+ } catch {
173
+ }
174
+ }
175
+ try {
176
+ (0, import_node_child_process3.execSync)("git rev-parse --git-dir", { stdio: "pipe" });
177
+ const branch = (0, import_node_child_process3.execSync)("git branch --show-current", { encoding: "utf-8" }).trim() || "unknown";
178
+ const commits = (0, import_node_child_process3.execSync)("git rev-list --count HEAD", { encoding: "utf-8" }).trim() || "0";
179
+ console.log(import_picocolors3.default.bold("Git"));
180
+ console.log(` Branch: ${branch}`);
181
+ console.log(` Commits: ${commits}`);
182
+ } catch {
183
+ }
184
+ console.log();
185
+ }
186
+
187
+ // src/commands/new.ts
188
+ var import_node_fs3 = require("fs");
189
+ var import_node_child_process6 = require("child_process");
190
+ var import_node_path2 = require("path");
191
+ var import_node_url = require("url");
192
+ var import_picocolors7 = __toESM(require("picocolors"), 1);
193
+
194
+ // src/prompts/index.ts
195
+ var p = __toESM(require("@clack/prompts"), 1);
196
+ var import_picocolors4 = __toESM(require("picocolors"), 1);
197
+ function handleCancel(value) {
198
+ if (p.isCancel(value)) {
199
+ p.cancel("Operation cancelled.");
200
+ process.exit(0);
201
+ }
202
+ return value;
203
+ }
204
+ async function select2(options) {
205
+ const result = await p.select({
206
+ message: options.message,
207
+ options: options.options,
208
+ initialValue: options.initialValue
209
+ });
210
+ return handleCancel(result);
211
+ }
212
+ async function text2(options) {
213
+ const result = await p.text(options);
214
+ return handleCancel(result);
215
+ }
216
+ async function confirm2(options) {
217
+ const result = await p.confirm({
218
+ ...options,
219
+ active: "Yes",
220
+ inactive: "No"
221
+ });
222
+ return handleCancel(result);
223
+ }
224
+ async function multilineText(options) {
225
+ console.log();
226
+ console.log(import_picocolors4.default.bold(options.message));
227
+ if (options.placeholder) {
228
+ console.log(import_picocolors4.default.dim(options.placeholder));
229
+ }
230
+ console.log();
231
+ const { createInterface: createInterface2 } = await import("readline");
232
+ const rl = createInterface2({
233
+ input: process.stdin,
234
+ output: process.stdout
235
+ });
236
+ const lines = [];
237
+ return new Promise((resolve) => {
238
+ rl.on("line", (line) => {
239
+ if (line === "") {
240
+ rl.close();
241
+ resolve(lines.join("\n"));
242
+ } else {
243
+ lines.push(line);
244
+ }
245
+ });
246
+ rl.on("close", () => {
247
+ resolve(lines.join("\n"));
248
+ });
249
+ });
250
+ }
251
+
252
+ // src/lib/claude.ts
253
+ var import_node_child_process4 = require("child_process");
254
+ var import_node_readline = require("readline");
255
+ var import_picocolors5 = __toESM(require("picocolors"), 1);
256
+ async function runClaude(options) {
257
+ const { prompt, streaming = false, onOutput } = options;
258
+ const timeoutMs = options.timeoutMs || config.iterationTimeoutMinutes * 60 * 1e3;
259
+ const args = ["-p", "--dangerously-skip-permissions"];
260
+ if (streaming) {
261
+ args.push("--no-session-persistence", "--verbose", "--output-format", "stream-json");
262
+ }
263
+ return new Promise((resolve) => {
264
+ let output = "";
265
+ let timedOut = false;
266
+ const child = (0, import_node_child_process4.spawn)("claude", args, {
267
+ stdio: ["pipe", "pipe", "pipe"],
268
+ env: {
269
+ ...process.env,
270
+ CLAUDE_CODE_EXIT_AFTER_STOP_DELAY: "30000"
271
+ }
272
+ });
273
+ const timeoutId = setTimeout(() => {
274
+ timedOut = true;
275
+ child.kill("SIGTERM");
276
+ setTimeout(() => {
277
+ if (!child.killed) {
278
+ child.kill("SIGKILL");
279
+ }
280
+ }, 1e4);
281
+ }, timeoutMs);
282
+ child.stdin.write(prompt);
283
+ child.stdin.end();
284
+ if (streaming && onOutput) {
285
+ const rl = (0, import_node_readline.createInterface)({ input: child.stdout });
286
+ rl.on("line", (line) => {
287
+ output += line + "\n";
288
+ try {
289
+ const parsed = JSON.parse(line);
290
+ if (parsed.type === "assistant") {
291
+ const content = parsed.message?.content;
292
+ if (Array.isArray(content)) {
293
+ for (const item of content) {
294
+ if (item.type === "text" && item.text) {
295
+ onOutput(item.text);
296
+ } else if (item.type === "tool_use") {
297
+ onOutput(`\u{1F527} ${item.name}`);
298
+ }
299
+ }
300
+ }
301
+ } else if (parsed.type === "result") {
302
+ onOutput(`\u2705 ${parsed.subtype || "done"}`);
303
+ }
304
+ } catch {
305
+ onOutput(line);
306
+ }
307
+ });
308
+ } else {
309
+ child.stdout.on("data", (data) => {
310
+ output += data.toString();
311
+ });
312
+ }
313
+ let stderr = "";
314
+ child.stderr.on("data", (data) => {
315
+ stderr += data.toString();
316
+ });
317
+ child.on("close", (code) => {
318
+ clearTimeout(timeoutId);
319
+ if (timedOut) {
320
+ resolve({
321
+ success: false,
322
+ output,
323
+ error: "Timed out"
324
+ });
325
+ } else if (code === 0) {
326
+ resolve({
327
+ success: true,
328
+ output
329
+ });
330
+ } else {
331
+ resolve({
332
+ success: false,
333
+ output,
334
+ error: stderr || `Exit code: ${code}`
335
+ });
336
+ }
337
+ });
338
+ child.on("error", (err) => {
339
+ clearTimeout(timeoutId);
340
+ resolve({
341
+ success: false,
342
+ output,
343
+ error: err.message
344
+ });
345
+ });
346
+ });
347
+ }
348
+ async function runTaskIteration(iteration) {
349
+ const taskPrompt = `You are an autonomous coding agent. Complete ONE task from TODO.md.
350
+
351
+ ## Before Starting
352
+ Read these files to understand the project:
353
+ - CLAUDE.md \u2014 Project context and conventions
354
+ - specs/prd.md \u2014 What we are building
355
+ - specs/technical.md \u2014 How we are building it
356
+ - TODO.md \u2014 Task list
357
+
358
+ ## Workflow
359
+ 1. Find the FIRST unchecked task (- [ ]) in TODO.md
360
+ 2. Plan: Understand what needs to be done
361
+ 3. Implement: Write the code
362
+ 4. Review: Verify it works
363
+ 5. Mark complete: Change - [ ] to - [x] in TODO.md
364
+ 6. Commit: git add . && git commit -m "type(scope): description"
365
+
366
+ ## Rules
367
+ - ONE task per iteration
368
+ - Follow existing code patterns
369
+ - Use conventional commits (feat, fix, docs, refactor, test, chore)
370
+ - If blocked after 3 attempts, output RALPH_BLOCKED: <reason>
371
+
372
+ ## When Done
373
+ Output: RALPH_COMPLETE
374
+ If no tasks remain: RALPH_ALL_DONE`;
375
+ let lastResult = {
376
+ success: false,
377
+ output: "",
378
+ error: "No attempts made"
379
+ };
380
+ for (let attempt = 1; attempt <= config.maxClaudeAttempts; attempt++) {
381
+ console.log(import_picocolors5.default.yellow(`Attempt ${attempt}/${config.maxClaudeAttempts}`));
382
+ const result = await runClaude({
383
+ prompt: taskPrompt,
384
+ streaming: true,
385
+ onOutput: (line) => {
386
+ process.stdout.write(line);
387
+ if (!line.endsWith("\n")) {
388
+ process.stdout.write("\n");
389
+ }
390
+ }
391
+ });
392
+ lastResult = result;
393
+ if (result.success) {
394
+ return result;
395
+ }
396
+ const isTransient = result.output.includes("No messages returned") || result.output.includes("ECONNRESET") || result.output.includes("ETIMEDOUT") || result.error?.includes("ECONNRESET") || result.error?.includes("ETIMEDOUT");
397
+ if (isTransient && attempt < config.maxClaudeAttempts) {
398
+ console.log(import_picocolors5.default.yellow("Transient error, retrying..."));
399
+ await sleep(attempt * 5e3);
400
+ continue;
401
+ }
402
+ break;
403
+ }
404
+ return lastResult;
405
+ }
406
+
407
+ // src/commands/loop.ts
408
+ var import_node_fs2 = require("fs");
409
+ var import_node_child_process5 = require("child_process");
410
+ var import_picocolors6 = __toESM(require("picocolors"), 1);
411
+ function countUncheckedTasks() {
412
+ if (!(0, import_node_fs2.existsSync)(config.todoFile)) return 0;
413
+ const content = (0, import_node_fs2.readFileSync)(config.todoFile, "utf-8");
414
+ const lines = content.split("\n");
415
+ let count = 0;
416
+ for (const line of lines) {
417
+ if (/^\s*-\s*\[\s\]/.test(line)) {
418
+ count++;
419
+ }
420
+ }
421
+ return count;
422
+ }
423
+ function hasUncheckedTasks() {
424
+ if (!(0, import_node_fs2.existsSync)(config.todoFile)) return false;
425
+ const content = (0, import_node_fs2.readFileSync)(config.todoFile, "utf-8");
426
+ return /^\s*-\s*\[\s\]/m.test(content);
427
+ }
428
+ function getCurrentCommit() {
429
+ try {
430
+ return (0, import_node_child_process5.execSync)("git rev-parse HEAD", { encoding: "utf-8" }).trim();
431
+ } catch {
432
+ return "none";
433
+ }
434
+ }
435
+ function pushToOrigin() {
436
+ try {
437
+ (0, import_node_child_process5.execSync)("git push origin HEAD", { stdio: "pipe" });
438
+ console.log("Pushed to origin");
439
+ } catch {
440
+ }
441
+ }
442
+ async function cmdLoop() {
443
+ if (!(0, import_node_fs2.existsSync)(config.todoFile)) {
444
+ console.log(import_picocolors6.default.red(`Missing ${config.todoFile}`));
445
+ process.exit(1);
446
+ }
447
+ const maxIterations = countUncheckedTasks();
448
+ if (maxIterations === 0) {
449
+ console.log(import_picocolors6.default.green(`No unchecked tasks in ${config.todoFile}. All done!`));
450
+ process.exit(0);
451
+ }
452
+ console.log();
453
+ console.log(import_picocolors6.default.green("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
454
+ console.log(import_picocolors6.default.green(" Starting Build Loop"));
455
+ console.log(import_picocolors6.default.green(` Tasks remaining: ${maxIterations}`));
456
+ console.log(import_picocolors6.default.green(` Timeout: ${config.iterationTimeoutMinutes}m per task`));
457
+ console.log(import_picocolors6.default.green("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
458
+ let consecutiveFailures = 0;
459
+ let noCommitCycles = 0;
460
+ let lastCommit = getCurrentCommit();
461
+ for (let i = 1; i <= maxIterations; i++) {
462
+ console.log();
463
+ console.log(import_picocolors6.default.yellow(`\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 Task ${i} of ${maxIterations} \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`));
464
+ cleanupStaleProcesses();
465
+ const startTime = Date.now();
466
+ const result = await runTaskIteration(i);
467
+ const duration = Math.round((Date.now() - startTime) / 1e3);
468
+ console.log();
469
+ console.log(`Completed in ${duration}s (exit: ${result.success ? 0 : 1})`);
470
+ if (!result.success) {
471
+ consecutiveFailures++;
472
+ console.log(import_picocolors6.default.red(`Failure ${consecutiveFailures}/${config.maxConsecutiveFailures}`));
473
+ await sleep(3e4);
474
+ } else {
475
+ consecutiveFailures = 0;
476
+ }
477
+ const currentCommit = getCurrentCommit();
478
+ if (currentCommit === lastCommit) {
479
+ if (result.success) {
480
+ noCommitCycles++;
481
+ console.log(import_picocolors6.default.yellow(`No commit (${noCommitCycles}/${config.maxNoCommitCycles})`));
482
+ }
483
+ } else {
484
+ noCommitCycles = 0;
485
+ lastCommit = currentCommit;
486
+ console.log(import_picocolors6.default.green(`New commit: ${currentCommit.substring(0, 7)}`));
487
+ pushToOrigin();
488
+ }
489
+ if (consecutiveFailures >= config.maxConsecutiveFailures) {
490
+ console.log(import_picocolors6.default.red(`Stopping: ${config.maxConsecutiveFailures} consecutive failures`));
491
+ process.exit(1);
492
+ }
493
+ if (noCommitCycles >= config.maxNoCommitCycles) {
494
+ console.log(import_picocolors6.default.red(`Stopping: No commits for ${config.maxNoCommitCycles} cycles`));
495
+ process.exit(1);
496
+ }
497
+ if (!hasUncheckedTasks()) {
498
+ console.log();
499
+ console.log(import_picocolors6.default.green("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
500
+ console.log(import_picocolors6.default.green(" All tasks completed!"));
501
+ console.log(import_picocolors6.default.green("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
502
+ pushToOrigin();
503
+ process.exit(0);
504
+ }
505
+ await sleep(config.cooldownSeconds * 1e3);
506
+ }
507
+ console.log(import_picocolors6.default.yellow(`Reached max iterations (${maxIterations})`));
508
+ pushToOrigin();
509
+ }
510
+
511
+ // src/commands/new.ts
512
+ var __dirname = (0, import_node_path2.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl));
513
+ var LOCAL_TEMPLATES_DIR = ".chiefwiggum/templates";
514
+ var BUNDLED_TEMPLATES_DIR = (0, import_node_path2.join)(__dirname, "..", "templates");
515
+ async function cmdNew(planFile) {
516
+ console.log(import_picocolors7.default.bold("Project Setup"));
517
+ console.log();
518
+ console.log(`Working directory: ${import_picocolors7.default.cyan(process.cwd())}`);
519
+ console.log();
520
+ const shouldInit = await confirm2({
521
+ message: "Initialize Chief Wiggum in this directory?",
522
+ initialValue: true
523
+ });
524
+ if (!shouldInit) {
525
+ console.log(import_picocolors7.default.yellow("Aborted."));
526
+ process.exit(0);
527
+ }
528
+ await setupTemplates();
529
+ const existingFiles = [];
530
+ if ((0, import_node_fs3.existsSync)(config.todoFile)) existingFiles.push("TODO.md");
531
+ if ((0, import_node_fs3.existsSync)("specs")) existingFiles.push("specs/");
532
+ if (existingFiles.length > 0) {
533
+ console.log();
534
+ console.log(import_picocolors7.default.yellow("Existing files found:"));
535
+ for (const f of existingFiles) {
536
+ console.log(` - ${f}`);
537
+ }
538
+ console.log();
539
+ const shouldOverwrite = await confirm2({
540
+ message: "Overwrite these files?",
541
+ initialValue: false
542
+ });
543
+ if (!shouldOverwrite) {
544
+ console.log(import_picocolors7.default.yellow("Aborted."));
545
+ process.exit(0);
546
+ }
547
+ }
548
+ if (planFile) {
549
+ if (!(0, import_node_fs3.existsSync)(planFile)) {
550
+ console.log(import_picocolors7.default.red(`Plan file not found: ${planFile}`));
551
+ process.exit(1);
552
+ }
553
+ await generateFromPlan(planFile);
554
+ await cmdLoop();
555
+ return;
556
+ }
557
+ const setupChoice = await select2({
558
+ message: "How would you like to set up this project?",
559
+ options: [
560
+ { value: "plan", label: "From a plan file - I have a plan.md ready" },
561
+ { value: "describe", label: "Describe it - I'll tell you what to build" },
562
+ { value: "existing", label: "Existing TODO - Just start the loop with current TODO.md" }
563
+ ]
564
+ });
565
+ switch (setupChoice) {
566
+ case "plan": {
567
+ if ((0, import_node_fs3.existsSync)("plans")) {
568
+ const plans = (0, import_node_fs3.readdirSync)("plans").filter((f) => f.endsWith(".md"));
569
+ if (plans.length > 0) {
570
+ console.log();
571
+ console.log(import_picocolors7.default.bold("Available plans:"));
572
+ for (const f of plans) {
573
+ console.log(` ${import_picocolors7.default.cyan(`plans/${f}`)}`);
574
+ }
575
+ console.log();
576
+ }
577
+ }
578
+ const planPath = await text2({
579
+ message: "Path to plan file",
580
+ defaultValue: "plans/plan.md"
581
+ });
582
+ if (!(0, import_node_fs3.existsSync)(planPath)) {
583
+ console.log(import_picocolors7.default.red(`Plan file not found: ${planPath}`));
584
+ process.exit(1);
585
+ }
586
+ await generateFromPlan(planPath);
587
+ break;
588
+ }
589
+ case "describe":
590
+ await interactiveDescribe();
591
+ break;
592
+ case "existing":
593
+ if (!(0, import_node_fs3.existsSync)(config.todoFile)) {
594
+ console.log(import_picocolors7.default.red("No TODO.md found. Run 'chiefwiggum new' to set up first."));
595
+ process.exit(1);
596
+ }
597
+ break;
598
+ }
599
+ console.log();
600
+ const startChoice = await select2({
601
+ message: "Ready to start. What would you like to do?",
602
+ options: [
603
+ { value: "start", label: "Start the build loop now" },
604
+ { value: "review", label: "Review files first, then start manually with 'chiefwiggum loop'" },
605
+ { value: "exit", label: "Exit" }
606
+ ]
607
+ });
608
+ switch (startChoice) {
609
+ case "start":
610
+ await cmdLoop();
611
+ break;
612
+ case "review":
613
+ console.log();
614
+ console.log(import_picocolors7.default.green("Setup complete!") + " Review your files, then run:");
615
+ console.log(` ${import_picocolors7.default.cyan("chiefwiggum loop")}`);
616
+ break;
617
+ case "exit":
618
+ console.log(import_picocolors7.default.yellow("Exiting."));
619
+ break;
620
+ }
621
+ }
622
+ async function setupTemplates() {
623
+ if ((0, import_node_fs3.existsSync)(LOCAL_TEMPLATES_DIR)) {
624
+ console.log(import_picocolors7.default.dim(`Templates already exist at ${LOCAL_TEMPLATES_DIR}`));
625
+ return;
626
+ }
627
+ if (!(0, import_node_fs3.existsSync)(BUNDLED_TEMPLATES_DIR)) {
628
+ console.log(import_picocolors7.default.red("Bundled templates not found. Please reinstall chiefwiggum."));
629
+ process.exit(1);
630
+ }
631
+ console.log(import_picocolors7.default.cyan(`Setting up templates in ${LOCAL_TEMPLATES_DIR}...`));
632
+ (0, import_node_fs3.mkdirSync)(LOCAL_TEMPLATES_DIR, { recursive: true });
633
+ (0, import_node_fs3.cpSync)(BUNDLED_TEMPLATES_DIR, LOCAL_TEMPLATES_DIR, { recursive: true });
634
+ console.log(import_picocolors7.default.green(`\u2713 Templates copied to ${LOCAL_TEMPLATES_DIR}`));
635
+ console.log(import_picocolors7.default.dim(" You can customize these templates to change how specs are generated."));
636
+ console.log();
637
+ }
638
+ async function interactiveDescribe() {
639
+ console.log();
640
+ console.log(import_picocolors7.default.bold("Tell me about your project"));
641
+ console.log();
642
+ const projectName = await text2({
643
+ message: "Project name"
644
+ });
645
+ const projectType = await select2({
646
+ message: "What type of project is this?",
647
+ options: [
648
+ { value: "web", label: "Web application (Next.js, React, etc.)" },
649
+ { value: "api", label: "API / Backend service" },
650
+ { value: "cli", label: "CLI tool" },
651
+ { value: "library", label: "Library / Package" },
652
+ { value: "mobile", label: "Mobile app" },
653
+ { value: "other", label: "Other" }
654
+ ]
655
+ });
656
+ const techStack = await text2({
657
+ message: "Tech stack (e.g., Next.js, TypeScript, Convex)"
658
+ });
659
+ const description = await multilineText({
660
+ message: "Describe what you're building:",
661
+ placeholder: "(Enter description, then press Enter on empty line to finish)"
662
+ });
663
+ const featuresText = await multilineText({
664
+ message: "List key features (one per line, empty line to finish):"
665
+ });
666
+ const features = featuresText.split("\n").filter((line) => line.trim()).map((line) => `- ${line}`).join("\n");
667
+ const planContent = `# ${projectName}
668
+
669
+ ## Overview
670
+ ${description}
671
+
672
+ ## Project Type
673
+ ${projectType}
674
+
675
+ ## Tech Stack
676
+ ${techStack}
677
+
678
+ ## Key Features
679
+ ${features}
680
+ `;
681
+ (0, import_node_fs3.mkdirSync)("plans", { recursive: true });
682
+ const planPath = "plans/plan.md";
683
+ (0, import_node_fs3.writeFileSync)(planPath, planContent);
684
+ console.log();
685
+ console.log(import_picocolors7.default.green(`Plan saved to ${planPath}`));
686
+ await generateFromPlan(planPath);
687
+ }
688
+ async function generateFromPlan(planFile) {
689
+ const planContent = (0, import_node_fs3.readFileSync)(planFile, "utf-8");
690
+ console.log();
691
+ console.log(import_picocolors7.default.green("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
692
+ console.log(import_picocolors7.default.green(` Generating specs from: ${planFile}`));
693
+ console.log(import_picocolors7.default.green("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
694
+ if (!(0, import_node_fs3.existsSync)(LOCAL_TEMPLATES_DIR)) {
695
+ console.log(import_picocolors7.default.red(`Templates not found at: ${LOCAL_TEMPLATES_DIR}`));
696
+ console.log();
697
+ console.log("Run 'chiefwiggum new' to set up templates first.");
698
+ process.exit(1);
699
+ }
700
+ (0, import_node_fs3.mkdirSync)("specs", { recursive: true });
701
+ const prdTemplate = (0, import_node_fs3.readFileSync)(`${LOCAL_TEMPLATES_DIR}/prd-template.md`, "utf-8");
702
+ const techTemplate = (0, import_node_fs3.readFileSync)(`${LOCAL_TEMPLATES_DIR}/technical-template.md`, "utf-8");
703
+ const todoTemplate = (0, import_node_fs3.readFileSync)(`${LOCAL_TEMPLATES_DIR}/TODO-template.md`, "utf-8");
704
+ const claudeTemplate = (0, import_node_fs3.readFileSync)(`${LOCAL_TEMPLATES_DIR}/CLAUDE-template.md`, "utf-8");
705
+ console.log();
706
+ console.log(import_picocolors7.default.yellow("[1/4] Generating specs/prd.md..."));
707
+ const prdPrompt = `You are filling in a PRD template based on a project plan.
708
+
709
+ Here is the plan:
710
+ <plan>
711
+ ${planContent}
712
+ </plan>
713
+
714
+ Here is the template to fill in:
715
+ <template>
716
+ ${prdTemplate}
717
+ </template>
718
+
719
+ Fill in the template with specific details from the plan.
720
+ Replace all placeholder text in [brackets] with real content.
721
+ Write the completed PRD directly to specs/prd.md.
722
+ Do NOT ask questions \u2014 infer everything from the plan.`;
723
+ await runClaude({ prompt: prdPrompt });
724
+ console.log(import_picocolors7.default.green(" \u2713 specs/prd.md"));
725
+ console.log();
726
+ console.log(import_picocolors7.default.yellow("[2/4] Generating specs/technical.md..."));
727
+ const prdGenerated = (0, import_node_fs3.existsSync)("specs/prd.md") ? (0, import_node_fs3.readFileSync)("specs/prd.md", "utf-8") : "";
728
+ const techPrompt = `You are filling in a Technical Specification template based on a PRD.
729
+
730
+ Here is the PRD:
731
+ <prd>
732
+ ${prdGenerated}
733
+ </prd>
734
+
735
+ Here is the template to fill in:
736
+ <template>
737
+ ${techTemplate}
738
+ </template>
739
+
740
+ Fill in the template with specific technical details.
741
+ Replace all placeholder text in [brackets] with real content.
742
+ Write the completed spec directly to specs/technical.md.
743
+ Do NOT ask questions \u2014 infer everything from the PRD.`;
744
+ await runClaude({ prompt: techPrompt });
745
+ console.log(import_picocolors7.default.green(" \u2713 specs/technical.md"));
746
+ console.log();
747
+ if ((0, import_node_fs3.existsSync)("CLAUDE.md")) {
748
+ console.log(import_picocolors7.default.yellow("[3/4] Skipping CLAUDE.md (already exists)"));
749
+ } else {
750
+ console.log(import_picocolors7.default.yellow("[3/4] Generating CLAUDE.md..."));
751
+ const techGenerated2 = (0, import_node_fs3.existsSync)("specs/technical.md") ? (0, import_node_fs3.readFileSync)("specs/technical.md", "utf-8") : "";
752
+ const claudePrompt = `You are filling in a CLAUDE.md template for a project.
753
+
754
+ Here is the PRD:
755
+ <prd>
756
+ ${prdGenerated}
757
+ </prd>
758
+
759
+ Here is the Technical Spec:
760
+ <technical>
761
+ ${techGenerated2}
762
+ </technical>
763
+
764
+ Here is the template to fill in:
765
+ <template>
766
+ ${claudeTemplate}
767
+ </template>
768
+
769
+ Fill in the template with project-specific details.
770
+ Replace all placeholder text in [brackets] with real content.
771
+ Keep it concise - this is a quick reference for AI agents.
772
+ Write directly to CLAUDE.md.`;
773
+ await runClaude({ prompt: claudePrompt });
774
+ console.log(import_picocolors7.default.green(" \u2713 CLAUDE.md"));
775
+ }
776
+ console.log();
777
+ console.log(import_picocolors7.default.yellow("[4/4] Generating TODO.md..."));
778
+ const techGenerated = (0, import_node_fs3.existsSync)("specs/technical.md") ? (0, import_node_fs3.readFileSync)("specs/technical.md", "utf-8") : "";
779
+ const todoPrompt = `You are filling in a TODO template based on specs.
780
+
781
+ Here is the PRD:
782
+ <prd>
783
+ ${prdGenerated}
784
+ </prd>
785
+
786
+ Here is the Technical Specification:
787
+ <technical>
788
+ ${techGenerated}
789
+ </technical>
790
+
791
+ Here is the template to follow:
792
+ <template>
793
+ ${todoTemplate}
794
+ </template>
795
+
796
+ Create a phased TODO.md following the template structure.
797
+ Use checkbox format: - [ ] Task description
798
+ Keep tasks granular (1-2 hours max each).
799
+ End each phase with: - [ ] Phase N review
800
+ Write directly to TODO.md.`;
801
+ await runClaude({ prompt: todoPrompt });
802
+ console.log(import_picocolors7.default.green(" \u2713 TODO.md"));
803
+ console.log();
804
+ console.log(import_picocolors7.default.green("Specs generated:"));
805
+ console.log(" - specs/prd.md");
806
+ console.log(" - specs/technical.md");
807
+ if (!(0, import_node_fs3.existsSync)("CLAUDE.md")) {
808
+ console.log(" - CLAUDE.md");
809
+ }
810
+ console.log(" - TODO.md");
811
+ try {
812
+ (0, import_node_child_process6.execSync)("git rev-parse --git-dir", { stdio: "pipe" });
813
+ (0, import_node_child_process6.execSync)("git add specs/ TODO.md CLAUDE.md 2>/dev/null || true", { stdio: "pipe" });
814
+ (0, import_node_child_process6.execSync)('git commit -m "chore: generate specs from plan" 2>/dev/null || true', {
815
+ stdio: "pipe"
816
+ });
817
+ } catch {
818
+ }
819
+ }
820
+
821
+ // src/commands/clean.ts
822
+ var import_node_fs4 = require("fs");
823
+ var import_picocolors8 = __toESM(require("picocolors"), 1);
824
+ async function cmdClean() {
825
+ console.log(import_picocolors8.default.bold("Clean Project"));
826
+ console.log();
827
+ const filesToRemove = [];
828
+ if ((0, import_node_fs4.existsSync)(".chiefwiggum")) filesToRemove.push(".chiefwiggum/");
829
+ if ((0, import_node_fs4.existsSync)("specs")) filesToRemove.push("specs/");
830
+ if ((0, import_node_fs4.existsSync)("TODO.md")) filesToRemove.push("TODO.md");
831
+ if (filesToRemove.length === 0) {
832
+ console.log(import_picocolors8.default.dim("No chiefwiggum files found to clean."));
833
+ return;
834
+ }
835
+ console.log("Files to remove:");
836
+ for (const f of filesToRemove) {
837
+ console.log(` ${import_picocolors8.default.red(f)}`);
838
+ }
839
+ console.log();
840
+ const shouldClean = await confirm2({
841
+ message: "Remove these files?",
842
+ initialValue: false
843
+ });
844
+ if (!shouldClean) {
845
+ console.log(import_picocolors8.default.yellow("Aborted."));
846
+ return;
847
+ }
848
+ for (const f of filesToRemove) {
849
+ const path = f.replace(/\/$/, "");
850
+ (0, import_node_fs4.rmSync)(path, { recursive: true, force: true });
851
+ console.log(import_picocolors8.default.red(`\u2717 Removed ${f}`));
852
+ }
853
+ console.log();
854
+ console.log(import_picocolors8.default.green("Clean complete."));
855
+ }
856
+
857
+ // src/cli.ts
858
+ var __dirname2 = (0, import_node_path3.dirname)((0, import_node_url2.fileURLToPath)(importMetaUrl));
859
+ function getVersion() {
860
+ try {
861
+ const pkgPath = (0, import_node_path3.join)(__dirname2, "..", "package.json");
862
+ const pkg = JSON.parse((0, import_node_fs5.readFileSync)(pkgPath, "utf-8"));
863
+ return pkg.version || "dev";
864
+ } catch {
865
+ return "dev";
866
+ }
867
+ }
868
+ var version = getVersion();
869
+ async function main() {
870
+ setupSignalHandlers();
871
+ const program = new import_commander.Command();
872
+ program.name("chiefwiggum").description("Autonomous coding agent CLI. Point it at a plan, watch it build.").version(version);
873
+ program.command("new [plan]").description("Initialize project (interactive or from plan file)").action(async (plan) => {
874
+ await showBanner(version);
875
+ await cmdNew(plan);
876
+ });
877
+ program.command("loop").description("Run the build loop on existing TODO.md").action(async () => {
878
+ await showBanner(version);
879
+ await cmdLoop();
880
+ });
881
+ program.command("status").description("Show current project status").action(async () => {
882
+ await showBanner(version);
883
+ await cmdStatus();
884
+ });
885
+ program.command("clean").description("Remove chiefwiggum files from project").action(async () => {
886
+ await showBanner(version);
887
+ await cmdClean();
888
+ });
889
+ program.action(async () => {
890
+ await showBanner(version);
891
+ const { existsSync: existsSync5 } = await import("fs");
892
+ if (existsSync5("TODO.md")) {
893
+ await cmdLoop();
894
+ } else {
895
+ console.log(import_picocolors9.default.yellow("No TODO.md found. Run 'chiefwiggum new' to get started."));
896
+ process.exit(1);
897
+ }
898
+ });
899
+ await program.parseAsync();
900
+ }
901
+ main().catch((err) => {
902
+ console.error(import_picocolors9.default.red("Error:"), err.message);
903
+ process.exit(1);
904
+ });