bonzai-burn 1.0.12 → 1.0.14
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/package.json +1 -1
- package/payload-bonzai/config.json +5 -5
- package/src/baccept.js +2 -4
- package/src/bburn.js +165 -7
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"
|
|
4
|
-
"headlessClaude": true,
|
|
2
|
+
"provider": "cursor",
|
|
3
|
+
"headless": true,
|
|
5
4
|
"autoBurn": false,
|
|
5
|
+
"autoRun": false,
|
|
6
6
|
"lineLimit": {
|
|
7
|
-
"enabled":
|
|
7
|
+
"enabled": false,
|
|
8
8
|
"limit": 70,
|
|
9
9
|
"prompt": "Split any file with over {{ linelimit }} lines into smaller files."
|
|
10
10
|
},
|
|
11
11
|
"folderLimit": {
|
|
12
|
-
"enabled":
|
|
12
|
+
"enabled": false,
|
|
13
13
|
"limit": 10,
|
|
14
14
|
"prompt": "Split any folder with over {{ folderlimit }} items into smaller, compartmentalized folders."
|
|
15
15
|
}
|
package/src/baccept.js
CHANGED
|
@@ -35,9 +35,6 @@ async function accept() {
|
|
|
35
35
|
// Merge burn branch into original
|
|
36
36
|
execVisible(`git merge ${burnBranch} -m "Accept bonzai burn from ${burnBranch}"`);
|
|
37
37
|
|
|
38
|
-
// Delete burn branch
|
|
39
|
-
execVisible(`git branch -D ${burnBranch}`);
|
|
40
|
-
|
|
41
38
|
// If we made a WIP commit, we need to handle it
|
|
42
39
|
// The merge already includes the burn changes on top of the WIP commit
|
|
43
40
|
// So we can optionally squash or leave as-is
|
|
@@ -51,7 +48,8 @@ async function accept() {
|
|
|
51
48
|
exec('git config --unset bonzai.madeWipCommit');
|
|
52
49
|
|
|
53
50
|
console.log(`\n✓ Burn accepted and merged`);
|
|
54
|
-
console.log(`Now on: ${originalBranch}
|
|
51
|
+
console.log(`Now on: ${originalBranch}`);
|
|
52
|
+
console.log(`Branch kept: ${burnBranch}\n`);
|
|
55
53
|
|
|
56
54
|
} catch (error) {
|
|
57
55
|
console.error('❌ Accept failed:', error.message);
|
package/src/bburn.js
CHANGED
|
@@ -15,6 +15,36 @@ const CONFIG_FILE = 'config.json';
|
|
|
15
15
|
// Template folder in the package (ships as payload-bonzai, copied as bonzai)
|
|
16
16
|
const TEMPLATE_DIR = join(__dirname, '..', 'payload-bonzai');
|
|
17
17
|
|
|
18
|
+
// Parse --provider / -p argument, with config as fallback
|
|
19
|
+
function parseProvider(configDefault = 'claude') {
|
|
20
|
+
const args = process.argv.slice(2);
|
|
21
|
+
let provider = null;
|
|
22
|
+
|
|
23
|
+
for (let i = 0; i < args.length; i++) {
|
|
24
|
+
if (args[i] === '--provider' || args[i] === '-p') {
|
|
25
|
+
provider = args[i + 1];
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
if (args[i].startsWith('--provider=')) {
|
|
29
|
+
provider = args[i].split('=')[1];
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Use config default if no CLI arg provided
|
|
35
|
+
if (!provider) {
|
|
36
|
+
provider = configDefault;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const validProviders = ['claude', 'cursor'];
|
|
40
|
+
if (!validProviders.includes(provider)) {
|
|
41
|
+
console.error(`❌ Invalid provider: "${provider}". Must be one of: ${validProviders.join(', ')}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return provider;
|
|
46
|
+
}
|
|
47
|
+
|
|
18
48
|
function initializeBonzai() {
|
|
19
49
|
const bonzaiPath = join(process.cwd(), BONZAI_DIR);
|
|
20
50
|
const specsPath = join(bonzaiPath, SPECS_FILE);
|
|
@@ -69,7 +99,7 @@ function loadConfig(configPath) {
|
|
|
69
99
|
const content = fs.readFileSync(configPath, 'utf-8');
|
|
70
100
|
return JSON.parse(content);
|
|
71
101
|
} catch {
|
|
72
|
-
return {
|
|
102
|
+
return { headless: true };
|
|
73
103
|
}
|
|
74
104
|
}
|
|
75
105
|
|
|
@@ -118,7 +148,7 @@ function executeClaude(requirements, config) {
|
|
|
118
148
|
);
|
|
119
149
|
}
|
|
120
150
|
|
|
121
|
-
const headless = config.
|
|
151
|
+
const headless = config.headless !== false;
|
|
122
152
|
|
|
123
153
|
// Non-headless mode: run Claude interactively
|
|
124
154
|
if (!headless) {
|
|
@@ -251,6 +281,129 @@ function getToolIcon(toolName) {
|
|
|
251
281
|
return icons[toolName] || '🔹';
|
|
252
282
|
}
|
|
253
283
|
|
|
284
|
+
function executeCursor(requirements, config) {
|
|
285
|
+
// Check if cursor-agent CLI exists
|
|
286
|
+
try {
|
|
287
|
+
execSync('which cursor-agent', { encoding: 'utf-8', stdio: 'pipe' });
|
|
288
|
+
} catch (error) {
|
|
289
|
+
throw new Error(
|
|
290
|
+
'cursor-agent CLI not found.\n' +
|
|
291
|
+
'Install it with: npm install -g cursor-agent'
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const headless = config.headless !== false;
|
|
296
|
+
|
|
297
|
+
// Non-headless mode: run cursor-agent interactively
|
|
298
|
+
if (!headless) {
|
|
299
|
+
console.log('🖥️ Running in interactive mode...\n');
|
|
300
|
+
return new Promise((resolve, reject) => {
|
|
301
|
+
const args = ['-p', requirements];
|
|
302
|
+
|
|
303
|
+
const cursor = spawn('cursor-agent', args, {
|
|
304
|
+
stdio: 'inherit'
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
cursor.on('close', (code) => {
|
|
308
|
+
if (code === 0) {
|
|
309
|
+
resolve();
|
|
310
|
+
} else {
|
|
311
|
+
reject(new Error(`cursor-agent exited with code ${code}`));
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
cursor.on('error', (err) => {
|
|
316
|
+
reject(new Error(`Failed to execute cursor-agent: ${err.message}`));
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Headless mode with token tracking
|
|
322
|
+
let totalInputTokens = 0;
|
|
323
|
+
let totalOutputTokens = 0;
|
|
324
|
+
let lastToolName = '';
|
|
325
|
+
|
|
326
|
+
return new Promise((resolve, reject) => {
|
|
327
|
+
const args = [
|
|
328
|
+
'-p', requirements,
|
|
329
|
+
'--output-format', 'stream-json'
|
|
330
|
+
];
|
|
331
|
+
|
|
332
|
+
const cursor = spawn('cursor-agent', args, {
|
|
333
|
+
stdio: ['inherit', 'pipe', 'pipe']
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
let buffer = '';
|
|
337
|
+
|
|
338
|
+
cursor.stdout.on('data', (data) => {
|
|
339
|
+
buffer += data.toString();
|
|
340
|
+
|
|
341
|
+
// Process complete JSON lines
|
|
342
|
+
const lines = buffer.split('\n');
|
|
343
|
+
buffer = lines.pop(); // Keep incomplete line in buffer
|
|
344
|
+
|
|
345
|
+
for (const line of lines) {
|
|
346
|
+
if (!line.trim()) continue;
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
const event = JSON.parse(line);
|
|
350
|
+
|
|
351
|
+
// Track tokens from assistant messages
|
|
352
|
+
if (event.type === 'assistant' && event.message?.usage) {
|
|
353
|
+
const usage = event.message.usage;
|
|
354
|
+
if (usage.input_tokens) totalInputTokens += usage.input_tokens;
|
|
355
|
+
if (usage.output_tokens) totalOutputTokens += usage.output_tokens;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Show tool usage updates
|
|
359
|
+
if (event.type === 'content_block_start' && event.content_block?.type === 'tool_use') {
|
|
360
|
+
lastToolName = event.content_block.name || '';
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (event.type === 'content_block_stop' && lastToolName) {
|
|
364
|
+
const icon = getToolIcon(lastToolName);
|
|
365
|
+
console.log(` ${icon} ${lastToolName}`);
|
|
366
|
+
lastToolName = '';
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Show result events with usage info
|
|
370
|
+
if (event.type === 'result') {
|
|
371
|
+
if (event.usage) {
|
|
372
|
+
totalInputTokens = event.usage.input_tokens || totalInputTokens;
|
|
373
|
+
totalOutputTokens = event.usage.output_tokens || totalOutputTokens;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
} catch (e) {
|
|
378
|
+
// Not valid JSON, skip
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
cursor.stderr.on('data', (data) => {
|
|
384
|
+
const msg = data.toString().trim();
|
|
385
|
+
if (msg && !msg.includes('ExperimentalWarning')) {
|
|
386
|
+
console.error(msg);
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
cursor.on('close', (code) => {
|
|
391
|
+
// Print token summary
|
|
392
|
+
console.log(`\n📊 Tokens: ${totalInputTokens.toLocaleString()} in / ${totalOutputTokens.toLocaleString()} out`);
|
|
393
|
+
|
|
394
|
+
if (code === 0) {
|
|
395
|
+
resolve();
|
|
396
|
+
} else {
|
|
397
|
+
reject(new Error(`cursor-agent exited with code ${code}`));
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
cursor.on('error', (err) => {
|
|
402
|
+
reject(new Error(`Failed to execute cursor-agent: ${err.message}`));
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
254
407
|
async function burn() {
|
|
255
408
|
try {
|
|
256
409
|
// Initialize bonzai folder and specs.md on first execution
|
|
@@ -261,8 +414,8 @@ async function burn() {
|
|
|
261
414
|
const config = loadConfig(configPath);
|
|
262
415
|
const specs = loadSpecs(specsPath, config);
|
|
263
416
|
|
|
264
|
-
//
|
|
265
|
-
|
|
417
|
+
// Determine provider: CLI arg overrides config
|
|
418
|
+
const provider = parseProvider(config.provider || 'claude');
|
|
266
419
|
|
|
267
420
|
// Check if in git repo
|
|
268
421
|
try {
|
|
@@ -304,13 +457,18 @@ async function burn() {
|
|
|
304
457
|
exec(`git config bonzai.madeWipCommit ${madeWipCommit}`);
|
|
305
458
|
|
|
306
459
|
console.log(`📋 Specs loaded from: ${BONZAI_DIR}/${SPECS_FILE}`);
|
|
307
|
-
console.log(
|
|
460
|
+
console.log(`🤖 Provider: ${provider}`);
|
|
461
|
+
console.log(`⚙️ Headless mode: ${config.headless !== false ? 'on' : 'off'}`);
|
|
308
462
|
console.log('🔥 Running Bonzai burn...\n');
|
|
309
463
|
|
|
310
464
|
const startTime = Date.now();
|
|
311
465
|
|
|
312
|
-
// Execute
|
|
313
|
-
|
|
466
|
+
// Execute with the selected provider
|
|
467
|
+
if (provider === 'cursor') {
|
|
468
|
+
await executeCursor(specs, config);
|
|
469
|
+
} else {
|
|
470
|
+
await executeClaude(specs, config);
|
|
471
|
+
}
|
|
314
472
|
|
|
315
473
|
const duration = Math.round((Date.now() - startTime) / 1000);
|
|
316
474
|
|