bugzero 0.0.42 → 0.0.44
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 +20 -3
- package/dist/index.js +137 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -114,6 +114,7 @@ bugzero rescue
|
|
|
114
114
|
### Integrations and output
|
|
115
115
|
|
|
116
116
|
- **Markdown reports.** Every `hunt` and `rescue` run writes `bugzero-report.md` to the target project: a pass/fail summary of build, lint, and tests, the full error logs, the model used, token usage, and before/after diffs for every file patched. The terminal prints a clickable link straight to it.
|
|
117
|
+
- **JSON output.** Add `--json` to `hunt` or `rescue` to print the result as a single JSON document on stdout, with all human-readable logs moved to stderr and the exit code unchanged (0 on success, 1 otherwise), so scripts and CI can parse it. See [Scripting and CI](#scripting-and-ci).
|
|
117
118
|
- **GitHub Action mode.** Run the same repair loop in CI. Instead of patching a local checkout, the Action commits every changed file in one atomic commit through the GitHub API and opens a pull request — see [GitHub Action Mode](#github-action-mode) below.
|
|
118
119
|
|
|
119
120
|
---
|
|
@@ -191,7 +192,7 @@ OPENROUTER_API_KEY=your_openrouter_api_key_here
|
|
|
191
192
|
## Commands
|
|
192
193
|
|
|
193
194
|
### `bugzero hunt`
|
|
194
|
-
Run build, lint, and test checks. Reports all failures without modifying any files. Accepts `--timeout <seconds
|
|
195
|
+
Run build, lint, and test checks. Reports all failures without modifying any files. Accepts `--timeout <seconds>` and `--json`, like `rescue`.
|
|
195
196
|
|
|
196
197
|
```bash
|
|
197
198
|
bugzero hunt --path /path/to/your/project
|
|
@@ -213,6 +214,7 @@ bugzero rescue --path /path/to/your/project
|
|
|
213
214
|
| `--max-attempts <n>` | `-n` | `3` | Maximum AI fix iterations before giving up |
|
|
214
215
|
| `--timeout <seconds>` | `-t` | none | Stop any build, lint, or test command that runs longer than this many seconds |
|
|
215
216
|
| `--dry-run` | | `false` | Analyze only — no files will be written |
|
|
217
|
+
| `--json` | | `false` | Print the result as one JSON document on stdout (logs go to stderr) |
|
|
216
218
|
|
|
217
219
|
</details>
|
|
218
220
|
|
|
@@ -236,6 +238,19 @@ bugzero vibe
|
|
|
236
238
|
|
|
237
239
|
---
|
|
238
240
|
|
|
241
|
+
## Scripting and CI
|
|
242
|
+
|
|
243
|
+
`hunt` and `rescue` accept `--json`. Stdout then contains exactly one JSON document and every human-readable log line goes to stderr, so the output is safe to pipe. Exit codes don't change: `0` when everything passes or is fixed, `1` otherwise.
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
bugzero hunt --json | jq '.steps'
|
|
247
|
+
bugzero rescue --json --dry-run | jq -r '.outcome'
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
`hunt` reports each step (`build`, `lint`, `test`) as `passed`, `failed`, or `skipped`; failed steps include the command's output and whether it timed out. `rescue` reports an `outcome` of `already-passing`, `fixed`, `failed`, or `dry-run`, along with the model, token usage, and the files and root cause of any fix. Both include the path to the generated report. If a run can't start at all (for example, because of an invalid option), stdout is `{ "success": false, "error": "..." }`.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
239
254
|
## Configuration
|
|
240
255
|
|
|
241
256
|
BugZero reads a `.bugzerorc` JSON file in your target project directory to pick up model preferences:
|
|
@@ -269,8 +284,10 @@ BugZero provides clear, actionable output for common failure modes:
|
|
|
269
284
|
| Error | Message |
|
|
270
285
|
|---|---|
|
|
271
286
|
| Invalid API key (401) | Prompts you to check your `OPENROUTER_API_KEY` |
|
|
272
|
-
|
|
|
273
|
-
|
|
|
287
|
+
| 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` |
|
|
288
|
+
| Rate limit exceeded (429) | Says OpenRouter is rate limiting and suggests waiting, switching models, or adding credits |
|
|
289
|
+
| Prompt too large for the model | Says the prompt exceeded the model's context window and suggests a larger-context model with `bugzero vibe` |
|
|
290
|
+
| Model not found | Names the unavailable model ID and suggests running `bugzero vibe` |
|
|
274
291
|
| Patch not applied | Reports exact mismatch between AI suggestion and file content, after exhausting exact, whitespace, and fuzzy matching |
|
|
275
292
|
| Max attempts reached | Exits cleanly with the last error log for manual review |
|
|
276
293
|
|
package/dist/index.js
CHANGED
|
@@ -393,7 +393,7 @@ var require_dist = __commonJS({
|
|
|
393
393
|
while (true) {
|
|
394
394
|
const envPath = import_path2.default.join(dir, ".env");
|
|
395
395
|
if (import_fs2.default.existsSync(envPath)) {
|
|
396
|
-
import_dotenv.default.config({ path: envPath });
|
|
396
|
+
import_dotenv.default.config({ path: envPath, quiet: true });
|
|
397
397
|
return;
|
|
398
398
|
}
|
|
399
399
|
const parent = import_path2.default.dirname(dir);
|
|
@@ -402,7 +402,7 @@ var require_dist = __commonJS({
|
|
|
402
402
|
}
|
|
403
403
|
dir = parent;
|
|
404
404
|
}
|
|
405
|
-
import_dotenv.default.config();
|
|
405
|
+
import_dotenv.default.config({ quiet: true });
|
|
406
406
|
}
|
|
407
407
|
loadEnv();
|
|
408
408
|
var env = {
|
|
@@ -704,12 +704,47 @@ var require_dist3 = __commonJS({
|
|
|
704
704
|
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
705
705
|
var index_exports = {};
|
|
706
706
|
__export(index_exports, {
|
|
707
|
-
OpenRouterProvider: () => OpenRouterProvider
|
|
707
|
+
OpenRouterProvider: () => OpenRouterProvider,
|
|
708
|
+
getErrorHints: () => getErrorHints
|
|
708
709
|
});
|
|
709
710
|
module2.exports = __toCommonJS(index_exports);
|
|
710
711
|
var import_axios = __toESM2(require("axios"));
|
|
711
712
|
var import_fs2 = __toESM2(require("fs"));
|
|
712
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
|
+
}
|
|
713
748
|
function resolveModel(cwd) {
|
|
714
749
|
try {
|
|
715
750
|
const configPath = import_path2.default.resolve(cwd ?? process.cwd(), ".bugzerorc");
|
|
@@ -772,21 +807,9 @@ var require_dist3 = __commonJS({
|
|
|
772
807
|
}
|
|
773
808
|
console.error(`
|
|
774
809
|
\u274C [BugZero] OpenRouter API Error: ${msg}`);
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
console.error(`
|
|
779
|
-
\u{1F449} You have insufficient OpenRouter credits (or too low balance) for the selected model.`);
|
|
780
|
-
console.error(`\u{1F449} We suggest using the 'bugzero vibe' command to change to a FREE model:`);
|
|
781
|
-
console.error(` 1. Run the command: npx bugzero@latest vibe (or bugzero vibe)`);
|
|
782
|
-
console.error(` 2. Select "Custom Model" and enter one of these free model IDs:`);
|
|
783
|
-
console.error(` - google/gemma-2-9b-it:free`);
|
|
784
|
-
console.error(` - meta-llama/llama-3-8b-instruct:free`);
|
|
785
|
-
console.error(` - mistralai/mistral-7b-instruct:free`);
|
|
786
|
-
console.error(` - microsoft/phi-3-medium-128k-instruct:free`);
|
|
787
|
-
console.error(` - nvidia/llama-3.1-nemotron-70b-instruct:free
|
|
788
|
-
`);
|
|
789
|
-
}
|
|
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();
|
|
790
813
|
throw new Error(`OpenRouter API failed: ${msg}`);
|
|
791
814
|
}
|
|
792
815
|
}
|
|
@@ -1494,6 +1517,7 @@ var require_dist7 = __commonJS({
|
|
|
1494
1517
|
var index_exports = {};
|
|
1495
1518
|
__export(index_exports, {
|
|
1496
1519
|
DEFAULT_MAX_ATTEMPTS: () => DEFAULT_MAX_ATTEMPTS,
|
|
1520
|
+
REPORT_FILENAME: () => REPORT_FILENAME2,
|
|
1497
1521
|
detectFramework: () => detectFramework2,
|
|
1498
1522
|
detectPackageManager: () => import_analyzers2.detectPackageManager,
|
|
1499
1523
|
orchestrate: () => orchestrate2,
|
|
@@ -1602,6 +1626,7 @@ Re-run without \`--dry-run\` to let BugZero attempt a fix.
|
|
|
1602
1626
|
}
|
|
1603
1627
|
}
|
|
1604
1628
|
var import_ai_provider = require_dist3();
|
|
1629
|
+
var REPORT_FILENAME2 = "bugzero-report.md";
|
|
1605
1630
|
function statusLabel(result) {
|
|
1606
1631
|
if (result.skipped) return "\u23ED\uFE0F Skipped (no script defined)";
|
|
1607
1632
|
return result.success ? "\u2705 Passed" : "\u274C Failed";
|
|
@@ -1900,7 +1925,7 @@ ${rawOutput}
|
|
|
1900
1925
|
|
|
1901
1926
|
---
|
|
1902
1927
|
*Report generated by **BugZero** on ${(/* @__PURE__ */ new Date()).toLocaleString()}*`;
|
|
1903
|
-
const reportPath = import_path3.default.resolve(cwd,
|
|
1928
|
+
const reportPath = import_path3.default.resolve(cwd, REPORT_FILENAME2);
|
|
1904
1929
|
try {
|
|
1905
1930
|
import_fs3.default.writeFileSync(reportPath, content, "utf-8");
|
|
1906
1931
|
const clickableUrl = (0, import_url.pathToFileURL)(reportPath).toString();
|
|
@@ -1929,6 +1954,62 @@ var import_fs = __toESM(require("fs"));
|
|
|
1929
1954
|
var import_path = __toESM(require("path"));
|
|
1930
1955
|
var import_core = __toESM(require_dist7());
|
|
1931
1956
|
|
|
1957
|
+
// src/json-output.ts
|
|
1958
|
+
function stepStatus(step) {
|
|
1959
|
+
if (step.skipped) return "skipped";
|
|
1960
|
+
return step.success ? "passed" : "failed";
|
|
1961
|
+
}
|
|
1962
|
+
function stepJson(step) {
|
|
1963
|
+
const status = stepStatus(step);
|
|
1964
|
+
if (status !== "failed") return { status };
|
|
1965
|
+
return { status, timedOut: step.timedOut === true, output: step.output ?? "" };
|
|
1966
|
+
}
|
|
1967
|
+
function buildHuntJson(info, steps) {
|
|
1968
|
+
return {
|
|
1969
|
+
command: "hunt",
|
|
1970
|
+
success: steps.build.success && steps.lint.success && steps.test.success,
|
|
1971
|
+
path: info.path,
|
|
1972
|
+
framework: info.framework,
|
|
1973
|
+
packageManager: info.packageManager,
|
|
1974
|
+
steps: { build: stepJson(steps.build), lint: stepJson(steps.lint), test: stepJson(steps.test) },
|
|
1975
|
+
reportPath: info.reportPath
|
|
1976
|
+
};
|
|
1977
|
+
}
|
|
1978
|
+
function buildRescueJson(result, info) {
|
|
1979
|
+
let outcome;
|
|
1980
|
+
if (result.success && result.attempts === 0) outcome = "already-passing";
|
|
1981
|
+
else if (result.success) outcome = "fixed";
|
|
1982
|
+
else if (info.dryRun) outcome = "dry-run";
|
|
1983
|
+
else outcome = "failed";
|
|
1984
|
+
const tokens = result.promptTokens !== void 0 && result.completionTokens !== void 0 ? {
|
|
1985
|
+
prompt: result.promptTokens,
|
|
1986
|
+
completion: result.completionTokens,
|
|
1987
|
+
total: result.promptTokens + result.completionTokens
|
|
1988
|
+
} : null;
|
|
1989
|
+
return {
|
|
1990
|
+
command: "rescue",
|
|
1991
|
+
outcome,
|
|
1992
|
+
success: result.success,
|
|
1993
|
+
dryRun: info.dryRun,
|
|
1994
|
+
path: info.path,
|
|
1995
|
+
framework: info.framework,
|
|
1996
|
+
packageManager: info.packageManager,
|
|
1997
|
+
attempts: result.attempts,
|
|
1998
|
+
model: result.modelUsed || null,
|
|
1999
|
+
tokens,
|
|
2000
|
+
fix: result.appliedFix ? {
|
|
2001
|
+
rootCause: result.appliedFix.rootCause,
|
|
2002
|
+
explanation: result.appliedFix.explanation,
|
|
2003
|
+
files: result.appliedFix.patches.map((p) => p.filePath)
|
|
2004
|
+
} : null,
|
|
2005
|
+
error: result.error ?? null,
|
|
2006
|
+
reportPath: info.reportPath
|
|
2007
|
+
};
|
|
2008
|
+
}
|
|
2009
|
+
function buildErrorJson(message) {
|
|
2010
|
+
return { success: false, error: message };
|
|
2011
|
+
}
|
|
2012
|
+
|
|
1932
2013
|
// src/summary.ts
|
|
1933
2014
|
function tokenLines(result) {
|
|
1934
2015
|
if (result.promptTokens === void 0 || result.completionTokens === void 0) return [];
|
|
@@ -1988,9 +2069,23 @@ function parseTimeout(raw) {
|
|
|
1988
2069
|
(0, import_core.resolveTimeoutMs)(seconds);
|
|
1989
2070
|
return seconds;
|
|
1990
2071
|
}
|
|
2072
|
+
var JSON_DESCRIPTION = "Print the result as one JSON document on stdout (human-readable logs go to stderr)";
|
|
2073
|
+
function routeLogsToStderr() {
|
|
2074
|
+
const toStderr = (...args) => console.error(...args);
|
|
2075
|
+
console.log = toStderr;
|
|
2076
|
+
console.info = toStderr;
|
|
2077
|
+
console.debug = toStderr;
|
|
2078
|
+
}
|
|
2079
|
+
function finish(exitCode, json) {
|
|
2080
|
+
if (json === void 0) process.exit(exitCode);
|
|
2081
|
+
process.stdout.write(`${JSON.stringify(json, null, 2)}
|
|
2082
|
+
`, () => process.exit(exitCode));
|
|
2083
|
+
}
|
|
1991
2084
|
var program = new import_commander.Command();
|
|
1992
2085
|
program.name("bugzero").description("AI-powered build issue fixer \u2014 detects and repairs build errors automatically").version(version);
|
|
1993
|
-
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) => {
|
|
2086
|
+
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).option("--json", JSON_DESCRIPTION).action(async (opts) => {
|
|
2087
|
+
const json = opts.json === true;
|
|
2088
|
+
if (json) routeLogsToStderr();
|
|
1994
2089
|
try {
|
|
1995
2090
|
const cwd = import_path.default.resolve(opts.path);
|
|
1996
2091
|
const timeoutSeconds = parseTimeout(opts.timeout);
|
|
@@ -2014,14 +2109,22 @@ program.command("hunt").description("Run build, lint, and tests \u2014 report is
|
|
|
2014
2109
|
console.log(`
|
|
2015
2110
|
${allPassed ? "\u2705 All checks passed" : "\u274C Issues found \u2014 run 'bugzero rescue' to repair"}
|
|
2016
2111
|
`);
|
|
2017
|
-
|
|
2112
|
+
finish(
|
|
2113
|
+
allPassed ? 0 : 1,
|
|
2114
|
+
json ? buildHuntJson(
|
|
2115
|
+
{ path: cwd, framework, packageManager, reportPath: import_path.default.resolve(cwd, import_core.REPORT_FILENAME) },
|
|
2116
|
+
{ build, lint, test }
|
|
2117
|
+
) : void 0
|
|
2118
|
+
);
|
|
2018
2119
|
} catch (error) {
|
|
2019
2120
|
console.error(`
|
|
2020
2121
|
\u274C [BugZero] Execution failed: ${error.message || error}`);
|
|
2021
|
-
|
|
2122
|
+
finish(1, json ? buildErrorJson(String(error.message || error)) : void 0);
|
|
2022
2123
|
}
|
|
2023
2124
|
});
|
|
2024
|
-
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) => {
|
|
2125
|
+
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).option("--json", JSON_DESCRIPTION).action(async (opts) => {
|
|
2126
|
+
const json = opts.json === true;
|
|
2127
|
+
if (json) routeLogsToStderr();
|
|
2025
2128
|
try {
|
|
2026
2129
|
const cwd = import_path.default.resolve(opts.path);
|
|
2027
2130
|
const maxAttempts = (0, import_core.resolveMaxAttempts)(Number(opts.maxAttempts));
|
|
@@ -2043,11 +2146,20 @@ program.command("rescue").description("Detect build failures and automatically a
|
|
|
2043
2146
|
const summary = buildRescueSummary(result, { dryRun });
|
|
2044
2147
|
for (const line of summary.lines) console.log(line);
|
|
2045
2148
|
console.log();
|
|
2046
|
-
|
|
2149
|
+
finish(
|
|
2150
|
+
summary.exitCode,
|
|
2151
|
+
json ? buildRescueJson(result, {
|
|
2152
|
+
path: cwd,
|
|
2153
|
+
framework,
|
|
2154
|
+
packageManager,
|
|
2155
|
+
dryRun,
|
|
2156
|
+
reportPath: import_path.default.resolve(cwd, import_core.REPORT_FILENAME)
|
|
2157
|
+
}) : void 0
|
|
2158
|
+
);
|
|
2047
2159
|
} catch (error) {
|
|
2048
2160
|
console.error(`
|
|
2049
2161
|
\u274C [BugZero] Execution failed: ${error.message || error}`);
|
|
2050
|
-
|
|
2162
|
+
finish(1, json ? buildErrorJson(String(error.message || error)) : void 0);
|
|
2051
2163
|
}
|
|
2052
2164
|
});
|
|
2053
2165
|
program.command("protect").description("[Deprecated] Use 'hunt' instead").action(async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bugzero",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.44",
|
|
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)",
|