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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bonzai-burn",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Git branch-based cleanup tool with bburn, baccept, and brevert commands",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -1,15 +1,15 @@
1
1
  {
2
- "isClaude": false,
3
- "isCursor": true,
4
- "headlessClaude": true,
2
+ "provider": "cursor",
3
+ "headless": true,
5
4
  "autoBurn": false,
5
+ "autoRun": false,
6
6
  "lineLimit": {
7
- "enabled": true,
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": true,
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}\n`);
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 { headlessClaude: true };
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.headlessClaude !== false;
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
- // Check if Claude CLI exists and execute
265
- console.log('🔍 Checking for Claude Code CLI...');
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(`⚙️ Headless mode: ${config.headlessClaude !== false ? 'on' : 'off'}`);
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 Claude with specs from bonzai/specs.md
313
- await executeClaude(specs, config);
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