Skip to content

feat(list): page list/find like git (interactive-only) + higher TTY limit#6

Merged
alex-mextner merged 1 commit into
mainfrom
feat/task-list-pager
Jun 17, 2026
Merged

feat(list): page list/find like git (interactive-only) + higher TTY limit#6
alex-mextner merged 1 commit into
mainfrom
feat/task-list-pager

Conversation

@alex-mextner

Copy link
Copy Markdown
Owner

What

Adds the pagination half of two ROADMAP items:

  • "Commands should work OUTSIDE a repo; task list groups by repo/project"
  • "task list: fallback to all-tasks + pagination + session-vs-all messaging"
  • (refinement) "task list pager: interactive-only + higher limit there"

The outside-repo / grouping / session-fallback / session-vs-all messaging shipped in #5. This PR fills the remaining gap: the pager and the interactive-vs-piped result limit.

Behavior (git's pager model)

  • Human (non---json) list/find output pages through less only when stdout is an interactive TTY; piped/scripted output is plain text (scriptable). Short output that fits one screen prints directly (less -F).
  • Result cap follows interactivity: 100 in a terminal (the pager scrolls), 30 when piped, unless an explicit -n N is given (which always wins).
  • Opt out with --no-pager, NO_PAGER (any non-empty value), or an empty $PAGER/$TASK_PAGER (git's "cat, don't page"). Pager resolves $TASK_PAGER$PAGERlessmore; $LESS defaults to FRX unless the user set it.
  • --json is never paged (machine-readable stays un-paged, un-decorated).

Implementation

  • New tasklib/pager.py: a pure should_page decision and a defensive page() that pipes through the pager and falls back to a direct write on any failure. The pipe is forced utf-8 (ticket titles can be non-ASCII; a locale-encoding crash would otherwise escape and kill the list). stdin is always closed in finally so the pager sees EOF.
  • cli._emit_list is the single call site; _format_tickets / _format_groups now return strings (were direct prints) so output can be routed; JSON paths print straight to stdout.
  • _effective_limit picks the cap by interactivity; the -n default is None so an explicit value is distinguishable.

Tests

  • tests/test_pager.py — decision + routing against a real child-process pager ($TASK_PAGER): utf-8 non-ASCII, broken-pipe / non-zero-exit / missing-binary fallbacks, $LESS=FRX seeding (and not overriding a user $LESS), empty / whitespace pager, closed-stream isatty.
  • tests/test_list_pager.py + tests/test_cli_non_repo.pycmd_list/cmd_find wiring: interactive paging, piped-not-paged-even-with-pager-configured, --no-pager, NO_PAGER, --json never paged, explicit -n flows into the backend, grouped (cross-project) paging on a TTY, no double-emit to stdout.
  • 201 tests pass; smoke green.

Docs: README pagination section, AGENTS.md rule, install.py SKILL_MD.

Reviewed pre-commit via review diff --staged (glm). Two real bugs the review caught were fixed: utf-8 pipe encoding (was text=True → locale crash) and stdin-close-on-write-failure (FD/EOF).

🤖 Generated with Claude Code

…imit

Adds the pagination half of the ROADMAP items "Commands should work OUTSIDE
a repo; task list groups by repo/project" + "task list: fallback to all-tasks
+ pagination + session-vs-all messaging" (the outside-repo/grouping/session
fallback shipped in #5; this is the missing pager + interactive-limit refinement
"task list pager: interactive-only + higher limit there").

Behavior (git's pager model):
- Human (non-json) list/find output pages through less ONLY when stdout is an
  interactive TTY; piped/scripted output is plain text (scriptable). Short
  output that fits one screen prints directly (less -F).
- Result cap follows interactivity: 100 in a terminal (the pager scrolls), 30
  when piped, unless an explicit count is given (which always wins).
- Opt out with the no-pager flag, NO_PAGER (any non-empty value), or an empty
  PAGER/TASK_PAGER (git's "cat, don't page"). Pager resolves
  TASK_PAGER -> PAGER -> less -> more; LESS defaults to FRX.
- JSON output is never paged (machine-readable stays un-paged, un-decorated).

Implementation:
- New tasklib/pager.py: pure should_page decision (TTY + not-opted-out + a
  pager resolves) and a defensive page() that pipes through the pager and falls
  back to a direct write on any failure. Pipe is forced utf-8 (ticket titles can
  be non-ASCII; a locale-encoding crash would otherwise escape). stdin is always
  closed in finally so the pager sees EOF.
- cli._emit_list is the single call site; _format_tickets/_format_groups now
  return strings (were direct prints) so output can be routed; JSON paths print
  straight to stdout via _print_tickets_json/_print_groups_json.
- _effective_limit picks the cap by interactivity; the count default is None so
  an explicit value is distinguishable.

Tests: tasklib/pager.py decision + routing (real child-process pager via
TASK_PAGER, utf-8, broken-pipe/non-zero-exit/missing-binary fallbacks,
LESS=FRX seeding, empty/whitespace pager, closed-stream isatty); cmd_list/cmd_find
wiring (interactive paging, piped-not-paged-even-with-pager-configured, no-pager,
NO_PAGER, JSON never paged, explicit count flows into the backend, grouped paging
on a TTY). Docs: README pagination section, AGENTS.md rule, install.py SKILL_MD.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@alex-mextner alex-mextner merged commit 9d3cb83 into main Jun 17, 2026
4 checks passed
@alex-mextner alex-mextner deleted the feat/task-list-pager branch June 17, 2026 22:08

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 606633d431

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tests/test_pager.py
fake_pager.write_text(f'#!/bin/sh\ncat > "{sink}"\n', encoding="utf-8")
fake_pager.chmod(0o755)
out = _Stream(tty=True)
body = "#1 [todo] Починить заголовок — café ☕"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove Cyrillic from the test fixture

AGENTS.md sets the repo-wide style rule to use English-only code/comments/docs and explicitly says no Cyrillic; this newly added fixture introduces Cyrillic text. The encoding coverage can stay intact by using a Latin non-ASCII sample such as accents plus emoji, without violating the documented repository guideline.

Useful? React with 👍 / 👎.

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