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 +13 -13
- package/dist/mcp/index.js +29 -0
- package/dist/server/index.js +39 -6
- package/dist/skills/queue/SKILL.md +18 -14
- package/dist/ui/assets/{index-DJyujliA.js → index-CX31Dm5z.js} +39 -38
- package/dist/ui/assets/index-D_0VctQP.css +1 -0
- package/dist/ui/index.html +2 -2
- package/package.json +1 -1
- package/dist/ui/assets/index-DtjQcVHJ.css +0 -1
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
|
|
283
|
+
console.log("\u2705 Claude directory exists (~/.claude)");
|
|
284
284
|
} else {
|
|
285
|
-
console.log("\u274C
|
|
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
|
|
300
|
+
console.log("\u2705 MCP server configured in settings.json");
|
|
301
301
|
} else {
|
|
302
|
-
console.log("\u274C
|
|
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
|
|
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
|
|
323
|
+
console.log("\u2705 /queue skill installed");
|
|
324
324
|
} else {
|
|
325
|
-
console.log("\u274C
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
{
|
package/dist/server/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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(
|
|
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 "
|
|
88
|
-
|
|
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
|
-
|
|
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
|
-
|
|
96
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
|
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
|
|
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
|
|