bonzai-burn 1.0.5 โ†’ 1.0.7

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/btrim.js +91 -22
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bonzai-burn",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Git branch-based cleanup tool with btrim and brevert commands",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/btrim.js CHANGED
@@ -1,24 +1,46 @@
1
1
  #!/usr/bin/env node
2
2
  import { execSync, spawn } from 'child_process';
3
+ import crypto from 'crypto';
3
4
  import fs from 'fs';
4
- import { join } from 'path';
5
+ import { join, dirname } from 'path';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
5
10
 
6
11
  const BONZAI_DIR = 'bonzai';
7
12
  const SPECS_FILE = 'specs.md';
13
+ const CONFIG_FILE = 'config.json';
14
+
15
+ // Template folder in the package
16
+ const TEMPLATE_DIR = join(__dirname, '..', 'bonzai');
17
+
18
+ function getTemplate(filename) {
19
+ const templatePath = join(TEMPLATE_DIR, filename);
20
+ if (fs.existsSync(templatePath)) {
21
+ return fs.readFileSync(templatePath, 'utf-8');
22
+ }
23
+ return null;
24
+ }
8
25
 
9
26
  const DEFAULT_SPECS = `# Bonzai Specs
10
27
 
11
28
  Define your cleanup requirements below. btrim will follow these instructions.
12
29
 
13
- ## Example:
30
+ ## Requirements:
14
31
  - Remove unused imports
15
32
  - Delete files matching pattern "*.tmp"
16
33
  - Clean up console.log statements
17
34
  `;
18
35
 
36
+ const DEFAULT_CONFIG = {
37
+ headlessClaude: true
38
+ };
39
+
19
40
  function initializeBonzai() {
20
41
  const bonzaiPath = join(process.cwd(), BONZAI_DIR);
21
42
  const specsPath = join(bonzaiPath, SPECS_FILE);
43
+ const configPath = join(bonzaiPath, CONFIG_FILE);
22
44
 
23
45
  // Check if bonzai/ folder exists
24
46
  if (!fs.existsSync(bonzaiPath)) {
@@ -27,10 +49,18 @@ function initializeBonzai() {
27
49
  console.log(`๐Ÿ“ Created ${BONZAI_DIR}/ folder`);
28
50
  }
29
51
 
30
- // Generate bonzai/specs.md with template
52
+ // Generate bonzai/specs.md from package template
31
53
  if (!fs.existsSync(specsPath)) {
32
- fs.writeFileSync(specsPath, DEFAULT_SPECS);
54
+ const specsContent = getTemplate(SPECS_FILE) || DEFAULT_SPECS;
55
+ fs.writeFileSync(specsPath, specsContent);
33
56
  console.log(`๐Ÿ“ Created ${BONZAI_DIR}/${SPECS_FILE}`);
57
+ }
58
+
59
+ // Generate bonzai/config.json from package template
60
+ if (!fs.existsSync(configPath)) {
61
+ const configContent = getTemplate(CONFIG_FILE) || JSON.stringify(DEFAULT_CONFIG, null, 2) + '\n';
62
+ fs.writeFileSync(configPath, configContent);
63
+ console.log(`โš™๏ธ Created ${BONZAI_DIR}/${CONFIG_FILE}`);
34
64
  console.log(`\nโš ๏ธ Please edit ${BONZAI_DIR}/${SPECS_FILE} to define your cleanup rules before running btrim.\n`);
35
65
  process.exit(0);
36
66
  }
@@ -39,6 +69,7 @@ function initializeBonzai() {
39
69
  function ensureBonzaiDir() {
40
70
  const bonzaiPath = join(process.cwd(), BONZAI_DIR);
41
71
  const specsPath = join(bonzaiPath, SPECS_FILE);
72
+ const configPath = join(bonzaiPath, CONFIG_FILE);
42
73
 
43
74
  if (!fs.existsSync(bonzaiPath)) {
44
75
  fs.mkdirSync(bonzaiPath, { recursive: true });
@@ -46,11 +77,27 @@ function ensureBonzaiDir() {
46
77
  }
47
78
 
48
79
  if (!fs.existsSync(specsPath)) {
49
- fs.writeFileSync(specsPath, DEFAULT_SPECS);
80
+ const specsContent = getTemplate(SPECS_FILE) || DEFAULT_SPECS;
81
+ fs.writeFileSync(specsPath, specsContent);
50
82
  console.log(`๐Ÿ“ Created ${BONZAI_DIR}/${SPECS_FILE} - edit this file to define your cleanup specs\n`);
51
83
  }
52
84
 
53
- return specsPath;
85
+ if (!fs.existsSync(configPath)) {
86
+ const configContent = getTemplate(CONFIG_FILE) || JSON.stringify(DEFAULT_CONFIG, null, 2) + '\n';
87
+ fs.writeFileSync(configPath, configContent);
88
+ console.log(`โš™๏ธ Created ${BONZAI_DIR}/${CONFIG_FILE}\n`);
89
+ }
90
+
91
+ return { specsPath, configPath };
92
+ }
93
+
94
+ function loadConfig(configPath) {
95
+ try {
96
+ const content = fs.readFileSync(configPath, 'utf-8');
97
+ return { ...DEFAULT_CONFIG, ...JSON.parse(content) };
98
+ } catch {
99
+ return DEFAULT_CONFIG;
100
+ }
54
101
  }
55
102
 
56
103
  function loadSpecs(specsPath) {
@@ -66,7 +113,7 @@ function execVisible(command) {
66
113
  execSync(command, { stdio: 'inherit' });
67
114
  }
68
115
 
69
- function executeClaude(requirements) {
116
+ function executeClaude(requirements, config) {
70
117
  // Check if Claude CLI exists
71
118
  try {
72
119
  execSync('which claude', { encoding: 'utf-8', stdio: 'pipe' });
@@ -77,13 +124,40 @@ function executeClaude(requirements) {
77
124
  );
78
125
  }
79
126
 
80
- // Track token usage
127
+ const headless = config.headlessClaude !== false;
128
+
129
+ // Non-headless mode: run Claude interactively
130
+ if (!headless) {
131
+ console.log('๐Ÿ–ฅ๏ธ Running in interactive mode...\n');
132
+ return new Promise((resolve, reject) => {
133
+ const args = [
134
+ '--allowedTools', 'Read,Write,Edit,Bash',
135
+ '--permission-mode', 'dontAsk'
136
+ ];
137
+
138
+ const claude = spawn('claude', args, {
139
+ stdio: 'inherit'
140
+ });
141
+
142
+ claude.on('close', (code) => {
143
+ if (code === 0) {
144
+ resolve();
145
+ } else {
146
+ reject(new Error(`Claude exited with code ${code}`));
147
+ }
148
+ });
149
+
150
+ claude.on('error', (err) => {
151
+ reject(new Error(`Failed to execute Claude: ${err.message}`));
152
+ });
153
+ });
154
+ }
155
+
156
+ // Headless mode with token tracking
81
157
  let totalInputTokens = 0;
82
158
  let totalOutputTokens = 0;
83
159
  let lastToolName = '';
84
160
 
85
- // Execute Claude with streaming JSON output
86
-
87
161
  return new Promise((resolve, reject) => {
88
162
  const args = [
89
163
  '-p', requirements,
@@ -188,8 +262,9 @@ async function burn() {
188
262
  initializeBonzai();
189
263
 
190
264
  // Ensure bonzai directory and specs file exist
191
- const specsPath = ensureBonzaiDir();
265
+ const { specsPath, configPath } = ensureBonzaiDir();
192
266
  const specs = loadSpecs(specsPath);
267
+ const config = loadConfig(configPath);
193
268
 
194
269
  // Check if Claude CLI exists and execute
195
270
  console.log('๐Ÿ” Checking for Claude Code CLI...');
@@ -218,16 +293,9 @@ async function burn() {
218
293
  console.log(`โœ“ Work saved on ${originalBranch}\n`);
219
294
  }
220
295
 
221
- // Always use same burn branch name
222
- const burnBranch = 'bonzai-burn';
223
-
224
- // Delete existing burn branch if it exists
225
- try {
226
- exec(`git branch -D ${burnBranch}`);
227
- console.log(`๐Ÿงน Cleaned up old ${burnBranch} branch\n`);
228
- } catch {
229
- // Branch doesn't exist, that's fine
230
- }
296
+ // Generate unique branch name with short UUID
297
+ const shortId = crypto.randomUUID().slice(0, 8);
298
+ const burnBranch = `bonzai-burn-${shortId}`;
231
299
 
232
300
  console.log(`๐Ÿ“ Starting from: ${originalBranch}`);
233
301
  console.log(`๐ŸŒฟ Creating: ${burnBranch}\n`);
@@ -241,12 +309,13 @@ async function burn() {
241
309
  exec(`git config bonzai.madeWipCommit ${madeWipCommit}`);
242
310
 
243
311
  console.log(`๐Ÿ“‹ Specs loaded from: ${BONZAI_DIR}/${SPECS_FILE}`);
312
+ console.log(`โš™๏ธ Headless mode: ${config.headlessClaude !== false ? 'on' : 'off'}`);
244
313
  console.log('๐Ÿ”ฅ Running Bonzai burn...\n');
245
314
 
246
315
  const startTime = Date.now();
247
316
 
248
317
  // Execute Claude with specs from bonzai/specs.md
249
- await executeClaude(specs);
318
+ await executeClaude(specs, config);
250
319
 
251
320
  const duration = Math.round((Date.now() - startTime) / 1000);
252
321