bonzai-burn 1.0.38 → 1.0.40
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 +10 -20
- package/{src → dist}/bconfig.js +26 -4
- package/dist/graph-templates/receiver.js +66 -0
- package/dist/index.js +893 -0
- package/package.json +19 -10
- package/graph-templates/handlers/index.js +0 -22
- package/graph-templates/receiver.js +0 -47
- package/src/index.js +0 -99
- /package/{src → dist}/analyzer.js +0 -0
- /package/{src → dist}/bburn.js +0 -0
- /package/{src → dist}/bhook.js +0 -0
- /package/{graph-templates → dist/graph-templates}/config.js +0 -0
- /package/{graph-templates → dist/graph-templates}/ignore.txt +0 -0
- /package/{graph-templates/handlers → dist/graph-templates/loops/backend}/delete.js +0 -0
- /package/{graph-templates/handlers → dist/graph-templates/loops/backend}/git.js +0 -0
- /package/{graph-templates/handlers → dist/graph-templates/loops/backend}/open-cursor.js +0 -0
- /package/{graph-templates/handlers → dist/graph-templates/loops/backend}/shutdown.js +0 -0
- /package/{graph-templates/handlers → dist/graph-templates/loops/backend}/terminal.js +0 -0
- /package/{graph-templates/handlers → dist/graph-templates/loops/backend}/write.js +0 -0
- /package/{graph-templates/handlers → dist/graph-templates/loops/visualization}/list.js +0 -0
- /package/{graph-templates/handlers → dist/graph-templates/loops/visualization}/read.js +0 -0
- /package/{graph-templates/handlers → dist/graph-templates/loops/visualization}/scan_code_quality.js +0 -0
- /package/{graph-templates → dist/graph-templates}/utils/fileList.js +0 -0
- /package/{graph-templates → dist/graph-templates}/utils/ignore.js +0 -0
- /package/{graph-templates → dist/graph-templates}/utils/parsers.js +0 -0
- /package/{payload-bonzai → dist/payload-bonzai}/config.json +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# bonzai-burn
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Code analysis CLI for Claude Code.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,30 +8,20 @@ Automated code cleanup via Claude Code on a safe git branch.
|
|
|
8
8
|
npx bonzai-burn
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Requires: [Claude Code CLI](https://github.com/anthropics/claude-code)
|
|
12
|
-
|
|
13
11
|
## Usage
|
|
14
12
|
|
|
15
|
-
### 1. Run burn
|
|
16
|
-
|
|
17
13
|
```bash
|
|
18
|
-
npx
|
|
14
|
+
npx bonzai-burn # Initialize bonzai/ folder
|
|
15
|
+
npx bonzai-burn -b # Run code analysis
|
|
16
|
+
npx bonzai-burn -h # Install Claude Code hook
|
|
17
|
+
npx bonzai-burn -h -s # Check hook status
|
|
18
|
+
npx bonzai-burn -h -u # Uninstall hook
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
### 2. Review changes
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
git diff main
|
|
27
|
-
```
|
|
21
|
+
## Config
|
|
28
22
|
|
|
29
|
-
|
|
23
|
+
Edit `bonzai/config.json` to customize burn rules.
|
|
30
24
|
|
|
31
|
-
|
|
32
|
-
# Accept
|
|
33
|
-
baccept
|
|
25
|
+
---
|
|
34
26
|
|
|
35
|
-
|
|
36
|
-
brevert
|
|
37
|
-
```
|
|
27
|
+
> **Note:** Release configuration (loops, channels) is managed outside this repo. See internal docs.
|
package/{src → dist}/bconfig.js
RENAMED
|
@@ -3,12 +3,13 @@ import fs from 'fs';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { spawn, exec } from 'child_process';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
|
+
import { ENABLED_LOOPS } from './loops.config.js';
|
|
6
7
|
|
|
7
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
9
|
const __dirname = path.dirname(__filename);
|
|
9
10
|
|
|
10
11
|
// Template folder in the package
|
|
11
|
-
const TEMPLATE_DIR = path.join(__dirname, '
|
|
12
|
+
const TEMPLATE_DIR = path.join(__dirname, 'graph-templates');
|
|
12
13
|
|
|
13
14
|
// Helper function to recursively copy directory
|
|
14
15
|
function copyDirectory(src, dest) {
|
|
@@ -53,11 +54,32 @@ async function main() {
|
|
|
53
54
|
const configContent = fs.readFileSync(path.join(TEMPLATE_DIR, 'config.js'), 'utf8');
|
|
54
55
|
fs.writeFileSync(path.join(bonzaiDir, 'config.js'), configContent);
|
|
55
56
|
|
|
56
|
-
// Copy handlers
|
|
57
|
+
// Copy handlers from enabled loops
|
|
57
58
|
console.log('Copying handlers...');
|
|
58
|
-
const handlersSrc = path.join(TEMPLATE_DIR, 'handlers');
|
|
59
59
|
const handlersDest = path.join(bonzaiDir, 'handlers');
|
|
60
|
-
|
|
60
|
+
if (!fs.existsSync(handlersDest)) {
|
|
61
|
+
fs.mkdirSync(handlersDest, { recursive: true });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Copy visualization loop handlers
|
|
65
|
+
if (ENABLED_LOOPS.includes('visualization')) {
|
|
66
|
+
const vizSrc = path.join(TEMPLATE_DIR, 'loops', 'visualization');
|
|
67
|
+
if (fs.existsSync(vizSrc)) {
|
|
68
|
+
for (const file of fs.readdirSync(vizSrc)) {
|
|
69
|
+
fs.copyFileSync(path.join(vizSrc, file), path.join(handlersDest, file));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Copy backend loop handlers
|
|
75
|
+
if (ENABLED_LOOPS.includes('backend')) {
|
|
76
|
+
const backendSrc = path.join(TEMPLATE_DIR, 'loops', 'backend');
|
|
77
|
+
if (fs.existsSync(backendSrc)) {
|
|
78
|
+
for (const file of fs.readdirSync(backendSrc)) {
|
|
79
|
+
fs.copyFileSync(path.join(backendSrc, file), path.join(handlersDest, file));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
61
83
|
|
|
62
84
|
// Copy utils directory
|
|
63
85
|
console.log('Copying utils...');
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const express = require('./node_modules/express');
|
|
4
|
+
const cors = require('./node_modules/cors');
|
|
5
|
+
const http = require('http');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
const app = express();
|
|
10
|
+
const server = http.createServer(app);
|
|
11
|
+
|
|
12
|
+
app.use(cors());
|
|
13
|
+
app.use(express.json());
|
|
14
|
+
|
|
15
|
+
// Root route
|
|
16
|
+
app.get('/', (req, res) => {
|
|
17
|
+
res.json({ message: 'Bonzai Server', status: 'running' });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Dynamically load handlers based on what exists
|
|
21
|
+
const handlersDir = path.join(__dirname, 'handlers');
|
|
22
|
+
|
|
23
|
+
function tryLoad(name) {
|
|
24
|
+
const filePath = path.join(handlersDir, name + '.js');
|
|
25
|
+
if (fs.existsSync(filePath)) {
|
|
26
|
+
return require(filePath);
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Visualization loop handlers
|
|
32
|
+
const listHandler = tryLoad('list');
|
|
33
|
+
const readHandler = tryLoad('read');
|
|
34
|
+
const scanCodeQualityHandler = tryLoad('scan_code_quality');
|
|
35
|
+
|
|
36
|
+
if (listHandler) app.get('/list', listHandler);
|
|
37
|
+
if (readHandler) app.get('/read', readHandler);
|
|
38
|
+
if (scanCodeQualityHandler) app.post('/scan_code_quality', scanCodeQualityHandler);
|
|
39
|
+
|
|
40
|
+
// Backend loop handlers
|
|
41
|
+
const deleteHandler = tryLoad('delete');
|
|
42
|
+
const openCursorHandler = tryLoad('open-cursor');
|
|
43
|
+
const writeHandler = tryLoad('write');
|
|
44
|
+
const shutdownHandler = tryLoad('shutdown');
|
|
45
|
+
const gitHandlers = tryLoad('git');
|
|
46
|
+
const terminalHandlers = tryLoad('terminal');
|
|
47
|
+
|
|
48
|
+
if (deleteHandler) app.post('/delete', deleteHandler);
|
|
49
|
+
if (openCursorHandler) app.post('/open-cursor', openCursorHandler);
|
|
50
|
+
if (writeHandler) app.post('/write', writeHandler);
|
|
51
|
+
if (shutdownHandler) app.post('/shutdown', shutdownHandler);
|
|
52
|
+
if (gitHandlers) {
|
|
53
|
+
app.get('/git/burns', gitHandlers.listBurns);
|
|
54
|
+
app.post('/git/checkout', gitHandlers.checkoutBranch);
|
|
55
|
+
}
|
|
56
|
+
if (terminalHandlers) {
|
|
57
|
+
const { WebSocketServer } = require('./node_modules/ws');
|
|
58
|
+
const wss = new WebSocketServer({ server, path: '/terminal' });
|
|
59
|
+
terminalHandlers.setupTerminalWebSocket(wss);
|
|
60
|
+
app.get('/terminal', terminalHandlers.terminalHandler);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const port = 3001;
|
|
64
|
+
server.listen(port, () => {
|
|
65
|
+
console.log('File server running on http://localhost:' + port);
|
|
66
|
+
});
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,893 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn, exec, execSync } from 'child_process';
|
|
3
|
+
import fs4, { existsSync, mkdirSync, copyFileSync } from 'fs';
|
|
4
|
+
import path2, { dirname, join } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/loops.config.js
|
|
18
|
+
var CHANNELS, channel, ENABLED_LOOPS;
|
|
19
|
+
var init_loops_config = __esm({
|
|
20
|
+
"src/loops.config.js"() {
|
|
21
|
+
CHANNELS = {
|
|
22
|
+
dev: ["burn", "visualization", "backend"],
|
|
23
|
+
beta: ["burn", "visualization"],
|
|
24
|
+
stable: ["burn"]
|
|
25
|
+
};
|
|
26
|
+
channel = "dev";
|
|
27
|
+
ENABLED_LOOPS = CHANNELS[channel];
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
function commandExists(cmd) {
|
|
31
|
+
try {
|
|
32
|
+
execSync(`which ${cmd}`, { encoding: "utf-8", stdio: "pipe" });
|
|
33
|
+
return true;
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function isInstalledLocally(pkg, rootDir) {
|
|
39
|
+
return fs4.existsSync(path2.join(rootDir, "node_modules", pkg));
|
|
40
|
+
}
|
|
41
|
+
function installPackage(pkg, rootDir) {
|
|
42
|
+
try {
|
|
43
|
+
console.log(` Installing ${pkg}...`);
|
|
44
|
+
execSync(`cd "${rootDir}" && npm install --save-dev ${pkg}`, {
|
|
45
|
+
encoding: "utf-8",
|
|
46
|
+
stdio: "pipe"
|
|
47
|
+
});
|
|
48
|
+
return true;
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function hasEslintConfig(rootDir) {
|
|
54
|
+
const configFiles = [
|
|
55
|
+
"eslint.config.js",
|
|
56
|
+
"eslint.config.mjs",
|
|
57
|
+
"eslint.config.cjs",
|
|
58
|
+
// Legacy configs (ESLint < 9)
|
|
59
|
+
".eslintrc.js",
|
|
60
|
+
".eslintrc.cjs",
|
|
61
|
+
".eslintrc.json",
|
|
62
|
+
".eslintrc.yml",
|
|
63
|
+
".eslintrc.yaml",
|
|
64
|
+
".eslintrc"
|
|
65
|
+
];
|
|
66
|
+
return configFiles.some((file) => fs4.existsSync(path2.join(rootDir, file)));
|
|
67
|
+
}
|
|
68
|
+
function createEslintConfig(rootDir, config) {
|
|
69
|
+
var _a4;
|
|
70
|
+
const rules = ((_a4 = config.eslint) == null ? void 0 : _a4.rules) || ["no-unused-vars"];
|
|
71
|
+
const rulesObj = {};
|
|
72
|
+
for (const rule of rules) {
|
|
73
|
+
rulesObj[rule] = "error";
|
|
74
|
+
}
|
|
75
|
+
const rulesJson = JSON.stringify(rulesObj, null, 2).replace(/\n/g, "\n ");
|
|
76
|
+
const configContent = `// Auto-generated by bonzai-burn for ESLint v9+
|
|
77
|
+
export default [
|
|
78
|
+
{
|
|
79
|
+
files: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx', '**/*.mjs', '**/*.cjs'],
|
|
80
|
+
languageOptions: {
|
|
81
|
+
ecmaVersion: 'latest',
|
|
82
|
+
sourceType: 'module',
|
|
83
|
+
parserOptions: {
|
|
84
|
+
ecmaFeatures: {
|
|
85
|
+
jsx: true
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
rules: ${rulesJson}
|
|
90
|
+
}
|
|
91
|
+
];
|
|
92
|
+
`;
|
|
93
|
+
const configPath = path2.join(rootDir, "eslint.config.js");
|
|
94
|
+
try {
|
|
95
|
+
fs4.writeFileSync(configPath, configContent, "utf-8");
|
|
96
|
+
return true;
|
|
97
|
+
} catch (e) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function ensureEslint(rootDir, config) {
|
|
102
|
+
const cfg = config.eslint || {};
|
|
103
|
+
if (!cfg.enabled) {
|
|
104
|
+
return { available: false, reason: "Disabled in config" };
|
|
105
|
+
}
|
|
106
|
+
let eslintInstalled = commandExists("eslint") || isInstalledLocally("eslint", rootDir);
|
|
107
|
+
let installed = false;
|
|
108
|
+
if (!eslintInstalled) {
|
|
109
|
+
console.log("\u{1F4E6} ESLint not found, installing...");
|
|
110
|
+
if (installPackage("eslint", rootDir)) {
|
|
111
|
+
eslintInstalled = true;
|
|
112
|
+
installed = true;
|
|
113
|
+
} else {
|
|
114
|
+
return { available: false, reason: "Failed to install ESLint" };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
let configCreated = false;
|
|
118
|
+
if (!hasEslintConfig(rootDir)) {
|
|
119
|
+
console.log("\u{1F4C4} Creating eslint.config.js...");
|
|
120
|
+
if (createEslintConfig(rootDir, config)) {
|
|
121
|
+
configCreated = true;
|
|
122
|
+
} else {
|
|
123
|
+
return { available: false, reason: "Failed to create ESLint config" };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { available: true, installed, configCreated };
|
|
127
|
+
}
|
|
128
|
+
function ensureTypeScript(rootDir, config) {
|
|
129
|
+
const cfg = config.typescript || {};
|
|
130
|
+
if (!cfg.enabled) {
|
|
131
|
+
return { available: false, reason: "Disabled in config" };
|
|
132
|
+
}
|
|
133
|
+
if (!fs4.existsSync(path2.join(rootDir, "tsconfig.json"))) {
|
|
134
|
+
return { available: false, reason: "No tsconfig.json found" };
|
|
135
|
+
}
|
|
136
|
+
if (commandExists("tsc") || isInstalledLocally("typescript", rootDir)) {
|
|
137
|
+
return { available: true };
|
|
138
|
+
}
|
|
139
|
+
console.log("\u{1F4E6} TypeScript not found, installing...");
|
|
140
|
+
if (installPackage("typescript", rootDir)) {
|
|
141
|
+
return { available: true, installed: true };
|
|
142
|
+
}
|
|
143
|
+
return { available: false, reason: "Failed to install TypeScript" };
|
|
144
|
+
}
|
|
145
|
+
function listAllFiles(dir, basePath = "") {
|
|
146
|
+
const ignorePatterns = ["node_modules", ".git", ".DS_Store", "dist", "build", "coverage", "bonzai"];
|
|
147
|
+
let results = [];
|
|
148
|
+
try {
|
|
149
|
+
const entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
150
|
+
for (const entry of entries) {
|
|
151
|
+
const fullPath = path2.join(dir, entry.name);
|
|
152
|
+
const relativePath = path2.join(basePath, entry.name);
|
|
153
|
+
if (ignorePatterns.some((p) => entry.name === p) || entry.name.startsWith(".")) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (entry.isDirectory()) {
|
|
157
|
+
results = results.concat(listAllFiles(fullPath, relativePath));
|
|
158
|
+
} else {
|
|
159
|
+
results.push({
|
|
160
|
+
path: relativePath,
|
|
161
|
+
fullPath
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
} catch (e) {
|
|
166
|
+
}
|
|
167
|
+
return results;
|
|
168
|
+
}
|
|
169
|
+
function runEslintAnalysis(rootDir, config) {
|
|
170
|
+
const issues = [];
|
|
171
|
+
const cfg = config.eslint || {};
|
|
172
|
+
if (!cfg.enabled) {
|
|
173
|
+
return { issues, skipped: true, reason: "Disabled in config" };
|
|
174
|
+
}
|
|
175
|
+
const rules = cfg.rules || ["no-unused-vars"];
|
|
176
|
+
const ruleArgs = rules.map((r) => `--rule "${r}: error"`).join(" ");
|
|
177
|
+
const eslintCmd = isInstalledLocally("eslint", rootDir) ? `"${path2.join(rootDir, "node_modules", ".bin", "eslint")}"` : "eslint";
|
|
178
|
+
try {
|
|
179
|
+
const result = execSync(
|
|
180
|
+
`${eslintCmd} "${rootDir}" --format json ${ruleArgs} --ignore-pattern node_modules --ignore-pattern bonzai 2>/dev/null || true`,
|
|
181
|
+
{ encoding: "utf-8", stdio: "pipe", maxBuffer: 50 * 1024 * 1024 }
|
|
182
|
+
);
|
|
183
|
+
if (result.trim()) {
|
|
184
|
+
const eslintOutput = JSON.parse(result);
|
|
185
|
+
for (const file of eslintOutput) {
|
|
186
|
+
for (const msg of file.messages || []) {
|
|
187
|
+
if (msg.ruleId && rules.some((r) => msg.ruleId.includes(r.replace("no-", "")))) {
|
|
188
|
+
issues.push({
|
|
189
|
+
file: path2.relative(rootDir, file.filePath),
|
|
190
|
+
line: msg.line,
|
|
191
|
+
message: msg.message,
|
|
192
|
+
rule: msg.ruleId
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
} catch (e) {
|
|
199
|
+
return { issues, skipped: true, reason: "ESLint analysis failed" };
|
|
200
|
+
}
|
|
201
|
+
return { issues, skipped: false };
|
|
202
|
+
}
|
|
203
|
+
function runTypeScriptAnalysis(rootDir, config) {
|
|
204
|
+
const issues = [];
|
|
205
|
+
const cfg = config.typescript || {};
|
|
206
|
+
if (!cfg.enabled) {
|
|
207
|
+
return { issues, skipped: true, reason: "Disabled in config" };
|
|
208
|
+
}
|
|
209
|
+
const tsconfigPath = path2.join(rootDir, "tsconfig.json");
|
|
210
|
+
if (!fs4.existsSync(tsconfigPath)) {
|
|
211
|
+
return { issues, skipped: true, reason: "No tsconfig.json found" };
|
|
212
|
+
}
|
|
213
|
+
const tscCmd = isInstalledLocally("typescript", rootDir) ? `"${path2.join(rootDir, "node_modules", ".bin", "tsc")}"` : "tsc";
|
|
214
|
+
try {
|
|
215
|
+
const result = execSync(
|
|
216
|
+
`cd "${rootDir}" && ${tscCmd} --noEmit --noUnusedLocals --noUnusedParameters 2>&1 || true`,
|
|
217
|
+
{ encoding: "utf-8", stdio: "pipe", maxBuffer: 50 * 1024 * 1024 }
|
|
218
|
+
);
|
|
219
|
+
const lines = result.split("\n");
|
|
220
|
+
const errorRegex = /^(.+)\((\d+),(\d+)\):\s*error\s+TS(\d+):\s*(.+)$/;
|
|
221
|
+
for (const line of lines) {
|
|
222
|
+
const match = line.match(errorRegex);
|
|
223
|
+
if (match) {
|
|
224
|
+
const [, filePath, lineNum, , errorCode, message] = match;
|
|
225
|
+
if (["6133", "6196", "6198"].includes(errorCode)) {
|
|
226
|
+
issues.push({
|
|
227
|
+
file: path2.relative(rootDir, filePath),
|
|
228
|
+
line: parseInt(lineNum, 10),
|
|
229
|
+
message,
|
|
230
|
+
rule: `TS${errorCode}`
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
} catch (e) {
|
|
236
|
+
return { issues, skipped: true, reason: "TypeScript analysis failed" };
|
|
237
|
+
}
|
|
238
|
+
return { issues, skipped: false };
|
|
239
|
+
}
|
|
240
|
+
function checkLineLimits(files, config) {
|
|
241
|
+
const issues = [];
|
|
242
|
+
const cfg = config.lineLimit || {};
|
|
243
|
+
if (!cfg.enabled) {
|
|
244
|
+
return { issues, skipped: true, reason: "Disabled in config" };
|
|
245
|
+
}
|
|
246
|
+
const maxLines = cfg.limit || 500;
|
|
247
|
+
for (const file of files) {
|
|
248
|
+
if (file.path.endsWith(".json") || file.path.endsWith(".lock") || file.path.endsWith(".css")) {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
const content = fs4.readFileSync(file.fullPath, "utf-8");
|
|
253
|
+
const lineCount = content.split("\n").length;
|
|
254
|
+
if (lineCount > maxLines) {
|
|
255
|
+
issues.push({
|
|
256
|
+
file: file.path,
|
|
257
|
+
count: lineCount,
|
|
258
|
+
limit: maxLines
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
} catch (e) {
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
issues.sort((a, b) => b.count - a.count);
|
|
265
|
+
return { issues, skipped: false, prompt: cfg.prompt };
|
|
266
|
+
}
|
|
267
|
+
function checkFolderLimits(files, config) {
|
|
268
|
+
const issues = [];
|
|
269
|
+
const cfg = config.folderLimit || {};
|
|
270
|
+
if (!cfg.enabled) {
|
|
271
|
+
return { issues, skipped: true, reason: "Disabled in config" };
|
|
272
|
+
}
|
|
273
|
+
const maxItems = cfg.limit || 20;
|
|
274
|
+
const folderCounts = {};
|
|
275
|
+
for (const file of files) {
|
|
276
|
+
const dir = path2.dirname(file.path);
|
|
277
|
+
if (!folderCounts[dir]) {
|
|
278
|
+
folderCounts[dir] = 0;
|
|
279
|
+
}
|
|
280
|
+
folderCounts[dir]++;
|
|
281
|
+
}
|
|
282
|
+
for (const [folder, count] of Object.entries(folderCounts)) {
|
|
283
|
+
if (count > maxItems) {
|
|
284
|
+
issues.push({
|
|
285
|
+
file: folder,
|
|
286
|
+
count,
|
|
287
|
+
limit: maxItems
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
issues.sort((a, b) => b.count - a.count);
|
|
292
|
+
return { issues, skipped: false, prompt: cfg.prompt };
|
|
293
|
+
}
|
|
294
|
+
function checkMissingTests(files, config) {
|
|
295
|
+
const issues = [];
|
|
296
|
+
const cfg = config.testCheck || {};
|
|
297
|
+
if (!cfg.enabled) {
|
|
298
|
+
return { issues, skipped: true, reason: "Disabled in config" };
|
|
299
|
+
}
|
|
300
|
+
const patterns = cfg.patterns || {
|
|
301
|
+
".vue": ".test.js",
|
|
302
|
+
".jsx": ".test.jsx",
|
|
303
|
+
".tsx": ".test.tsx"
|
|
304
|
+
};
|
|
305
|
+
const testFiles = new Set(
|
|
306
|
+
files.filter((f) => f.path.includes(".test.") || f.path.includes(".spec.")).map((f) => f.path.toLowerCase())
|
|
307
|
+
);
|
|
308
|
+
for (const file of files) {
|
|
309
|
+
const ext = path2.extname(file.path);
|
|
310
|
+
const testExt = patterns[ext];
|
|
311
|
+
if (!testExt) continue;
|
|
312
|
+
if (file.path.includes(".test.") || file.path.includes(".spec.")) continue;
|
|
313
|
+
if (!file.path.startsWith("src/") && !file.path.startsWith("components/")) continue;
|
|
314
|
+
const baseName = path2.basename(file.path, ext);
|
|
315
|
+
const hasTest = [...testFiles].some((t) => t.includes(baseName.toLowerCase()) && t.includes(".test."));
|
|
316
|
+
if (!hasTest) {
|
|
317
|
+
issues.push({
|
|
318
|
+
file: file.path,
|
|
319
|
+
expectedTest: `${baseName}${testExt}`
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return { issues, skipped: false, prompt: cfg.prompt };
|
|
324
|
+
}
|
|
325
|
+
async function analyze(rootDir = process.cwd(), config = {}) {
|
|
326
|
+
var _a4, _b, _c;
|
|
327
|
+
const startTime = Date.now();
|
|
328
|
+
const files = listAllFiles(rootDir);
|
|
329
|
+
const toolStatus = {};
|
|
330
|
+
console.log("\u{1F527} Checking tools...");
|
|
331
|
+
const eslintStatus = ensureEslint(rootDir, config);
|
|
332
|
+
toolStatus.eslint = eslintStatus;
|
|
333
|
+
if (eslintStatus.installed && eslintStatus.configCreated) {
|
|
334
|
+
console.log(" \u2713 ESLint installed + config created");
|
|
335
|
+
} else if (eslintStatus.installed) {
|
|
336
|
+
console.log(" \u2713 ESLint installed");
|
|
337
|
+
} else if (eslintStatus.configCreated) {
|
|
338
|
+
console.log(" \u2713 ESLint ready (config created)");
|
|
339
|
+
} else if (eslintStatus.available) {
|
|
340
|
+
console.log(" \u2713 ESLint ready");
|
|
341
|
+
} else if ((_a4 = config.eslint) == null ? void 0 : _a4.enabled) {
|
|
342
|
+
console.log(` \u2717 ESLint: ${eslintStatus.reason}`);
|
|
343
|
+
}
|
|
344
|
+
const tsStatus = ensureTypeScript(rootDir, config);
|
|
345
|
+
toolStatus.typescript = tsStatus;
|
|
346
|
+
if (tsStatus.installed) {
|
|
347
|
+
console.log(" \u2713 TypeScript installed");
|
|
348
|
+
} else if (tsStatus.available) {
|
|
349
|
+
console.log(" \u2713 TypeScript ready");
|
|
350
|
+
} else if ((_b = config.typescript) == null ? void 0 : _b.enabled) {
|
|
351
|
+
console.log(` \u2717 TypeScript: ${tsStatus.reason}`);
|
|
352
|
+
}
|
|
353
|
+
console.log("");
|
|
354
|
+
const eslint = eslintStatus.available ? runEslintAnalysis(rootDir, config) : { issues: [], skipped: true, reason: eslintStatus.reason };
|
|
355
|
+
const typescript = tsStatus.available ? runTypeScriptAnalysis(rootDir, config) : { issues: [], skipped: true, reason: tsStatus.reason };
|
|
356
|
+
const lineLimit = checkLineLimits(files, config);
|
|
357
|
+
const folderLimit = checkFolderLimits(files, config);
|
|
358
|
+
const missingTests = checkMissingTests(files, config);
|
|
359
|
+
const duration = Date.now() - startTime;
|
|
360
|
+
return {
|
|
361
|
+
eslint,
|
|
362
|
+
typescript,
|
|
363
|
+
lineLimit,
|
|
364
|
+
folderLimit,
|
|
365
|
+
missingTests,
|
|
366
|
+
customRequirements: ((_c = config.customChecks) == null ? void 0 : _c.requirements) || null,
|
|
367
|
+
filesScanned: files.length,
|
|
368
|
+
durationMs: duration,
|
|
369
|
+
toolStatus
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function formatAnalysisResults(results) {
|
|
373
|
+
var _a4, _b;
|
|
374
|
+
let output = "";
|
|
375
|
+
let totalIssues = 0;
|
|
376
|
+
if (!results.eslint.skipped && results.eslint.issues.length > 0) {
|
|
377
|
+
output += `\u{1F5D1}\uFE0F UNUSED CODE (ESLint) - ${results.eslint.issues.length} issues
|
|
378
|
+
`;
|
|
379
|
+
for (const issue of results.eslint.issues.slice(0, 15)) {
|
|
380
|
+
output += ` ${issue.file}:${issue.line} - ${issue.message}
|
|
381
|
+
`;
|
|
382
|
+
}
|
|
383
|
+
if (results.eslint.issues.length > 15) {
|
|
384
|
+
output += ` ... and ${results.eslint.issues.length - 15} more
|
|
385
|
+
`;
|
|
386
|
+
}
|
|
387
|
+
output += "\n";
|
|
388
|
+
totalIssues += results.eslint.issues.length;
|
|
389
|
+
}
|
|
390
|
+
if (!results.typescript.skipped && results.typescript.issues.length > 0) {
|
|
391
|
+
output += `\u{1F537} UNUSED CODE (TypeScript) - ${results.typescript.issues.length} issues
|
|
392
|
+
`;
|
|
393
|
+
for (const issue of results.typescript.issues.slice(0, 15)) {
|
|
394
|
+
output += ` ${issue.file}:${issue.line} - ${issue.message}
|
|
395
|
+
`;
|
|
396
|
+
}
|
|
397
|
+
if (results.typescript.issues.length > 15) {
|
|
398
|
+
output += ` ... and ${results.typescript.issues.length - 15} more
|
|
399
|
+
`;
|
|
400
|
+
}
|
|
401
|
+
output += "\n";
|
|
402
|
+
totalIssues += results.typescript.issues.length;
|
|
403
|
+
}
|
|
404
|
+
if (!results.lineLimit.skipped && results.lineLimit.issues.length > 0) {
|
|
405
|
+
output += `\u{1F4CF} FILES OVER LINE LIMIT - ${results.lineLimit.issues.length} files
|
|
406
|
+
`;
|
|
407
|
+
for (const issue of results.lineLimit.issues) {
|
|
408
|
+
output += ` ${issue.file} - ${issue.count} lines (limit: ${issue.limit})
|
|
409
|
+
`;
|
|
410
|
+
}
|
|
411
|
+
if (results.lineLimit.prompt) {
|
|
412
|
+
output += `
|
|
413
|
+
\u2192 ${results.lineLimit.prompt.replace(/\{\{\s*linelimit\s*\}\}/gi, ((_a4 = results.lineLimit.issues[0]) == null ? void 0 : _a4.limit) || "")}
|
|
414
|
+
`;
|
|
415
|
+
}
|
|
416
|
+
output += "\n";
|
|
417
|
+
totalIssues += results.lineLimit.issues.length;
|
|
418
|
+
}
|
|
419
|
+
if (!results.folderLimit.skipped && results.folderLimit.issues.length > 0) {
|
|
420
|
+
output += `\u{1F4C1} FOLDERS OVER ITEM LIMIT - ${results.folderLimit.issues.length} folders
|
|
421
|
+
`;
|
|
422
|
+
for (const issue of results.folderLimit.issues) {
|
|
423
|
+
output += ` ${issue.file}/ - ${issue.count} items (limit: ${issue.limit})
|
|
424
|
+
`;
|
|
425
|
+
}
|
|
426
|
+
if (results.folderLimit.prompt) {
|
|
427
|
+
output += `
|
|
428
|
+
\u2192 ${results.folderLimit.prompt.replace(/\{\{\s*folderlimit\s*\}\}/gi, ((_b = results.folderLimit.issues[0]) == null ? void 0 : _b.limit) || "")}
|
|
429
|
+
`;
|
|
430
|
+
}
|
|
431
|
+
output += "\n";
|
|
432
|
+
totalIssues += results.folderLimit.issues.length;
|
|
433
|
+
}
|
|
434
|
+
if (!results.missingTests.skipped && results.missingTests.issues.length > 0) {
|
|
435
|
+
output += `\u{1F9EA} MISSING TESTS - ${results.missingTests.issues.length} files
|
|
436
|
+
`;
|
|
437
|
+
for (const issue of results.missingTests.issues.slice(0, 10)) {
|
|
438
|
+
output += ` ${issue.file} \u2192 needs ${issue.expectedTest}
|
|
439
|
+
`;
|
|
440
|
+
}
|
|
441
|
+
if (results.missingTests.issues.length > 10) {
|
|
442
|
+
output += ` ... and ${results.missingTests.issues.length - 10} more
|
|
443
|
+
`;
|
|
444
|
+
}
|
|
445
|
+
output += "\n";
|
|
446
|
+
totalIssues += results.missingTests.issues.length;
|
|
447
|
+
}
|
|
448
|
+
if (results.customRequirements) {
|
|
449
|
+
output += `\u{1F4CB} CUSTOM REQUIREMENTS
|
|
450
|
+
`;
|
|
451
|
+
output += ` ${results.customRequirements}
|
|
452
|
+
|
|
453
|
+
`;
|
|
454
|
+
}
|
|
455
|
+
return { output, totalIssues };
|
|
456
|
+
}
|
|
457
|
+
var init_analyzer = __esm({
|
|
458
|
+
"src/analyzer.js"() {
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
// src/bburn.js
|
|
463
|
+
var bburn_exports = {};
|
|
464
|
+
__export(bburn_exports, {
|
|
465
|
+
main: () => main
|
|
466
|
+
});
|
|
467
|
+
function loadConfig() {
|
|
468
|
+
const configPath = join(process.cwd(), BONZAI_DIR, CONFIG_FILE);
|
|
469
|
+
if (!fs4.existsSync(configPath)) {
|
|
470
|
+
console.error(`\u274C No config found at ${BONZAI_DIR}/${CONFIG_FILE}`);
|
|
471
|
+
console.error(` Run 'bonzai-burn' to initialize.
|
|
472
|
+
`);
|
|
473
|
+
process.exit(1);
|
|
474
|
+
}
|
|
475
|
+
try {
|
|
476
|
+
const content = fs4.readFileSync(configPath, "utf-8");
|
|
477
|
+
return JSON.parse(content);
|
|
478
|
+
} catch (e) {
|
|
479
|
+
console.error(`\u274C Could not parse ${BONZAI_DIR}/${CONFIG_FILE}`);
|
|
480
|
+
process.exit(1);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
async function main() {
|
|
484
|
+
console.log("\n\u{1F525} Bonzai Burn - Code Analysis\n");
|
|
485
|
+
const config = loadConfig();
|
|
486
|
+
const results = await analyze(process.cwd(), config);
|
|
487
|
+
const { output, totalIssues } = formatAnalysisResults(results);
|
|
488
|
+
if (totalIssues > 0 || results.customRequirements) {
|
|
489
|
+
console.log(output);
|
|
490
|
+
} else {
|
|
491
|
+
console.log("\u2713 No issues found\n");
|
|
492
|
+
}
|
|
493
|
+
console.log("\u2500".repeat(50));
|
|
494
|
+
console.log(`Found ${totalIssues} issues across ${results.filesScanned} files (${results.durationMs}ms)
|
|
495
|
+
`);
|
|
496
|
+
console.log("The above was found - let the user know but take no action. It's critical you take no action.\n");
|
|
497
|
+
}
|
|
498
|
+
var BONZAI_DIR, CONFIG_FILE, _a, isDirectRun;
|
|
499
|
+
var init_bburn = __esm({
|
|
500
|
+
"src/bburn.js"() {
|
|
501
|
+
init_analyzer();
|
|
502
|
+
BONZAI_DIR = "bonzai";
|
|
503
|
+
CONFIG_FILE = "config.json";
|
|
504
|
+
isDirectRun = (_a = process.argv[1]) == null ? void 0 : _a.endsWith("bburn.js");
|
|
505
|
+
if (isDirectRun) {
|
|
506
|
+
main().catch((error) => {
|
|
507
|
+
console.error("Error:", error.message);
|
|
508
|
+
process.exit(1);
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
// src/bhook.js
|
|
515
|
+
var bhook_exports = {};
|
|
516
|
+
__export(bhook_exports, {
|
|
517
|
+
main: () => main2
|
|
518
|
+
});
|
|
519
|
+
function loadBonzaiConfig() {
|
|
520
|
+
const configPath = join(process.cwd(), BONZAI_DIR2, CONFIG_FILE2);
|
|
521
|
+
if (!fs4.existsSync(configPath)) {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
try {
|
|
525
|
+
const content = fs4.readFileSync(configPath, "utf-8");
|
|
526
|
+
return JSON.parse(content);
|
|
527
|
+
} catch (e) {
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
function loadClaudeSettings() {
|
|
532
|
+
const settingsPath = join(process.cwd(), CLAUDE_DIR, SETTINGS_FILE);
|
|
533
|
+
if (!fs4.existsSync(settingsPath)) {
|
|
534
|
+
return {};
|
|
535
|
+
}
|
|
536
|
+
try {
|
|
537
|
+
const content = fs4.readFileSync(settingsPath, "utf-8");
|
|
538
|
+
return JSON.parse(content);
|
|
539
|
+
} catch (e) {
|
|
540
|
+
return {};
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
function saveClaudeSettings(settings) {
|
|
544
|
+
const claudeDir = join(process.cwd(), CLAUDE_DIR);
|
|
545
|
+
const settingsPath = join(claudeDir, SETTINGS_FILE);
|
|
546
|
+
if (!fs4.existsSync(claudeDir)) {
|
|
547
|
+
fs4.mkdirSync(claudeDir, { recursive: true });
|
|
548
|
+
}
|
|
549
|
+
fs4.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
550
|
+
}
|
|
551
|
+
function hasBburnHook(settings) {
|
|
552
|
+
var _a4;
|
|
553
|
+
const stopHooks = ((_a4 = settings.hooks) == null ? void 0 : _a4.Stop) || [];
|
|
554
|
+
return stopHooks.some(
|
|
555
|
+
(entry) => {
|
|
556
|
+
var _a5;
|
|
557
|
+
return (_a5 = entry.hooks) == null ? void 0 : _a5.some((hook) => hook.command === "bburn");
|
|
558
|
+
}
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
function installHook() {
|
|
562
|
+
const settings = loadClaudeSettings();
|
|
563
|
+
if (hasBburnHook(settings)) {
|
|
564
|
+
console.log("\u2713 bburn hook already installed\n");
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (!settings.hooks) {
|
|
568
|
+
settings.hooks = {};
|
|
569
|
+
}
|
|
570
|
+
if (!settings.hooks.Stop) {
|
|
571
|
+
settings.hooks.Stop = [];
|
|
572
|
+
}
|
|
573
|
+
settings.hooks.Stop.push({
|
|
574
|
+
hooks: [
|
|
575
|
+
{
|
|
576
|
+
type: "command",
|
|
577
|
+
command: "bburn"
|
|
578
|
+
}
|
|
579
|
+
]
|
|
580
|
+
});
|
|
581
|
+
saveClaudeSettings(settings);
|
|
582
|
+
console.log("\u2713 Installed bburn as Claude Code Stop hook");
|
|
583
|
+
console.log(" bburn will run after every Claude Code message\n");
|
|
584
|
+
}
|
|
585
|
+
function uninstallHook() {
|
|
586
|
+
const settings = loadClaudeSettings();
|
|
587
|
+
if (!hasBburnHook(settings)) {
|
|
588
|
+
console.log("\u2713 bburn hook not installed\n");
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
settings.hooks.Stop = settings.hooks.Stop.filter(
|
|
592
|
+
(entry) => {
|
|
593
|
+
var _a4;
|
|
594
|
+
return !((_a4 = entry.hooks) == null ? void 0 : _a4.some((hook) => hook.command === "bburn"));
|
|
595
|
+
}
|
|
596
|
+
);
|
|
597
|
+
if (settings.hooks.Stop.length === 0) {
|
|
598
|
+
delete settings.hooks.Stop;
|
|
599
|
+
}
|
|
600
|
+
if (Object.keys(settings.hooks).length === 0) {
|
|
601
|
+
delete settings.hooks;
|
|
602
|
+
}
|
|
603
|
+
saveClaudeSettings(settings);
|
|
604
|
+
console.log("\u2713 Removed bburn hook from Claude Code\n");
|
|
605
|
+
}
|
|
606
|
+
function showStatus() {
|
|
607
|
+
var _a4;
|
|
608
|
+
const settings = loadClaudeSettings();
|
|
609
|
+
const config = loadBonzaiConfig();
|
|
610
|
+
console.log("\n\u{1F525} Bonzai Hook Status\n");
|
|
611
|
+
const autoBurnEnabled = ((_a4 = config == null ? void 0 : config.autoBurn) == null ? void 0 : _a4.enabled) ?? false;
|
|
612
|
+
console.log(`Config autoBurn: ${autoBurnEnabled ? "enabled" : "disabled"}`);
|
|
613
|
+
const hookInstalled = hasBburnHook(settings);
|
|
614
|
+
console.log(`Claude hook: ${hookInstalled ? "installed" : "not installed"}
|
|
615
|
+
`);
|
|
616
|
+
if (autoBurnEnabled && !hookInstalled) {
|
|
617
|
+
console.log('Run "bhook install" to install the hook\n');
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
async function main2(subArgs = []) {
|
|
621
|
+
const args = subArgs.length > 0 ? subArgs : process.argv.slice(2);
|
|
622
|
+
const command = args[0];
|
|
623
|
+
switch (command) {
|
|
624
|
+
case "uninstall":
|
|
625
|
+
case "remove":
|
|
626
|
+
case "-u":
|
|
627
|
+
uninstallHook();
|
|
628
|
+
break;
|
|
629
|
+
case "status":
|
|
630
|
+
case "-s":
|
|
631
|
+
showStatus();
|
|
632
|
+
break;
|
|
633
|
+
case "install":
|
|
634
|
+
case "-i":
|
|
635
|
+
default:
|
|
636
|
+
installHook();
|
|
637
|
+
break;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
var BONZAI_DIR2, CONFIG_FILE2, CLAUDE_DIR, SETTINGS_FILE, _a2, isDirectRun2;
|
|
641
|
+
var init_bhook = __esm({
|
|
642
|
+
"src/bhook.js"() {
|
|
643
|
+
BONZAI_DIR2 = "bonzai";
|
|
644
|
+
CONFIG_FILE2 = "config.json";
|
|
645
|
+
CLAUDE_DIR = ".claude";
|
|
646
|
+
SETTINGS_FILE = "settings.local.json";
|
|
647
|
+
isDirectRun2 = (_a2 = process.argv[1]) == null ? void 0 : _a2.endsWith("bhook.js");
|
|
648
|
+
if (isDirectRun2) {
|
|
649
|
+
main2().catch((error) => {
|
|
650
|
+
console.error("Error:", error.message);
|
|
651
|
+
process.exit(1);
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
// src/bconfig.js
|
|
658
|
+
var bconfig_exports = {};
|
|
659
|
+
__export(bconfig_exports, {
|
|
660
|
+
main: () => main3
|
|
661
|
+
});
|
|
662
|
+
function copyDirectory(src, dest) {
|
|
663
|
+
if (!fs4.existsSync(dest)) {
|
|
664
|
+
fs4.mkdirSync(dest, { recursive: true });
|
|
665
|
+
}
|
|
666
|
+
const entries = fs4.readdirSync(src, { withFileTypes: true });
|
|
667
|
+
for (const entry of entries) {
|
|
668
|
+
const srcPath = path2.join(src, entry.name);
|
|
669
|
+
const destPath = path2.join(dest, entry.name);
|
|
670
|
+
if (entry.isDirectory()) {
|
|
671
|
+
copyDirectory(srcPath, destPath);
|
|
672
|
+
} else {
|
|
673
|
+
fs4.copyFileSync(srcPath, destPath);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
async function main3() {
|
|
678
|
+
const currentDir = process.cwd();
|
|
679
|
+
const bonzaiDir = path2.join(currentDir, "bonzai");
|
|
680
|
+
const receiverPath = path2.join(bonzaiDir, "receiver.js");
|
|
681
|
+
console.log("Setting up local file server...");
|
|
682
|
+
if (!fs4.existsSync(bonzaiDir)) {
|
|
683
|
+
console.log("Creating bonzai directory...");
|
|
684
|
+
fs4.mkdirSync(bonzaiDir);
|
|
685
|
+
}
|
|
686
|
+
console.log("Writing receiver.js...");
|
|
687
|
+
const receiverContent = fs4.readFileSync(path2.join(TEMPLATE_DIR, "receiver.js"), "utf8");
|
|
688
|
+
fs4.writeFileSync(receiverPath, receiverContent);
|
|
689
|
+
fs4.chmodSync(receiverPath, "755");
|
|
690
|
+
console.log("Writing config.js...");
|
|
691
|
+
const configContent = fs4.readFileSync(path2.join(TEMPLATE_DIR, "config.js"), "utf8");
|
|
692
|
+
fs4.writeFileSync(path2.join(bonzaiDir, "config.js"), configContent);
|
|
693
|
+
console.log("Copying handlers...");
|
|
694
|
+
const handlersDest = path2.join(bonzaiDir, "handlers");
|
|
695
|
+
if (!fs4.existsSync(handlersDest)) {
|
|
696
|
+
fs4.mkdirSync(handlersDest, { recursive: true });
|
|
697
|
+
}
|
|
698
|
+
if (ENABLED_LOOPS.includes("visualization")) {
|
|
699
|
+
const vizSrc = path2.join(TEMPLATE_DIR, "loops", "visualization");
|
|
700
|
+
if (fs4.existsSync(vizSrc)) {
|
|
701
|
+
for (const file of fs4.readdirSync(vizSrc)) {
|
|
702
|
+
fs4.copyFileSync(path2.join(vizSrc, file), path2.join(handlersDest, file));
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (ENABLED_LOOPS.includes("backend")) {
|
|
707
|
+
const backendSrc = path2.join(TEMPLATE_DIR, "loops", "backend");
|
|
708
|
+
if (fs4.existsSync(backendSrc)) {
|
|
709
|
+
for (const file of fs4.readdirSync(backendSrc)) {
|
|
710
|
+
fs4.copyFileSync(path2.join(backendSrc, file), path2.join(handlersDest, file));
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
console.log("Copying utils...");
|
|
715
|
+
const utilsSrc = path2.join(TEMPLATE_DIR, "utils");
|
|
716
|
+
const utilsDest = path2.join(bonzaiDir, "utils");
|
|
717
|
+
copyDirectory(utilsSrc, utilsDest);
|
|
718
|
+
const ignoreTargetPath = path2.join(bonzaiDir, ".ignore");
|
|
719
|
+
if (!fs4.existsSync(ignoreTargetPath)) {
|
|
720
|
+
console.log("Writing .ignore file...");
|
|
721
|
+
const ignoreContent = fs4.readFileSync(path2.join(TEMPLATE_DIR, "ignore.txt"), "utf8");
|
|
722
|
+
fs4.writeFileSync(ignoreTargetPath, ignoreContent);
|
|
723
|
+
}
|
|
724
|
+
const packageJsonPath = path2.join(bonzaiDir, "package.json");
|
|
725
|
+
let packageJson = {};
|
|
726
|
+
if (fs4.existsSync(packageJsonPath)) {
|
|
727
|
+
packageJson = JSON.parse(fs4.readFileSync(packageJsonPath, "utf8"));
|
|
728
|
+
} else {
|
|
729
|
+
packageJson = {
|
|
730
|
+
name: "bonzai-server",
|
|
731
|
+
version: "1.0.0",
|
|
732
|
+
description: "Dependencies for bonzai graph server",
|
|
733
|
+
main: "receiver.js",
|
|
734
|
+
scripts: {
|
|
735
|
+
test: 'echo "Error: no test specified" && exit 1'
|
|
736
|
+
},
|
|
737
|
+
author: "",
|
|
738
|
+
license: "ISC"
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
if (!packageJson.dependencies) {
|
|
742
|
+
packageJson.dependencies = {};
|
|
743
|
+
}
|
|
744
|
+
packageJson.dependencies.express = "^4.18.2";
|
|
745
|
+
packageJson.dependencies.cors = "^2.8.5";
|
|
746
|
+
packageJson.dependencies["@babel/parser"] = "^7.23.0";
|
|
747
|
+
packageJson.dependencies.ws = "^8.14.2";
|
|
748
|
+
packageJson.dependencies["node-pty"] = "^1.0.0";
|
|
749
|
+
if (!packageJson.scripts) {
|
|
750
|
+
packageJson.scripts = {};
|
|
751
|
+
}
|
|
752
|
+
packageJson.scripts["file-server"] = "node receiver.js";
|
|
753
|
+
fs4.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
754
|
+
console.log("Installing dependencies...");
|
|
755
|
+
return new Promise((resolve, reject) => {
|
|
756
|
+
const npm = spawn("npm", ["install"], {
|
|
757
|
+
stdio: "inherit",
|
|
758
|
+
cwd: bonzaiDir
|
|
759
|
+
});
|
|
760
|
+
npm.on("close", (code) => {
|
|
761
|
+
if (code === 0) {
|
|
762
|
+
const nodePtyPrebuilds = path2.join(bonzaiDir, "node_modules", "node-pty", "prebuilds");
|
|
763
|
+
if (fs4.existsSync(nodePtyPrebuilds)) {
|
|
764
|
+
const archDirs = ["darwin-arm64", "darwin-x64", "linux-x64", "linux-arm64"];
|
|
765
|
+
for (const arch of archDirs) {
|
|
766
|
+
const spawnHelperPath = path2.join(nodePtyPrebuilds, arch, "spawn-helper");
|
|
767
|
+
if (fs4.existsSync(spawnHelperPath)) {
|
|
768
|
+
try {
|
|
769
|
+
fs4.chmodSync(spawnHelperPath, "755");
|
|
770
|
+
console.log(`Fixed node-pty spawn-helper permissions (${arch})`);
|
|
771
|
+
} catch (e) {
|
|
772
|
+
console.warn(`Warning: Could not fix spawn-helper permissions for ${arch}:`, e.message);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
console.log("\nListener endpoints successfully deployed");
|
|
778
|
+
console.log("All code stays on your machine\n");
|
|
779
|
+
console.log("Relay server running on localhost:3001");
|
|
780
|
+
console.log("Terminal WebSocket available at ws://localhost:3001/terminal");
|
|
781
|
+
console.log("Diagram available at https://bonzai.dev/\n");
|
|
782
|
+
const server = spawn("node", ["receiver.js"], {
|
|
783
|
+
stdio: "inherit",
|
|
784
|
+
cwd: bonzaiDir,
|
|
785
|
+
env: {
|
|
786
|
+
...process.env,
|
|
787
|
+
BONZAI_REPO_DIR: currentDir
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
exec("open https://bonzai.dev/");
|
|
791
|
+
server.on("close", (serverCode) => {
|
|
792
|
+
console.log(`
|
|
793
|
+
Server stopped with code ${serverCode}`);
|
|
794
|
+
process.exit(serverCode);
|
|
795
|
+
});
|
|
796
|
+
server.on("error", (err) => {
|
|
797
|
+
console.error("Error starting server:", err.message);
|
|
798
|
+
process.exit(1);
|
|
799
|
+
});
|
|
800
|
+
process.on("SIGINT", () => {
|
|
801
|
+
console.log("\nShutting down server...");
|
|
802
|
+
server.kill("SIGINT");
|
|
803
|
+
});
|
|
804
|
+
process.on("SIGTERM", () => {
|
|
805
|
+
console.log("\nShutting down server...");
|
|
806
|
+
server.kill("SIGTERM");
|
|
807
|
+
});
|
|
808
|
+
resolve();
|
|
809
|
+
} else {
|
|
810
|
+
reject(new Error("npm install failed with code " + code));
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
npm.on("error", (err) => {
|
|
814
|
+
reject(err);
|
|
815
|
+
});
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
var __filename$1, __dirname$1, TEMPLATE_DIR, _a3, isDirectRun3;
|
|
819
|
+
var init_bconfig = __esm({
|
|
820
|
+
"src/bconfig.js"() {
|
|
821
|
+
init_loops_config();
|
|
822
|
+
__filename$1 = fileURLToPath(import.meta.url);
|
|
823
|
+
__dirname$1 = path2.dirname(__filename$1);
|
|
824
|
+
TEMPLATE_DIR = path2.join(__dirname$1, "graph-templates");
|
|
825
|
+
isDirectRun3 = (_a3 = process.argv[1]) == null ? void 0 : _a3.endsWith("bconfig.js");
|
|
826
|
+
if (isDirectRun3) {
|
|
827
|
+
main3().catch(console.error);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
// src/index.js
|
|
833
|
+
init_loops_config();
|
|
834
|
+
var __filename2 = fileURLToPath(import.meta.url);
|
|
835
|
+
var __dirname2 = dirname(__filename2);
|
|
836
|
+
var BONZAI_DIR3 = "bonzai";
|
|
837
|
+
var TEMPLATE_DIR2 = join(__dirname2, "payload-bonzai");
|
|
838
|
+
function showHelp() {
|
|
839
|
+
let help = `
|
|
840
|
+
Usage: npx bonzai-burn [option]
|
|
841
|
+
|
|
842
|
+
Options:
|
|
843
|
+
(no option) Initialize bonzai in current directory
|
|
844
|
+
-b, --burn Run code analysis
|
|
845
|
+
-h, --hook Manage Claude Code stop hook
|
|
846
|
+
--help Show this help message`;
|
|
847
|
+
if (ENABLED_LOOPS.includes("visualization") || ENABLED_LOOPS.includes("backend")) {
|
|
848
|
+
help = help.replace("--help", "-c, --config Launch visualization server\n --help");
|
|
849
|
+
}
|
|
850
|
+
console.log(help);
|
|
851
|
+
}
|
|
852
|
+
function init() {
|
|
853
|
+
const currentDir = process.cwd();
|
|
854
|
+
const bonzaiPath = join(currentDir, BONZAI_DIR3);
|
|
855
|
+
if (existsSync(bonzaiPath)) {
|
|
856
|
+
console.log(`${BONZAI_DIR3}/ already exists`);
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
mkdirSync(bonzaiPath, { recursive: true });
|
|
860
|
+
copyFileSync(join(TEMPLATE_DIR2, "config.json"), join(bonzaiPath, "config.json"));
|
|
861
|
+
console.log(`Created ${BONZAI_DIR3}/ folder with config.json`);
|
|
862
|
+
console.log(`Edit ${BONZAI_DIR3}/config.json to configure your burn rules`);
|
|
863
|
+
console.log(`Run 'npx bonzai-burn -b' to analyze your codebase`);
|
|
864
|
+
}
|
|
865
|
+
async function main4() {
|
|
866
|
+
const args = process.argv.slice(2);
|
|
867
|
+
const flag = args[0];
|
|
868
|
+
if (ENABLED_LOOPS.includes("burn")) {
|
|
869
|
+
if (flag === "-b" || flag === "--burn") {
|
|
870
|
+
const { main: burnMain } = await Promise.resolve().then(() => (init_bburn(), bburn_exports));
|
|
871
|
+
return burnMain == null ? void 0 : burnMain();
|
|
872
|
+
}
|
|
873
|
+
if (flag === "-h" || flag === "--hook") {
|
|
874
|
+
const { main: hookMain } = await Promise.resolve().then(() => (init_bhook(), bhook_exports));
|
|
875
|
+
return hookMain == null ? void 0 : hookMain(args.slice(1));
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
if (ENABLED_LOOPS.includes("visualization") || ENABLED_LOOPS.includes("backend")) {
|
|
879
|
+
if (flag === "-c" || flag === "--config") {
|
|
880
|
+
const { main: configMain } = await Promise.resolve().then(() => (init_bconfig(), bconfig_exports));
|
|
881
|
+
return configMain == null ? void 0 : configMain();
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (flag === "--help") {
|
|
885
|
+
showHelp();
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
init();
|
|
889
|
+
}
|
|
890
|
+
main4().catch((error) => {
|
|
891
|
+
console.error("Error:", error.message);
|
|
892
|
+
process.exit(1);
|
|
893
|
+
});
|
package/package.json
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bonzai-burn",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.40",
|
|
4
4
|
"description": "Git branch-based cleanup tool with bburn, baccept, and brevert commands",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "
|
|
6
|
+
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"bonzai-burn": "./
|
|
9
|
-
"bburn": "./
|
|
10
|
-
"
|
|
11
|
-
|
|
8
|
+
"bonzai-burn": "./dist/index.js",
|
|
9
|
+
"bburn": "./dist/bburn.js",
|
|
10
|
+
"bhook": "./dist/bhook.js"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"dev": "RELEASE_CHANNEL=dev node src/index.js",
|
|
14
|
+
"build": "RELEASE_CHANNEL=stable tsup",
|
|
15
|
+
"build:beta": "RELEASE_CHANNEL=beta tsup",
|
|
16
|
+
"build:dev": "RELEASE_CHANNEL=dev tsup",
|
|
17
|
+
"prepublishOnly": "npm run build",
|
|
18
|
+
"release": "npm version patch && npm publish",
|
|
19
|
+
"release:beta": "npm run build:beta && npm publish --tag beta --ignore-scripts",
|
|
20
|
+
"release:dev": "npm run build:dev && npm publish --tag dev --ignore-scripts"
|
|
12
21
|
},
|
|
13
22
|
"keywords": [
|
|
14
23
|
"git",
|
|
@@ -20,14 +29,14 @@
|
|
|
20
29
|
"node": ">=16.0.0"
|
|
21
30
|
},
|
|
22
31
|
"files": [
|
|
23
|
-
"
|
|
24
|
-
"payload-bonzai",
|
|
25
|
-
"graph-templates"
|
|
32
|
+
"dist"
|
|
26
33
|
],
|
|
27
34
|
"dependencies": {
|
|
28
35
|
"@modelcontextprotocol/sdk": "^1.25.3"
|
|
29
36
|
},
|
|
30
37
|
"devDependencies": {
|
|
31
|
-
"eslint": "^9.39.2"
|
|
38
|
+
"eslint": "^9.39.2",
|
|
39
|
+
"tsup": "^8.5.1",
|
|
40
|
+
"typescript": "^5.9.3"
|
|
32
41
|
}
|
|
33
42
|
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// Root route - simple API documentation
|
|
2
|
-
function indexHandler(req, res) {
|
|
3
|
-
res.json({
|
|
4
|
-
message: 'Local File Server API',
|
|
5
|
-
endpoints: {
|
|
6
|
-
'GET /list': 'List all files in the directory',
|
|
7
|
-
'GET /read?path=<filepath>': 'Read file content',
|
|
8
|
-
'POST /delete': 'Delete file or directory (body: {path})',
|
|
9
|
-
'POST /open-cursor': 'Open Cursor (body: {path, line?})',
|
|
10
|
-
'POST /shutdown': 'Gracefully shutdown the server',
|
|
11
|
-
'POST /scan_code_quality': 'Scan code quality (body: {projectPath})',
|
|
12
|
-
'POST /write': 'Write file content (body: {path, content})',
|
|
13
|
-
'GET /git/burns': 'List all bonzai-burn branches',
|
|
14
|
-
'POST /git/checkout': 'Checkout a branch (body: {branchName})',
|
|
15
|
-
'WS /terminal': 'Interactive terminal via WebSocket'
|
|
16
|
-
},
|
|
17
|
-
example: 'Try: /list or /read?path=README.md'
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
module.exports = indexHandler;
|
|
22
|
-
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const express = require('./node_modules/express');
|
|
4
|
-
const cors = require('./node_modules/cors');
|
|
5
|
-
const http = require('http');
|
|
6
|
-
const { WebSocketServer } = require('./node_modules/ws');
|
|
7
|
-
|
|
8
|
-
// Import handlers
|
|
9
|
-
const indexHandler = require('./handlers/index');
|
|
10
|
-
const listHandler = require('./handlers/list');
|
|
11
|
-
const readHandler = require('./handlers/read');
|
|
12
|
-
const deleteHandler = require('./handlers/delete');
|
|
13
|
-
const openCursorHandler = require('./handlers/open-cursor');
|
|
14
|
-
const shutdownHandler = require('./handlers/shutdown');
|
|
15
|
-
const scanCodeQualityHandler = require('./handlers/scan_code_quality');
|
|
16
|
-
const writeHandler = require('./handlers/write');
|
|
17
|
-
const { listBurns, checkoutBranch } = require('./handlers/git');
|
|
18
|
-
const { terminalHandler, setupTerminalWebSocket } = require('./handlers/terminal');
|
|
19
|
-
|
|
20
|
-
const app = express();
|
|
21
|
-
const server = http.createServer(app);
|
|
22
|
-
|
|
23
|
-
// WebSocket server for terminal
|
|
24
|
-
const wss = new WebSocketServer({ server, path: '/terminal' });
|
|
25
|
-
setupTerminalWebSocket(wss);
|
|
26
|
-
|
|
27
|
-
app.use(cors());
|
|
28
|
-
app.use(express.json());
|
|
29
|
-
|
|
30
|
-
// Register routes
|
|
31
|
-
app.get('/', indexHandler);
|
|
32
|
-
app.get('/list', listHandler);
|
|
33
|
-
app.get('/read', readHandler);
|
|
34
|
-
app.post('/delete', deleteHandler);
|
|
35
|
-
app.post('/open-cursor', openCursorHandler);
|
|
36
|
-
app.post('/shutdown', shutdownHandler);
|
|
37
|
-
app.post('/scan_code_quality', scanCodeQualityHandler);
|
|
38
|
-
app.post('/write', writeHandler);
|
|
39
|
-
app.get('/git/burns', listBurns);
|
|
40
|
-
app.post('/git/checkout', checkoutBranch);
|
|
41
|
-
app.get('/terminal', terminalHandler);
|
|
42
|
-
|
|
43
|
-
const port = 3001;
|
|
44
|
-
server.listen(port, () => {
|
|
45
|
-
console.log('📂 File server running on http://localhost:' + port);
|
|
46
|
-
console.log('🖥️ Terminal WebSocket available at ws://localhost:' + port + '/terminal');
|
|
47
|
-
});
|
package/src/index.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, mkdirSync, copyFileSync } from 'fs';
|
|
3
|
-
import { join, dirname } from 'path';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
const __dirname = dirname(__filename);
|
|
8
|
-
|
|
9
|
-
const BONZAI_DIR = 'bonzai';
|
|
10
|
-
const TEMPLATE_DIR = join(__dirname, '..', 'payload-bonzai');
|
|
11
|
-
|
|
12
|
-
function showHelp() {
|
|
13
|
-
console.log(`
|
|
14
|
-
🌳 Bonzai Burn - Code Analysis Tool
|
|
15
|
-
|
|
16
|
-
Usage: npx bonzai-burn [option]
|
|
17
|
-
|
|
18
|
-
Options:
|
|
19
|
-
(no option) Initialize bonzai in current directory
|
|
20
|
-
-b, --burn Run code analysis (bburn)
|
|
21
|
-
-c, --config Launch visualization server (bconfig)
|
|
22
|
-
-h, --hook Manage Claude Code stop hook (bhook)
|
|
23
|
-
--help Show this help message
|
|
24
|
-
|
|
25
|
-
Hook subcommands (-h):
|
|
26
|
-
-h Install hook (default)
|
|
27
|
-
-h -i Install hook
|
|
28
|
-
-h -s Show hook status
|
|
29
|
-
-h -u Uninstall hook
|
|
30
|
-
|
|
31
|
-
Examples:
|
|
32
|
-
npx bonzai-burn # Initialize bonzai folder
|
|
33
|
-
npx bonzai-burn -b # Run burn analysis
|
|
34
|
-
npx bonzai-burn -c # Start graph server
|
|
35
|
-
npx bonzai-burn -h # Install hook
|
|
36
|
-
npx bonzai-burn -h -s # Check hook status
|
|
37
|
-
`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function init() {
|
|
41
|
-
const currentDir = process.cwd();
|
|
42
|
-
const bonzaiPath = join(currentDir, BONZAI_DIR);
|
|
43
|
-
|
|
44
|
-
if (existsSync(bonzaiPath)) {
|
|
45
|
-
console.log(`📁 ${BONZAI_DIR}/ already exists`);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
mkdirSync(bonzaiPath, { recursive: true });
|
|
50
|
-
copyFileSync(join(TEMPLATE_DIR, 'config.json'), join(bonzaiPath, 'config.json'));
|
|
51
|
-
console.log(`📁 Created ${BONZAI_DIR}/ folder with config.json`);
|
|
52
|
-
console.log(`📝 Edit ${BONZAI_DIR}/config.json to configure your burn rules`);
|
|
53
|
-
console.log(`🔥 Run 'npx bonzai-burn -b' to analyze your codebase`);
|
|
54
|
-
console.log('');
|
|
55
|
-
console.log('┌─────────────────────────────────────────────────────────────────────┐');
|
|
56
|
-
console.log('│ │');
|
|
57
|
-
console.log('│ 🌳 Run `npx bonzai-burn -c` to configure your cleanup settings │');
|
|
58
|
-
console.log('│ │');
|
|
59
|
-
console.log('└─────────────────────────────────────────────────────────────────────┘');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async function main() {
|
|
63
|
-
const args = process.argv.slice(2);
|
|
64
|
-
const flag = args[0];
|
|
65
|
-
|
|
66
|
-
switch (flag) {
|
|
67
|
-
case '-b':
|
|
68
|
-
case '--burn': {
|
|
69
|
-
const { main: burnMain } = await import('./bburn.js');
|
|
70
|
-
if (burnMain) await burnMain();
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
case '-c':
|
|
74
|
-
case '--config': {
|
|
75
|
-
const { main: configMain } = await import('./bconfig.js');
|
|
76
|
-
if (configMain) await configMain();
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
case '-h':
|
|
80
|
-
case '--hook': {
|
|
81
|
-
const { main: hookMain } = await import('./bhook.js');
|
|
82
|
-
// Pass remaining args as subcommands (e.g., -h -s → ['-s'])
|
|
83
|
-
const subArgs = args.slice(1);
|
|
84
|
-
if (hookMain) await hookMain(subArgs);
|
|
85
|
-
break;
|
|
86
|
-
}
|
|
87
|
-
case '--help':
|
|
88
|
-
showHelp();
|
|
89
|
-
break;
|
|
90
|
-
default:
|
|
91
|
-
init();
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
main().catch((error) => {
|
|
97
|
-
console.error('Error:', error.message);
|
|
98
|
-
process.exit(1);
|
|
99
|
-
});
|
|
File without changes
|
/package/{src → dist}/bburn.js
RENAMED
|
File without changes
|
/package/{src → dist}/bhook.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/{graph-templates/handlers → dist/graph-templates/loops/visualization}/scan_code_quality.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|