bonzai-burn 1.0.3 → 1.0.5

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.3",
3
+ "version": "1.0.5",
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/brevert.js CHANGED
File without changes
package/src/btrim.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { execSync } from 'child_process';
3
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
2
+ import { execSync, spawn } from 'child_process';
3
+ import fs from 'fs';
4
4
  import { join } from 'path';
5
5
 
6
6
  const BONZAI_DIR = 'bonzai';
@@ -16,17 +16,37 @@ Define your cleanup requirements below. btrim will follow these instructions.
16
16
  - Clean up console.log statements
17
17
  `;
18
18
 
19
+ function initializeBonzai() {
20
+ const bonzaiPath = join(process.cwd(), BONZAI_DIR);
21
+ const specsPath = join(bonzaiPath, SPECS_FILE);
22
+
23
+ // Check if bonzai/ folder exists
24
+ if (!fs.existsSync(bonzaiPath)) {
25
+ // Create bonzai/ folder
26
+ fs.mkdirSync(bonzaiPath, { recursive: true });
27
+ console.log(`šŸ“ Created ${BONZAI_DIR}/ folder`);
28
+ }
29
+
30
+ // Generate bonzai/specs.md with template
31
+ if (!fs.existsSync(specsPath)) {
32
+ fs.writeFileSync(specsPath, DEFAULT_SPECS);
33
+ console.log(`šŸ“ Created ${BONZAI_DIR}/${SPECS_FILE}`);
34
+ console.log(`\nāš ļø Please edit ${BONZAI_DIR}/${SPECS_FILE} to define your cleanup rules before running btrim.\n`);
35
+ process.exit(0);
36
+ }
37
+ }
38
+
19
39
  function ensureBonzaiDir() {
20
40
  const bonzaiPath = join(process.cwd(), BONZAI_DIR);
21
41
  const specsPath = join(bonzaiPath, SPECS_FILE);
22
42
 
23
- if (!existsSync(bonzaiPath)) {
24
- mkdirSync(bonzaiPath, { recursive: true });
43
+ if (!fs.existsSync(bonzaiPath)) {
44
+ fs.mkdirSync(bonzaiPath, { recursive: true });
25
45
  console.log(`šŸ“ Created ${BONZAI_DIR}/ folder\n`);
26
46
  }
27
47
 
28
- if (!existsSync(specsPath)) {
29
- writeFileSync(specsPath, DEFAULT_SPECS);
48
+ if (!fs.existsSync(specsPath)) {
49
+ fs.writeFileSync(specsPath, DEFAULT_SPECS);
30
50
  console.log(`šŸ“ Created ${BONZAI_DIR}/${SPECS_FILE} - edit this file to define your cleanup specs\n`);
31
51
  }
32
52
 
@@ -34,7 +54,7 @@ function ensureBonzaiDir() {
34
54
  }
35
55
 
36
56
  function loadSpecs(specsPath) {
37
- const content = readFileSync(specsPath, 'utf-8');
57
+ const content = fs.readFileSync(specsPath, 'utf-8');
38
58
  return `You are a code cleanup assistant. Follow these specifications:\n\n${content}`;
39
59
  }
40
60
 
@@ -46,21 +66,133 @@ function execVisible(command) {
46
66
  execSync(command, { stdio: 'inherit' });
47
67
  }
48
68
 
69
+ function executeClaude(requirements) {
70
+ // Check if Claude CLI exists
71
+ try {
72
+ execSync('which claude', { encoding: 'utf-8', stdio: 'pipe' });
73
+ } catch (error) {
74
+ throw new Error(
75
+ 'Claude Code CLI not found.\n' +
76
+ 'Install it with: npm install -g @anthropic-ai/claude-code'
77
+ );
78
+ }
79
+
80
+ // Track token usage
81
+ let totalInputTokens = 0;
82
+ let totalOutputTokens = 0;
83
+ let lastToolName = '';
84
+
85
+ // Execute Claude with streaming JSON output
86
+
87
+ return new Promise((resolve, reject) => {
88
+ const args = [
89
+ '-p', requirements,
90
+ '--allowedTools', 'Read,Write,Edit,Bash',
91
+ '--permission-mode', 'dontAsk',
92
+ '--output-format', 'stream-json',
93
+ '--verbose'
94
+ ];
95
+
96
+ const claude = spawn('claude', args, {
97
+ stdio: ['inherit', 'pipe', 'pipe']
98
+ });
99
+
100
+ let buffer = '';
101
+
102
+ claude.stdout.on('data', (data) => {
103
+ buffer += data.toString();
104
+
105
+ // Process complete JSON lines
106
+ const lines = buffer.split('\n');
107
+ buffer = lines.pop(); // Keep incomplete line in buffer
108
+
109
+ for (const line of lines) {
110
+ if (!line.trim()) continue;
111
+
112
+ try {
113
+ const event = JSON.parse(line);
114
+
115
+ // Track tokens from assistant messages
116
+ if (event.type === 'assistant' && event.message?.usage) {
117
+ const usage = event.message.usage;
118
+ if (usage.input_tokens) totalInputTokens += usage.input_tokens;
119
+ if (usage.output_tokens) totalOutputTokens += usage.output_tokens;
120
+ }
121
+
122
+ // Show tool usage updates
123
+ if (event.type === 'content_block_start' && event.content_block?.type === 'tool_use') {
124
+ lastToolName = event.content_block.name || '';
125
+ }
126
+
127
+ if (event.type === 'content_block_stop' && lastToolName) {
128
+ const icon = getToolIcon(lastToolName);
129
+ console.log(` ${icon} ${lastToolName}`);
130
+ lastToolName = '';
131
+ }
132
+
133
+ // Show result events with file info
134
+ if (event.type === 'result') {
135
+ if (event.usage) {
136
+ // Final usage stats
137
+ totalInputTokens = event.usage.input_tokens || totalInputTokens;
138
+ totalOutputTokens = event.usage.output_tokens || totalOutputTokens;
139
+ }
140
+ }
141
+
142
+ } catch (e) {
143
+ // Not valid JSON, skip
144
+ }
145
+ }
146
+ });
147
+
148
+ claude.stderr.on('data', (data) => {
149
+ // Show errors but don't clutter with minor stderr
150
+ const msg = data.toString().trim();
151
+ if (msg && !msg.includes('ExperimentalWarning')) {
152
+ console.error(msg);
153
+ }
154
+ });
155
+
156
+ claude.on('close', (code) => {
157
+ // Print token summary
158
+ console.log(`\nšŸ“Š Tokens: ${totalInputTokens.toLocaleString()} in / ${totalOutputTokens.toLocaleString()} out`);
159
+
160
+ if (code === 0) {
161
+ resolve();
162
+ } else {
163
+ reject(new Error(`Claude exited with code ${code}`));
164
+ }
165
+ });
166
+
167
+ claude.on('error', (err) => {
168
+ reject(new Error(`Failed to execute Claude: ${err.message}`));
169
+ });
170
+ });
171
+ }
172
+
173
+ function getToolIcon(toolName) {
174
+ const icons = {
175
+ 'Read': 'šŸ“–',
176
+ 'Write': 'āœļø',
177
+ 'Edit': 'šŸ”§',
178
+ 'Bash': 'šŸ’»',
179
+ 'Glob': 'šŸ”',
180
+ 'Grep': 'šŸ”Ž'
181
+ };
182
+ return icons[toolName] || 'šŸ”¹';
183
+ }
184
+
49
185
  async function burn() {
50
186
  try {
187
+ // Initialize bonzai folder and specs.md on first execution
188
+ initializeBonzai();
189
+
51
190
  // Ensure bonzai directory and specs file exist
52
191
  const specsPath = ensureBonzaiDir();
53
192
  const specs = loadSpecs(specsPath);
54
193
 
55
- // Check if Claude CLI exists
194
+ // Check if Claude CLI exists and execute
56
195
  console.log('šŸ” Checking for Claude Code CLI...');
57
- try {
58
- exec('which claude');
59
- } catch {
60
- console.error('āŒ Claude Code CLI not found');
61
- console.error('Install: npm install -g @anthropic-ai/claude-code');
62
- process.exit(1);
63
- }
64
196
 
65
197
  // Check if in git repo
66
198
  try {
@@ -114,7 +246,7 @@ async function burn() {
114
246
  const startTime = Date.now();
115
247
 
116
248
  // Execute Claude with specs from bonzai/specs.md
117
- execVisible(`claude -p "${specs.replace(/"/g, '\\"')}" --allowedTools "Read,Write,Edit,Bash" --permission-mode dontAsk`);
249
+ await executeClaude(specs);
118
250
 
119
251
  const duration = Math.round((Date.now() - startTime) / 1000);
120
252
 
@@ -135,6 +267,9 @@ async function burn() {
135
267
 
136
268
  } catch (error) {
137
269
  console.error('āŒ Burn failed:', error.message);
270
+ if (error.message.includes('Claude Code CLI not found')) {
271
+ console.error('\n' + error.message);
272
+ }
138
273
  process.exit(1);
139
274
  }
140
275
  }
package/src/index.js CHANGED
File without changes