bugzero 0.0.41 → 0.0.43
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 +7 -3
- package/dist/index.js +145 -58
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -102,6 +102,7 @@ bugzero rescue
|
|
|
102
102
|
- **Backups on every patch.** Each file BugZero modifies gets a `.opspilot.bak` copy of its original content first.
|
|
103
103
|
- **Dry-run mode.** `bugzero rescue --dry-run` runs validation and error analysis, but makes no fix attempts and never modifies your source files (it still writes a report that diagnoses the failures it found). It exits with code 1 if it found failures and 0 if everything already passes, so you can use it as a check in scripts.
|
|
104
104
|
- **Prompt size limits.** Very long error output and very large source files are shortened before being sent to the model (for logs, the start and end are kept), so a big failure can't exceed the model's context window or waste tokens.
|
|
105
|
+
- **Command timeouts.** `--timeout <seconds>` (available on both `hunt` and `rescue`) stops any build, lint, or test command that runs longer than that. It kills the whole process tree rather than just the shell, so a hung test run cannot linger in the background, and the step is reported as failed along with whatever output it produced. Without it, a command that hangs will hang BugZero too, so it is worth setting in CI.
|
|
105
106
|
|
|
106
107
|
### Project awareness
|
|
107
108
|
|
|
@@ -190,7 +191,7 @@ OPENROUTER_API_KEY=your_openrouter_api_key_here
|
|
|
190
191
|
## Commands
|
|
191
192
|
|
|
192
193
|
### `bugzero hunt`
|
|
193
|
-
Run build, lint, and test checks. Reports all failures without modifying any files.
|
|
194
|
+
Run build, lint, and test checks. Reports all failures without modifying any files. Accepts `--timeout <seconds>`, like `rescue`.
|
|
194
195
|
|
|
195
196
|
```bash
|
|
196
197
|
bugzero hunt --path /path/to/your/project
|
|
@@ -210,6 +211,7 @@ bugzero rescue --path /path/to/your/project
|
|
|
210
211
|
|---|---|---|---|
|
|
211
212
|
| `--path <dir>` | `-p` | `.` | Path to the target project |
|
|
212
213
|
| `--max-attempts <n>` | `-n` | `3` | Maximum AI fix iterations before giving up |
|
|
214
|
+
| `--timeout <seconds>` | `-t` | none | Stop any build, lint, or test command that runs longer than this many seconds |
|
|
213
215
|
| `--dry-run` | | `false` | Analyze only — no files will be written |
|
|
214
216
|
|
|
215
217
|
</details>
|
|
@@ -267,8 +269,10 @@ BugZero provides clear, actionable output for common failure modes:
|
|
|
267
269
|
| Error | Message |
|
|
268
270
|
|---|---|
|
|
269
271
|
| Invalid API key (401) | Prompts you to check your `OPENROUTER_API_KEY` |
|
|
270
|
-
|
|
|
271
|
-
|
|
|
272
|
+
| Insufficient credits (402) | Says your OpenRouter balance is too low, links to the credits page, and suggests switching to a cheaper or free model with `bugzero vibe` |
|
|
273
|
+
| Rate limit exceeded (429) | Says OpenRouter is rate limiting and suggests waiting, switching models, or adding credits |
|
|
274
|
+
| Prompt too large for the model | Says the prompt exceeded the model's context window and suggests a larger-context model with `bugzero vibe` |
|
|
275
|
+
| Model not found | Names the unavailable model ID and suggests running `bugzero vibe` |
|
|
272
276
|
| Patch not applied | Reports exact mismatch between AI suggestion and file content, after exhausting exact, whitespace, and fuzzy matching |
|
|
273
277
|
| Max attempts reached | Exits cleanly with the last error log for manual review |
|
|
274
278
|
|
package/dist/index.js
CHANGED
|
@@ -479,9 +479,9 @@ var require_dist2 = __commonJS({
|
|
|
479
479
|
});
|
|
480
480
|
module2.exports = __toCommonJS(index_exports);
|
|
481
481
|
var import_fs2 = __toESM2(require("fs"));
|
|
482
|
-
var import_path2 = __toESM2(require("path"));
|
|
483
|
-
var import_fs3 = __toESM2(require("fs"));
|
|
484
482
|
var import_path3 = __toESM2(require("path"));
|
|
483
|
+
var import_fs3 = __toESM2(require("fs"));
|
|
484
|
+
var import_path2 = __toESM2(require("path"));
|
|
485
485
|
var VALID_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
|
|
486
486
|
var LOCKFILE_MANAGERS = [
|
|
487
487
|
{ file: "pnpm-workspace.yaml", manager: "pnpm" },
|
|
@@ -493,7 +493,7 @@ var require_dist2 = __commonJS({
|
|
|
493
493
|
];
|
|
494
494
|
function detectPackageManager3(cwd = process.cwd()) {
|
|
495
495
|
try {
|
|
496
|
-
const pkgPath =
|
|
496
|
+
const pkgPath = import_path2.default.join(cwd, "package.json");
|
|
497
497
|
if (import_fs3.default.existsSync(pkgPath)) {
|
|
498
498
|
const pkg = JSON.parse(import_fs3.default.readFileSync(pkgPath, "utf-8"));
|
|
499
499
|
if (typeof pkg.packageManager === "string") {
|
|
@@ -506,39 +506,76 @@ var require_dist2 = __commonJS({
|
|
|
506
506
|
} catch {
|
|
507
507
|
}
|
|
508
508
|
for (const { file, manager } of LOCKFILE_MANAGERS) {
|
|
509
|
-
if (import_fs3.default.existsSync(
|
|
509
|
+
if (import_fs3.default.existsSync(import_path2.default.join(cwd, file))) return manager;
|
|
510
510
|
}
|
|
511
511
|
return "npm";
|
|
512
512
|
}
|
|
513
513
|
function getRunScriptCommand(manager, script) {
|
|
514
514
|
return `${manager} run ${script}`;
|
|
515
515
|
}
|
|
516
|
-
|
|
516
|
+
var import_child_process = require("child_process");
|
|
517
|
+
var import_path22 = __toESM2(require("path"));
|
|
518
|
+
var import_tree_kill = __toESM2(require("tree-kill"));
|
|
519
|
+
function killProcessTree(pid) {
|
|
520
|
+
if (process.platform === "win32") {
|
|
521
|
+
const systemRoot = process.env.SystemRoot ?? process.env.windir ?? "C:\\Windows";
|
|
522
|
+
const taskkill = import_path22.default.join(systemRoot, "System32", "taskkill.exe");
|
|
523
|
+
(0, import_child_process.spawnSync)(taskkill, ["/pid", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
(0, import_tree_kill.default)(pid, "SIGKILL");
|
|
527
|
+
}
|
|
528
|
+
var KILL_GRACE_MS = 5e3;
|
|
529
|
+
async function runCommand(command, cwd, options = {}) {
|
|
517
530
|
const { execa } = await import("execa");
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
env
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
output: result.all ?? result.stdout
|
|
531
|
-
};
|
|
532
|
-
} catch (err) {
|
|
531
|
+
const subprocess = execa(command, {
|
|
532
|
+
shell: true,
|
|
533
|
+
cwd: cwd ?? process.cwd(),
|
|
534
|
+
all: true,
|
|
535
|
+
env: {
|
|
536
|
+
...process.env,
|
|
537
|
+
CI: "true"
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
const timedOutResult = (output, confirmedStopped) => {
|
|
541
|
+
const seconds = options.timeoutMs / 1e3;
|
|
542
|
+
const note = confirmedStopped ? "and was stopped" : "and could not be confirmed stopped";
|
|
533
543
|
return {
|
|
534
544
|
success: false,
|
|
535
|
-
|
|
545
|
+
timedOut: true,
|
|
546
|
+
output: `${output}
|
|
547
|
+
|
|
548
|
+
[BugZero] Command timed out after ${seconds}s ${note}: ${command}`
|
|
536
549
|
};
|
|
550
|
+
};
|
|
551
|
+
let timedOut = false;
|
|
552
|
+
let timer;
|
|
553
|
+
let graceTimer;
|
|
554
|
+
const gaveUp = new Promise((resolve) => {
|
|
555
|
+
if (options.timeoutMs === void 0) return;
|
|
556
|
+
timer = setTimeout(() => {
|
|
557
|
+
timedOut = true;
|
|
558
|
+
if (subprocess.pid !== void 0) killProcessTree(subprocess.pid);
|
|
559
|
+
graceTimer = setTimeout(() => resolve(timedOutResult("", false)), KILL_GRACE_MS);
|
|
560
|
+
}, options.timeoutMs);
|
|
561
|
+
});
|
|
562
|
+
const finished = subprocess.then(
|
|
563
|
+
(result) => ({ success: true, output: result.all ?? result.stdout }),
|
|
564
|
+
(err) => {
|
|
565
|
+
const output = err.all ?? err.stderr ?? err.stdout ?? err.message;
|
|
566
|
+
return timedOut ? timedOutResult(output, true) : { success: false, output };
|
|
567
|
+
}
|
|
568
|
+
);
|
|
569
|
+
try {
|
|
570
|
+
return await Promise.race([finished, gaveUp]);
|
|
571
|
+
} finally {
|
|
572
|
+
clearTimeout(timer);
|
|
573
|
+
clearTimeout(graceTimer);
|
|
537
574
|
}
|
|
538
575
|
}
|
|
539
576
|
function readScripts(cwd = process.cwd()) {
|
|
540
577
|
try {
|
|
541
|
-
const pkgPath =
|
|
578
|
+
const pkgPath = import_path3.default.join(cwd, "package.json");
|
|
542
579
|
if (!import_fs2.default.existsSync(pkgPath)) return [];
|
|
543
580
|
const pkg = JSON.parse(import_fs2.default.readFileSync(pkgPath, "utf-8"));
|
|
544
581
|
if (typeof pkg.scripts !== "object" || pkg.scripts === null) return [];
|
|
@@ -547,15 +584,15 @@ var require_dist2 = __commonJS({
|
|
|
547
584
|
return [];
|
|
548
585
|
}
|
|
549
586
|
}
|
|
550
|
-
async function runScript(script, cwd) {
|
|
587
|
+
async function runScript(script, cwd, options) {
|
|
551
588
|
if (!readScripts(cwd).includes(script)) {
|
|
552
589
|
return { success: true, output: "", skipped: true };
|
|
553
590
|
}
|
|
554
|
-
return runCommand(getRunScriptCommand(detectPackageManager3(cwd), script), cwd);
|
|
591
|
+
return runCommand(getRunScriptCommand(detectPackageManager3(cwd), script), cwd, options);
|
|
555
592
|
}
|
|
556
|
-
var runBuild = (cwd) => runScript("build", cwd);
|
|
557
|
-
var runLint = (cwd) => runScript("lint", cwd);
|
|
558
|
-
var runTests = (cwd) => runScript("test", cwd);
|
|
593
|
+
var runBuild = (cwd, options) => runScript("build", cwd, options);
|
|
594
|
+
var runLint = (cwd, options) => runScript("lint", cwd, options);
|
|
595
|
+
var runTests = (cwd, options) => runScript("test", cwd, options);
|
|
559
596
|
var TS_ERROR_RE = /^(.+)\((\d+),(\d+)\):\s+error\s+TS\d+:\s+(.+)$/gm;
|
|
560
597
|
var ESLINT_ERROR_RE = /^\s+(\d+):(\d+)\s+error\s+(.+)$/gm;
|
|
561
598
|
var JEST_FAIL_RE = /● (.+)/g;
|
|
@@ -610,7 +647,7 @@ var require_dist2 = __commonJS({
|
|
|
610
647
|
{ step: "lint", label: "lint", run: runLint },
|
|
611
648
|
{ step: "test", label: "tests", run: runTests }
|
|
612
649
|
];
|
|
613
|
-
async function runValidationPipeline(cwd) {
|
|
650
|
+
async function runValidationPipeline(cwd, options) {
|
|
614
651
|
const targetCwd = cwd ?? process.cwd();
|
|
615
652
|
const scripts = readScripts(targetCwd);
|
|
616
653
|
let ranAnyStep = false;
|
|
@@ -621,7 +658,7 @@ var require_dist2 = __commonJS({
|
|
|
621
658
|
}
|
|
622
659
|
ranAnyStep = true;
|
|
623
660
|
console.log(`[BugZero] Running ${label}...`);
|
|
624
|
-
const result = await run(targetCwd);
|
|
661
|
+
const result = await run(targetCwd, options);
|
|
625
662
|
if (!result.success) {
|
|
626
663
|
return { success: false, step, output: result.output };
|
|
627
664
|
}
|
|
@@ -667,12 +704,47 @@ var require_dist3 = __commonJS({
|
|
|
667
704
|
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
668
705
|
var index_exports = {};
|
|
669
706
|
__export(index_exports, {
|
|
670
|
-
OpenRouterProvider: () => OpenRouterProvider
|
|
707
|
+
OpenRouterProvider: () => OpenRouterProvider,
|
|
708
|
+
getErrorHints: () => getErrorHints
|
|
671
709
|
});
|
|
672
710
|
module2.exports = __toCommonJS(index_exports);
|
|
673
711
|
var import_axios = __toESM2(require("axios"));
|
|
674
712
|
var import_fs2 = __toESM2(require("fs"));
|
|
675
713
|
var import_path2 = __toESM2(require("path"));
|
|
714
|
+
var CREDITS_PATTERN = /insufficient credits|credit balance|add more credits|requires more credits|token_limit_exceeded/i;
|
|
715
|
+
var CONTEXT_PATTERN = /context length|context window|maximum context|context_length_exceeded|string_too_long|too many tokens|reduce the length/i;
|
|
716
|
+
var MODEL_PATTERN = /not a valid model|no endpoints found|model.{0,40}(not found|does not exist|unavailable)/i;
|
|
717
|
+
function getErrorHints({ status, message, model }) {
|
|
718
|
+
if (status === 401) {
|
|
719
|
+
return ["\u{1F449} Please check if your OPENROUTER_API_KEY is correct and active."];
|
|
720
|
+
}
|
|
721
|
+
if (status === 402 || CREDITS_PATTERN.test(message)) {
|
|
722
|
+
return [
|
|
723
|
+
"\u{1F449} Your OpenRouter credit balance is too low for this request.",
|
|
724
|
+
" Add credits at https://openrouter.ai/settings/credits, or run 'bugzero vibe' to switch to a cheaper model",
|
|
725
|
+
" (models that cost nothing are listed at https://openrouter.ai/models?max_price=0)."
|
|
726
|
+
];
|
|
727
|
+
}
|
|
728
|
+
if (status === 429) {
|
|
729
|
+
return [
|
|
730
|
+
"\u{1F449} OpenRouter is rate limiting these requests. Wait a moment and try again.",
|
|
731
|
+
" Free models have stricter limits; run 'bugzero vibe' to switch models, or add credits to raise them."
|
|
732
|
+
];
|
|
733
|
+
}
|
|
734
|
+
if (CONTEXT_PATTERN.test(message)) {
|
|
735
|
+
return [
|
|
736
|
+
`\u{1F449} The prompt was too large for the context window of "${model}".`,
|
|
737
|
+
" Run 'bugzero vibe' to pick a model with a larger context window, or try again on a smaller failing scope."
|
|
738
|
+
];
|
|
739
|
+
}
|
|
740
|
+
if (status === 404 || MODEL_PATTERN.test(message)) {
|
|
741
|
+
return [
|
|
742
|
+
`\u{1F449} The model "${model}" isn't available on OpenRouter (it wasn't found, or no provider currently serves it).`,
|
|
743
|
+
" Run 'bugzero vibe' to choose a different model."
|
|
744
|
+
];
|
|
745
|
+
}
|
|
746
|
+
return [];
|
|
747
|
+
}
|
|
676
748
|
function resolveModel(cwd) {
|
|
677
749
|
try {
|
|
678
750
|
const configPath = import_path2.default.resolve(cwd ?? process.cwd(), ".bugzerorc");
|
|
@@ -735,21 +807,9 @@ var require_dist3 = __commonJS({
|
|
|
735
807
|
}
|
|
736
808
|
console.error(`
|
|
737
809
|
\u274C [BugZero] OpenRouter API Error: ${msg}`);
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
console.error(`
|
|
742
|
-
\u{1F449} You have insufficient OpenRouter credits (or too low balance) for the selected model.`);
|
|
743
|
-
console.error(`\u{1F449} We suggest using the 'bugzero vibe' command to change to a FREE model:`);
|
|
744
|
-
console.error(` 1. Run the command: npx bugzero@latest vibe (or bugzero vibe)`);
|
|
745
|
-
console.error(` 2. Select "Custom Model" and enter one of these free model IDs:`);
|
|
746
|
-
console.error(` - google/gemma-2-9b-it:free`);
|
|
747
|
-
console.error(` - meta-llama/llama-3-8b-instruct:free`);
|
|
748
|
-
console.error(` - mistralai/mistral-7b-instruct:free`);
|
|
749
|
-
console.error(` - microsoft/phi-3-medium-128k-instruct:free`);
|
|
750
|
-
console.error(` - nvidia/llama-3.1-nemotron-70b-instruct:free
|
|
751
|
-
`);
|
|
752
|
-
}
|
|
810
|
+
const hints = getErrorHints({ status: error.response?.status, message: msg, model });
|
|
811
|
+
for (const line of hints) console.error(line);
|
|
812
|
+
if (hints.length > 0) console.error();
|
|
753
813
|
throw new Error(`OpenRouter API failed: ${msg}`);
|
|
754
814
|
}
|
|
755
815
|
}
|
|
@@ -1307,6 +1367,11 @@ var require_dist6 = __commonJS({
|
|
|
1307
1367
|
completionTokens: (0, import_langgraph.Annotation)({
|
|
1308
1368
|
reducer: (old, value) => old + value,
|
|
1309
1369
|
default: () => 0
|
|
1370
|
+
}),
|
|
1371
|
+
/** Per-command timeout in milliseconds for validation steps (undefined = no limit) */
|
|
1372
|
+
timeoutMs: (0, import_langgraph.Annotation)({
|
|
1373
|
+
reducer: (_, value) => value,
|
|
1374
|
+
default: () => void 0
|
|
1310
1375
|
})
|
|
1311
1376
|
});
|
|
1312
1377
|
var import_ai_provider = require_dist3();
|
|
@@ -1387,7 +1452,7 @@ Any other files from that same suggestion were rolled back to keep the change at
|
|
|
1387
1452
|
};
|
|
1388
1453
|
}
|
|
1389
1454
|
console.log("[BugZero] Re-running validation pipeline to verify fix\u2026");
|
|
1390
|
-
const result = await (0, import_analyzers.runValidationPipeline)(state.cwd);
|
|
1455
|
+
const result = await (0, import_analyzers.runValidationPipeline)(state.cwd, { timeoutMs: state.timeoutMs });
|
|
1391
1456
|
if (result.success) {
|
|
1392
1457
|
console.log("[BugZero] \u2705 Validation succeeded!");
|
|
1393
1458
|
} else {
|
|
@@ -1456,7 +1521,8 @@ var require_dist7 = __commonJS({
|
|
|
1456
1521
|
detectPackageManager: () => import_analyzers2.detectPackageManager,
|
|
1457
1522
|
orchestrate: () => orchestrate2,
|
|
1458
1523
|
orchestrateAndFix: () => orchestrateAndFix2,
|
|
1459
|
-
resolveMaxAttempts: () => resolveMaxAttempts2
|
|
1524
|
+
resolveMaxAttempts: () => resolveMaxAttempts2,
|
|
1525
|
+
resolveTimeoutMs: () => resolveTimeoutMs2
|
|
1460
1526
|
});
|
|
1461
1527
|
module2.exports = __toCommonJS(index_exports);
|
|
1462
1528
|
var import_fs2 = __toESM2(require("fs"));
|
|
@@ -1537,6 +1603,13 @@ Re-run without \`--dry-run\` to let BugZero attempt a fix.
|
|
|
1537
1603
|
`;
|
|
1538
1604
|
}
|
|
1539
1605
|
var DEFAULT_MAX_ATTEMPTS = 3;
|
|
1606
|
+
function resolveTimeoutMs2(seconds) {
|
|
1607
|
+
if (seconds === void 0) return void 0;
|
|
1608
|
+
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
1609
|
+
throw new Error(`--timeout must be a positive number of seconds (received ${seconds}).`);
|
|
1610
|
+
}
|
|
1611
|
+
return Math.round(seconds * 1e3);
|
|
1612
|
+
}
|
|
1540
1613
|
function resolveMaxAttempts2(value) {
|
|
1541
1614
|
const attempts = value ?? DEFAULT_MAX_ATTEMPTS;
|
|
1542
1615
|
if (!Number.isInteger(attempts) || attempts < 1) {
|
|
@@ -1559,10 +1632,11 @@ Re-run without \`--dry-run\` to let BugZero attempt a fix.
|
|
|
1559
1632
|
async function orchestrate2(options = {}) {
|
|
1560
1633
|
const cwd = options.cwd ?? process.cwd();
|
|
1561
1634
|
assertNodeProject(cwd);
|
|
1635
|
+
const timeoutMs = resolveTimeoutMs2(options.timeoutSeconds);
|
|
1562
1636
|
const [build, lint, test] = await Promise.all([
|
|
1563
|
-
(0, import_analyzers.runBuild)(cwd),
|
|
1564
|
-
(0, import_analyzers.runLint)(cwd),
|
|
1565
|
-
(0, import_analyzers.runTests)(cwd)
|
|
1637
|
+
(0, import_analyzers.runBuild)(cwd, { timeoutMs }),
|
|
1638
|
+
(0, import_analyzers.runLint)(cwd, { timeoutMs }),
|
|
1639
|
+
(0, import_analyzers.runTests)(cwd, { timeoutMs })
|
|
1566
1640
|
]);
|
|
1567
1641
|
await writeReport(cwd, "hunt", {
|
|
1568
1642
|
success: build.success && lint.success && test.success,
|
|
@@ -1576,8 +1650,9 @@ Re-run without \`--dry-run\` to let BugZero attempt a fix.
|
|
|
1576
1650
|
const cwd = options.cwd ?? process.cwd();
|
|
1577
1651
|
assertNodeProject(cwd);
|
|
1578
1652
|
const maxAttempts = resolveMaxAttempts2(options.maxAttempts);
|
|
1653
|
+
const timeoutMs = resolveTimeoutMs2(options.timeoutSeconds);
|
|
1579
1654
|
console.log(`[BugZero] Running validation pipeline in: ${cwd}`);
|
|
1580
|
-
const pipelineResult = await (0, import_analyzers.runValidationPipeline)(cwd);
|
|
1655
|
+
const pipelineResult = await (0, import_analyzers.runValidationPipeline)(cwd, { timeoutMs });
|
|
1581
1656
|
if (pipelineResult.success) {
|
|
1582
1657
|
console.log("[BugZero] Validation already passing \u2014 nothing to fix.");
|
|
1583
1658
|
await writeReport(cwd, "rescue", {
|
|
@@ -1610,7 +1685,8 @@ Re-run without \`--dry-run\` to let BugZero attempt a fix.
|
|
|
1610
1685
|
const finalState = await (0, import_agents.runRepairWorkflow)({
|
|
1611
1686
|
cwd,
|
|
1612
1687
|
logs: pipelineResult.output,
|
|
1613
|
-
maxAttempts
|
|
1688
|
+
maxAttempts,
|
|
1689
|
+
timeoutMs
|
|
1614
1690
|
});
|
|
1615
1691
|
const result = {
|
|
1616
1692
|
success: finalState.success,
|
|
@@ -1928,20 +2004,29 @@ function buildRescueSummary(result, options) {
|
|
|
1928
2004
|
|
|
1929
2005
|
// src/index.ts
|
|
1930
2006
|
var { version } = JSON.parse(import_fs.default.readFileSync(import_path.default.join(__dirname, "..", "package.json"), "utf-8"));
|
|
2007
|
+
var TIMEOUT_DESCRIPTION = "Stop any build/lint/test command that runs longer than this many seconds";
|
|
2008
|
+
function parseTimeout(raw) {
|
|
2009
|
+
if (raw === void 0) return void 0;
|
|
2010
|
+
const seconds = Number(raw);
|
|
2011
|
+
(0, import_core.resolveTimeoutMs)(seconds);
|
|
2012
|
+
return seconds;
|
|
2013
|
+
}
|
|
1931
2014
|
var program = new import_commander.Command();
|
|
1932
2015
|
program.name("bugzero").description("AI-powered build issue fixer \u2014 detects and repairs build errors automatically").version(version);
|
|
1933
|
-
program.command("hunt").description("Run build, lint, and tests \u2014 report issues without modifying files").option("-p, --path <dir>", "Path to the target project", ".").action(async (opts) => {
|
|
2016
|
+
program.command("hunt").description("Run build, lint, and tests \u2014 report issues without modifying files").option("-p, --path <dir>", "Path to the target project", ".").option("-t, --timeout <seconds>", TIMEOUT_DESCRIPTION).action(async (opts) => {
|
|
1934
2017
|
try {
|
|
1935
2018
|
const cwd = import_path.default.resolve(opts.path);
|
|
2019
|
+
const timeoutSeconds = parseTimeout(opts.timeout);
|
|
1936
2020
|
const framework = (0, import_core.detectFramework)(cwd);
|
|
1937
2021
|
const packageManager = (0, import_core.detectPackageManager)(cwd);
|
|
1938
2022
|
console.log(`
|
|
1939
2023
|
\u{1F50D} BugZero \u2014 Analyzing project`);
|
|
1940
2024
|
console.log(` Path: ${cwd}`);
|
|
1941
2025
|
console.log(` Framework: ${framework}`);
|
|
1942
|
-
console.log(` Package manager: ${packageManager}
|
|
1943
|
-
`);
|
|
1944
|
-
|
|
2026
|
+
console.log(` Package manager: ${packageManager}`);
|
|
2027
|
+
if (timeoutSeconds !== void 0) console.log(` Timeout: ${timeoutSeconds}s per command`);
|
|
2028
|
+
console.log();
|
|
2029
|
+
const { build, lint, test } = await (0, import_core.orchestrate)({ cwd, timeoutSeconds });
|
|
1945
2030
|
console.log("\u2500\u2500 Pipeline Summary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1946
2031
|
const label = (r) => r.skipped ? "\u23ED\uFE0F Skipped (no script)" : r.success ? "\u2705 Passed" : "\u274C Failed (see report)";
|
|
1947
2032
|
console.log(` Build: ${label(build)}`);
|
|
@@ -1959,10 +2044,11 @@ ${allPassed ? "\u2705 All checks passed" : "\u274C Issues found \u2014 run 'bugz
|
|
|
1959
2044
|
process.exit(1);
|
|
1960
2045
|
}
|
|
1961
2046
|
});
|
|
1962
|
-
program.command("rescue").description("Detect build failures and automatically apply AI-generated fixes").option("-p, --path <dir>", "Path to the target project", ".").option("-n, --max-attempts <number>", "Maximum AI fix attempts", "3").option("--dry-run", "Analyse only \u2014 print what would be fixed without writing files", false).action(async (opts) => {
|
|
2047
|
+
program.command("rescue").description("Detect build failures and automatically apply AI-generated fixes").option("-p, --path <dir>", "Path to the target project", ".").option("-n, --max-attempts <number>", "Maximum AI fix attempts", "3").option("-t, --timeout <seconds>", TIMEOUT_DESCRIPTION).option("--dry-run", "Analyse only \u2014 print what would be fixed without writing files", false).action(async (opts) => {
|
|
1963
2048
|
try {
|
|
1964
2049
|
const cwd = import_path.default.resolve(opts.path);
|
|
1965
2050
|
const maxAttempts = (0, import_core.resolveMaxAttempts)(Number(opts.maxAttempts));
|
|
2051
|
+
const timeoutSeconds = parseTimeout(opts.timeout);
|
|
1966
2052
|
const dryRun = opts.dryRun;
|
|
1967
2053
|
const framework = (0, import_core.detectFramework)(cwd);
|
|
1968
2054
|
const packageManager = (0, import_core.detectPackageManager)(cwd);
|
|
@@ -1972,9 +2058,10 @@ program.command("rescue").description("Detect build failures and automatically a
|
|
|
1972
2058
|
console.log(` Framework: ${framework}`);
|
|
1973
2059
|
console.log(` Package manager: ${packageManager}`);
|
|
1974
2060
|
console.log(` Max attempts: ${maxAttempts}`);
|
|
2061
|
+
if (timeoutSeconds !== void 0) console.log(` Timeout: ${timeoutSeconds}s per command`);
|
|
1975
2062
|
if (dryRun) console.log(` Mode: DRY RUN (no files will be modified)`);
|
|
1976
2063
|
console.log();
|
|
1977
|
-
const result = await (0, import_core.orchestrateAndFix)({ cwd, maxAttempts, dryRun });
|
|
2064
|
+
const result = await (0, import_core.orchestrateAndFix)({ cwd, maxAttempts, dryRun, timeoutSeconds });
|
|
1978
2065
|
console.log("\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1979
2066
|
const summary = buildRescueSummary(result, { dryRun });
|
|
1980
2067
|
for (const line of summary.lines) console.log(line);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bugzero",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.43",
|
|
4
4
|
"description": "Autonomous AI-powered CLI and GitHub Action that detects build, lint, and test failures, generates multi-file patches, applies them atomically with fuzzy matching, and re-verifies until your project is green.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Saksham Agarwal (https://github.com/Saksham1800t)",
|
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
"@langchain/langgraph": "^1.3.2",
|
|
49
49
|
"axios": "^1.16.1",
|
|
50
50
|
"commander": "^15.0.0",
|
|
51
|
-
"execa": "^7.2.0"
|
|
51
|
+
"execa": "^7.2.0",
|
|
52
|
+
"tree-kill": "^1.2.2"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
55
|
"@opspilot/agents": "workspace:*",
|