From bb5fa2880f8ee81e02c3cb67675abdf7da3df57d Mon Sep 17 00:00:00 2001 From: lucaronin Date: Mon, 30 Mar 2026 17:28:21 +0200 Subject: [PATCH] feat: add /laputa-next-task and /laputa-done slash commands, clean CLAUDE.md --- .claude/commands/laputa-done.md | 28 +++++++++++ .claude/commands/laputa-next-task.md | 34 +++++++++++++ CLAUDE.md | 75 ++++------------------------ 3 files changed, 73 insertions(+), 64 deletions(-) create mode 100644 .claude/commands/laputa-done.md create mode 100644 .claude/commands/laputa-next-task.md diff --git a/.claude/commands/laputa-done.md b/.claude/commands/laputa-done.md new file mode 100644 index 00000000..16ccee4e --- /dev/null +++ b/.claude/commands/laputa-done.md @@ -0,0 +1,28 @@ +# /laputa-done + +Mark a Laputa task as done: move to In Review, notify Brian, then self-dispatch the next task. + +Run this after Phase 1 (Playwright) and Phase 2 (native app QA) both pass. + +## Steps + +**1. Move task to In Review** + +```bash +curl -s -X POST "https://api.todoist.com/api/v1/tasks/$ARGUMENTS/move" \ + -H "Authorization: Bearer $TODOIST_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"section_id": "6g3XjX33FF4Vj86M"}' +``` + +**2. Notify Brian** + +```bash +openclaw system event --text "laputa-task-done:$ARGUMENTS" --mode now +``` + +**3. Pick the next task** + +Run `/laputa-next-task` to get the next task and start working on it immediately. + +If `/laputa-next-task` returns `NO_TASKS` → exit cleanly. The hourly watchdog will restart you when new tasks arrive. diff --git a/.claude/commands/laputa-next-task.md b/.claude/commands/laputa-next-task.md new file mode 100644 index 00000000..cbd1f77a --- /dev/null +++ b/.claude/commands/laputa-next-task.md @@ -0,0 +1,34 @@ +# /laputa-next-task + +Pick the next Laputa task from Todoist and move it to In Progress. + +Priority order: **To Rework** first, then **Open** (sorted by Todoist priority p1→p4). + +## Steps + +1. Fetch tasks from To Rework (`6g6QqvR9rRpvJWvv`), then Open (`6g3XjWR832hVHhCM`) +2. Sort each section by priority (p1 highest) +3. Take the first available task +4. Move it to In Progress (`6g3XjWjfmJFcGgHM`) via Todoist API: + +```bash +curl -s -X POST "https://api.todoist.com/api/v1/tasks//move" \ + -H "Authorization: Bearer $TODOIST_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"section_id": "6g3XjWjfmJFcGgHM"}' +``` + +5. Fetch the full task details (description, comments) from Todoist: + +```bash +curl -s "https://api.todoist.com/api/v1/tasks/" \ + -H "Authorization: Bearer $TODOIST_API_KEY" + +curl -s "https://api.todoist.com/api/v1/comments?task_id=" \ + -H "Authorization: Bearer $TODOIST_API_KEY" +``` + +6. For To Rework tasks: read the ❌ QA failed comment — it tells you exactly what to fix +7. Output: task ID, title, and full description so you can start working immediately + +If no tasks are available in either section → output `NO_TASKS` and exit cleanly. diff --git a/CLAUDE.md b/CLAUDE.md index c9d95ee5..2a94195d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,13 +10,11 @@ This is how you pick up and complete a task. Follow this order every time. ### 1a. Pick up a task -Tasks come from Todoist (project `6g3XjQFwv9V8Pxfv`). Priority order: -1. **To Rework** (`6g6QqvR9rRpvJWvv`) — fix failed tasks before starting new ones -2. **Open** (`6g3XjWR832hVHhCM`) — sorted by Todoist priority (p1 → p4) +Run `/laputa-next-task` — it fetches the next task from Todoist (To Rework first, then Open), moves it to In Progress, and returns the full description. When starting a task: -- Read the task description fully -- For To Rework: read the ❌ QA failed comment — it tells you exactly what to fix +- Read the task description and comments fully +- For To Rework: the ❌ QA failed comment tells you exactly what to fix - Check `docs/adr/` for relevant architecture decisions before making structural choices ### 1b. Implement @@ -28,68 +26,17 @@ When starting a task: ### 1c. When done — three mandatory steps -**Step 1: Move task to In Review** -```bash -curl -s -X POST "https://api.todoist.com/api/v1/tasks//move" \ - -H "Authorization: Bearer $TODOIST_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{"section_id": "6g3XjX33FF4Vj86M"}' +**Step 1: Move task to In### 1c. When done + +After Phase 1 (Playwright) and Phase 2 (native QA) both pass, run: + +``` +/laputa-done ``` -**Step 2: Notify Brian** -```bash -openclaw system event --text "laputa-task-done:" --mode now -``` +This moves the task to In Review, notifies Brian, and self-dispatches the next task automatically. -**Step 3: Self-dispatch — pick the next task** - -Check Todoist for the next task (To Rework first, then Open, sorted by priority). Skip To Rework tasks whose last comment is ❌ with no follow-up human feedback. If a task is available, move it to In Progress (`6g3XjWjfmJFcGgHM`) and start immediately. If nothing is available, exit — the watchdog will restart you. - -```python -# Todoist section IDs: -# To Rework: 6g6QqvR9rRpvJWvv | Open: 6g3XjWR832hVHhCM | In Progress: 6g3XjWjfmJFcGgHM -import os, json, urllib.request, sys - -token = os.environ["TODOIST_API_KEY"] - -def get_tasks(section_id): - req = urllib.request.Request( - f"https://api.todoist.com/api/v1/tasks?project_id=6g3XjQFwv9V8Pxfv§ion_id={section_id}", - headers={"Authorization": f"Bearer {token}"} - ) - with urllib.request.urlopen(req) as r: - data = json.load(r) - return data if isinstance(data, list) else data.get("results", []) - -def get_comments(task_id): - req = urllib.request.Request( - f"https://api.todoist.com/api/v1/comments?task_id={task_id}", - headers={"Authorization": f"Bearer {token}"} - ) - with urllib.request.urlopen(req) as r: - data = json.load(r) - return data if isinstance(data, list) else data.get("results", []) - -for section_id in ["6g6QqvR9rRpvJWvv", "6g3XjWR832hVHhCM"]: - tasks = sorted(get_tasks(section_id), key=lambda t: t.get("priority", 4), reverse=True) - for task in tasks: - req = urllib.request.Request( - f"https://api.todoist.com/api/v1/tasks/{task['id']}/move", - data=json.dumps({"section_id": "6g3XjWjfmJFcGgHM"}).encode(), - headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, - method="POST" - ) - with urllib.request.urlopen(req) as r: - t = json.load(r) - print(f"Next task: {t['content']} ({t['id']})") - sys.exit(0) - -print("NO_TASKS — exiting") -``` - -### 1d. QA phases - -Both phases run **before** you fire the done signal (steps 1–3 above). +nal (steps 1–3 above). **Phase 1 — Playwright (you):**