bugzero 0.0.34 → 0.0.36
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/LICENSE +21 -0
- package/README.md +25 -16
- package/dist/index.js +90 -47
- package/package.json +41 -4
- package/.turbo/turbo-build.log +0 -14
- package/demo.svg +0 -81
- package/src/index.ts +0 -198
- package/tsconfig.json +0 -8
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Saksham Agarwal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
[](https://www.npmjs.com/package/bugzero)
|
|
9
9
|
[](https://github.com/Saksham1800t/Bugzero/actions/workflows/ci.yml)
|
|
10
10
|
[](https://opensource.org/licenses/MIT)
|
|
11
|
-
[](https://nodejs.org)
|
|
12
12
|
[](https://pnpm.io/)
|
|
13
13
|
|
|
14
14
|
**BugZero** is an autonomous, AI-powered CLI that detects and repairs build, lint, and test failures — locally or as a GitHub Action that opens the fix as a pull request.
|
|
@@ -88,21 +88,30 @@ bugzero rescue
|
|
|
88
88
|
|
|
89
89
|
---
|
|
90
90
|
|
|
91
|
-
##
|
|
91
|
+
## Features
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
93
|
+
### Repair engine
|
|
94
|
+
|
|
95
|
+
- **Autonomous repair loop.** `bugzero rescue` runs your build, lint, and tests. When something fails, it sends the error output — along with the relevant source files and directory listings as context — to an LLM, applies the fix the model proposes, and re-runs validation. It repeats, feeding any patch failures back to the model, until everything passes or the attempt limit is reached (3 by default; change it with `--max-attempts`). The loop is a stateful LangGraph graph: analyze, patch, verify.
|
|
96
|
+
- **Multi-file patches.** One fix can edit several files at once — for example, a renamed export plus every file that imports it. All patches in an attempt are applied as a single atomic unit: if any one of them fails to apply, every file touched in that attempt is restored to its original content, so you never end up with a half-applied fix.
|
|
97
|
+
- **Fuzzy patch matching.** LLMs rarely reproduce code byte-for-byte, so BugZero locates each patch in three tiers: an exact match first, then a whitespace-normalized line match (tolerates indentation and spacing drift), then a similarity-scored match (average per-line similarity of at least 85%). If two locations look equally plausible, it refuses to guess and reports a failed patch instead of risking an edit in the wrong place.
|
|
98
|
+
- **Multi-model support.** Works with any model available on OpenRouter — DeepSeek, Claude, Gemini, GPT-4o, Llama, and more. Choose one interactively with `bugzero vibe`; the choice is saved per project in `.bugzerorc`.
|
|
99
|
+
|
|
100
|
+
### Safety
|
|
101
|
+
|
|
102
|
+
- **Backups on every patch.** Each file BugZero modifies gets a `.opspilot.bak` copy of its original content first.
|
|
103
|
+
- **Dry-run mode.** `bugzero rescue --dry-run` runs validation and error analysis, but makes no fix attempts and never modifies your source files (a report is still written).
|
|
104
|
+
|
|
105
|
+
### Project awareness
|
|
106
|
+
|
|
107
|
+
- **Framework detection.** Identifies Next.js, NestJS, Vite, Remix, Astro, SvelteKit, Nuxt, Angular, Create React App, or a plain TypeScript project from its config files and dependencies, and shows the result in the run banner.
|
|
108
|
+
- **Package manager detection.** Runs build, lint, and test with the tool your project actually uses — npm, pnpm, yarn, or bun. It checks the `packageManager` field in `package.json` first, then `pnpm-workspace.yaml` or a lockfile, and falls back to npm.
|
|
109
|
+
- **Error parsing.** Recognizes TypeScript compiler errors (file, line, column, message), ESLint errors, and Jest failures to classify what broke and how many errors were found. Output in any other format is still passed to the model as raw logs.
|
|
110
|
+
|
|
111
|
+
### Integrations and output
|
|
112
|
+
|
|
113
|
+
- **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.
|
|
114
|
+
- **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.
|
|
106
115
|
|
|
107
116
|
---
|
|
108
117
|
|
|
@@ -269,7 +278,7 @@ BugZero provides clear, actionable output for common failure modes:
|
|
|
269
278
|
Clone the repository and install dependencies. This project uses a pnpm monorepo managed by Turborepo, with a Vitest test suite and GitHub Actions CI.
|
|
270
279
|
|
|
271
280
|
### Prerequisites
|
|
272
|
-
- Node.js
|
|
281
|
+
- Node.js 22.12 or later
|
|
273
282
|
- pnpm
|
|
274
283
|
|
|
275
284
|
### Install & build
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
30
|
var require_main = __commonJS({
|
|
31
31
|
"../../node_modules/.pnpm/dotenv@17.4.2/node_modules/dotenv/lib/main.js"(exports2, module2) {
|
|
32
32
|
"use strict";
|
|
33
|
-
var
|
|
33
|
+
var fs2 = require("fs");
|
|
34
34
|
var path2 = require("path");
|
|
35
35
|
var os = require("os");
|
|
36
36
|
var crypto = require("crypto");
|
|
@@ -162,7 +162,7 @@ var require_main = __commonJS({
|
|
|
162
162
|
if (options && options.path && options.path.length > 0) {
|
|
163
163
|
if (Array.isArray(options.path)) {
|
|
164
164
|
for (const filepath of options.path) {
|
|
165
|
-
if (
|
|
165
|
+
if (fs2.existsSync(filepath)) {
|
|
166
166
|
possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
|
|
167
167
|
}
|
|
168
168
|
}
|
|
@@ -172,7 +172,7 @@ var require_main = __commonJS({
|
|
|
172
172
|
} else {
|
|
173
173
|
possibleVaultPath = path2.resolve(process.cwd(), ".env.vault");
|
|
174
174
|
}
|
|
175
|
-
if (
|
|
175
|
+
if (fs2.existsSync(possibleVaultPath)) {
|
|
176
176
|
return possibleVaultPath;
|
|
177
177
|
}
|
|
178
178
|
return null;
|
|
@@ -225,7 +225,7 @@ var require_main = __commonJS({
|
|
|
225
225
|
const parsedAll = {};
|
|
226
226
|
for (const path3 of optionPaths) {
|
|
227
227
|
try {
|
|
228
|
-
const parsed = DotenvModule.parse(
|
|
228
|
+
const parsed = DotenvModule.parse(fs2.readFileSync(path3, { encoding }));
|
|
229
229
|
DotenvModule.populate(parsedAll, parsed, options);
|
|
230
230
|
} catch (e) {
|
|
231
231
|
if (debug) {
|
|
@@ -386,13 +386,13 @@ var require_dist = __commonJS({
|
|
|
386
386
|
});
|
|
387
387
|
module2.exports = __toCommonJS(index_exports);
|
|
388
388
|
var import_dotenv = __toESM2(require_main());
|
|
389
|
-
var
|
|
389
|
+
var import_fs2 = __toESM2(require("fs"));
|
|
390
390
|
var import_path2 = __toESM2(require("path"));
|
|
391
391
|
function loadEnv() {
|
|
392
392
|
let dir = process.cwd();
|
|
393
393
|
while (true) {
|
|
394
394
|
const envPath = import_path2.default.join(dir, ".env");
|
|
395
|
-
if (
|
|
395
|
+
if (import_fs2.default.existsSync(envPath)) {
|
|
396
396
|
import_dotenv.default.config({ path: envPath });
|
|
397
397
|
return;
|
|
398
398
|
}
|
|
@@ -408,14 +408,14 @@ var require_dist = __commonJS({
|
|
|
408
408
|
var env = {
|
|
409
409
|
openRouterKey: process.env.OPENROUTER_API_KEY || ""
|
|
410
410
|
};
|
|
411
|
-
var
|
|
411
|
+
var import_fs22 = __toESM2(require("fs"));
|
|
412
412
|
var import_path22 = __toESM2(require("path"));
|
|
413
413
|
var CONFIG_FILENAME = ".bugzerorc";
|
|
414
414
|
function getLocalConfig(cwd = process.cwd()) {
|
|
415
415
|
try {
|
|
416
416
|
const configPath = import_path22.default.resolve(cwd, CONFIG_FILENAME);
|
|
417
|
-
if (
|
|
418
|
-
const content =
|
|
417
|
+
if (import_fs22.default.existsSync(configPath)) {
|
|
418
|
+
const content = import_fs22.default.readFileSync(configPath, "utf-8");
|
|
419
419
|
return JSON.parse(content);
|
|
420
420
|
}
|
|
421
421
|
} catch (e) {
|
|
@@ -424,7 +424,7 @@ var require_dist = __commonJS({
|
|
|
424
424
|
}
|
|
425
425
|
function saveLocalConfig(config, cwd = process.cwd()) {
|
|
426
426
|
const configPath = import_path22.default.resolve(cwd, CONFIG_FILENAME);
|
|
427
|
-
|
|
427
|
+
import_fs22.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
428
428
|
}
|
|
429
429
|
function getSelectedModel(cwd = process.cwd()) {
|
|
430
430
|
const config = getLocalConfig(cwd);
|
|
@@ -466,6 +466,8 @@ var require_dist2 = __commonJS({
|
|
|
466
466
|
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
467
467
|
var index_exports = {};
|
|
468
468
|
__export(index_exports, {
|
|
469
|
+
detectPackageManager: () => detectPackageManager3,
|
|
470
|
+
getRunScriptCommand: () => getRunScriptCommand,
|
|
469
471
|
parseErrors: () => parseErrors,
|
|
470
472
|
runBuild: () => runBuild,
|
|
471
473
|
runCommand: () => runCommand,
|
|
@@ -474,6 +476,39 @@ var require_dist2 = __commonJS({
|
|
|
474
476
|
runValidationPipeline: () => runValidationPipeline
|
|
475
477
|
});
|
|
476
478
|
module2.exports = __toCommonJS(index_exports);
|
|
479
|
+
var import_fs2 = __toESM2(require("fs"));
|
|
480
|
+
var import_path2 = __toESM2(require("path"));
|
|
481
|
+
var VALID_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
|
|
482
|
+
var LOCKFILE_MANAGERS = [
|
|
483
|
+
{ file: "pnpm-workspace.yaml", manager: "pnpm" },
|
|
484
|
+
{ file: "pnpm-lock.yaml", manager: "pnpm" },
|
|
485
|
+
{ file: "yarn.lock", manager: "yarn" },
|
|
486
|
+
{ file: "bun.lockb", manager: "bun" },
|
|
487
|
+
{ file: "bun.lock", manager: "bun" },
|
|
488
|
+
{ file: "package-lock.json", manager: "npm" }
|
|
489
|
+
];
|
|
490
|
+
function detectPackageManager3(cwd = process.cwd()) {
|
|
491
|
+
try {
|
|
492
|
+
const pkgPath = import_path2.default.join(cwd, "package.json");
|
|
493
|
+
if (import_fs2.default.existsSync(pkgPath)) {
|
|
494
|
+
const pkg = JSON.parse(import_fs2.default.readFileSync(pkgPath, "utf-8"));
|
|
495
|
+
if (typeof pkg.packageManager === "string") {
|
|
496
|
+
const name = pkg.packageManager.split("@")[0].trim();
|
|
497
|
+
if (VALID_MANAGERS.includes(name)) {
|
|
498
|
+
return name;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
} catch {
|
|
503
|
+
}
|
|
504
|
+
for (const { file, manager } of LOCKFILE_MANAGERS) {
|
|
505
|
+
if (import_fs2.default.existsSync(import_path2.default.join(cwd, file))) return manager;
|
|
506
|
+
}
|
|
507
|
+
return "npm";
|
|
508
|
+
}
|
|
509
|
+
function getRunScriptCommand(manager, script) {
|
|
510
|
+
return `${manager} run ${script}`;
|
|
511
|
+
}
|
|
477
512
|
async function runCommand(command, cwd) {
|
|
478
513
|
const { execa } = await import("execa");
|
|
479
514
|
try {
|
|
@@ -497,9 +532,9 @@ var require_dist2 = __commonJS({
|
|
|
497
532
|
};
|
|
498
533
|
}
|
|
499
534
|
}
|
|
500
|
-
var runBuild = (cwd) => runCommand("
|
|
501
|
-
var runLint = (cwd) => runCommand("
|
|
502
|
-
var runTests = (cwd) => runCommand("
|
|
535
|
+
var runBuild = (cwd) => runCommand(getRunScriptCommand(detectPackageManager3(cwd), "build"), cwd);
|
|
536
|
+
var runLint = (cwd) => runCommand(getRunScriptCommand(detectPackageManager3(cwd), "lint"), cwd);
|
|
537
|
+
var runTests = (cwd) => runCommand(getRunScriptCommand(detectPackageManager3(cwd), "test"), cwd);
|
|
503
538
|
var TS_ERROR_RE = /^(.+)\((\d+),(\d+)\):\s+error\s+TS\d+:\s+(.+)$/gm;
|
|
504
539
|
var ESLINT_ERROR_RE = /^\s+(\d+):(\d+)\s+error\s+(.+)$/gm;
|
|
505
540
|
var JEST_FAIL_RE = /● (.+)/g;
|
|
@@ -549,15 +584,15 @@ var require_dist2 = __commonJS({
|
|
|
549
584
|
}
|
|
550
585
|
return errors;
|
|
551
586
|
}
|
|
552
|
-
var
|
|
553
|
-
var
|
|
587
|
+
var import_fs22 = __toESM2(require("fs"));
|
|
588
|
+
var import_path22 = __toESM2(require("path"));
|
|
554
589
|
async function runValidationPipeline(cwd) {
|
|
555
590
|
const targetCwd = cwd ?? process.cwd();
|
|
556
591
|
let scripts = [];
|
|
557
592
|
try {
|
|
558
|
-
const pkgPath =
|
|
559
|
-
if (
|
|
560
|
-
const pkg = JSON.parse(
|
|
593
|
+
const pkgPath = import_path22.default.resolve(targetCwd, "package.json");
|
|
594
|
+
if (import_fs22.default.existsSync(pkgPath)) {
|
|
595
|
+
const pkg = JSON.parse(import_fs22.default.readFileSync(pkgPath, "utf-8"));
|
|
561
596
|
scripts = Object.keys(pkg.scripts || {});
|
|
562
597
|
}
|
|
563
598
|
} catch {
|
|
@@ -623,13 +658,13 @@ var require_dist3 = __commonJS({
|
|
|
623
658
|
});
|
|
624
659
|
module2.exports = __toCommonJS(index_exports);
|
|
625
660
|
var import_axios = __toESM2(require("axios"));
|
|
626
|
-
var
|
|
661
|
+
var import_fs2 = __toESM2(require("fs"));
|
|
627
662
|
var import_path2 = __toESM2(require("path"));
|
|
628
663
|
function resolveModel(cwd) {
|
|
629
664
|
try {
|
|
630
665
|
const configPath = import_path2.default.resolve(cwd ?? process.cwd(), ".bugzerorc");
|
|
631
|
-
if (
|
|
632
|
-
const config = JSON.parse(
|
|
666
|
+
if (import_fs2.default.existsSync(configPath)) {
|
|
667
|
+
const config = JSON.parse(import_fs2.default.readFileSync(configPath, "utf-8"));
|
|
633
668
|
if (config.model) return config.model;
|
|
634
669
|
}
|
|
635
670
|
} catch {
|
|
@@ -745,13 +780,13 @@ var require_dist4 = __commonJS({
|
|
|
745
780
|
buildAnalysisPrompt: () => buildAnalysisPrompt
|
|
746
781
|
});
|
|
747
782
|
module2.exports = __toCommonJS(index_exports);
|
|
748
|
-
var
|
|
783
|
+
var import_fs2 = __toESM2(require("fs"));
|
|
749
784
|
var import_path2 = __toESM2(require("path"));
|
|
750
785
|
function resolveExistingPathPrefix(candidate) {
|
|
751
786
|
let current = candidate.replace(/(?::\d+)+$/, "").replace(/['"()\[\]`]+$/, "").trim();
|
|
752
787
|
while (current.length > 0) {
|
|
753
788
|
try {
|
|
754
|
-
if (
|
|
789
|
+
if (import_fs2.default.existsSync(current)) {
|
|
755
790
|
return current;
|
|
756
791
|
}
|
|
757
792
|
} catch {
|
|
@@ -811,7 +846,7 @@ var require_dist4 = __commonJS({
|
|
|
811
846
|
if (processedFiles.has(filePath)) return;
|
|
812
847
|
processedFiles.add(filePath);
|
|
813
848
|
try {
|
|
814
|
-
const content =
|
|
849
|
+
const content = import_fs2.default.readFileSync(filePath, "utf-8");
|
|
815
850
|
context += `
|
|
816
851
|
--- File: ${relPath} ---
|
|
817
852
|
\`\`\`
|
|
@@ -823,18 +858,18 @@ ${content}
|
|
|
823
858
|
};
|
|
824
859
|
for (const absPath of resolvedPaths) {
|
|
825
860
|
try {
|
|
826
|
-
const stats =
|
|
861
|
+
const stats = import_fs2.default.statSync(absPath);
|
|
827
862
|
if (stats.isFile()) {
|
|
828
863
|
addFileToContext(absPath);
|
|
829
864
|
const parentDir = import_path2.default.dirname(absPath);
|
|
830
|
-
const filesInDir =
|
|
865
|
+
const filesInDir = import_fs2.default.readdirSync(parentDir);
|
|
831
866
|
const relParentDir = import_path2.default.relative(cwd, parentDir) || ".";
|
|
832
867
|
context += `
|
|
833
868
|
--- Directory files in "${relParentDir}": ---
|
|
834
869
|
`;
|
|
835
870
|
for (const file of filesInDir) {
|
|
836
871
|
try {
|
|
837
|
-
const fileStats =
|
|
872
|
+
const fileStats = import_fs2.default.statSync(import_path2.default.join(parentDir, file));
|
|
838
873
|
const marker = fileStats.isDirectory() ? "/" : "";
|
|
839
874
|
context += `- ${file}${marker}
|
|
840
875
|
`;
|
|
@@ -846,13 +881,13 @@ ${content}
|
|
|
846
881
|
} else if (stats.isDirectory()) {
|
|
847
882
|
const relParentDir = import_path2.default.relative(cwd, absPath) || ".";
|
|
848
883
|
if (relParentDir !== ".") {
|
|
849
|
-
const filesInDir =
|
|
884
|
+
const filesInDir = import_fs2.default.readdirSync(absPath);
|
|
850
885
|
context += `
|
|
851
886
|
--- Directory files in "${relParentDir}": ---
|
|
852
887
|
`;
|
|
853
888
|
for (const file of filesInDir) {
|
|
854
889
|
try {
|
|
855
|
-
const fileStats =
|
|
890
|
+
const fileStats = import_fs2.default.statSync(import_path2.default.join(absPath, file));
|
|
856
891
|
const marker = fileStats.isDirectory() ? "/" : "";
|
|
857
892
|
context += `- ${file}${marker}
|
|
858
893
|
`;
|
|
@@ -866,7 +901,7 @@ ${content}
|
|
|
866
901
|
const filePathFull = import_path2.default.join(absPath, file);
|
|
867
902
|
const fileExt = import_path2.default.extname(file).toLowerCase();
|
|
868
903
|
try {
|
|
869
|
-
if (
|
|
904
|
+
if (import_fs2.default.statSync(filePathFull).isFile() && [".js", ".jsx", ".ts", ".tsx", ".json", ".css"].includes(fileExt)) {
|
|
870
905
|
addFileToContext(filePathFull);
|
|
871
906
|
}
|
|
872
907
|
} catch {
|
|
@@ -973,7 +1008,7 @@ var require_dist5 = __commonJS({
|
|
|
973
1008
|
lineSimilarity: () => lineSimilarity
|
|
974
1009
|
});
|
|
975
1010
|
module2.exports = __toCommonJS(index_exports);
|
|
976
|
-
var
|
|
1011
|
+
var import_fs2 = __toESM2(require("fs"));
|
|
977
1012
|
var import_path2 = __toESM2(require("path"));
|
|
978
1013
|
var SIMILARITY_THRESHOLD = 0.85;
|
|
979
1014
|
var AMBIGUITY_MARGIN = 0.05;
|
|
@@ -1076,13 +1111,13 @@ var require_dist5 = __commonJS({
|
|
|
1076
1111
|
}
|
|
1077
1112
|
function applyPatch(absoluteFilePath, search, replace) {
|
|
1078
1113
|
try {
|
|
1079
|
-
if (!
|
|
1114
|
+
if (!import_fs2.default.existsSync(absoluteFilePath)) {
|
|
1080
1115
|
return {
|
|
1081
1116
|
success: false,
|
|
1082
1117
|
error: `File not found: ${absoluteFilePath}`
|
|
1083
1118
|
};
|
|
1084
1119
|
}
|
|
1085
|
-
const original =
|
|
1120
|
+
const original = import_fs2.default.readFileSync(absoluteFilePath, "utf-8");
|
|
1086
1121
|
const match = findMatch(original, search);
|
|
1087
1122
|
if (!match) {
|
|
1088
1123
|
return {
|
|
@@ -1091,26 +1126,26 @@ var require_dist5 = __commonJS({
|
|
|
1091
1126
|
};
|
|
1092
1127
|
}
|
|
1093
1128
|
const backupPath = absoluteFilePath + ".opspilot.bak";
|
|
1094
|
-
|
|
1129
|
+
import_fs2.default.writeFileSync(backupPath, original, "utf-8");
|
|
1095
1130
|
const fixed = original.slice(0, match.start) + replace + original.slice(match.end);
|
|
1096
|
-
|
|
1131
|
+
import_fs2.default.writeFileSync(absoluteFilePath, fixed, "utf-8");
|
|
1097
1132
|
return { success: true, backupPath, matchStrategy: match.strategy };
|
|
1098
1133
|
} catch (err) {
|
|
1099
1134
|
return { success: false, error: err.message };
|
|
1100
1135
|
}
|
|
1101
1136
|
}
|
|
1102
|
-
var
|
|
1137
|
+
var import_fs22 = __toESM2(require("fs"));
|
|
1103
1138
|
var import_path22 = __toESM2(require("path"));
|
|
1104
1139
|
function rollback(originalContent, backupPaths) {
|
|
1105
1140
|
for (const [absolutePath, content] of originalContent) {
|
|
1106
1141
|
try {
|
|
1107
|
-
|
|
1142
|
+
import_fs22.default.writeFileSync(absolutePath, content, "utf-8");
|
|
1108
1143
|
} catch {
|
|
1109
1144
|
}
|
|
1110
1145
|
}
|
|
1111
1146
|
for (const backupPath of backupPaths) {
|
|
1112
1147
|
try {
|
|
1113
|
-
|
|
1148
|
+
import_fs22.default.unlinkSync(backupPath);
|
|
1114
1149
|
} catch {
|
|
1115
1150
|
}
|
|
1116
1151
|
}
|
|
@@ -1126,7 +1161,7 @@ var require_dist5 = __commonJS({
|
|
|
1126
1161
|
const absolutePath = import_path22.default.resolve(cwd, patch.filePath);
|
|
1127
1162
|
if (!originalContent.has(absolutePath)) {
|
|
1128
1163
|
try {
|
|
1129
|
-
originalContent.set(absolutePath,
|
|
1164
|
+
originalContent.set(absolutePath, import_fs22.default.readFileSync(absolutePath, "utf-8"));
|
|
1130
1165
|
} catch {
|
|
1131
1166
|
rollback(originalContent, backupPaths);
|
|
1132
1167
|
return {
|
|
@@ -1375,14 +1410,15 @@ var require_dist7 = __commonJS({
|
|
|
1375
1410
|
var index_exports = {};
|
|
1376
1411
|
__export(index_exports, {
|
|
1377
1412
|
detectFramework: () => detectFramework2,
|
|
1413
|
+
detectPackageManager: () => import_analyzers2.detectPackageManager,
|
|
1378
1414
|
orchestrate: () => orchestrate2,
|
|
1379
1415
|
orchestrateAndFix: () => orchestrateAndFix2
|
|
1380
1416
|
});
|
|
1381
1417
|
module2.exports = __toCommonJS(index_exports);
|
|
1382
|
-
var
|
|
1418
|
+
var import_fs2 = __toESM2(require("fs"));
|
|
1383
1419
|
var import_path2 = __toESM2(require("path"));
|
|
1384
1420
|
function detectFramework2(cwd = process.cwd()) {
|
|
1385
|
-
const has = (...files) => files.some((f) =>
|
|
1421
|
+
const has = (...files) => files.some((f) => import_fs2.default.existsSync(import_path2.default.join(cwd, f)));
|
|
1386
1422
|
if (has("next.config.js", "next.config.ts", "next.config.mjs")) return "nextjs";
|
|
1387
1423
|
if (has("nest-cli.json")) return "nestjs";
|
|
1388
1424
|
if (has("vite.config.ts", "vite.config.js", "vite.config.mts")) return "vite";
|
|
@@ -1391,9 +1427,9 @@ var require_dist7 = __commonJS({
|
|
|
1391
1427
|
if (has("svelte.config.js", "svelte.config.ts")) return "sveltekit";
|
|
1392
1428
|
if (has("nuxt.config.ts", "nuxt.config.js")) return "nuxt";
|
|
1393
1429
|
const pkgPath = import_path2.default.join(cwd, "package.json");
|
|
1394
|
-
if (
|
|
1430
|
+
if (import_fs2.default.existsSync(pkgPath)) {
|
|
1395
1431
|
try {
|
|
1396
|
-
const pkg = JSON.parse(
|
|
1432
|
+
const pkg = JSON.parse(import_fs2.default.readFileSync(pkgPath, "utf-8"));
|
|
1397
1433
|
const deps = {
|
|
1398
1434
|
...pkg.dependencies ?? {},
|
|
1399
1435
|
...pkg.devDependencies ?? {}
|
|
@@ -1408,7 +1444,7 @@ var require_dist7 = __commonJS({
|
|
|
1408
1444
|
}
|
|
1409
1445
|
var import_analyzers = require_dist2();
|
|
1410
1446
|
var import_agents = require_dist6();
|
|
1411
|
-
var
|
|
1447
|
+
var import_fs22 = __toESM2(require("fs"));
|
|
1412
1448
|
var import_path22 = __toESM2(require("path"));
|
|
1413
1449
|
var import_url = require("url");
|
|
1414
1450
|
var import_ai_provider = require_dist3();
|
|
@@ -1691,7 +1727,7 @@ ${rawOutput}
|
|
|
1691
1727
|
*Report generated by **BugZero** on ${(/* @__PURE__ */ new Date()).toLocaleString()}*`;
|
|
1692
1728
|
const reportPath = import_path22.default.resolve(cwd, "bugzero-report.md");
|
|
1693
1729
|
try {
|
|
1694
|
-
|
|
1730
|
+
import_fs22.default.writeFileSync(reportPath, content, "utf-8");
|
|
1695
1731
|
const clickableUrl = (0, import_url.pathToFileURL)(reportPath).toString();
|
|
1696
1732
|
console.log(`
|
|
1697
1733
|
\u{1F4DD} Generated report successfully!
|
|
@@ -1707,24 +1743,29 @@ ${rawOutput}
|
|
|
1707
1743
|
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");
|
|
1708
1744
|
}
|
|
1709
1745
|
}
|
|
1746
|
+
var import_analyzers2 = require_dist2();
|
|
1710
1747
|
}
|
|
1711
1748
|
});
|
|
1712
1749
|
|
|
1713
1750
|
// src/index.ts
|
|
1714
1751
|
var import_config = __toESM(require_dist());
|
|
1715
1752
|
var import_commander = require("commander");
|
|
1753
|
+
var import_fs = __toESM(require("fs"));
|
|
1716
1754
|
var import_path = __toESM(require("path"));
|
|
1717
1755
|
var import_core = __toESM(require_dist7());
|
|
1756
|
+
var { version } = JSON.parse(import_fs.default.readFileSync(import_path.default.join(__dirname, "..", "package.json"), "utf-8"));
|
|
1718
1757
|
var program = new import_commander.Command();
|
|
1719
|
-
program.name("bugzero").description("AI-powered build issue fixer \u2014 detects and repairs build errors automatically").version(
|
|
1758
|
+
program.name("bugzero").description("AI-powered build issue fixer \u2014 detects and repairs build errors automatically").version(version);
|
|
1720
1759
|
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) => {
|
|
1721
1760
|
try {
|
|
1722
1761
|
const cwd = import_path.default.resolve(opts.path);
|
|
1723
1762
|
const framework = (0, import_core.detectFramework)(cwd);
|
|
1763
|
+
const packageManager = (0, import_core.detectPackageManager)(cwd);
|
|
1724
1764
|
console.log(`
|
|
1725
1765
|
\u{1F50D} BugZero \u2014 Analyzing project`);
|
|
1726
1766
|
console.log(` Path: ${cwd}`);
|
|
1727
|
-
console.log(` Framework: ${framework}
|
|
1767
|
+
console.log(` Framework: ${framework}`);
|
|
1768
|
+
console.log(` Package manager: ${packageManager}
|
|
1728
1769
|
`);
|
|
1729
1770
|
const { build, lint, test } = await (0, import_core.orchestrate)({ cwd });
|
|
1730
1771
|
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");
|
|
@@ -1749,10 +1790,12 @@ program.command("rescue").description("Detect build failures and automatically a
|
|
|
1749
1790
|
const maxAttempts = parseInt(opts.maxAttempts, 10);
|
|
1750
1791
|
const dryRun = opts.dryRun;
|
|
1751
1792
|
const framework = (0, import_core.detectFramework)(cwd);
|
|
1793
|
+
const packageManager = (0, import_core.detectPackageManager)(cwd);
|
|
1752
1794
|
console.log(`
|
|
1753
1795
|
\u{1F916} BugZero Fix`);
|
|
1754
1796
|
console.log(` Path: ${cwd}`);
|
|
1755
1797
|
console.log(` Framework: ${framework}`);
|
|
1798
|
+
console.log(` Package manager: ${packageManager}`);
|
|
1756
1799
|
console.log(` Max attempts: ${maxAttempts}`);
|
|
1757
1800
|
if (dryRun) console.log(` Mode: DRY RUN (no files will be modified)`);
|
|
1758
1801
|
console.log();
|
package/package.json
CHANGED
|
@@ -1,11 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bugzero",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.0.36",
|
|
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
|
+
"author": "Saksham Agarwal (https://github.com/Saksham1800t)",
|
|
7
|
+
"homepage": "https://github.com/Saksham1800t/Bugzero#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Saksham1800t/Bugzero.git",
|
|
11
|
+
"directory": "apps/bugzero"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/Saksham1800t/Bugzero/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"ai",
|
|
18
|
+
"llm",
|
|
19
|
+
"autofix",
|
|
20
|
+
"auto-fix",
|
|
21
|
+
"build-fixer",
|
|
22
|
+
"bug-fix",
|
|
23
|
+
"code-repair",
|
|
24
|
+
"self-healing",
|
|
25
|
+
"typescript",
|
|
26
|
+
"eslint",
|
|
27
|
+
"jest",
|
|
28
|
+
"cli",
|
|
29
|
+
"langgraph",
|
|
30
|
+
"openrouter",
|
|
31
|
+
"github-action",
|
|
32
|
+
"ci",
|
|
33
|
+
"developer-tools"
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=22.12.0"
|
|
37
|
+
},
|
|
6
38
|
"bin": {
|
|
7
39
|
"bugzero": "dist/index.js"
|
|
8
40
|
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist",
|
|
43
|
+
"README.md",
|
|
44
|
+
"LICENSE"
|
|
45
|
+
],
|
|
9
46
|
"dependencies": {
|
|
10
47
|
"@langchain/core": "^1.1.48",
|
|
11
48
|
"@langchain/langgraph": "^1.3.2",
|
|
@@ -21,6 +58,6 @@
|
|
|
21
58
|
"main": "./dist/index.js",
|
|
22
59
|
"types": "./dist/index.d.ts",
|
|
23
60
|
"scripts": {
|
|
24
|
-
"build": "tsup src/index.ts --format cjs --dts"
|
|
61
|
+
"build": "tsup src/index.ts --format cjs --dts --clean"
|
|
25
62
|
}
|
|
26
|
-
}
|
|
63
|
+
}
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> bugzero@0.0.34 build /home/runner/work/Bugzero/Bugzero/apps/bugzero
|
|
3
|
-
> tsup src/index.ts --format cjs --dts
|
|
4
|
-
|
|
5
|
-
[34mCLI[39m Building entry: src/index.ts
|
|
6
|
-
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
|
-
[34mCLI[39m tsup v8.5.1
|
|
8
|
-
[34mCLI[39m Target: es2022
|
|
9
|
-
[34mCJS[39m Build start
|
|
10
|
-
[32mCJS[39m [1mdist/index.js [22m[32m77.80 KB[39m
|
|
11
|
-
[32mCJS[39m ⚡️ Build success in 57ms
|
|
12
|
-
[34mDTS[39m Build start
|
|
13
|
-
[32mDTS[39m ⚡️ Build success in 1273ms
|
|
14
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m20.00 B[39m
|
package/demo.svg
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
<svg viewBox="0 0 700 520" xmlns="http://www.w3.org/2000/svg" font-family="SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace">
|
|
2
|
-
<title>bugzero rescue demo</title>
|
|
3
|
-
<desc>Illustrative animation of "bugzero rescue" fixing a small TypeScript error. Log lines are the tool's real output format; the example error is a stand-in, not a captured recording.</desc>
|
|
4
|
-
|
|
5
|
-
<style>
|
|
6
|
-
text { font-size: 13px; }
|
|
7
|
-
.prompt { fill: #58a6ff; }
|
|
8
|
-
.out { fill: #c9d1d9; }
|
|
9
|
-
.ok { fill: #3fb950; }
|
|
10
|
-
.dim { fill: #8b949e; }
|
|
11
|
-
.dot { }
|
|
12
|
-
.titlebar-label { fill: #8b949e; font-size: 12px; }
|
|
13
|
-
|
|
14
|
-
@keyframes gA { 0% { opacity: 0; } 2% { opacity: 1; } 90% { opacity: 1; } 97% { opacity: 0; } 100% { opacity: 0; } }
|
|
15
|
-
@keyframes gB { 0%, 6% { opacity: 0; } 9% { opacity: 1; } 90% { opacity: 1; } 97% { opacity: 0; } 100% { opacity: 0; } }
|
|
16
|
-
@keyframes gC { 0%, 16% { opacity: 0; } 19% { opacity: 1; } 90% { opacity: 1; } 97% { opacity: 0; } 100% { opacity: 0; } }
|
|
17
|
-
@keyframes gD { 0%, 26% { opacity: 0; } 29% { opacity: 1; } 90% { opacity: 1; } 97% { opacity: 0; } 100% { opacity: 0; } }
|
|
18
|
-
@keyframes gE { 0%, 38% { opacity: 0; } 41% { opacity: 1; } 90% { opacity: 1; } 97% { opacity: 0; } 100% { opacity: 0; } }
|
|
19
|
-
@keyframes gF { 0%, 48% { opacity: 0; } 50% { opacity: 1; } 90% { opacity: 1; } 97% { opacity: 0; } 100% { opacity: 0; } }
|
|
20
|
-
@keyframes gG { 0%, 56% { opacity: 0; } 59% { opacity: 1; } 90% { opacity: 1; } 97% { opacity: 0; } 100% { opacity: 0; } }
|
|
21
|
-
|
|
22
|
-
.groupA { animation: gA 14s ease-in-out infinite; }
|
|
23
|
-
.groupB { animation: gB 14s ease-in-out infinite; }
|
|
24
|
-
.groupC { animation: gC 14s ease-in-out infinite; }
|
|
25
|
-
.groupD { animation: gD 14s ease-in-out infinite; }
|
|
26
|
-
.groupE { animation: gE 14s ease-in-out infinite; }
|
|
27
|
-
.groupF { animation: gF 14s ease-in-out infinite; }
|
|
28
|
-
.groupG { animation: gG 14s ease-in-out infinite; }
|
|
29
|
-
</style>
|
|
30
|
-
|
|
31
|
-
<rect x="0" y="0" width="700" height="520" rx="10" fill="#0d1117" />
|
|
32
|
-
<rect x="0" y="0" width="700" height="34" rx="10" fill="#161b22" />
|
|
33
|
-
<rect x="0" y="24" width="700" height="10" fill="#161b22" />
|
|
34
|
-
|
|
35
|
-
<circle class="dot" cx="20" cy="17" r="6" fill="#ff5f56" />
|
|
36
|
-
<circle class="dot" cx="40" cy="17" r="6" fill="#ffbd2e" />
|
|
37
|
-
<circle class="dot" cx="60" cy="17" r="6" fill="#27c93f" />
|
|
38
|
-
<text class="titlebar-label" x="350" y="21" text-anchor="middle">bugzero rescue</text>
|
|
39
|
-
|
|
40
|
-
<g class="groupA">
|
|
41
|
-
<text class="prompt" x="18" y="54">$ bugzero rescue --path ./demo-project</text>
|
|
42
|
-
</g>
|
|
43
|
-
|
|
44
|
-
<g class="groupB">
|
|
45
|
-
<text class="out" x="18" y="83">🤖 BugZero Fix</text>
|
|
46
|
-
<text class="out" x="18" y="102"> Path: ./demo-project</text>
|
|
47
|
-
<text class="out" x="18" y="121"> Framework: typescript</text>
|
|
48
|
-
<text class="out" x="18" y="140"> Max attempts: 3</text>
|
|
49
|
-
</g>
|
|
50
|
-
|
|
51
|
-
<g class="groupC">
|
|
52
|
-
<text class="out" x="18" y="169">[BugZero] Running validation pipeline in: ./demo-project</text>
|
|
53
|
-
<text class="out" x="18" y="188">[BugZero] Running build...</text>
|
|
54
|
-
<text class="out" x="18" y="207">[BugZero] Detected 1 error(s) (type: typescript)</text>
|
|
55
|
-
</g>
|
|
56
|
-
|
|
57
|
-
<g class="groupD">
|
|
58
|
-
<text class="out" x="18" y="236">[BugZero] Attempt 1/3 — asking AI…</text>
|
|
59
|
-
</g>
|
|
60
|
-
|
|
61
|
-
<g class="groupE">
|
|
62
|
-
<text class="out" x="18" y="265">[BugZero] Patching 1 file: src/sum.ts</text>
|
|
63
|
-
<text class="out" x="18" y="284">[BugZero] Reason: Fixed subtraction typo in the sum() function</text>
|
|
64
|
-
<text class="out" x="18" y="303">[BugZero] Backup saved to: src/sum.ts.opspilot.bak</text>
|
|
65
|
-
<text class="out" x="18" y="322">[BugZero] Re-running validation pipeline to verify fix…</text>
|
|
66
|
-
</g>
|
|
67
|
-
|
|
68
|
-
<g class="groupF">
|
|
69
|
-
<text class="ok" x="18" y="351">[BugZero] ✅ Validation succeeded!</text>
|
|
70
|
-
</g>
|
|
71
|
-
|
|
72
|
-
<g class="groupG">
|
|
73
|
-
<text class="dim" x="18" y="380">──────────────────────────────────────────────</text>
|
|
74
|
-
<text class="ok" x="18" y="399">✅ Build fixed after 1 attempt(s)!</text>
|
|
75
|
-
<text class="out" x="18" y="418"> Model: deepseek/deepseek-chat</text>
|
|
76
|
-
<text class="out" x="18" y="437"> Tokens: Prompt: 812 | Completion: 96 | Total: 908</text>
|
|
77
|
-
<text class="out" x="18" y="456"> File: src/sum.ts</text>
|
|
78
|
-
<text class="out" x="18" y="475"> Reason: Fixed subtraction typo in the sum() function</text>
|
|
79
|
-
<text class="out" x="18" y="494"> Root cause: sum() used the - operator instead of +</text>
|
|
80
|
-
</g>
|
|
81
|
-
</svg>
|
package/src/index.ts
DELETED
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import "@opspilot/config"; // loads .env before anything else
|
|
4
|
-
import { Command } from "commander";
|
|
5
|
-
import path from "path";
|
|
6
|
-
import { orchestrate, orchestrateAndFix, detectFramework } from "@opspilot/core";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const program = new Command();
|
|
10
|
-
|
|
11
|
-
program
|
|
12
|
-
.name("bugzero")
|
|
13
|
-
.description("AI-powered build issue fixer — detects and repairs build errors automatically")
|
|
14
|
-
.version("0.0.1");
|
|
15
|
-
|
|
16
|
-
// ── bugzero hunt ─────────────────────────────────────────────────────────────
|
|
17
|
-
program
|
|
18
|
-
.command("hunt")
|
|
19
|
-
.description("Run build, lint, and tests — report issues without modifying files")
|
|
20
|
-
.option("-p, --path <dir>", "Path to the target project", ".")
|
|
21
|
-
.action(async (opts) => {
|
|
22
|
-
try {
|
|
23
|
-
const cwd = path.resolve(opts.path);
|
|
24
|
-
const framework = detectFramework(cwd);
|
|
25
|
-
|
|
26
|
-
console.log(`\n🔍 BugZero — Analyzing project`);
|
|
27
|
-
console.log(` Path: ${cwd}`);
|
|
28
|
-
console.log(` Framework: ${framework}\n`);
|
|
29
|
-
|
|
30
|
-
const { build, lint, test } = await orchestrate({ cwd });
|
|
31
|
-
|
|
32
|
-
console.log("── Pipeline Summary ───────────────────────────");
|
|
33
|
-
console.log(` Build: ${build.success ? "✅ Passed" : "❌ Failed (see report)"}`);
|
|
34
|
-
console.log(` Lint: ${lint.success ? "✅ Passed" : "❌ Failed (see report)"}`);
|
|
35
|
-
console.log(` Tests: ${test.success ? "✅ Passed" : "❌ Failed (see report)"}`);
|
|
36
|
-
console.log("───────────────────────────────────────────────");
|
|
37
|
-
|
|
38
|
-
const allPassed = build.success && lint.success && test.success;
|
|
39
|
-
console.log(`\n${allPassed ? "✅ All checks passed" : "❌ Issues found — run 'bugzero rescue' to repair"}\n`);
|
|
40
|
-
process.exit(allPassed ? 0 : 1);
|
|
41
|
-
} catch (error: any) {
|
|
42
|
-
console.error(`\n❌ [BugZero] Execution failed: ${error.message || error}`);
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
// ── bugzero rescue ───────────────────────────────────────────────────────────
|
|
48
|
-
program
|
|
49
|
-
.command("rescue")
|
|
50
|
-
.description("Detect build failures and automatically apply AI-generated fixes")
|
|
51
|
-
.option("-p, --path <dir>", "Path to the target project", ".")
|
|
52
|
-
.option("-n, --max-attempts <number>", "Maximum AI fix attempts", "3")
|
|
53
|
-
.option("--dry-run", "Analyse only — print what would be fixed without writing files", false)
|
|
54
|
-
.action(async (opts) => {
|
|
55
|
-
try {
|
|
56
|
-
const cwd = path.resolve(opts.path);
|
|
57
|
-
const maxAttempts = parseInt(opts.maxAttempts, 10);
|
|
58
|
-
const dryRun: boolean = opts.dryRun;
|
|
59
|
-
const framework = detectFramework(cwd);
|
|
60
|
-
|
|
61
|
-
console.log(`\n🤖 BugZero Fix`);
|
|
62
|
-
console.log(` Path: ${cwd}`);
|
|
63
|
-
console.log(` Framework: ${framework}`);
|
|
64
|
-
console.log(` Max attempts: ${maxAttempts}`);
|
|
65
|
-
if (dryRun) console.log(` Mode: DRY RUN (no files will be modified)`);
|
|
66
|
-
console.log();
|
|
67
|
-
|
|
68
|
-
const result = await orchestrateAndFix({ cwd, maxAttempts, dryRun });
|
|
69
|
-
|
|
70
|
-
console.log("\n──────────────────────────────────────────────");
|
|
71
|
-
if (result.success) {
|
|
72
|
-
console.log(`✅ Build fixed after ${result.attempts} attempt(s)!`);
|
|
73
|
-
console.log(` Model: ${result.modelUsed || "unknown"}`);
|
|
74
|
-
if (result.promptTokens !== undefined && result.completionTokens !== undefined) {
|
|
75
|
-
console.log(` Tokens: Prompt: ${result.promptTokens} | Completion: ${result.completionTokens} | Total: ${result.promptTokens + result.completionTokens}`);
|
|
76
|
-
}
|
|
77
|
-
if (result.appliedFix) {
|
|
78
|
-
const files = result.appliedFix.patches.map((p) => p.filePath).join(", ");
|
|
79
|
-
console.log(` File${result.appliedFix.patches.length === 1 ? "" : "s"}: ${files}`);
|
|
80
|
-
console.log(` Reason: ${result.appliedFix.explanation}`);
|
|
81
|
-
console.log(` Root cause: ${result.appliedFix.rootCause}`);
|
|
82
|
-
}
|
|
83
|
-
} else {
|
|
84
|
-
console.log(`❌ Could not fix build after ${result.attempts} attempt(s).`);
|
|
85
|
-
console.log(` Model: ${result.modelUsed || "unknown"}`);
|
|
86
|
-
if (result.promptTokens !== undefined && result.completionTokens !== undefined) {
|
|
87
|
-
console.log(` Tokens: Prompt: ${result.promptTokens} | Completion: ${result.completionTokens} | Total: ${result.promptTokens + result.completionTokens}`);
|
|
88
|
-
}
|
|
89
|
-
if (result.error) console.log(` ${result.error}`);
|
|
90
|
-
console.log(`\n👉 Full unresolved error logs have been listed in the generated report.`);
|
|
91
|
-
}
|
|
92
|
-
console.log();
|
|
93
|
-
|
|
94
|
-
process.exit(result.success ? 0 : 1);
|
|
95
|
-
} catch (error: any) {
|
|
96
|
-
console.error(`\n❌ [BugZero] Execution failed: ${error.message || error}`);
|
|
97
|
-
process.exit(1);
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// ── bugzero protect (legacy) ────────────────────────────────────────────────
|
|
102
|
-
program
|
|
103
|
-
.command("protect")
|
|
104
|
-
.description("[Deprecated] Use 'hunt' instead")
|
|
105
|
-
.action(async () => {
|
|
106
|
-
console.warn("⚠️ 'protect' is deprecated — use 'bugzero hunt' instead.\n");
|
|
107
|
-
const result = await orchestrate();
|
|
108
|
-
console.log(result);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// ── bugzero vibe ─────────────────────────────────────────────────────────────
|
|
112
|
-
program
|
|
113
|
-
.command("vibe")
|
|
114
|
-
.description("Select which AI model (brain) to use for resolving bugs")
|
|
115
|
-
.action(async () => {
|
|
116
|
-
const { getLocalConfig, saveLocalConfig } = await import("@opspilot/config");
|
|
117
|
-
const readline = await import("readline");
|
|
118
|
-
const axios = (await import("axios")).default;
|
|
119
|
-
|
|
120
|
-
console.log(`\n🧠 BugZero Brain Selector`);
|
|
121
|
-
console.log(`──────────────────────────────────────────────`);
|
|
122
|
-
|
|
123
|
-
let popularModels: any[] = [];
|
|
124
|
-
try {
|
|
125
|
-
console.log("🌀 Fetching active models list from OpenRouter...");
|
|
126
|
-
const response = await axios.get("https://openrouter.ai/api/v1/models", { timeout: 8000 });
|
|
127
|
-
const allModels = response.data?.data || [];
|
|
128
|
-
const popularModelIds = [
|
|
129
|
-
"deepseek/deepseek-chat",
|
|
130
|
-
"deepseek/deepseek-r1",
|
|
131
|
-
"anthropic/claude-3.5-sonnet",
|
|
132
|
-
"google/gemini-2.5-pro",
|
|
133
|
-
"google/gemini-2.5-flash",
|
|
134
|
-
"openai/gpt-4o",
|
|
135
|
-
"meta-llama/llama-3.3-70b-instruct"
|
|
136
|
-
];
|
|
137
|
-
popularModels = allModels.filter((m: any) => popularModelIds.includes(m.id));
|
|
138
|
-
} catch (e: any) {
|
|
139
|
-
console.warn("⚠️ Failed to fetch models list from OpenRouter. Using cached default list.");
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (popularModels.length === 0) {
|
|
143
|
-
popularModels = [
|
|
144
|
-
{ id: "deepseek/deepseek-chat", name: "DeepSeek V3 (Chat)" },
|
|
145
|
-
{ id: "deepseek/deepseek-r1", name: "DeepSeek R1" },
|
|
146
|
-
{ id: "anthropic/claude-3.5-sonnet", name: "Claude 3.5 Sonnet" },
|
|
147
|
-
{ id: "google/gemini-2.5-pro", name: "Gemini 2.5 Pro" },
|
|
148
|
-
{ id: "google/gemini-2.5-flash", name: "Gemini 2.5 Flash" },
|
|
149
|
-
{ id: "openai/gpt-4o", name: "GPT-4o" },
|
|
150
|
-
{ id: "meta-llama/llama-3.3-70b-instruct", name: "Llama 3.3 Instruct" }
|
|
151
|
-
];
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const currentModel = getLocalConfig().model || "deepseek/deepseek-chat";
|
|
155
|
-
console.log(`\nCurrent active model: \x1b[36m${currentModel}\x1b[0m\n`);
|
|
156
|
-
|
|
157
|
-
popularModels.forEach((m, idx) => {
|
|
158
|
-
console.log(` [${idx + 1}] ${m.name} (${m.id})`);
|
|
159
|
-
});
|
|
160
|
-
console.log(` [${popularModels.length + 1}] 🖋️ Custom Model (Type manually)`);
|
|
161
|
-
console.log();
|
|
162
|
-
|
|
163
|
-
const rl = readline.createInterface({
|
|
164
|
-
input: process.stdin,
|
|
165
|
-
output: process.stdout,
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
rl.question(`Select a number (1-${popularModels.length + 1}): `, (answer) => {
|
|
169
|
-
const choice = parseInt(answer.trim(), 10);
|
|
170
|
-
if (isNaN(choice) || choice < 1 || choice > popularModels.length + 1) {
|
|
171
|
-
console.log("❌ Invalid selection. Keeping current model.");
|
|
172
|
-
rl.close();
|
|
173
|
-
process.exit(1);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (choice === popularModels.length + 1) {
|
|
177
|
-
rl.question("Enter custom OpenRouter Model ID (e.g. microsoft/phi-3-medium-128k-instruct): ", (customModel) => {
|
|
178
|
-
const selected = customModel.trim();
|
|
179
|
-
if (!selected) {
|
|
180
|
-
console.log("❌ No model entered. Keeping current model.");
|
|
181
|
-
} else {
|
|
182
|
-
saveLocalConfig({ model: selected });
|
|
183
|
-
console.log(`\n✅ Saved! Model set to: \x1b[32m${selected}\x1b[0m`);
|
|
184
|
-
console.log(` Saved config to: .bugzerorc`);
|
|
185
|
-
}
|
|
186
|
-
rl.close();
|
|
187
|
-
});
|
|
188
|
-
} else {
|
|
189
|
-
const selected = popularModels[choice - 1].id;
|
|
190
|
-
saveLocalConfig({ model: selected });
|
|
191
|
-
console.log(`\n✅ Saved! Model set to: \x1b[32m${selected}\x1b[0m`);
|
|
192
|
-
console.log(` Saved config to: .bugzerorc`);
|
|
193
|
-
rl.close();
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
program.parse();
|