Skip to content

Commit fb85537

Browse files
himattmclaude
andcommitted
Add atq CLI and improve agent configuration docs
- Add `atq` CLI tool for inspecting the queue (list, logs, clear) - Update README with agent compatibility table and Bash allowed rules warning - Add examples/ directory with Claude Code setup guide 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 958248f commit fb85537

6 files changed

Lines changed: 354 additions & 33 deletions

File tree

README.md

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -222,14 +222,16 @@ See [Windsurf MCP docs](https://docs.windsurf.com/windsurf/cascade/mcp) for deta
222222

223223
## Usage
224224

225-
The `run_task` tool is automatically used by agents for heavy operations:
225+
Agents use the `run_task` MCP tool for expensive operations:
226226

227-
**Build Tools:** gradle, gradlew, bazel, make, cmake, mvn, cargo build, go build, npm/yarn/pnpm build
227+
**Build Tools:** gradle, bazel, make, cmake, mvn, cargo build, go build, npm/yarn/pnpm build
228228

229229
**Container Operations:** docker build, docker-compose, podman, kubectl, helm
230230

231231
**Test Suites:** pytest, jest, mocha, rspec
232232

233+
> **Note:** Some agents automatically prefer MCP tools (Amp, Copilot, Windsurf). Others may need [configuration](#agent-configuration-notes) to prefer `run_task` over built-in shell commands.
234+
233235
### Tool Parameters
234236

235237
| Parameter | Required | Description |
@@ -251,34 +253,36 @@ run_task(
251253
)
252254
```
253255

254-
### Note for Claude Code Users
255-
256-
Claude Code defaults to its built-in Bash tool for shell commands. To ensure it uses `run_task` instead, add instructions to your `CLAUDE.md` file.
256+
### Agent Configuration Notes
257257

258-
Add to `~/.claude/CLAUDE.md` (global) or `.claude/CLAUDE.md` (project-specific):
258+
Some agents need additional configuration to use the queue instead of built-in shell commands.
259259

260-
```markdown
261-
## Build Queue
260+
| Agent | Extra Setup | Notes |
261+
|-------|-------------|-------|
262+
| Amp, Copilot, Windsurf | ❌ None | Works out of the box |
263+
| **Claude Code, Cursor** | ✅ Required | Must remove Bash allowed rules |
264+
| Cline, Firebender | ⚠️ Maybe | Check agent docs |
262265

263-
For expensive build commands, ALWAYS use the `run_task` MCP tool instead of Bash.
266+
> [!IMPORTANT]
267+
> **Claude Code users:** If you have allowed rules like `Bash(gradle:*)` or `Bash(./gradlew:*)`, the agent will use Bash directly and **bypass the queue entirely**. You must remove these rules for the queue to work.
268+
>
269+
> Check both `settings.json` and `settings.local.json` (project and global) for rules like:
270+
> - `Bash(gradle:*)`, `Bash(./gradlew:*)`, `Bash(ANDROID_SERIAL=* ./gradlew:*)`
271+
> - `Bash(docker build:*)`, `Bash(pytest:*)`, etc.
272+
>
273+
> See [Claude Code setup guide](examples/claude-code/SETUP.md) for the full fix.
264274
265-
**Commands that MUST use run_task:**
266-
- gradle, gradlew, ./gradlew (any Gradle command)
267-
- bazel, bazelisk
268-
- docker build, docker-compose
269-
- npm run build, yarn build, pnpm build
270-
- pytest, jest, mocha
275+
#### Quick Agent Setup
271276

272-
**How to use:**
273-
- command: The full shell command
274-
- working_directory: Absolute path to the project root
275-
- env_vars: Environment variables like "ANDROID_SERIAL=emulator-5560"
277+
After installing the MCP server, tell your agent:
276278

277-
NEVER run these commands directly via Bash. Always use the run_task MCP tool to prevent resource contention.
279+
```
280+
"Configure agent-task-queue - use examples/<agent-name>/SETUP.md if available"
278281
```
279282

280-
> [!NOTE]
281-
> Other agents like Amp may automatically use MCP tools without additional configuration.
283+
**Available setup guides:**
284+
- [Claude Code setup](examples/claude-code/SETUP.md) - 3-step configuration
285+
- [Other agents](examples/) - Contributions welcome!
282286

283287
## Configuration
284288

@@ -399,28 +403,49 @@ FAILED exit=1 12.5s output=/tmp/agent-task-queue/output/task_9.log
399403

400404
**Manual cleanup**: Use the `clear_task_logs` tool to delete all output files.
401405

402-
## Troubleshooting
406+
## CLI Tool
403407

404-
### "Database is locked" errors
408+
The `atq` command lets you inspect the queue.
409+
410+
### Install CLI
405411

406-
The SQLite database uses WAL mode for concurrency. If you see lock errors:
407412
```bash
408-
ps aux | grep task_queue # Check for zombie processes
409-
rm -rf /tmp/agent-task-queue/ # Delete and restart
413+
uv tool install agent-task-queue
414+
```
415+
416+
This installs both the MCP server and the `atq` CLI persistently.
417+
418+
### Usage
419+
420+
```bash
421+
atq list # Show current queue
422+
atq logs # Show recent activity
423+
atq logs -n 50 # Show last 50 entries
424+
atq clear # Clear stuck tasks
425+
atq --data-dir PATH # Use custom data directory
410426
```
411427

428+
Respects `TASK_QUEUE_DATA_DIR` environment variable.
429+
430+
> **Note:** Without installing, you can run one-off commands with:
431+
> ```bash
432+
> uvx --from agent-task-queue atq list
433+
> ```
434+
435+
## Troubleshooting
436+
412437
### Tasks stuck in queue
413438
414439
```bash
415-
sqlite3 /tmp/agent-task-queue/queue.db "SELECT * FROM queue;" # Check status
416-
sqlite3 /tmp/agent-task-queue/queue.db "DELETE FROM queue;" # Clear all
440+
atq list # Check queue status
441+
atq clear # Clear all tasks
417442
```
418443
419-
### View metrics
444+
### "Database is locked" errors
420445

421446
```bash
422-
cat /tmp/agent-task-queue/agent-task-queue-logs.json | jq . # Pretty print logs
423-
tail -f /tmp/agent-task-queue/agent-task-queue-logs.json # Follow live
447+
ps aux | grep task_queue # Check for zombie processes
448+
rm -rf /tmp/agent-task-queue/ # Delete and restart
424449
```
425450

426451
### Server not connecting

atq.py

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
#!/usr/bin/env python3
2+
"""
3+
atq - Agent Task Queue CLI
4+
5+
Simple CLI to inspect the task queue.
6+
"""
7+
8+
import argparse
9+
import os
10+
import sqlite3
11+
from datetime import datetime
12+
from pathlib import Path
13+
14+
15+
def get_data_dir(args):
16+
"""Get data directory from args or environment."""
17+
if args.data_dir:
18+
return Path(args.data_dir)
19+
return Path(os.environ.get("TASK_QUEUE_DATA_DIR", "/tmp/agent-task-queue"))
20+
21+
22+
def cmd_list(args):
23+
"""List all tasks in the queue."""
24+
data_dir = get_data_dir(args)
25+
db_path = data_dir / "queue.db"
26+
27+
if not db_path.exists():
28+
print(f"No queue database found at {db_path}")
29+
print("Queue is empty (no tasks have been run yet)")
30+
return
31+
32+
conn = sqlite3.connect(db_path, timeout=5.0)
33+
conn.row_factory = sqlite3.Row
34+
35+
try:
36+
rows = conn.execute(
37+
"SELECT * FROM queue ORDER BY queue_name, id"
38+
).fetchall()
39+
40+
if not rows:
41+
print("Queue is empty")
42+
return
43+
44+
# Group by queue name
45+
queues = {}
46+
for row in rows:
47+
qname = row["queue_name"]
48+
if qname not in queues:
49+
queues[qname] = []
50+
queues[qname].append(row)
51+
52+
for qname, tasks in queues.items():
53+
print(f"\n[{qname}] ({len(tasks)} task(s))")
54+
print("-" * 50)
55+
56+
for task in tasks:
57+
status = task["status"].upper()
58+
task_id = task["id"]
59+
pid = task["pid"] or "-"
60+
child_pid = task["child_pid"] or "-"
61+
created = task["created_at"]
62+
63+
# Format timestamp
64+
if created:
65+
try:
66+
dt = datetime.fromisoformat(created)
67+
created = dt.strftime("%H:%M:%S")
68+
except ValueError:
69+
pass
70+
71+
status_icon = "🔄" if status == "RUNNING" else "⏳"
72+
print(f" {status_icon} #{task_id} {status} (pid={pid}, child={child_pid}) @ {created}")
73+
74+
finally:
75+
conn.close()
76+
77+
78+
def cmd_clear(args):
79+
"""Clear all tasks from the queue."""
80+
data_dir = get_data_dir(args)
81+
db_path = data_dir / "queue.db"
82+
83+
if not db_path.exists():
84+
print("No queue database found")
85+
return
86+
87+
conn = sqlite3.connect(db_path, timeout=5.0)
88+
try:
89+
# Check how many tasks exist
90+
count = conn.execute("SELECT COUNT(*) FROM queue").fetchone()[0]
91+
if count == 0:
92+
print("Queue is already empty")
93+
return
94+
95+
response = input(f"Clear {count} task(s) from queue? [y/N] ")
96+
if response.lower() != 'y':
97+
print("Cancelled")
98+
return
99+
100+
cursor = conn.execute("DELETE FROM queue")
101+
conn.commit()
102+
print(f"Cleared {cursor.rowcount} task(s) from queue")
103+
finally:
104+
conn.close()
105+
106+
107+
def cmd_logs(args):
108+
"""Show recent log entries."""
109+
data_dir = get_data_dir(args)
110+
log_path = data_dir / "agent-task-queue-logs.json"
111+
112+
if not log_path.exists():
113+
print(f"No log file found at {log_path}")
114+
return
115+
116+
import json
117+
118+
lines = log_path.read_text().strip().split("\n")
119+
recent = lines[-args.n:] if len(lines) > args.n else lines
120+
121+
for line in recent:
122+
try:
123+
entry = json.loads(line)
124+
ts = entry.get("timestamp", "")[:19].replace("T", " ")
125+
event = entry.get("event", "unknown")
126+
task_id = entry.get("task_id", "")
127+
queue = entry.get("queue_name", "")
128+
129+
# Format based on event type
130+
if event == "task_completed":
131+
exit_code = entry.get("exit_code", "?")
132+
duration = entry.get("duration_seconds", "?")
133+
print(f"{ts} [{queue}] #{task_id} completed exit={exit_code} {duration}s")
134+
elif event == "task_started":
135+
wait = entry.get("wait_time_seconds", 0)
136+
print(f"{ts} [{queue}] #{task_id} started (waited {wait}s)")
137+
elif event == "task_queued":
138+
print(f"{ts} [{queue}] #{task_id} queued")
139+
elif event == "task_timeout":
140+
print(f"{ts} [{queue}] #{task_id} TIMEOUT")
141+
elif event == "task_error":
142+
error = entry.get("error", "?")
143+
print(f"{ts} [{queue}] #{task_id} ERROR: {error}")
144+
elif event == "zombie_cleared":
145+
reason = entry.get("reason", "?")
146+
print(f"{ts} [{queue}] #{task_id} zombie cleared ({reason})")
147+
else:
148+
print(f"{ts} {event}")
149+
except json.JSONDecodeError:
150+
print(line)
151+
152+
153+
def main():
154+
parser = argparse.ArgumentParser(
155+
prog="atq",
156+
description="Agent Task Queue CLI - inspect and manage the task queue",
157+
)
158+
parser.add_argument(
159+
"--data-dir",
160+
help="Data directory (default: $TASK_QUEUE_DATA_DIR or /tmp/agent-task-queue)",
161+
)
162+
163+
subparsers = parser.add_subparsers(dest="command", help="Commands")
164+
165+
# list
166+
subparsers.add_parser("list", help="List tasks in queue")
167+
168+
# clear
169+
subparsers.add_parser("clear", help="Clear all tasks from queue")
170+
171+
# logs
172+
logs_parser = subparsers.add_parser("logs", help="Show recent log entries")
173+
logs_parser.add_argument("-n", type=int, default=20, help="Number of entries (default: 20)")
174+
175+
args = parser.parse_args()
176+
177+
if args.command == "list":
178+
cmd_list(args)
179+
elif args.command == "clear":
180+
cmd_clear(args)
181+
elif args.command == "logs":
182+
cmd_logs(args)
183+
else:
184+
parser.print_help()
185+
186+
187+
if __name__ == "__main__":
188+
main()

examples/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Agent Setup Guides
2+
3+
Some AI coding agents need configuration to prefer MCP tools over built-in shell commands. This directory contains agent-specific setup guides.
4+
5+
## Available Guides
6+
7+
### [Claude Code](claude-code/)
8+
- **SETUP.md** - 3-step configuration guide
9+
- **CLAUDE.md** - Example config snippet
10+
11+
**Why needed:** Claude Code defaults to built-in Bash tool. Configuration ensures it uses `run_task` for expensive operations.
12+
13+
## Agent Compatibility
14+
15+
| Agent | MCP Support | Extra Config Needed? | Status |
16+
|-------|-------------|---------------------|---------|
17+
| Amp | ✅ Yes | ❌ No | Works out of the box |
18+
| Claude Code | ✅ Yes | ✅ Yes | [Setup guide available](claude-code/) |
19+
| Cline | ✅ Yes | ⚠️ Maybe | Needs testing |
20+
| Copilot (VS Code) | ✅ Yes | ❌ No | Works out of the box |
21+
| Cursor | ✅ Yes | ✅ Yes | Setup guide needed |
22+
| Firebender | ✅ Yes | ⚠️ Maybe | Needs testing |
23+
| Windsurf | ✅ Yes | ❌ No | Works out of the box |
24+
25+
## Contributing
26+
27+
To add a setup guide for another agent:
28+
29+
1. **Create directory:** `mkdir examples/<agent-name>`
30+
2. **Add SETUP.md** with clear, agent-readable instructions (keep it under 60 lines)
31+
3. **Include example configs** if needed
32+
4. **Update this README** with agent compatibility info
33+
34+
**Goal:** Agents should be able to follow SETUP.md automatically without human intervention.

examples/claude-code/CLAUDE.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
## Build Queue
2+
3+
For expensive build commands, ALWAYS use the `run_task` MCP tool instead of Bash.
4+
5+
**Commands that MUST use run_task:**
6+
- gradle, bazel, make, cmake, mvn, cargo build, go build
7+
- docker build, docker-compose, kubectl, helm
8+
- npm/yarn/pnpm build, pytest, jest, mocha
9+
10+
**Usage:**
11+
- command: The full shell command
12+
- working_directory: Absolute path to project root
13+
- env_vars: Optional like "KEY=value,KEY2=value2"
14+
15+
NEVER run these via Bash. Always use run_task MCP tool.

0 commit comments

Comments
 (0)