claude-queue 1.0.4 → 1.0.6

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/dist/cli.js CHANGED
@@ -280,9 +280,9 @@ async function runDoctor(port, fix) {
280
280
  let warnings = 0;
281
281
  const claudeDirExists = existsSync3(CLAUDE_DIR);
282
282
  if (claudeDirExists) {
283
- console.log("\u2705 Claude directory exists (~/.claude)");
283
+ console.log("\u2705 Claude directory exists (~/.claude)");
284
284
  } else {
285
- console.log("\u274C Claude directory not found (~/.claude)");
285
+ console.log("\u274C Claude directory not found (~/.claude)");
286
286
  issues++;
287
287
  if (fix) {
288
288
  mkdirSync3(CLAUDE_DIR, { recursive: true });
@@ -297,9 +297,9 @@ async function runDoctor(port, fix) {
297
297
  const mcpServers = settings.mcpServers || {};
298
298
  mcpConfigured = !!mcpServers["claude-queue"];
299
299
  if (mcpConfigured) {
300
- console.log("\u2705 MCP server configured in settings.json");
300
+ console.log("\u2705 MCP server configured in settings.json");
301
301
  } else {
302
- console.log("\u274C MCP server not configured");
302
+ console.log("\u274C MCP server not configured");
303
303
  issues++;
304
304
  if (fix) {
305
305
  await configureMcp();
@@ -311,7 +311,7 @@ async function runDoctor(port, fix) {
311
311
  warnings++;
312
312
  }
313
313
  } else {
314
- console.log("\u274C settings.json not found");
314
+ console.log("\u274C settings.json not found");
315
315
  issues++;
316
316
  if (fix) {
317
317
  await configureMcp();
@@ -320,9 +320,9 @@ async function runDoctor(port, fix) {
320
320
  }
321
321
  const skillPath = join4(SKILLS_DIR, "queue", "SKILL.md");
322
322
  if (existsSync3(skillPath)) {
323
- console.log("\u2705 /queue skill installed");
323
+ console.log("\u2705 /queue skill installed");
324
324
  } else {
325
- console.log("\u274C /queue skill not installed");
325
+ console.log("\u274C /queue skill not installed");
326
326
  issues++;
327
327
  if (fix) {
328
328
  installSkills();
@@ -330,7 +330,7 @@ async function runDoctor(port, fix) {
330
330
  }
331
331
  }
332
332
  if (existsSync3(KANBAN_DIR)) {
333
- console.log("\u2705 Queue data directory exists (~/.claude-queue)");
333
+ console.log("\u2705 Queue data directory exists (~/.claude-queue)");
334
334
  } else {
335
335
  console.log("\u26A0\uFE0F Queue data directory not created yet");
336
336
  warnings++;
@@ -339,14 +339,14 @@ async function runDoctor(port, fix) {
339
339
  if (existsSync3(dbPath)) {
340
340
  const stats = await import("fs/promises").then((fs) => fs.stat(dbPath));
341
341
  const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
342
- console.log(`\u2705 Database exists (${sizeMB} MB)`);
342
+ console.log(`\u2705 Database exists (${sizeMB} MB)`);
343
343
  } else {
344
344
  console.log("\u26A0\uFE0F Database not created yet (will be created on first run)");
345
345
  warnings++;
346
346
  }
347
347
  const serverRunning = await isServerRunning(port);
348
348
  if (serverRunning) {
349
- console.log(`\u2705 Server running on port ${port}`);
349
+ console.log(`\u2705 Server running on port ${port}`);
350
350
  try {
351
351
  const response = await fetch(`http://localhost:${port}/api/maintenance/stats`);
352
352
  if (response.ok) {
@@ -361,7 +361,7 @@ async function runDoctor(port, fix) {
361
361
  }
362
362
  const pid = getRunningPid();
363
363
  if (pid) {
364
- console.log(`\u2705 PID file valid (${pid})`);
364
+ console.log(`\u2705 PID file valid (${pid})`);
365
365
  } else if (existsSync3(join4(KANBAN_DIR, "server.pid"))) {
366
366
  console.log("\u26A0\uFE0F Stale PID file found");
367
367
  warnings++;
@@ -372,12 +372,12 @@ async function runDoctor(port, fix) {
372
372
  }
373
373
  console.log("\n" + "\u2500".repeat(40));
374
374
  if (issues === 0 && warnings === 0) {
375
- console.log("\u2705 All checks passed! Your setup looks good.\n");
375
+ console.log("\u2705 All checks passed! Your setup looks good.\n");
376
376
  } else if (issues === 0) {
377
377
  console.log(`\u26A0\uFE0F ${warnings} warning(s), but no critical issues.
378
378
  `);
379
379
  } else {
380
- console.log(`\u274C ${issues} issue(s) found, ${warnings} warning(s).
380
+ console.log(`\u274C ${issues} issue(s) found, ${warnings} warning(s).
381
381
  `);
382
382
  if (!fix) {
383
383
  console.log("Run with --fix to attempt automatic fixes:");
package/dist/mcp/index.js CHANGED
@@ -166,6 +166,22 @@ async function handleCompleteTask(args) {
166
166
  ]
167
167
  };
168
168
  }
169
+ async function handleMoveTask(args) {
170
+ const taskId = args?.taskId;
171
+ const status = args?.status;
172
+ const task = await httpPost(`/api/tasks/${taskId}/move`, {
173
+ status,
174
+ position: 0
175
+ });
176
+ return {
177
+ content: [
178
+ {
179
+ type: "text",
180
+ text: `Task "${task.title}" moved to ${status}`
181
+ }
182
+ ]
183
+ };
184
+ }
169
185
 
170
186
  // src/handlers/create-task.ts
171
187
  async function handleCreateTask(args) {
@@ -466,6 +482,19 @@ server.registerTool(
466
482
  return await handleCompleteTask(args);
467
483
  }
468
484
  );
485
+ server.registerTool(
486
+ "queue_move_task",
487
+ {
488
+ description: "Move a task to a different status column (backlog, ready, in_progress, or done). Use this when a task is cancelled or needs to be deferred.",
489
+ inputSchema: {
490
+ taskId: z.string().describe("Task ID"),
491
+ status: z.enum(["backlog", "ready", "in_progress", "done"]).describe("Target status to move the task to")
492
+ }
493
+ },
494
+ async (args) => {
495
+ return await handleMoveTask(args);
496
+ }
497
+ );
469
498
  server.registerTool(
470
499
  "queue_check_comments",
471
500
  {
@@ -400,10 +400,11 @@ function logTaskActivity(taskId, type, oldValue = null, newValue = null) {
400
400
  }
401
401
 
402
402
  // src/utils/mappers.ts
403
- function rowToTask(row) {
403
+ function rowToTask(row, pendingAction = null) {
404
404
  return {
405
405
  ...row,
406
- blocked: Boolean(row.blocked)
406
+ blocked: Boolean(row.blocked),
407
+ pending_action: pendingAction
407
408
  };
408
409
  }
409
410
  function rowToComment(row) {
@@ -441,7 +442,25 @@ router2.get("/project/:projectId", (req, res) => {
441
442
  }
442
443
  query += " ORDER BY position ASC";
443
444
  const tasks = db2.prepare(query).all(...params);
444
- res.json(tasks.map(rowToTask));
445
+ const pendingActionsQuery = db2.prepare(`
446
+ SELECT task_id, content FROM comments
447
+ WHERE task_id IN (${tasks.map(() => "?").join(",")})
448
+ AND author = 'user'
449
+ AND seen = 0
450
+ AND (content LIKE '%[ACTION:CANCEL]%' OR content LIKE '%[ACTION:RESET]%')
451
+ `);
452
+ const pendingActions = {};
453
+ if (tasks.length > 0) {
454
+ const actionComments = pendingActionsQuery.all(...tasks.map((t) => t.id));
455
+ for (const comment of actionComments) {
456
+ if (comment.content.includes("[ACTION:CANCEL]")) {
457
+ pendingActions[comment.task_id] = "cancel";
458
+ } else if (comment.content.includes("[ACTION:RESET]")) {
459
+ pendingActions[comment.task_id] = "reset";
460
+ }
461
+ }
462
+ }
463
+ res.json(tasks.map((task) => rowToTask(task, pendingActions[task.id] || null)));
445
464
  });
446
465
  router2.get("/:id", (req, res) => {
447
466
  const db2 = getDb();
@@ -452,8 +471,15 @@ router2.get("/:id", (req, res) => {
452
471
  }
453
472
  const commentsRaw = db2.prepare("SELECT * FROM comments WHERE task_id = ? ORDER BY created_at ASC").all(req.params.id);
454
473
  const activitiesRaw = db2.prepare("SELECT * FROM task_activity WHERE task_id = ? ORDER BY created_at ASC").all(req.params.id);
474
+ let pendingAction = null;
475
+ const unseenActionComment = commentsRaw.find(
476
+ (c) => c.author === "user" && !c.seen && (c.content.includes("[ACTION:CANCEL]") || c.content.includes("[ACTION:RESET]"))
477
+ );
478
+ if (unseenActionComment) {
479
+ pendingAction = unseenActionComment.content.includes("[ACTION:CANCEL]") ? "cancel" : "reset";
480
+ }
455
481
  const result = {
456
- ...rowToTask(task),
482
+ ...rowToTask(task, pendingAction),
457
483
  comments: commentsRaw.map(rowToComment),
458
484
  activities: activitiesRaw.map(rowToTaskActivity)
459
485
  };
@@ -1273,10 +1299,17 @@ function createServer(port = 3333) {
1273
1299
  ];
1274
1300
  const uiPath = uiPaths.find((p) => existsSync4(join5(p, "index.html")));
1275
1301
  if (uiPath) {
1276
- app.use(express.static(uiPath));
1302
+ const indexPath = join5(uiPath, "index.html");
1303
+ app.use(express.static(uiPath, { dotfiles: "allow" }));
1277
1304
  app.get("/{*splat}", (_req, res) => {
1278
- res.sendFile(join5(uiPath, "index.html"));
1305
+ res.sendFile(indexPath, { dotfiles: "allow" }, (err) => {
1306
+ if (err && !res.headersSent) {
1307
+ res.status(404).send("Not found");
1308
+ }
1309
+ });
1279
1310
  });
1311
+ } else {
1312
+ log(`Warning: UI not found. Checked paths: ${uiPaths.join(", ")}`);
1280
1313
  }
1281
1314
  }
1282
1315
  const server = app.listen(port, () => {
@@ -84,31 +84,35 @@ Work through tasks autonomously.
84
84
 
85
85
  Repeat continuously:
86
86
 
87
- 1. **Check for tasks**: Call `queue_get_tasks` with status "ready"
88
- 2. **If no ready tasks**:
87
+ 1. **Check for in-progress tasks first**: Call `queue_get_tasks` with status "in_progress"
88
+ - If there's already an in-progress task, this means you were previously working on it (possibly before context compression/summarization)
89
+ - Resume working on that task from step 6 (don't claim it again - it's already claimed)
90
+ - The task's `starting_commit` field contains the commit hash from when it was claimed
91
+ 2. **Check for ready tasks**: If no in-progress tasks, call `queue_get_tasks` with status "ready"
92
+ 3. **If no ready tasks**:
89
93
  - Poll every 30 seconds for up to 3 minutes (6 polls)
90
94
  - If still no tasks after 3 minutes, inform user and stop
91
- 3. **Claim task immediately**: When a ready task is found, IMMEDIATELY claim it to prevent the user from making changes while you prepare:
95
+ 4. **Claim task immediately**: When a ready task is found, IMMEDIATELY claim it to prevent the user from making changes while you prepare:
92
96
  - Run `git rev-parse --is-inside-work-tree 2>/dev/null && git rev-parse HEAD` in a single command to check if git repo and get commit hash
93
97
  - Call `queue_claim_task` with the task ID AND the starting_commit (use empty string "" if not a git repo)
94
98
  - This moves the task to in_progress right away so user sees it's being worked on
95
- 4. **Read existing comments**: Call `queue_check_comments` to see any context or instructions the user may have already added
96
- 5. **Work on the task**:
99
+ 5. **Read existing comments**: Call `queue_check_comments` to see any context or instructions the user may have already added
100
+ 6. **Work on the task**:
97
101
  - Update activity with `queue_update_activity` as you work
98
102
  - Do the actual work - write code, fix bugs, etc.
99
103
  - **Check for user feedback**: Call `queue_check_comments` periodically (before major steps) to see if user left new comments
100
104
  - If there are new comments, read them and accommodate the feedback in your work
101
- 6. **If blocked**:
105
+ 7. **If blocked**:
102
106
  - Call `queue_set_blocked` with your question
103
107
  - Call `queue_wait_for_reply` to wait for response
104
- - If `{ "deleted": true }` and this is a git repo, run `git reset --hard <starting_commit>` (the commit you passed in step 3) and go to step 1
108
+ - If `{ "deleted": true }` and this is a git repo, run `git reset --hard <starting_commit>` (the commit from step 4 or from the task's `starting_commit` field) and go to step 1
105
109
  - If `{ "deleted": true }` and NOT a git repo, just go to step 1 (changes cannot be auto-reverted)
106
110
  - If `{ "timeout": true }`, call `queue_wait_for_reply` again
107
- 7. **Final check**: Before completing, call `queue_check_comments` one last time to ensure no new feedback was left during your work
108
- 8. **Commit** (git repos only): Commit changes before marking the task as complete
109
- 9. **Add summary** (REQUIRED): ALWAYS add a completion summary using `queue_add_comment` BEFORE calling complete. Example: "✅ Completed: Added X feature to Y component. Modified files: A.ts, B.tsx. Key changes: implemented Z logic."
110
- 10. **Complete**: Call `queue_complete_task` to move the task to Done (the comment from step 9 must already be added)
111
- 11. **Repeat** from step 1
111
+ 8. **Final check**: Before completing, call `queue_check_comments` one last time to ensure no new feedback was left during your work
112
+ 9. **Commit** (git repos only): Commit changes before marking the task as complete
113
+ 10. **Add summary** (REQUIRED): ALWAYS add a completion summary using `queue_add_comment` BEFORE calling complete. Example: "✅ Completed: Added X feature to Y component. Modified files: A.ts, B.tsx. Key changes: implemented Z logic."
114
+ 11. **Complete**: Call `queue_complete_task` to move the task to Done (the comment from step 10 must already be added)
115
+ 12. **Repeat** from step 1
112
116
 
113
117
  ## Rules
114
118
 
@@ -147,9 +151,9 @@ This ensures the user gets a chance to review and respond to your suggestions be
147
151
  User can trigger actions via the UI that leave special comments. When checking comments, look for these patterns:
148
152
 
149
153
  - `[ACTION:RESET]` - User wants to reset all changes. If in a git repo, run `git reset --hard <starting_commit>` to undo all changes including commits, then start the task fresh from the beginning. If not in a git repo, just start fresh (previous changes cannot be auto-reverted).
150
- - `[ACTION:CANCEL]` - User wants to cancel the task. If in a git repo, run `git reset --hard <starting_commit>` to undo all changes including commits. Then move the task to backlog status using the move API. If not in a git repo, just move to backlog (changes cannot be auto-reverted).
154
+ - `[ACTION:CANCEL]` - User wants to cancel the task. If in a git repo, run `git reset --hard <starting_commit>` to undo all changes including commits. Then call `queue_move_task` with status "backlog" to move the task to backlog. If not in a git repo, just call `queue_move_task` to move to backlog (changes cannot be auto-reverted).
151
155
 
152
- **Note**: The `starting_commit` is stored in the task when you claim it (step 3). For in-progress tasks, it's also returned by `queue_get_tasks` in the task's `starting_commit` field. This will be empty for non-git directories.
156
+ **Note**: The `starting_commit` is stored in the task when you claim it (step 4). For in-progress tasks, it's also returned by `queue_get_tasks` in the task's `starting_commit` field. This will be empty for non-git directories.
153
157
 
154
158
  ## Note
155
159