next-task — what should I start next?
Answers "what are my next tasks, in priority order?" and "I have 15 minutes — what fits?", then offers to start the pick immediately. Read-only: it never changes a status or merges.
Code before model
The ordering is not decided by the model. The model's only job is to gather the open work (the part that needs tool calls) and narrate the result. The actual ranking — priority → urgency → effort, the time-box filter, and the single recommendation — is computed by scripts/next-task-rank.mjs. This keeps the decision deterministic and auditable (same inputs → same order), per the efficiency principle. Do not re-sort or re-pick in prose — present what the script returns.
Steps
-
Resolve the actor — current ClickUp member (
clickup_get_workspace_members/clickup_resolve_assignees) and GitHub user (get_me). -
Fetch open ClickUp tasks assigned to the actor (
clickup_filter_tasks, scoped to the actor, open statuses). For each capture:priority→ map ClickUp'surgent | high | normal | lowstraight through.dueDate→ the task due date (ISOYYYY-MM-DD), elsenull.storyPoints→ the Story points custom field, resolved by name (don't hardcode the field id — seepre-planning). 1 point = 1 working day.blocked→trueif the task has an unresolved blocking dependency or a blocked status.
-
Fetch assigned GitHub Issues (
search_issuesassignee:@me state:open, orlist_issues). For each capture:effortLabel→ theeffort:xs|s|m|l|xllabel (strip theeffort:prefix), elsenull.priority→ from anurgency:*/priority:*label if present, elsenull.dueDate→ milestone due date if set, elsenull.blocked→trueif it carries ablocked/status:blockedlabel.
-
Normalize each item to the script's input shape and collect into one array:
{"source": "clickup" | "github","ref": "CU-86xxxx" | "#123","title": "…","url": "…","priority": "urgent"|"high"|"normal"|"low"|null,"dueDate": "2026-06-10" | null,"storyPoints": 3 | null,"effortLabel": "xs"|"s"|"m"|"l"|"xl" | null,"blocked": false} -
Rank in the script — write the array to a temp file and run it (a temp file avoids shell-quoting issues with titles):
node scripts/next-task-rank.mjs --file /tmp/next-task-items.json # full ranked listnode scripts/next-task-rank.mjs --file /tmp/next-task-items.json --fits-in 2h # only what fits 2hIt returns
{ now, fitsInHours, ranked[], recommended }. Each ranked item carrieseffortHours,fits, and a computedrankReason. -
Narrate the result:
- A short ranked list (top ~7), each line:
ref · title · rankReason(the script's reason string), with the link. - One
recommended: <ref>line — the script'srecommended— and its one-linewhy. - If
--fits-inwas given andrecommendedisnull, say plainly that nothing fits the box and name the smallest item. - Offer: "Hand this to
/continue?" — on yes, seed/continuestep 2 with the chosen ref.
- A short ranked list (top ~7), each line:
-
Offer to size the unsized (the only mutation this skill makes — opt-in, confirmed).
--fits-inis useless when every item iseffort unknown(no ClickUp Story points, no GitHubeffort:*label), so a time-box can silently return nothing. For each unsized item, propose an estimate from its content — a GitHubeffort:bucket (xs|s|m|l|xl) or a ClickUp story-point number (1pt = 1 working day) — each with a one-line rationale, and offer to write it back: set the ClickUp Story points custom field (resolved by name, perpre-planning) or add the GitHubeffort:*label. Apply only the items the developer approves; then the next run (and--fits-in) is meaningful. If the developer declines, re-rank in-session using the proposed estimates so this run still answers "what fits?" — but do not persist them. Never write an estimate without confirmation.
Ranking model (what the script encodes)
- Order:
blockeditems sink to the bottom (can't be started); otherwisepriority(urgent→low, unset last) →urgency(overdue → due ≤1d → this week → later/none) →effortascending → soonest due date. - Effort → hours:
xs≈0.5h, s≈2h, m≈8h (1d), l≈24h (3d), xl≈40h; ClickUpstoryPoints × 8h. Used for the ascending tiebreaker and the--fits-inbudget. --fits-in:15m | 2h | 1d→ an item fits when its effort ≤ the budget. A short box surfaces only small tasks (a 2h box → xs+s; m/l/xl drop out). Unknown-effort items never auto-fit a box — they're listed but flagged. When everything is unknown the box returns nothing useful — step 7 offers to size the unsized so the box becomes meaningful (and the next run inherits it).
Boundaries
- Read-only, with one opt-in exception. Produces a ranked list + a recommendation; never changes a status, assigns, merges, or pushes. The only write it may perform is the confirmed sizing write-back in step 7 (a Story-points value or an
effort:*label) — offered, never automatic. To act on the pick, hand it to/continue. - Reports reality. If a tracker is unreachable, rank what was reachable and name what was skipped — never fabricate tasks or invent priorities/efforts. Items with no priority or effort still appear (ranked last in their tie-break), flagged "no priority" / "effort unknown".
- For "everything on my plate grouped by urgency" use
/status; for a postable digest use/daily-summary.