bueller-wheel 0.1.0 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +94 -20
  2. package/dist/index.js +178 -35
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Bueller Wheel
2
2
 
3
- A headless Claude Code issue processor that runs in a loop and resolves issues or tickets written in markdown
3
+ > Life moves pretty fast. If you don't stop and look around once in a while, you could miss it.
4
+
5
+ This is a headless issue processor that runs in a loop and uses Claude to resolve issues or ticket files written in markdown. It plays nicely with Claude Code and uses the sames settings config.
4
6
 
5
7
  ## Quick Start
6
8
 
@@ -10,28 +12,26 @@ mkdir -p issues/open
10
12
  echo "@user: Please create a test file with 'Hello World'" > issues/open/p0-100-my-task.md
11
13
 
12
14
  # Run bueller-wheel to complete the task
13
- npx bueller-wheel
15
+ npx bueller-wheel --run
14
16
  ```
15
17
 
16
18
  ## Why?
17
19
 
18
- Claude Code agents work better when they focus on one thing at a time. Bueller Wheel helps defer context until it's needed through two mechanisms:
19
-
20
- **Issues as directory-based threads** - Each task becomes a reviewable conversation stored as markdown that gets moved along a file-based "kanban" board. This solves a few problems:
21
- - **Code review**: When Claude burns through multiple tasks, it's hard to know what happened. The issues structure creates discrete, reviewable units of work.
22
- - **Iteration without re-prompting**: Markdown files are structured as a back-and-forth between you and Claude, which lets you append follow-ups naturally without starting over.
23
- - **Human-editable prompts**: Storing issues as simple markdown (not JSON or a database) makes it easy to edit, append, or retroactively clean up conversations to improve prompting.
20
+ You want Claude to autonomously work on a large pile of issues while you go on a day trip into the city. Bueller Wheel helps tackle several issues you might encounter:
24
21
 
25
- **FAQ system** - Common mistakes get documented once, then referenced automatically. Instead of repeating corrections or watching Claude make the same errors over and over, capture solutions in FAQ files that the agent checks when stuck.
22
+ - **Claude stops processing after a few issues**: Claude tends to stop processing after completing a few tasks. Bueller Wheel keeps prompting Claude to work until all of the issues have been resolved.
23
+ - **Claude forgets what it's doing**: As Claude uses up its context window, it tends to forget what it was working on. Bueller Wheel runs Claude with a fresh context window and prompt for each issue.
24
+ - **You forget what Claude was doing**: If you successfully get Claude 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.
25
+ - **Claude keeps making the same mistakes**: An agent 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.
26
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.
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**. That said, you can make [parallel branches and agents](#working-with-multiple-branches) work.
28
28
 
29
29
  ## How It Works
30
30
 
31
31
  **The Processing Loop**
32
32
 
33
33
  1. Bueller Wheel finds the next issue in `issues/open/` (sorted alphabetically by filename)
34
- 2. Claude Code reads the issue and works on the task
34
+ 2. Claude reads the issue and works on the task
35
35
  3. Claude appends its work summary to the issue file
36
36
  4. Claude decides the outcome:
37
37
  - **CONTINUE**: Keep working (stays in `open/`)
@@ -45,7 +45,7 @@ Claude Code agents work better when they focus on one thing at a time. Bueller W
45
45
  - Move issues from `review/` or `stuck/` back to `open/` if more work is required
46
46
  - Delete issues from `review/` when done reviewing, or archive them however you want
47
47
 
48
- **Each iteration is a fresh Claude Code session** - no memory between iterations, which keeps context focused.
48
+ **Each iteration is a fresh Claude session** - no memory between iterations, which keeps context focused.
49
49
 
50
50
  **Inherits your project's Claude Code setup** - `bueller-wheel` uses the Anthropic API credential from whichever user you're logged in as. It inherits the same `.claude/settings.json` or `.claude/settings.local.json` as the Claude Code project it's used in. Whatever permissions apply to your regular `claude` CLI should also apply to `bueller-wheel`, with the exception that `bueller-wheel` starts in "accept edits" mode.
51
51
 
@@ -139,18 +139,37 @@ The `open/` directory acts as an inbox for the agent on the current branch. The
139
139
  ## CLI Options
140
140
 
141
141
  ```bash
142
- npx bueller-wheel --issues-dir ./my-issues --faq-dir ./my-faq --max-iterations 50 --git-commit --prompt ./my-prompt.md
142
+ # Start the agent loop with various options
143
+ npx bueller-wheel --run
144
+ npx bueller-wheel --git
145
+ npx bueller-wheel --max 50
146
+ npx bueller-wheel --continue "fix the bug"
147
+
148
+ # Summarize issues
149
+ npx bueller-wheel --summarize p1-003-task.md
150
+ npx bueller-wheel --summarize p1-003.md p2-005.md --index 1
151
+
152
+ # Combine with other options
153
+ npx bueller-wheel --run --issues-dir ./my-issues --faq-dir ./my-faq
154
+ npx bueller-wheel --max 50 --git --prompt ./my-prompt.md
143
155
 
144
156
  # Or if installed globally
145
- bueller-wheel --issues-dir ./my-issues --faq-dir ./my-faq --max-iterations 50 --git-commit --prompt ./my-prompt.md
157
+ bueller-wheel --run
158
+ bueller-wheel --git
146
159
  ```
147
160
 
161
+ **Run Commands** (one required):
162
+ - `--run`: Explicitly start the agent loop with defaults
163
+ - `--git`: Enable automatic git commits and start the loop
164
+ - `--max <number>`: Start with maximum N iterations (default: `25`)
165
+ - `--continue [prompt]`: Continue from previous session. Optional prompt defaults to "continue" if not provided
166
+ - `--summarize <issue...>`: Display abbreviated summaries of issue conversation history
167
+
168
+ **Configuration Options**:
148
169
  - `--issues-dir <path>`: Issues directory (default: `./issues`)
149
170
  - `--faq-dir <path>`: FAQ directory (default: `./faq`)
150
- - `--max-iterations <number>`: Maximum iterations (default: `25`)
151
- - `--git-commit`: Enable automatic git commits after each iteration (default: disabled)
152
171
  - `--prompt <path>`: Path to custom prompt template file (default: `<issues-dir>/prompt.md`)
153
- - `--continue [prompt]`: Continue from previous session. Optional prompt defaults to "continue" if not provided
172
+ - `--index <N>` or `--index <M,N>`: Expand specific messages when using `--summarize` (see below)
154
173
 
155
174
  ### Custom Prompt Templates
156
175
 
@@ -212,7 +231,7 @@ Note that only the immediate prior iteration is continued. The next iteration wi
212
231
 
213
232
  ### Git Auto-Commit
214
233
 
215
- When `--git-commit` is enabled, Bueller Wheel will automatically create a git commit after each iteration where work was done on an issue.
234
+ When `--git` is enabled, Bueller Wheel will automatically create a git commit after each iteration where work was done on an issue.
216
235
 
217
236
  The commit message format includes the issue ID (the filename minus the `.md`) and status:
218
237
  ```
@@ -221,13 +240,68 @@ p0-002-git in progress
221
240
  p0-002-git stuck
222
241
  p0-002-git unknown
223
242
  ```
243
+
244
+ ### Issue Summarization
245
+
246
+ The `--summarize` command provides a quick way to review issue conversation history without opening files. This is especially useful for:
247
+ - Quickly understanding what happened in an issue
248
+ - Reviewing multiple issues at once
249
+ - Checking the status and progress of work
250
+
251
+ **Basic Usage:**
252
+
253
+ ```bash
254
+ # Summarize a single issue (by filename - searches across open/, review/, stuck/)
255
+ npx bueller-wheel --summarize p1-003-task.md
256
+
257
+ # Summarize by partial filename
258
+ npx bueller-wheel --summarize p1-003.md
259
+
260
+ # Summarize with full path
261
+ npx bueller-wheel --summarize /path/to/issues/open/p1-003-task.md
262
+
263
+ # Summarize multiple issues
264
+ npx bueller-wheel --summarize p1-003.md p1-004.md p2-001.md
265
+ ```
266
+
267
+ **Summary Format:**
268
+
269
+ By default, summaries show:
270
+ - Issue filename and status (open/review/stuck)
271
+ - First message: up to 300 characters
272
+ - Middle messages: up to 80 characters each (abbreviated)
273
+ - Last message: up to 300 characters
274
+
275
+ **Expanding Messages:**
276
+
277
+ Use `--index` to expand specific messages to their full content:
278
+
279
+ ```bash
280
+ # Expand a single message at index 2
281
+ npx bueller-wheel --summarize p1-003.md --index 2
282
+
283
+ # Expand a range of messages (indices 1 through 3)
284
+ npx bueller-wheel --summarize p1-003.md --index 1,3
285
+
286
+ # Works with multiple issues (expands same indices for all)
287
+ npx bueller-wheel --summarize p1-003.md p1-004.md --index 0,2
288
+ ```
289
+
290
+ **Note:** Message indices are 0-based (first message is index 0).
291
+
224
292
  ## Development
225
293
 
226
- `pnpm run dev` will execute the current `src/index.ts` script file with whatever args you pass to it.
294
+ `pnpm run dev` will execute the current `src/index.ts` script file with whatever args you pass to it. For example:
295
+
296
+ ```bash
297
+ pnpm run dev -- --run
298
+ pnpm run dev -- --git
299
+ pnpm run dev -- --max 10
300
+ ```
227
301
 
228
302
  ## End-to-End Testing
229
303
 
230
- **These tests use your actual live instance of Claude Code!**
304
+ **These tests use your actual Anthropic / Claude credentials!**
231
305
 
232
306
  ```bash
233
307
  # Run all tests
package/dist/index.js CHANGED
@@ -3,24 +3,42 @@ 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
+ import { expandMessages, formatIssueSummary, resolveIssueReference, summarizeIssue, } from './issue-summarize.js';
7
+ // Colors for output
8
+ const colors = {
9
+ red: '\x1b[0;31m',
10
+ green: '\x1b[0;32m',
11
+ yellow: '\x1b[1;33m',
12
+ blue: '\x1b[0;34m',
13
+ cyan: '\x1b[0;36m',
14
+ reset: '\x1b[0m',
15
+ };
6
16
  const ISSUE_DIR_OPEN = 'open';
7
17
  const ISSUE_DIR_REVIEW = 'review';
8
18
  const ISSUE_DIR_STUCK = 'stuck';
9
19
  function showHelp() {
10
20
  console.log(`
11
- Bueller - Headless Claude Code Issue Processor
21
+ Bueller Wheel - Headless Claude Code Issue Processor
12
22
 
13
23
  USAGE:
14
- bueller [OPTIONS]
24
+ bueller-wheel run [OPTIONS] Start the agent loop
25
+ bueller-wheel issue ISSUE... [OPTIONS] View issue summaries
26
+
27
+ COMMANDS:
28
+ run Start the agent loop to process issues
29
+ issue ISSUE... Summarize one or more issues (accepts file paths or filenames)
15
30
 
16
31
  OPTIONS:
17
32
  --help Show this help message and exit
33
+ --git Enable automatic git commits (on by default, run command only)
34
+ --no-git Disable automatic git commits (run command only)
35
+ --max N Maximum number of iterations to run (default: 25, run command only)
36
+ --continue [PROMPT] Continue from previous session (default prompt: "continue", run command only)
37
+ --index N Expand message at index N (issue command only)
38
+ --index M,N Expand message range from M to N (issue command only)
18
39
  --issues-dir DIR Directory containing issue queue (default: ./issues)
19
40
  --faq-dir DIR Directory containing FAQ/troubleshooting guides (default: ./faq)
20
- --max-iterations N Maximum number of iterations to run (default: 25)
21
- --git, --git-commit Enable automatic git commits after each iteration
22
- --prompt FILE Custom prompt template file (default: ./issues/prompt.md)
23
- --continue [PROMPT] Continue from previous session (default prompt: "continue")
41
+ --prompt FILE Custom prompt template file (default: ./issues/prompt.md, run command only)
24
42
 
25
43
  DIRECTORY STRUCTURE:
26
44
  issues/
@@ -39,10 +57,14 @@ ISSUE FILE FORMAT:
39
57
  p2: Non-blocking follow-up
40
58
 
41
59
  EXAMPLES:
42
- bueller
43
- bueller --issues-dir ./my-issues --faq-dir ./my-faq
44
- bueller --max-iterations 50 --git-commit
45
- bueller --prompt ./custom-prompt.md
60
+ bueller-wheel run
61
+ bueller-wheel run --no-git
62
+ bueller-wheel run --max 50
63
+ bueller-wheel run --continue "fix the bug"
64
+ bueller-wheel run --issues-dir ./my-issues --faq-dir ./my-faq
65
+ bueller-wheel issue p1-003-read-helper-002.md
66
+ bueller-wheel issue p1-003 p2-005 --index 1
67
+ bueller-wheel issue /path/to/issue.md --index 0,2
46
68
 
47
69
  For more information, visit: https://github.com/anthropics/bueller
48
70
  `);
@@ -50,18 +72,52 @@ For more information, visit: https://github.com/anthropics/bueller
50
72
  function parseArgs() {
51
73
  const args = process.argv.slice(2);
52
74
  // Check for help flag first
53
- if (args.includes('--help') || args.includes('-h')) {
75
+ if (args.includes('--help') || args.includes('-h') || args.length === 0) {
54
76
  showHelp();
55
77
  process.exit(0);
56
78
  }
79
+ // First argument should be the command
80
+ const command = args[0];
81
+ if (command !== 'run' && command !== 'issue') {
82
+ console.error(`${colors.red}Error: Unknown command "${command}". Use "run" or "issue".${colors.reset}\n`);
83
+ showHelp();
84
+ process.exit(1);
85
+ }
86
+ // Define recognized flags
87
+ const recognizedFlags = new Set([
88
+ '--issues-dir',
89
+ '--faq-dir',
90
+ '--max',
91
+ '--git',
92
+ '--no-git',
93
+ '--prompt',
94
+ '--continue',
95
+ '--index',
96
+ '--help',
97
+ '-h',
98
+ ]);
99
+ // Check for unrecognized flags
100
+ for (const arg of args.slice(1)) {
101
+ // Check if this looks like a flag (starts with -)
102
+ if (arg.startsWith('-')) {
103
+ if (!recognizedFlags.has(arg)) {
104
+ console.error(`${colors.red}Error: Unrecognized flag: ${arg}${colors.reset}\n`);
105
+ showHelp();
106
+ process.exit(1);
107
+ }
108
+ }
109
+ }
57
110
  let issuesDir = './issues';
58
111
  let faqDir = './faq';
59
112
  let maxIterations = 25;
60
- let gitCommit = false;
113
+ let gitCommit = true;
61
114
  let promptFile = path.join('./issues', 'prompt.md');
62
115
  let continueMode = false;
63
116
  let continuePrompt = 'continue';
64
- for (let i = 0; i < args.length; i++) {
117
+ const issueReferences = [];
118
+ let issueIndex;
119
+ // Parse arguments starting from index 1 (skip the command)
120
+ for (let i = 1; i < args.length; i++) {
65
121
  if (args[i] === '--issues-dir' && i + 1 < args.length) {
66
122
  issuesDir = args[++i];
67
123
  // Update default prompt file location if issues-dir is changed
@@ -72,12 +128,15 @@ function parseArgs() {
72
128
  else if (args[i] === '--faq-dir' && i + 1 < args.length) {
73
129
  faqDir = args[++i];
74
130
  }
75
- else if (args[i] === '--max-iterations' && i + 1 < args.length) {
131
+ else if (args[i] === '--max' && i + 1 < args.length) {
76
132
  maxIterations = parseInt(args[++i], 10);
77
133
  }
78
- else if (args[i] === '--git' || args[i] === '--git-commit') {
134
+ else if (args[i] === '--git') {
79
135
  gitCommit = true;
80
136
  }
137
+ else if (args[i] === '--no-git') {
138
+ gitCommit = false;
139
+ }
81
140
  else if (args[i] === '--prompt' && i + 1 < args.length) {
82
141
  promptFile = args[++i];
83
142
  }
@@ -88,6 +147,21 @@ function parseArgs() {
88
147
  continuePrompt = args[++i];
89
148
  }
90
149
  }
150
+ else if (args[i] === '--index' && i + 1 < args.length) {
151
+ issueIndex = args[++i];
152
+ }
153
+ else if (!args[i].startsWith('--')) {
154
+ // Non-flag argument - collect as issue reference for issue command
155
+ if (command === 'issue') {
156
+ issueReferences.push(args[i]);
157
+ }
158
+ }
159
+ }
160
+ // Validate issue command
161
+ if (command === 'issue' && issueReferences.length === 0) {
162
+ console.error(`${colors.red}Error: "issue" command requires at least one issue reference.${colors.reset}\n`);
163
+ showHelp();
164
+ process.exit(1);
91
165
  }
92
166
  return {
93
167
  issuesDir,
@@ -97,6 +171,9 @@ function parseArgs() {
97
171
  promptFile,
98
172
  continueMode,
99
173
  continuePrompt,
174
+ command: command,
175
+ issueReferences,
176
+ issueIndex,
100
177
  };
101
178
  }
102
179
  async function ensureDirectories(issuesDir, faqDir) {
@@ -136,7 +213,7 @@ function gitCommit(issueFile, status) {
136
213
  encoding: 'utf-8',
137
214
  }).trim();
138
215
  if (!untrackedFiles) {
139
- console.log('No changes to commit');
216
+ console.log(`${colors.cyan}No changes to commit${colors.reset}`);
140
217
  return;
141
218
  }
142
219
  }
@@ -150,10 +227,10 @@ function gitCommit(issueFile, status) {
150
227
  execSync(`git commit -m "${commitMessage.replace(/"/g, '\\"')}"`, {
151
228
  stdio: 'inherit',
152
229
  });
153
- console.log(`\nGit commit created: ${commitMessage}`);
230
+ console.log(`${colors.green}\nGit commit created: ${commitMessage}${colors.reset}`);
154
231
  }
155
232
  catch (error) {
156
- console.error(`Failed to create git commit: ${String(error)}`);
233
+ console.error(`${colors.red}Failed to create git commit: ${String(error)}${colors.reset}`);
157
234
  }
158
235
  }
159
236
  function getDefaultPromptTemplate() {
@@ -205,9 +282,16 @@ Here is a summary of the work I have done:
205
282
 
206
283
  Your issue file: [ISSUE_FILE_PATH]
207
284
 
285
+ Issue files may be long. Use CLI commands to read:
286
+ - To summarize: \`npx bueller-wheel issue [ISSUE_FILE_PATH]\`
287
+ - To expand: \`npx bueller-wheel issue [ISSUE_FILE_PATH] --index <start>,<end>\`
288
+
208
289
  1. **Read the issue**: Parse the conversation history in [ISSUE_FILE_PATH] to understand the task
209
290
  2. **Work on the task**: Do what the issue requests. When encountering issues, always check for a relevant guide in [FAQ_DIR]/ first.
210
- 3. **Append your response**: Add your summary to [ISSUE_FILE_PATH] using this format:
291
+ 3. **Verify**: Verify the following pass:
292
+ - [ ] \`pnpm run lint:fix\`
293
+ - [ ] \`pnpm run typecheck\`
294
+ 4. **Append your response**: Add your summary to [ISSUE_FILE_PATH] using this format:
211
295
  \`\`\`
212
296
  ---
213
297
 
@@ -219,7 +303,7 @@ Your issue file: [ISSUE_FILE_PATH]
219
303
  - Item 3
220
304
  \`\`\`
221
305
 
222
- 4. **Decide the outcome**: Choose ONE of the following actions:
306
+ 5. **Decide the outcome**: Choose ONE of the following actions:
223
307
 
224
308
  a. **CONTINUE** - You made progress but the task isn't complete yet
225
309
  - Leave the issue in \`[ISSUES_DIR]/[ISSUE_DIR_OPEN]/\` for the next iteration
@@ -250,18 +334,22 @@ Your issue file: [ISSUE_FILE_PATH]
250
334
 
251
335
  **Critical:** ALWAYS check the FAQ directory ([FAQ_DIR]/) to see if there is a guide when you encounter a problem.
252
336
 
337
+ ## Adding to the FAQ
338
+
339
+ Consider adding a **CONCISE** FAQ in [FAQ_DIR]/ for non-obvious solutions, recurring issues, or multi-step troubleshooting that would help future agents. Skip trivial/one-off problems or topics already documented.
340
+
253
341
  Now, please process the issue at [ISSUE_FILE_PATH].`;
254
342
  }
255
343
  async function loadOrCreatePromptTemplate(promptFile) {
256
344
  // If prompt file exists, load it
257
345
  try {
258
346
  await fs.access(promptFile);
259
- console.log(`Loading prompt template from: ${promptFile}`);
347
+ console.log(`${colors.cyan}Loading prompt template from: ${promptFile}${colors.reset}`);
260
348
  return await fs.readFile(promptFile, 'utf-8');
261
349
  }
262
350
  catch {
263
351
  // Otherwise, create the default prompt template
264
- console.log(`Prompt file not found. Creating default template at: ${promptFile}`);
352
+ console.log(`${colors.yellow}Prompt file not found. Creating default template at: ${promptFile}${colors.reset}`);
265
353
  const defaultTemplate = getDefaultPromptTemplate();
266
354
  // Ensure the directory exists
267
355
  const promptDir = path.dirname(promptFile);
@@ -289,7 +377,7 @@ function buildSystemPrompt(template, issuesDir, faqDir, issueFile) {
289
377
  }
290
378
  function logToolUse(block) {
291
379
  process.stdout.write('\n');
292
- process.stdout.write(`[${block.name}] `);
380
+ process.stdout.write(`${colors.cyan}[${block.name}]${colors.reset} `);
293
381
  switch (block.name.toLowerCase()) {
294
382
  case 'read':
295
383
  case 'write':
@@ -302,18 +390,33 @@ function logToolUse(block) {
302
390
  case 'glob':
303
391
  process.stdout.write(`${block.input?.pattern}`);
304
392
  break;
393
+ case 'grep': {
394
+ const pattern = block.input?.pattern;
395
+ const glob = block.input?.glob;
396
+ const path = block.input?.path;
397
+ if (pattern) {
398
+ process.stdout.write(`${pattern}`);
399
+ }
400
+ if (glob) {
401
+ process.stdout.write(` (${glob})`);
402
+ }
403
+ if (path) {
404
+ process.stdout.write(` (${path})`);
405
+ }
406
+ break;
407
+ }
305
408
  case 'todowrite': {
306
409
  for (const todo of block.input?.todos ?? []) {
307
410
  process.stdout.write('\n');
308
411
  switch (todo.status) {
309
412
  case 'in_progress':
310
- process.stdout.write('⧖');
413
+ process.stdout.write(`${colors.yellow}⧖${colors.reset}`);
311
414
  break;
312
415
  case 'pending':
313
416
  process.stdout.write('☐');
314
417
  break;
315
418
  case 'completed':
316
- process.stdout.write('✓');
419
+ process.stdout.write(`${colors.green}✓${colors.reset}`);
317
420
  break;
318
421
  default:
319
422
  process.stdout.write(todo.status);
@@ -361,24 +464,62 @@ function logSDKMessage(item) {
361
464
  async function runAgent(options) {
362
465
  const { template, issuesDir, faqDir, issueFile, continueMode, continuePrompt } = options;
363
466
  const systemPrompt = buildSystemPrompt(template, issuesDir, faqDir, issueFile);
364
- console.log('\n--- Starting agent ---');
467
+ console.log(`${colors.blue}\n--- Starting agent ---${colors.reset}`);
365
468
  const stream = query({
366
469
  prompt: continueMode ? continuePrompt : systemPrompt,
367
470
  options: {
368
471
  settingSources: ['local', 'project', 'user'],
369
472
  permissionMode: 'acceptEdits',
370
473
  continue: continueMode,
474
+ canUseTool: async (toolName, input) => {
475
+ console.log(`${colors.red}Auto-denied tool:${colors.reset} ${toolName} ${String(input?.['command'] ?? input?.['file_path'] ?? '')}`);
476
+ return {
477
+ behavior: 'deny',
478
+ message: `You are running autonomously. The user cannot grant permission. Find a workaround or mark the issue as stuck. Check ${faqDir}/ to see if this is covered.`,
479
+ };
480
+ },
371
481
  },
372
482
  });
373
483
  for await (const item of stream) {
374
484
  logSDKMessage(item);
375
485
  }
376
- console.log('\n--- Agent finished ---');
486
+ console.log(`${colors.blue}\n--- Agent finished ---${colors.reset}`);
487
+ }
488
+ async function runIssue(config) {
489
+ for (const issueRef of config.issueReferences) {
490
+ // Normalize issue reference - add .md extension if missing
491
+ let normalizedRef = issueRef;
492
+ if (!issueRef.endsWith('.md')) {
493
+ normalizedRef = `${issueRef}.md`;
494
+ }
495
+ const located = await resolveIssueReference(normalizedRef, config.issuesDir);
496
+ if (!located) {
497
+ console.error(`${colors.red}Error: Could not find issue: ${issueRef}${colors.reset}\n`);
498
+ continue;
499
+ }
500
+ try {
501
+ let summary = await summarizeIssue(located);
502
+ // Apply index expansion if specified
503
+ if (config.issueIndex) {
504
+ summary = expandMessages(summary, config.issueIndex);
505
+ }
506
+ const formatted = formatIssueSummary(summary, config.issueIndex);
507
+ console.log(formatted);
508
+ }
509
+ catch (error) {
510
+ console.error(`${colors.red}Error summarizing ${issueRef}: ${String(error)}${colors.reset}\n`);
511
+ }
512
+ }
377
513
  }
378
514
  async function main() {
379
515
  const config = parseArgs();
380
- console.log('Bueller? Bueller?');
381
- console.log('-----------------');
516
+ // Handle issue command
517
+ if (config.command === 'issue') {
518
+ await runIssue(config);
519
+ return;
520
+ }
521
+ console.log(`${colors.cyan}Bueller? Bueller?${colors.reset}`);
522
+ console.log(`${colors.cyan}-----------------${colors.reset}`);
382
523
  console.log(`Issues directory: ${config.issuesDir}`);
383
524
  console.log(`FAQ directory: ${config.faqDir}`);
384
525
  console.log(`Max iterations: ${config.maxIterations}`);
@@ -393,21 +534,23 @@ async function main() {
393
534
  let iteration = 0;
394
535
  while (iteration < config.maxIterations) {
395
536
  iteration++;
396
- console.log(`\n### Iteration ${iteration} ###\n`);
537
+ console.log(`${colors.yellow}\n### Iteration ${iteration} ###${colors.reset}\n`);
397
538
  const openIssues = await getOpenIssues(config.issuesDir);
398
539
  if (openIssues.length === 0) {
399
- console.log('No more issues in open/. Exiting.');
540
+ console.log(`${colors.green}No more issues in open/. Exiting.${colors.reset}`);
400
541
  break;
401
542
  }
402
543
  console.log(`Found ${openIssues.length} open issue(s)`);
403
544
  console.log(`Next issue: ${openIssues[0]}`);
404
545
  const currentIssue = openIssues[0];
546
+ // Only use continue mode on the first iteration
547
+ const isFirstIteration = iteration === 1;
405
548
  await runAgent({
406
549
  template: promptTemplate,
407
550
  issuesDir: config.issuesDir,
408
551
  faqDir: config.faqDir,
409
552
  issueFile: currentIssue,
410
- continueMode: config.continueMode,
553
+ continueMode: config.continueMode && isFirstIteration,
411
554
  continuePrompt: config.continuePrompt,
412
555
  });
413
556
  // Auto-commit if enabled and there's a current issue
@@ -451,12 +594,12 @@ async function main() {
451
594
  }
452
595
  }
453
596
  if (iteration >= config.maxIterations) {
454
- console.log(`\nReached maximum iterations (${config.maxIterations}). Exiting.`);
597
+ console.log(`${colors.yellow}\nReached maximum iterations (${config.maxIterations}). Exiting.${colors.reset}`);
455
598
  }
456
- console.log('\nDone!');
599
+ console.log(`${colors.green}\nDone!${colors.reset}`);
457
600
  }
458
601
  main().catch((error) => {
459
- console.error('Error:', error);
602
+ console.error(`${colors.red}Error:${colors.reset}`, error);
460
603
  process.exit(1);
461
604
  });
462
605
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bueller-wheel",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Headless Claude Code issue processor - A wrapper that runs Claude Code in a loop to process issues from a directory queue",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",