Skip to content

feat(cli): add tasks list to close CLI <-> MCP list_tasks drift#54

Merged
fyodoriv merged 1 commit into
mainfrom
cli-add-list-command
May 3, 2026
Merged

feat(cli): add tasks list to close CLI <-> MCP list_tasks drift#54
fyodoriv merged 1 commit into
mainfrom
cli-add-list-command

Conversation

@fyodoriv
Copy link
Copy Markdown
Collaborator

@fyodoriv fyodoriv commented May 3, 2026

Summary

closes `cli-add-list-command`

The MCP server has exposed `list_tasks` with priority/tag/unclaimed/unblocked filters since v0.6, but the CLI had no equivalent — agents scripting through `tasks` could only call `tasks pick` (one task) or parse TASKS.md themselves. Close the gap:

``` tasks list # all tasks, P0 first tasks list --priority P0 --tag backend tasks list --unclaimed --unblocked # only pickable work tasks list --priority P0 --json ```

Default output is `\t\t

` (tab-separated, `-` for tasks without an ID). `--json` returns the same structured fields the MCP `list_tasks` response uses (`id`, `summary`, `priority`, `tags`, `blocked`, `claimed`, `file`, `line`) so callers can swap backends without changing scripts.

Implementation reuses `loadAllTasks`, `getAllTaskIds`, and `isBlocked` from `@tasks-md/parser` — the same predicates `pickBestTask` and the MCP server's `listTasksFromFiles` use — so CLI and MCP cannot drift.

Why needed

Agents that orchestrate work via the CLI (taskgrind, etc.) had to either call MCP for enumeration or hand-parse TASKS.md. `tasks list` gives them a first-class enumerator that mirrors the MCP `list_tasks` response field-for-field.

Test plan

  • `npm run build`
  • `npm test` — all 397 tests pass (173 in cli, +12 new)
  • `npm run lint`
  • `npx -y @tasks-md/lint TASKS.md`
  • Manual smoke against this repo's TASKS.md confirmed CLI output matches MCP path:

``` $ tasks list --priority P2 --unclaimed --unblocked P2 user-stories-acceptance-sharpening Sharpen vague acceptance ... P2 docs-accuracy-batch-readme-cli Documentation accuracy batch ... P2 user-stories-polish-batch Polish batch ... ```

New unit tests pin every claim in the acceptance criteria:

  • `returns all tasks priority-sorted (P0 first)`
  • `filters by priority case-insensitively`
  • `filters by tag case-insensitively`
  • `filters unclaimed only`
  • `filters unblocked only`
  • `combines filters (priority + unclaimed + unblocked)`
  • `marks blocked status correctly`
  • `returns id, tags, claimed, file, line in structured form`
  • `list prints priority+id+summary tab-separated by default`
  • `list --json outputs valid round-trippable JSON`
  • `list --priority --unclaimed matches MCP list_tasks for the same filter`
  • `list reports empty match clearly`

Scope

  • One new top-level command (`tasks list`); no behavior changes elsewhere
  • Documentation lockstep across packages/cli/README.md, README.md, story 07, and the user-stories index

🤖 Written by an agent, not Fyodor. Ping me if this looks off.

closes cli-add-list-command

The MCP server has exposed `list_tasks` with priority/tag/unclaimed/
unblocked filters since v0.6, but the CLI had no equivalent — agents
scripting through `tasks` could only call `tasks pick` (one task) or
parse TASKS.md themselves. Close the gap:

  tasks list                                  # all tasks, P0 first
  tasks list --priority P0 --tag backend
  tasks list --unclaimed --unblocked          # only pickable work
  tasks list --priority P0 --json

Default output is `<priority>\t<id>\t<summary>` (tab-separated, `-`
for tasks without an ID). `--json` returns the same structured fields
the MCP `list_tasks` response uses (id, summary, priority, tags,
blocked, claimed, file, line) so callers can swap backends without
changing scripts.

Implementation reuses `loadAllTasks`, `getAllTaskIds`, and `isBlocked`
from @tasks-md/parser — the same predicates `pickBestTask` and the
MCP server's `listTasksFromFiles` use — so CLI and MCP cannot drift.

Pin tests so the same drift fails CI next time:
  cli.test.ts -> 8 listTasks unit tests covering every filter +
                 priority sort + structured output + claimed prefix
              -> 4 CLI integration tests covering tab-separated default,
                 --json round-trip, --priority --unclaimed parity with
                 the MCP path, and empty-match messaging
              -> --help test now also asserts `list` is advertised

Docs updated in lockstep:
  README.md                                          (CLI examples)
  packages/cli/README.md                             (new tasks list section)
  docs/user-stories/07-monitor-queue-health.md       (Enumerate Tasks Programmatically section)
  docs/user-stories/README.md                        (story 07 + automation table)
@fyodoriv fyodoriv merged commit ef58b8d into main May 3, 2026
4 checks passed
@fyodoriv fyodoriv deleted the cli-add-list-command branch May 3, 2026 15:59
fyodoriv added a commit that referenced this pull request May 3, 2026
…59)

closes cli-restore-json-on-read-commands

Commit ccf1360 (2026-03-17, "remove unused features") dropped the
--json flag from tasks pick/stats/diff, but PR #54 (2026-05-03) added
--json to the new tasks list command. The CLI surface ended up
inconsistent: one read command was JSON-scriptable, three were not.

Restoring --json across all four read commands (pick / list / stats /
diff) so any script can choose a command and parse its output with the
same `JSON.parse(stdout)` call.

Behavior:
- pick --json: emits {picked: false} when the queue is empty,
  otherwise {picked: true, summary, priority, file, line, metadata,
  candidates, unblocks}. Same shape as the historical implementation
  in commit a567140.
- stats --json: full QueueStats object (same type the lib already
  exports — total, byPriority, blocked, claimed, available, fileCount,
  throughput, topAgents).
- diff --json: full QueueDiff object (ref, added, removed, claimed,
  hasChanges).
- list --json: unchanged.

Tests added in cli.test.ts pinning all four JSON paths plus a --help
parity check that fails CI if any read command stops advertising
--json:
- pick --json outputs structured JSON
- pick --json outputs {picked: false} for empty queue
- stats --json outputs structured JSON
- diff --json outputs structured JSON (uses git add -f to bypass any
  global gitignore that excludes TASKS.md in the test temp dir)
- --help advertises --json for every read command

Verified: npm run build, npm test (402 tests pass, +5 new), npm run
lint, npx -y @tasks-md/lint TASKS.md.

---
_🤖 Written by an agent, not Fyodor. Ping me if this looks off._
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant