bugzero 0.0.35 → 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 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
  [![npm downloads](https://img.shields.io/npm/dm/bugzero.svg?style=flat-square)](https://www.npmjs.com/package/bugzero)
9
9
  [![CI](https://img.shields.io/github/actions/workflow/status/Saksham1800t/Bugzero/ci.yml?branch=main&label=CI&style=flat-square)](https://github.com/Saksham1800t/Bugzero/actions/workflows/ci.yml)
10
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](https://opensource.org/licenses/MIT)
11
- [![Node.js](https://img.shields.io/badge/Node.js-18%2B-green?style=flat-square)](https://nodejs.org)
11
+ [![Node.js](https://img.shields.io/badge/Node.js-22.12%2B-green?style=flat-square)](https://nodejs.org)
12
12
  [![pnpm](https://img.shields.io/badge/maintained%20with-pnpm-cc00ff.svg?style=flat-square)](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,22 +88,30 @@ bugzero rescue
88
88
 
89
89
  ---
90
90
 
91
- ## Key Features
91
+ ## Features
92
92
 
93
- | Feature | Description |
94
- |---|---|
95
- | Autonomous repair loop | Runs analyze, patch, verify in a stateful LangGraph graph |
96
- | Multi-model support | DeepSeek, Claude, GPT-4o, Gemini, Llama via OpenRouter |
97
- | Framework detection | Automatically identifies your project's tooling setup |
98
- | Package manager detection | Runs build/lint/test with npm, pnpm, yarn, or bun — whichever the target project actually uses |
99
- | **Multi-file patches** | A single fix attempt can edit several files at once, applied atomically — if any file's patch fails, every file touched in that attempt is rolled back |
100
- | **Fuzzy patch matching** | Falls back from exact match to whitespace-normalized and similarity-scored matching when the AI's snippet doesn't match the file verbatim |
101
- | Safe file patching | Every modified file receives a `.opspilot.bak` backup |
102
- | Error type parsing | Parses TypeScript, ESLint, and Jest error formats |
103
- | Dry-run mode | Analyze and report without writing any files |
104
- | Local model config | Save preferred model to `.bugzerorc` per project |
105
- | Markdown Reports | Auto-generates a detailed `bugzero-report.md` with error logs, model name, token usage, and code diffs — plus a clickable link straight to your terminal |
106
- | **GitHub Action mode** | Run the same repair loop in CI and open a pull request with the fix instead of patching a local checkout — see below |
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.
107
115
 
108
116
  ---
109
117
 
@@ -270,7 +278,7 @@ BugZero provides clear, actionable output for common failure modes:
270
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.
271
279
 
272
280
  ### Prerequisites
273
- - Node.js 18 or later
281
+ - Node.js 22.12 or later
274
282
  - pnpm
275
283
 
276
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 fs = require("fs");
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 (fs.existsSync(filepath)) {
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 (fs.existsSync(possibleVaultPath)) {
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(fs.readFileSync(path3, { encoding }));
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 import_fs = __toESM2(require("fs"));
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 (import_fs.default.existsSync(envPath)) {
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 import_fs2 = __toESM2(require("fs"));
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 (import_fs2.default.existsSync(configPath)) {
418
- const content = import_fs2.default.readFileSync(configPath, "utf-8");
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
- import_fs2.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
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);
@@ -476,7 +476,7 @@ var require_dist2 = __commonJS({
476
476
  runValidationPipeline: () => runValidationPipeline
477
477
  });
478
478
  module2.exports = __toCommonJS(index_exports);
479
- var import_fs = __toESM2(require("fs"));
479
+ var import_fs2 = __toESM2(require("fs"));
480
480
  var import_path2 = __toESM2(require("path"));
481
481
  var VALID_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
482
482
  var LOCKFILE_MANAGERS = [
@@ -490,8 +490,8 @@ var require_dist2 = __commonJS({
490
490
  function detectPackageManager3(cwd = process.cwd()) {
491
491
  try {
492
492
  const pkgPath = import_path2.default.join(cwd, "package.json");
493
- if (import_fs.default.existsSync(pkgPath)) {
494
- const pkg = JSON.parse(import_fs.default.readFileSync(pkgPath, "utf-8"));
493
+ if (import_fs2.default.existsSync(pkgPath)) {
494
+ const pkg = JSON.parse(import_fs2.default.readFileSync(pkgPath, "utf-8"));
495
495
  if (typeof pkg.packageManager === "string") {
496
496
  const name = pkg.packageManager.split("@")[0].trim();
497
497
  if (VALID_MANAGERS.includes(name)) {
@@ -502,7 +502,7 @@ var require_dist2 = __commonJS({
502
502
  } catch {
503
503
  }
504
504
  for (const { file, manager } of LOCKFILE_MANAGERS) {
505
- if (import_fs.default.existsSync(import_path2.default.join(cwd, file))) return manager;
505
+ if (import_fs2.default.existsSync(import_path2.default.join(cwd, file))) return manager;
506
506
  }
507
507
  return "npm";
508
508
  }
@@ -584,15 +584,15 @@ var require_dist2 = __commonJS({
584
584
  }
585
585
  return errors;
586
586
  }
587
- var import_fs2 = __toESM2(require("fs"));
587
+ var import_fs22 = __toESM2(require("fs"));
588
588
  var import_path22 = __toESM2(require("path"));
589
589
  async function runValidationPipeline(cwd) {
590
590
  const targetCwd = cwd ?? process.cwd();
591
591
  let scripts = [];
592
592
  try {
593
593
  const pkgPath = import_path22.default.resolve(targetCwd, "package.json");
594
- if (import_fs2.default.existsSync(pkgPath)) {
595
- const pkg = JSON.parse(import_fs2.default.readFileSync(pkgPath, "utf-8"));
594
+ if (import_fs22.default.existsSync(pkgPath)) {
595
+ const pkg = JSON.parse(import_fs22.default.readFileSync(pkgPath, "utf-8"));
596
596
  scripts = Object.keys(pkg.scripts || {});
597
597
  }
598
598
  } catch {
@@ -658,13 +658,13 @@ var require_dist3 = __commonJS({
658
658
  });
659
659
  module2.exports = __toCommonJS(index_exports);
660
660
  var import_axios = __toESM2(require("axios"));
661
- var import_fs = __toESM2(require("fs"));
661
+ var import_fs2 = __toESM2(require("fs"));
662
662
  var import_path2 = __toESM2(require("path"));
663
663
  function resolveModel(cwd) {
664
664
  try {
665
665
  const configPath = import_path2.default.resolve(cwd ?? process.cwd(), ".bugzerorc");
666
- if (import_fs.default.existsSync(configPath)) {
667
- const config = JSON.parse(import_fs.default.readFileSync(configPath, "utf-8"));
666
+ if (import_fs2.default.existsSync(configPath)) {
667
+ const config = JSON.parse(import_fs2.default.readFileSync(configPath, "utf-8"));
668
668
  if (config.model) return config.model;
669
669
  }
670
670
  } catch {
@@ -780,13 +780,13 @@ var require_dist4 = __commonJS({
780
780
  buildAnalysisPrompt: () => buildAnalysisPrompt
781
781
  });
782
782
  module2.exports = __toCommonJS(index_exports);
783
- var import_fs = __toESM2(require("fs"));
783
+ var import_fs2 = __toESM2(require("fs"));
784
784
  var import_path2 = __toESM2(require("path"));
785
785
  function resolveExistingPathPrefix(candidate) {
786
786
  let current = candidate.replace(/(?::\d+)+$/, "").replace(/['"()\[\]`]+$/, "").trim();
787
787
  while (current.length > 0) {
788
788
  try {
789
- if (import_fs.default.existsSync(current)) {
789
+ if (import_fs2.default.existsSync(current)) {
790
790
  return current;
791
791
  }
792
792
  } catch {
@@ -846,7 +846,7 @@ var require_dist4 = __commonJS({
846
846
  if (processedFiles.has(filePath)) return;
847
847
  processedFiles.add(filePath);
848
848
  try {
849
- const content = import_fs.default.readFileSync(filePath, "utf-8");
849
+ const content = import_fs2.default.readFileSync(filePath, "utf-8");
850
850
  context += `
851
851
  --- File: ${relPath} ---
852
852
  \`\`\`
@@ -858,18 +858,18 @@ ${content}
858
858
  };
859
859
  for (const absPath of resolvedPaths) {
860
860
  try {
861
- const stats = import_fs.default.statSync(absPath);
861
+ const stats = import_fs2.default.statSync(absPath);
862
862
  if (stats.isFile()) {
863
863
  addFileToContext(absPath);
864
864
  const parentDir = import_path2.default.dirname(absPath);
865
- const filesInDir = import_fs.default.readdirSync(parentDir);
865
+ const filesInDir = import_fs2.default.readdirSync(parentDir);
866
866
  const relParentDir = import_path2.default.relative(cwd, parentDir) || ".";
867
867
  context += `
868
868
  --- Directory files in "${relParentDir}": ---
869
869
  `;
870
870
  for (const file of filesInDir) {
871
871
  try {
872
- const fileStats = import_fs.default.statSync(import_path2.default.join(parentDir, file));
872
+ const fileStats = import_fs2.default.statSync(import_path2.default.join(parentDir, file));
873
873
  const marker = fileStats.isDirectory() ? "/" : "";
874
874
  context += `- ${file}${marker}
875
875
  `;
@@ -881,13 +881,13 @@ ${content}
881
881
  } else if (stats.isDirectory()) {
882
882
  const relParentDir = import_path2.default.relative(cwd, absPath) || ".";
883
883
  if (relParentDir !== ".") {
884
- const filesInDir = import_fs.default.readdirSync(absPath);
884
+ const filesInDir = import_fs2.default.readdirSync(absPath);
885
885
  context += `
886
886
  --- Directory files in "${relParentDir}": ---
887
887
  `;
888
888
  for (const file of filesInDir) {
889
889
  try {
890
- const fileStats = import_fs.default.statSync(import_path2.default.join(absPath, file));
890
+ const fileStats = import_fs2.default.statSync(import_path2.default.join(absPath, file));
891
891
  const marker = fileStats.isDirectory() ? "/" : "";
892
892
  context += `- ${file}${marker}
893
893
  `;
@@ -901,7 +901,7 @@ ${content}
901
901
  const filePathFull = import_path2.default.join(absPath, file);
902
902
  const fileExt = import_path2.default.extname(file).toLowerCase();
903
903
  try {
904
- if (import_fs.default.statSync(filePathFull).isFile() && [".js", ".jsx", ".ts", ".tsx", ".json", ".css"].includes(fileExt)) {
904
+ if (import_fs2.default.statSync(filePathFull).isFile() && [".js", ".jsx", ".ts", ".tsx", ".json", ".css"].includes(fileExt)) {
905
905
  addFileToContext(filePathFull);
906
906
  }
907
907
  } catch {
@@ -1008,7 +1008,7 @@ var require_dist5 = __commonJS({
1008
1008
  lineSimilarity: () => lineSimilarity
1009
1009
  });
1010
1010
  module2.exports = __toCommonJS(index_exports);
1011
- var import_fs = __toESM2(require("fs"));
1011
+ var import_fs2 = __toESM2(require("fs"));
1012
1012
  var import_path2 = __toESM2(require("path"));
1013
1013
  var SIMILARITY_THRESHOLD = 0.85;
1014
1014
  var AMBIGUITY_MARGIN = 0.05;
@@ -1111,13 +1111,13 @@ var require_dist5 = __commonJS({
1111
1111
  }
1112
1112
  function applyPatch(absoluteFilePath, search, replace) {
1113
1113
  try {
1114
- if (!import_fs.default.existsSync(absoluteFilePath)) {
1114
+ if (!import_fs2.default.existsSync(absoluteFilePath)) {
1115
1115
  return {
1116
1116
  success: false,
1117
1117
  error: `File not found: ${absoluteFilePath}`
1118
1118
  };
1119
1119
  }
1120
- const original = import_fs.default.readFileSync(absoluteFilePath, "utf-8");
1120
+ const original = import_fs2.default.readFileSync(absoluteFilePath, "utf-8");
1121
1121
  const match = findMatch(original, search);
1122
1122
  if (!match) {
1123
1123
  return {
@@ -1126,26 +1126,26 @@ var require_dist5 = __commonJS({
1126
1126
  };
1127
1127
  }
1128
1128
  const backupPath = absoluteFilePath + ".opspilot.bak";
1129
- import_fs.default.writeFileSync(backupPath, original, "utf-8");
1129
+ import_fs2.default.writeFileSync(backupPath, original, "utf-8");
1130
1130
  const fixed = original.slice(0, match.start) + replace + original.slice(match.end);
1131
- import_fs.default.writeFileSync(absoluteFilePath, fixed, "utf-8");
1131
+ import_fs2.default.writeFileSync(absoluteFilePath, fixed, "utf-8");
1132
1132
  return { success: true, backupPath, matchStrategy: match.strategy };
1133
1133
  } catch (err) {
1134
1134
  return { success: false, error: err.message };
1135
1135
  }
1136
1136
  }
1137
- var import_fs2 = __toESM2(require("fs"));
1137
+ var import_fs22 = __toESM2(require("fs"));
1138
1138
  var import_path22 = __toESM2(require("path"));
1139
1139
  function rollback(originalContent, backupPaths) {
1140
1140
  for (const [absolutePath, content] of originalContent) {
1141
1141
  try {
1142
- import_fs2.default.writeFileSync(absolutePath, content, "utf-8");
1142
+ import_fs22.default.writeFileSync(absolutePath, content, "utf-8");
1143
1143
  } catch {
1144
1144
  }
1145
1145
  }
1146
1146
  for (const backupPath of backupPaths) {
1147
1147
  try {
1148
- import_fs2.default.unlinkSync(backupPath);
1148
+ import_fs22.default.unlinkSync(backupPath);
1149
1149
  } catch {
1150
1150
  }
1151
1151
  }
@@ -1161,7 +1161,7 @@ var require_dist5 = __commonJS({
1161
1161
  const absolutePath = import_path22.default.resolve(cwd, patch.filePath);
1162
1162
  if (!originalContent.has(absolutePath)) {
1163
1163
  try {
1164
- originalContent.set(absolutePath, import_fs2.default.readFileSync(absolutePath, "utf-8"));
1164
+ originalContent.set(absolutePath, import_fs22.default.readFileSync(absolutePath, "utf-8"));
1165
1165
  } catch {
1166
1166
  rollback(originalContent, backupPaths);
1167
1167
  return {
@@ -1415,10 +1415,10 @@ var require_dist7 = __commonJS({
1415
1415
  orchestrateAndFix: () => orchestrateAndFix2
1416
1416
  });
1417
1417
  module2.exports = __toCommonJS(index_exports);
1418
- var import_fs = __toESM2(require("fs"));
1418
+ var import_fs2 = __toESM2(require("fs"));
1419
1419
  var import_path2 = __toESM2(require("path"));
1420
1420
  function detectFramework2(cwd = process.cwd()) {
1421
- const has = (...files) => files.some((f) => import_fs.default.existsSync(import_path2.default.join(cwd, f)));
1421
+ const has = (...files) => files.some((f) => import_fs2.default.existsSync(import_path2.default.join(cwd, f)));
1422
1422
  if (has("next.config.js", "next.config.ts", "next.config.mjs")) return "nextjs";
1423
1423
  if (has("nest-cli.json")) return "nestjs";
1424
1424
  if (has("vite.config.ts", "vite.config.js", "vite.config.mts")) return "vite";
@@ -1427,9 +1427,9 @@ var require_dist7 = __commonJS({
1427
1427
  if (has("svelte.config.js", "svelte.config.ts")) return "sveltekit";
1428
1428
  if (has("nuxt.config.ts", "nuxt.config.js")) return "nuxt";
1429
1429
  const pkgPath = import_path2.default.join(cwd, "package.json");
1430
- if (import_fs.default.existsSync(pkgPath)) {
1430
+ if (import_fs2.default.existsSync(pkgPath)) {
1431
1431
  try {
1432
- const pkg = JSON.parse(import_fs.default.readFileSync(pkgPath, "utf-8"));
1432
+ const pkg = JSON.parse(import_fs2.default.readFileSync(pkgPath, "utf-8"));
1433
1433
  const deps = {
1434
1434
  ...pkg.dependencies ?? {},
1435
1435
  ...pkg.devDependencies ?? {}
@@ -1444,7 +1444,7 @@ var require_dist7 = __commonJS({
1444
1444
  }
1445
1445
  var import_analyzers = require_dist2();
1446
1446
  var import_agents = require_dist6();
1447
- var import_fs2 = __toESM2(require("fs"));
1447
+ var import_fs22 = __toESM2(require("fs"));
1448
1448
  var import_path22 = __toESM2(require("path"));
1449
1449
  var import_url = require("url");
1450
1450
  var import_ai_provider = require_dist3();
@@ -1727,7 +1727,7 @@ ${rawOutput}
1727
1727
  *Report generated by **BugZero** on ${(/* @__PURE__ */ new Date()).toLocaleString()}*`;
1728
1728
  const reportPath = import_path22.default.resolve(cwd, "bugzero-report.md");
1729
1729
  try {
1730
- import_fs2.default.writeFileSync(reportPath, content, "utf-8");
1730
+ import_fs22.default.writeFileSync(reportPath, content, "utf-8");
1731
1731
  const clickableUrl = (0, import_url.pathToFileURL)(reportPath).toString();
1732
1732
  console.log(`
1733
1733
  \u{1F4DD} Generated report successfully!
@@ -1750,10 +1750,12 @@ ${rawOutput}
1750
1750
  // src/index.ts
1751
1751
  var import_config = __toESM(require_dist());
1752
1752
  var import_commander = require("commander");
1753
+ var import_fs = __toESM(require("fs"));
1753
1754
  var import_path = __toESM(require("path"));
1754
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"));
1755
1757
  var program = new import_commander.Command();
1756
- program.name("bugzero").description("AI-powered build issue fixer \u2014 detects and repairs build errors automatically").version("0.0.1");
1758
+ program.name("bugzero").description("AI-powered build issue fixer \u2014 detects and repairs build errors automatically").version(version);
1757
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) => {
1758
1760
  try {
1759
1761
  const cwd = import_path.default.resolve(opts.path);
package/package.json CHANGED
@@ -1,11 +1,48 @@
1
1
  {
2
2
  "name": "bugzero",
3
- "version": "0.0.35",
4
- "description": "An autonomous, AI-powered CLI tool for automatic build error detection and repair.",
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
+ }
@@ -1,14 +0,0 @@
1
-
2
- > bugzero@0.0.35 build /home/runner/work/Bugzero/Bugzero/apps/bugzero
3
- > tsup src/index.ts --format cjs --dts
4
-
5
- CLI Building entry: src/index.ts
6
- CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.5.1
8
- CLI Target: es2022
9
- CJS Build start
10
- CJS dist/index.js 79.68 KB
11
- CJS ⚡️ Build success in 54ms
12
- DTS Build start
13
- DTS ⚡️ Build success in 1265ms
14
- DTS dist/index.d.ts 20.00 B
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,202 +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, detectPackageManager } 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
- const packageManager = detectPackageManager(cwd);
26
-
27
- console.log(`\n🔍 BugZero — Analyzing project`);
28
- console.log(` Path: ${cwd}`);
29
- console.log(` Framework: ${framework}`);
30
- console.log(` Package manager: ${packageManager}\n`);
31
-
32
- const { build, lint, test } = await orchestrate({ cwd });
33
-
34
- console.log("── Pipeline Summary ───────────────────────────");
35
- console.log(` Build: ${build.success ? "✅ Passed" : "❌ Failed (see report)"}`);
36
- console.log(` Lint: ${lint.success ? "✅ Passed" : "❌ Failed (see report)"}`);
37
- console.log(` Tests: ${test.success ? "✅ Passed" : "❌ Failed (see report)"}`);
38
- console.log("───────────────────────────────────────────────");
39
-
40
- const allPassed = build.success && lint.success && test.success;
41
- console.log(`\n${allPassed ? "✅ All checks passed" : "❌ Issues found — run 'bugzero rescue' to repair"}\n`);
42
- process.exit(allPassed ? 0 : 1);
43
- } catch (error: any) {
44
- console.error(`\n❌ [BugZero] Execution failed: ${error.message || error}`);
45
- process.exit(1);
46
- }
47
- });
48
-
49
- // ── bugzero rescue ───────────────────────────────────────────────────────────
50
- program
51
- .command("rescue")
52
- .description("Detect build failures and automatically apply AI-generated fixes")
53
- .option("-p, --path <dir>", "Path to the target project", ".")
54
- .option("-n, --max-attempts <number>", "Maximum AI fix attempts", "3")
55
- .option("--dry-run", "Analyse only — print what would be fixed without writing files", false)
56
- .action(async (opts) => {
57
- try {
58
- const cwd = path.resolve(opts.path);
59
- const maxAttempts = parseInt(opts.maxAttempts, 10);
60
- const dryRun: boolean = opts.dryRun;
61
- const framework = detectFramework(cwd);
62
- const packageManager = detectPackageManager(cwd);
63
-
64
- console.log(`\n🤖 BugZero Fix`);
65
- console.log(` Path: ${cwd}`);
66
- console.log(` Framework: ${framework}`);
67
- console.log(` Package manager: ${packageManager}`);
68
- console.log(` Max attempts: ${maxAttempts}`);
69
- if (dryRun) console.log(` Mode: DRY RUN (no files will be modified)`);
70
- console.log();
71
-
72
- const result = await orchestrateAndFix({ cwd, maxAttempts, dryRun });
73
-
74
- console.log("\n──────────────────────────────────────────────");
75
- if (result.success) {
76
- console.log(`✅ Build fixed after ${result.attempts} attempt(s)!`);
77
- console.log(` Model: ${result.modelUsed || "unknown"}`);
78
- if (result.promptTokens !== undefined && result.completionTokens !== undefined) {
79
- console.log(` Tokens: Prompt: ${result.promptTokens} | Completion: ${result.completionTokens} | Total: ${result.promptTokens + result.completionTokens}`);
80
- }
81
- if (result.appliedFix) {
82
- const files = result.appliedFix.patches.map((p) => p.filePath).join(", ");
83
- console.log(` File${result.appliedFix.patches.length === 1 ? "" : "s"}: ${files}`);
84
- console.log(` Reason: ${result.appliedFix.explanation}`);
85
- console.log(` Root cause: ${result.appliedFix.rootCause}`);
86
- }
87
- } else {
88
- console.log(`❌ Could not fix build after ${result.attempts} attempt(s).`);
89
- console.log(` Model: ${result.modelUsed || "unknown"}`);
90
- if (result.promptTokens !== undefined && result.completionTokens !== undefined) {
91
- console.log(` Tokens: Prompt: ${result.promptTokens} | Completion: ${result.completionTokens} | Total: ${result.promptTokens + result.completionTokens}`);
92
- }
93
- if (result.error) console.log(` ${result.error}`);
94
- console.log(`\n👉 Full unresolved error logs have been listed in the generated report.`);
95
- }
96
- console.log();
97
-
98
- process.exit(result.success ? 0 : 1);
99
- } catch (error: any) {
100
- console.error(`\n❌ [BugZero] Execution failed: ${error.message || error}`);
101
- process.exit(1);
102
- }
103
- });
104
-
105
- // ── bugzero protect (legacy) ────────────────────────────────────────────────
106
- program
107
- .command("protect")
108
- .description("[Deprecated] Use 'hunt' instead")
109
- .action(async () => {
110
- console.warn("⚠️ 'protect' is deprecated — use 'bugzero hunt' instead.\n");
111
- const result = await orchestrate();
112
- console.log(result);
113
- });
114
-
115
- // ── bugzero vibe ─────────────────────────────────────────────────────────────
116
- program
117
- .command("vibe")
118
- .description("Select which AI model (brain) to use for resolving bugs")
119
- .action(async () => {
120
- const { getLocalConfig, saveLocalConfig } = await import("@opspilot/config");
121
- const readline = await import("readline");
122
- const axios = (await import("axios")).default;
123
-
124
- console.log(`\n🧠 BugZero Brain Selector`);
125
- console.log(`──────────────────────────────────────────────`);
126
-
127
- let popularModels: any[] = [];
128
- try {
129
- console.log("🌀 Fetching active models list from OpenRouter...");
130
- const response = await axios.get("https://openrouter.ai/api/v1/models", { timeout: 8000 });
131
- const allModels = response.data?.data || [];
132
- const popularModelIds = [
133
- "deepseek/deepseek-chat",
134
- "deepseek/deepseek-r1",
135
- "anthropic/claude-3.5-sonnet",
136
- "google/gemini-2.5-pro",
137
- "google/gemini-2.5-flash",
138
- "openai/gpt-4o",
139
- "meta-llama/llama-3.3-70b-instruct"
140
- ];
141
- popularModels = allModels.filter((m: any) => popularModelIds.includes(m.id));
142
- } catch (e: any) {
143
- console.warn("⚠️ Failed to fetch models list from OpenRouter. Using cached default list.");
144
- }
145
-
146
- if (popularModels.length === 0) {
147
- popularModels = [
148
- { id: "deepseek/deepseek-chat", name: "DeepSeek V3 (Chat)" },
149
- { id: "deepseek/deepseek-r1", name: "DeepSeek R1" },
150
- { id: "anthropic/claude-3.5-sonnet", name: "Claude 3.5 Sonnet" },
151
- { id: "google/gemini-2.5-pro", name: "Gemini 2.5 Pro" },
152
- { id: "google/gemini-2.5-flash", name: "Gemini 2.5 Flash" },
153
- { id: "openai/gpt-4o", name: "GPT-4o" },
154
- { id: "meta-llama/llama-3.3-70b-instruct", name: "Llama 3.3 Instruct" }
155
- ];
156
- }
157
-
158
- const currentModel = getLocalConfig().model || "deepseek/deepseek-chat";
159
- console.log(`\nCurrent active model: \x1b[36m${currentModel}\x1b[0m\n`);
160
-
161
- popularModels.forEach((m, idx) => {
162
- console.log(` [${idx + 1}] ${m.name} (${m.id})`);
163
- });
164
- console.log(` [${popularModels.length + 1}] 🖋️ Custom Model (Type manually)`);
165
- console.log();
166
-
167
- const rl = readline.createInterface({
168
- input: process.stdin,
169
- output: process.stdout,
170
- });
171
-
172
- rl.question(`Select a number (1-${popularModels.length + 1}): `, (answer) => {
173
- const choice = parseInt(answer.trim(), 10);
174
- if (isNaN(choice) || choice < 1 || choice > popularModels.length + 1) {
175
- console.log("❌ Invalid selection. Keeping current model.");
176
- rl.close();
177
- process.exit(1);
178
- }
179
-
180
- if (choice === popularModels.length + 1) {
181
- rl.question("Enter custom OpenRouter Model ID (e.g. microsoft/phi-3-medium-128k-instruct): ", (customModel) => {
182
- const selected = customModel.trim();
183
- if (!selected) {
184
- console.log("❌ No model entered. Keeping current model.");
185
- } else {
186
- saveLocalConfig({ model: selected });
187
- console.log(`\n✅ Saved! Model set to: \x1b[32m${selected}\x1b[0m`);
188
- console.log(` Saved config to: .bugzerorc`);
189
- }
190
- rl.close();
191
- });
192
- } else {
193
- const selected = popularModels[choice - 1].id;
194
- saveLocalConfig({ model: selected });
195
- console.log(`\n✅ Saved! Model set to: \x1b[32m${selected}\x1b[0m`);
196
- console.log(` Saved config to: .bugzerorc`);
197
- rl.close();
198
- }
199
- });
200
- });
201
-
202
- program.parse();
package/tsconfig.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "rootDir": "src"
6
- },
7
- "include": ["src"]
8
- }