bueller-wheel 0.1.0 → 0.2.0
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/README.md +7 -9
- package/dist/index.js +37 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Bueller Wheel
|
|
2
2
|
|
|
3
|
-
A headless Claude Code issue processor that runs in a loop and resolves issues or
|
|
3
|
+
A headless Claude Code issue processor that runs in a loop and resolves issues or ticket files written in markdown
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
@@ -15,16 +15,14 @@ npx bueller-wheel
|
|
|
15
15
|
|
|
16
16
|
## Why?
|
|
17
17
|
|
|
18
|
-
Claude Code
|
|
18
|
+
You want Claude Code to autonomously work on a large pile of issues while you eat lunch and watch a baseball game. Bueller Wheel helps tackle several issues you might encounter:
|
|
19
19
|
|
|
20
|
-
**
|
|
21
|
-
- **
|
|
22
|
-
- **
|
|
23
|
-
- **
|
|
20
|
+
- **Claude stops processing after a few issues**: Claude Code tends to stop processing after completing a few tasks. Bueller Wheel keeps prompting Claude Code to work until all of the issues have been resolved.
|
|
21
|
+
- **Claude forgets what it's doing**: As Claude Code uses up its context window, it tends to forget what it was working on. Bueller Wheel runs Claude Code with a fresh context window and prompt for each issue.
|
|
22
|
+
- **You forget what Claude was doing**: If you successfully get Claude Code to work on a large number of tasks, you end up with a pile of code to review. Bueller Wheel structures each issue as a discrete reviewable chunk of work, in a format amenable to multiple iterations of feedback between you and Claude.
|
|
23
|
+
- **Claude keeps making the same mistakes**: A Claude that forgets its history is doomed to repeat it. Bueller Wheel sets up an FAQ directory for Claude to speed up resolution of frequent pitfalls.
|
|
24
24
|
|
|
25
|
-
**
|
|
26
|
-
|
|
27
|
-
**Note:** Bueller Wheel is not a full-fledged task management system. It has no concept of assignment or dependency apart from linear file ordering. The sweet spot for this tool is **solo developers working on a single branch**, but you can make [parallel branches and agents](#working-with-multiple-branches) work.
|
|
25
|
+
**Note**: Bueller Wheel is not a full-fledged task management system. It has no concept of assignment or dependency apart from linear file ordering. The sweet spot for this tool is **solo developers working on a single branch**. That said, you can make [parallel branches and agents](#working-with-multiple-branches) work.
|
|
28
26
|
|
|
29
27
|
## How It Works
|
|
30
28
|
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,15 @@ import { execSync } from 'node:child_process';
|
|
|
3
3
|
import * as fs from 'node:fs/promises';
|
|
4
4
|
import * as path from 'node:path';
|
|
5
5
|
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
6
|
+
// Colors for output
|
|
7
|
+
const colors = {
|
|
8
|
+
red: '\x1b[0;31m',
|
|
9
|
+
green: '\x1b[0;32m',
|
|
10
|
+
yellow: '\x1b[1;33m',
|
|
11
|
+
blue: '\x1b[0;34m',
|
|
12
|
+
cyan: '\x1b[0;36m',
|
|
13
|
+
reset: '\x1b[0m',
|
|
14
|
+
};
|
|
6
15
|
const ISSUE_DIR_OPEN = 'open';
|
|
7
16
|
const ISSUE_DIR_REVIEW = 'review';
|
|
8
17
|
const ISSUE_DIR_STUCK = 'stuck';
|
|
@@ -136,7 +145,7 @@ function gitCommit(issueFile, status) {
|
|
|
136
145
|
encoding: 'utf-8',
|
|
137
146
|
}).trim();
|
|
138
147
|
if (!untrackedFiles) {
|
|
139
|
-
console.log(
|
|
148
|
+
console.log(`${colors.cyan}No changes to commit${colors.reset}`);
|
|
140
149
|
return;
|
|
141
150
|
}
|
|
142
151
|
}
|
|
@@ -150,10 +159,10 @@ function gitCommit(issueFile, status) {
|
|
|
150
159
|
execSync(`git commit -m "${commitMessage.replace(/"/g, '\\"')}"`, {
|
|
151
160
|
stdio: 'inherit',
|
|
152
161
|
});
|
|
153
|
-
console.log(
|
|
162
|
+
console.log(`${colors.green}\nGit commit created: ${commitMessage}${colors.reset}`);
|
|
154
163
|
}
|
|
155
164
|
catch (error) {
|
|
156
|
-
console.error(
|
|
165
|
+
console.error(`${colors.red}Failed to create git commit: ${String(error)}${colors.reset}`);
|
|
157
166
|
}
|
|
158
167
|
}
|
|
159
168
|
function getDefaultPromptTemplate() {
|
|
@@ -256,12 +265,12 @@ async function loadOrCreatePromptTemplate(promptFile) {
|
|
|
256
265
|
// If prompt file exists, load it
|
|
257
266
|
try {
|
|
258
267
|
await fs.access(promptFile);
|
|
259
|
-
console.log(
|
|
268
|
+
console.log(`${colors.cyan}Loading prompt template from: ${promptFile}${colors.reset}`);
|
|
260
269
|
return await fs.readFile(promptFile, 'utf-8');
|
|
261
270
|
}
|
|
262
271
|
catch {
|
|
263
272
|
// Otherwise, create the default prompt template
|
|
264
|
-
console.log(
|
|
273
|
+
console.log(`${colors.yellow}Prompt file not found. Creating default template at: ${promptFile}${colors.reset}`);
|
|
265
274
|
const defaultTemplate = getDefaultPromptTemplate();
|
|
266
275
|
// Ensure the directory exists
|
|
267
276
|
const promptDir = path.dirname(promptFile);
|
|
@@ -289,7 +298,7 @@ function buildSystemPrompt(template, issuesDir, faqDir, issueFile) {
|
|
|
289
298
|
}
|
|
290
299
|
function logToolUse(block) {
|
|
291
300
|
process.stdout.write('\n');
|
|
292
|
-
process.stdout.write(
|
|
301
|
+
process.stdout.write(`${colors.cyan}[${block.name}]${colors.reset} `);
|
|
293
302
|
switch (block.name.toLowerCase()) {
|
|
294
303
|
case 'read':
|
|
295
304
|
case 'write':
|
|
@@ -302,18 +311,29 @@ function logToolUse(block) {
|
|
|
302
311
|
case 'glob':
|
|
303
312
|
process.stdout.write(`${block.input?.pattern}`);
|
|
304
313
|
break;
|
|
314
|
+
case 'grep': {
|
|
315
|
+
const pattern = block.input?.pattern;
|
|
316
|
+
const glob = block.input?.glob;
|
|
317
|
+
if (pattern) {
|
|
318
|
+
process.stdout.write(`${pattern}`);
|
|
319
|
+
}
|
|
320
|
+
if (glob) {
|
|
321
|
+
process.stdout.write(` (${glob})`);
|
|
322
|
+
}
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
305
325
|
case 'todowrite': {
|
|
306
326
|
for (const todo of block.input?.todos ?? []) {
|
|
307
327
|
process.stdout.write('\n');
|
|
308
328
|
switch (todo.status) {
|
|
309
329
|
case 'in_progress':
|
|
310
|
-
process.stdout.write(
|
|
330
|
+
process.stdout.write(`${colors.yellow}⧖${colors.reset}`);
|
|
311
331
|
break;
|
|
312
332
|
case 'pending':
|
|
313
333
|
process.stdout.write('☐');
|
|
314
334
|
break;
|
|
315
335
|
case 'completed':
|
|
316
|
-
process.stdout.write(
|
|
336
|
+
process.stdout.write(`${colors.green}✓${colors.reset}`);
|
|
317
337
|
break;
|
|
318
338
|
default:
|
|
319
339
|
process.stdout.write(todo.status);
|
|
@@ -361,7 +381,7 @@ function logSDKMessage(item) {
|
|
|
361
381
|
async function runAgent(options) {
|
|
362
382
|
const { template, issuesDir, faqDir, issueFile, continueMode, continuePrompt } = options;
|
|
363
383
|
const systemPrompt = buildSystemPrompt(template, issuesDir, faqDir, issueFile);
|
|
364
|
-
console.log(
|
|
384
|
+
console.log(`${colors.blue}\n--- Starting agent ---${colors.reset}`);
|
|
365
385
|
const stream = query({
|
|
366
386
|
prompt: continueMode ? continuePrompt : systemPrompt,
|
|
367
387
|
options: {
|
|
@@ -373,12 +393,12 @@ async function runAgent(options) {
|
|
|
373
393
|
for await (const item of stream) {
|
|
374
394
|
logSDKMessage(item);
|
|
375
395
|
}
|
|
376
|
-
console.log(
|
|
396
|
+
console.log(`${colors.blue}\n--- Agent finished ---${colors.reset}`);
|
|
377
397
|
}
|
|
378
398
|
async function main() {
|
|
379
399
|
const config = parseArgs();
|
|
380
|
-
console.log(
|
|
381
|
-
console.log(
|
|
400
|
+
console.log(`${colors.cyan}Bueller? Bueller?${colors.reset}`);
|
|
401
|
+
console.log(`${colors.cyan}-----------------${colors.reset}`);
|
|
382
402
|
console.log(`Issues directory: ${config.issuesDir}`);
|
|
383
403
|
console.log(`FAQ directory: ${config.faqDir}`);
|
|
384
404
|
console.log(`Max iterations: ${config.maxIterations}`);
|
|
@@ -393,10 +413,10 @@ async function main() {
|
|
|
393
413
|
let iteration = 0;
|
|
394
414
|
while (iteration < config.maxIterations) {
|
|
395
415
|
iteration++;
|
|
396
|
-
console.log(
|
|
416
|
+
console.log(`${colors.yellow}\n### Iteration ${iteration} ###${colors.reset}\n`);
|
|
397
417
|
const openIssues = await getOpenIssues(config.issuesDir);
|
|
398
418
|
if (openIssues.length === 0) {
|
|
399
|
-
console.log(
|
|
419
|
+
console.log(`${colors.green}No more issues in open/. Exiting.${colors.reset}`);
|
|
400
420
|
break;
|
|
401
421
|
}
|
|
402
422
|
console.log(`Found ${openIssues.length} open issue(s)`);
|
|
@@ -451,12 +471,12 @@ async function main() {
|
|
|
451
471
|
}
|
|
452
472
|
}
|
|
453
473
|
if (iteration >= config.maxIterations) {
|
|
454
|
-
console.log(
|
|
474
|
+
console.log(`${colors.yellow}\nReached maximum iterations (${config.maxIterations}). Exiting.${colors.reset}`);
|
|
455
475
|
}
|
|
456
|
-
console.log(
|
|
476
|
+
console.log(`${colors.green}\nDone!${colors.reset}`);
|
|
457
477
|
}
|
|
458
478
|
main().catch((error) => {
|
|
459
|
-
console.error(
|
|
479
|
+
console.error(`${colors.red}Error:${colors.reset}`, error);
|
|
460
480
|
process.exit(1);
|
|
461
481
|
});
|
|
462
482
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED