bonzai-burn 1.0.3 → 1.0.4

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.4",
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,132 @@ 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
+ ];
94
+
95
+ const claude = spawn('claude', args, {
96
+ stdio: ['inherit', 'pipe', 'pipe']
97
+ });
98
+
99
+ let buffer = '';
100
+
101
+ claude.stdout.on('data', (data) => {
102
+ buffer += data.toString();
103
+
104
+ // Process complete JSON lines
105
+ const lines = buffer.split('\n');
106
+ buffer = lines.pop(); // Keep incomplete line in buffer
107
+
108
+ for (const line of lines) {
109
+ if (!line.trim()) continue;
110
+
111
+ try {
112
+ const event = JSON.parse(line);
113
+
114
+ // Track tokens from assistant messages
115
+ if (event.type === 'assistant' && event.message?.usage) {
116
+ const usage = event.message.usage;
117
+ if (usage.input_tokens) totalInputTokens += usage.input_tokens;
118
+ if (usage.output_tokens) totalOutputTokens += usage.output_tokens;
119
+ }
120
+
121
+ // Show tool usage updates
122
+ if (event.type === 'content_block_start' && event.content_block?.type === 'tool_use') {
123
+ lastToolName = event.content_block.name || '';
124
+ }
125
+
126
+ if (event.type === 'content_block_stop' && lastToolName) {
127
+ const icon = getToolIcon(lastToolName);
128
+ console.log(` ${icon} ${lastToolName}`);
129
+ lastToolName = '';
130
+ }
131
+
132
+ // Show result events with file info
133
+ if (event.type === 'result') {
134
+ if (event.usage) {
135
+ // Final usage stats
136
+ totalInputTokens = event.usage.input_tokens || totalInputTokens;
137
+ totalOutputTokens = event.usage.output_tokens || totalOutputTokens;
138
+ }
139
+ }
140
+
141
+ } catch (e) {
142
+ // Not valid JSON, skip
143
+ }
144
+ }
145
+ });
146
+
147
+ claude.stderr.on('data', (data) => {
148
+ // Show errors but don't clutter with minor stderr
149
+ const msg = data.toString().trim();
150
+ if (msg && !msg.includes('ExperimentalWarning')) {
151
+ console.error(msg);
152
+ }
153
+ });
154
+
155
+ claude.on('close', (code) => {
156
+ // Print token summary
157
+ console.log(`\nšŸ“Š Tokens: ${totalInputTokens.toLocaleString()} in / ${totalOutputTokens.toLocaleString()} out`);
158
+
159
+ if (code === 0) {
160
+ resolve();
161
+ } else {
162
+ reject(new Error(`Claude exited with code ${code}`));
163
+ }
164
+ });
165
+
166
+ claude.on('error', (err) => {
167
+ reject(new Error(`Failed to execute Claude: ${err.message}`));
168
+ });
169
+ });
170
+ }
171
+
172
+ function getToolIcon(toolName) {
173
+ const icons = {
174
+ 'Read': 'šŸ“–',
175
+ 'Write': 'āœļø',
176
+ 'Edit': 'šŸ”§',
177
+ 'Bash': 'šŸ’»',
178
+ 'Glob': 'šŸ”',
179
+ 'Grep': 'šŸ”Ž'
180
+ };
181
+ return icons[toolName] || 'šŸ”¹';
182
+ }
183
+
49
184
  async function burn() {
50
185
  try {
186
+ // Initialize bonzai folder and specs.md on first execution
187
+ initializeBonzai();
188
+
51
189
  // Ensure bonzai directory and specs file exist
52
190
  const specsPath = ensureBonzaiDir();
53
191
  const specs = loadSpecs(specsPath);
54
192
 
55
- // Check if Claude CLI exists
193
+ // Check if Claude CLI exists and execute
56
194
  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
195
 
65
196
  // Check if in git repo
66
197
  try {
@@ -114,7 +245,7 @@ async function burn() {
114
245
  const startTime = Date.now();
115
246
 
116
247
  // Execute Claude with specs from bonzai/specs.md
117
- execVisible(`claude -p "${specs.replace(/"/g, '\\"')}" --allowedTools "Read,Write,Edit,Bash" --permission-mode dontAsk`);
248
+ await executeClaude(specs);
118
249
 
119
250
  const duration = Math.round((Date.now() - startTime) / 1000);
120
251
 
@@ -135,6 +266,9 @@ async function burn() {
135
266
 
136
267
  } catch (error) {
137
268
  console.error('āŒ Burn failed:', error.message);
269
+ if (error.message.includes('Claude Code CLI not found')) {
270
+ console.error('\n' + error.message);
271
+ }
138
272
  process.exit(1);
139
273
  }
140
274
  }
package/src/index.js CHANGED
File without changes