tasks

Flat actionable task list across the portfolio with direct URLs — PRs to review, issues to triage, comments, failing CI.

SKILL.md

GitHub

Writing: read docs/skills/shared/writing-rule.md before you write a durable artifact.

/tasks — Actionable Task List

A single ordered list of "things to click on right now". Where /inbox groups items by category, /tasks flattens everything into a prioritised TODO with one URL per line. Optimised for "I have 30 minutes, what should I do?".

Path resolution

Read the registry path via portfolio_registry, the per-project docs dir via portfolio_projects_dir, and the ideas backlog via portfolio_ideas_backlog — all from .claude/hooks/_lib-portfolio-paths.sh. Source the helper at the top of any bash block that touches those paths:

source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-read-config.sh"
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-portfolio-paths.sh"
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-tracker.sh"
registry=$(portfolio_registry)

Defaults match today's single-fork layout (./apexyard.projects.yaml, ./projects, ./projects/ideas-backlog.md). Adopters in split-portfolio mode override the portfolio.{registry, projects_dir, ideas_backlog} keys in .claude/project-config.json. Don't hardcode literal apexyard.projects.yaml or projects/ paths in bash blocks — the helper resolves whichever mode the adopter is in. See docs/multi-project.md.

Tracker-agnostic issue listing (#710 / AgDR-0093)

The issue sources below call tracker_list from _lib-tracker.sh (per repo: from the registry) instead of hardcoding gh issue list, so /tasks works on GitLab-tracked projects too:

# tracker_list <owner/repo> [state=…] [assignee=@me|none|<user>] [author=…] [labels=csv] [search=…] [since=ISO] [limit=N]
# → JSON array [{ref,number,state,title,url,labels,updatedAt}, …]  ([] on empty/unavailable)

Check that each read was complete (#1441). A call that fills its limit may be a short read, and a failed call prints []. Use tracker_list_to, which returns the verdict on stdout:

tmp=$(mktemp) || return 1
status=$(tracker_list_to "$tmp" "$repo" assignee=@me labels=priority-high limit=50 2>/dev/null); rc=$?
if [ "$status" = "TRUNCATED" ]; then                 # raise the limit and read again
  status=$(tracker_list_to "$tmp" "$repo" assignee=@me labels=priority-high limit=200 2>/dev/null); rc=$?
fi
case "$status" in
  UNKNOWN)
    if [ "$rc" -ne 0 ]; then
      echo "$repo: could not read issues"            # a failure, never print 0
    else
      echo "$repo: read $(jq -r 'length' < "$tmp"), completeness unknown"
    fi ;;
  TRUNCATED) echo "$repo: at least $(jq -r 'length' < "$tmp"), still not the end" ;;
esac
items=$(cat "$tmp"); rm -f "$tmp"

One retry is enough for a task list. A second TRUNCATED means the project has more matching issues than any one screen should show, so say "at least N" rather than reading further. A glab project stops at 100 whatever limit you ask for.

Split UNKNOWN on the exit status. Non-zero is a failed read. Zero with UNKNOWN is a successful read this library cannot judge for completeness, which is what a custom adapter returns when no limit is passed. Reporting that as a failure would be its own false claim.

Pass an explicit limit where you can. The rows in the table below pass none, so they take the configured default (tracker.list_default_limit, 30) on gh and glab, and they report UNKNOWN on a custom tracker.

Do not write items=$(tracker_list …) and then read TRACKER_LIST_STATUS. A subshell cannot pass that variable back, so you would read a stale verdict from an earlier project.

Scope caveat (forge axis, #711). The PR sources still call gh pr list / gh api …/pulls. The PR/MR forge abstraction is #711; until it lands, those sources are GitHub-only, so /tasks is issue-axis tracker-agnostic, not fully tracker-agnostic. Filters GitHub expresses but GitLab can't (mentions:, commenter:, assignee=none on glab) degrade to a gh-only path or empty, noted per source.

Usage

/tasks
/tasks --top 10
/tasks --priority high
/tasks --markdown

Scope

Iterates every project in apexyard.projects.yaml (the registry at the root of your ops repo).

Where tasks come from

The task list is the union of:

Source Tool Axis
PRs awaiting your review gh pr list --search "review-requested:@me is:open" forge (#711)
PRs you authored that are blocked on you (changes requested, conflicts, failing CI) gh pr list --search "author:@me is:open review:changes_requested" forge (#711)
Issues assigned to you with high priority tracker_list "$repo" assignee=@me labels=priority-high,priority-critical issue ✓
Issues you opened with new comments tracker_list "$repo" state=open author=@me + client-side comment-recency filter (commenter: is gh-only) issue ✓
Mentions in unresolved threads gh-only: gh search issues "mentions:@me is:open" (empty on non-gh trackers) issue (gh-only)
PR comments awaiting your response gh api repos/{repo}/pulls/{n}/comments filtered to threads where you were mentioned and the last reply isn't yours forge (#711)
Open Critical/High issues with no assignee in projects you own tracker_list "$repo" labels=priority-critical assignee=none (assignee=none degrades on glab) issue ✓
Failing CI on your authored open PRs from statusCheckRollup forge (#711)

Label semantics note: labels=priority-high,priority-critical is passed to each CLI's native --label handling, which is AND on both gh and glab. The intent here (high OR critical) is a pre-existing GitHub-search limitation carried over unchanged — call the two labels in separate tracker_list passes and union the results if strict OR is needed.

Prioritisation

Tasks are scored and sorted. Higher score = closer to the top.

Signal Score
Failing CI on your own PR +100
PR ready to merge (approved, CI green) — yours +90
Critical issue assigned to you +80
PR review requested by name on you +70
PR review requested on a team you're in +60
Changes requested on your PR +50
High-priority issue assigned to you +40
New comment on an issue you opened +30
Mention in an open thread +20
Medium-priority issue assigned to you +10
Low-priority issue assigned to you +1

Tie-breakers:

  1. Older updatedAt first (stale things bubble up)
  2. Same project as your current working directory first
  3. Alphabetical by repo

Output format

Default (terminal):

TASKS — 2026-04-06 09:14
========================
12 actionable items across 3 projects.

  1. [MERGE]  example-app#41  Add health endpoint        — approved + CI green
              https://github.com/your-org/example-app/pull/41
  2. [FIX-CI] example-app#42  CSV export                 — lint job failed
              https://github.com/your-org/example-app/pull/42
  3. [REVIEW] billing-api#8   Fix invoice rounding       — review requested 3h ago
              https://github.com/your-org/billing-api/pull/8
  4. [TRIAGE] example-app#117 [Bug] Login fails Safari   — priority-high, unassigned
              https://github.com/your-org/example-app/issues/117
  5. [REPLY]  marketing#5     Hero copy refresh          — designer commented 1h ago
              https://github.com/your-org/marketing/issues/5
  6. [FIX]    example-app#39  Refactor session store     — Code Reviewer requested changes
              https://github.com/your-org/example-app/pull/39
  …

Markdown (--markdown, suitable for pasting into a TODO file):

## Tasks — 2026-04-06

- [ ] **MERGE** example-app#41 — Add health endpoint (approved, CI green) — https://…
- [ ] **FIX-CI** example-app#42 — CSV export, lint job failed — https://…
- [ ] **REVIEW** billing-api#8 — Fix invoice rounding — https://…
- [ ] **TRIAGE** example-app#117 — Login fails on Safari (priority-high) — https://…
- [ ] **REPLY** marketing#5 — Hero copy refresh, designer commented — https://…

JSON (--json):

[
  {
    "id": "example-app#41",
    "type": "MERGE",
    "score": 90,
    "title": "Add health endpoint",
    "url": "https://...",
    "project": "example-app",
    "reason": "approved + CI green"
  },
  ...
]

Action types

Type Meaning
MERGE Approved + CI green — click merge
FIX-CI Your PR has a failing check
REVIEW A PR is waiting on your review
FIX Your PR has changes requested
TRIAGE An issue needs labelling, priority, or assignment
REPLY A comment is waiting on your response
WORK An issue assigned to you with no PR yet
DECIDE A decision is blocking progress (/decide needed)

Filters

Flag Effect
--top N Only show the top N items
--priority high Only critical and high priority
--project <name> Limit to one project from the registry
--type MERGE,REVIEW Comma-separated action types
--markdown Markdown checklist output
--json JSON output

Rules

  1. Always include the URL on every line — the user needs to click
  2. One action per line — don't bundle "review and merge" as one task
  3. Always sort by score, then staleness — never alphabetical by default
  4. Multi-project aware — iterate the registry, not the org
  5. Never modify anything — read-only
  6. Skip resolved items — if a PR is merged or an issue is closed mid-run, drop it
  7. Cap at 50 items by default — if there are more, show count and suggest filters
  8. Prefer actionable over informational — "11 PRs reviewed by others" is not a task

Related skills

  • /inbox — same data, grouped by category instead of flattened
  • /status — current project state
  • /projects — portfolio table

Part of ApexYard — multi-project SDLC framework for Claude Code · MIT.