feat(filter): sort tasks the way the Todoist apps do - #479
Open
craigcarlyle wants to merge 1 commit into
Open
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
craigcarlyle
force-pushed
the
craigcarlyle/fix-issue-473
branch
from
August 17, 2026 18:10
712c7b8 to
f2ed126
Compare
4 tasks
craigcarlyle
force-pushed
the
craigcarlyle/fix-issue-473
branch
from
August 17, 2026 18:30
f2ed126 to
627fe70
Compare
gnapse
force-pushed
the
craigcarlyle/task-sort
branch
from
August 18, 2026 12:19
835a2ae to
0c56b60
Compare
gnapse
force-pushed
the
craigcarlyle/fix-issue-473
branch
from
August 18, 2026 12:19
627fe70 to
7f7b859
Compare
craigcarlyle
force-pushed
the
craigcarlyle/fix-issue-473
branch
from
August 18, 2026 15:36
7f7b859 to
e774af4
Compare
craigcarlyle
force-pushed
the
craigcarlyle/task-sort
branch
from
August 18, 2026 15:36
0c56b60 to
79b8ebb
Compare
craigcarlyle
force-pushed
the
craigcarlyle/fix-issue-473
branch
from
August 18, 2026 16:10
e774af4 to
1ea8879
Compare
craigcarlyle
marked this pull request as draft
August 18, 2026 16:24
craigcarlyle
force-pushed
the
craigcarlyle/fix-issue-473
branch
3 times, most recently
from
August 19, 2026 19:36
fe760d7 to
c74a731
Compare
craigcarlyle
marked this pull request as ready for review
August 19, 2026 19:51
doistbot
reviewed
Aug 19, 2026
doistbot
left a comment
Member
There was a problem hiding this comment.
td filter view now sorts tasks to match the Todoist apps, reading the saved view sort via the SDK and adding --sort/--sort-order flags, with graceful fallbacks and JSON/NDJSON parity.
Few things worth tightening:
- Rebase onto current
main— this branch downgrades theopendependency (and its transitive deps) and reverts version metadata, both unrelated to the feature. fetchProjects()only fetches the first page; when projects are paginated, later projects won't appear inprojectOrder/workspaceOrder, breaking default/project/workspace sorts. Fetch all pages before building the order map.- Don't swallow
getViewOptions()errors into an empty list — a transient API/auth failure silently renders a saved sort in default order. Let the error propagate; a genuinely empty response already handles the no-saved-sort case. queryUsesDatesonly strips the first word of named references, so a project like#due dateleavesdatebehind and triggers the wrong hierarchy. Consume the full named-reference operand before applying the date pattern.--sort nonestill clones every section array throughsortTasks; skip the sort block entirely when the field isnoneto avoid the unnecessary copy.
I also included a few optional follow-up notes in the details below.
Optional follow-up notes (4)
src/lib/task-sort.ts:57: This comment says
defaulthas no field, butSORTED_BY_FIELD['default']is'MANUAL'(viaFIELD_BY_SORTED_BY.MANUAL === 'default'); onlynoneis absent. A reader would expectsortTasksto passsortedBy: nullfor the default sort when it actually passes'MANUAL'. Update the comment to reflect the actual mapping.src/commands/filter/filter.test.ts:1107: The test name says the view options "cannot be read," but this mocks
getViewOptionsresolving to[](a valid empty response), soloadViewOptions' catch branch — the logging +return []fallback — is never exercised. UsemockApi.getViewOptions.mockRejectedValue(new Error('boom'))to actually cover that path, or rename the test to describe the empty-response case.src/lib/task-sort.test.ts:266: These assertions cover a branch that can't be reached through the CLI:
--sort/--sort-orderare registered withwithCaseInsensitiveChoices, which sets Commanderchoices, so invalid values are rejected during parsing beforeparseTaskSortField/parseTaskSortDirectionever run. TheCliErrorthrows are dead code in the command path; either drop the defensive validation (and this test) or note that it's only guarding the exported helper, not user-facing behavior.src/lib/task-sort.test.ts:276:
sortNeedsProjectsandsortNeedsCollaboratorsare one-line predicates (field !== 'none'andfield === 'assignee') whose observable behavior — whethergetProjects/ collaborators are fetched — is already verified by the integration tests in filter.test.ts ('fetches projects in JSON mode' and 'skips the project fetch when nothing is sorted'). These unit tests re-state the implementation without adding regression signal.
`td filter view` printed tasks in whatever order the API returned. Todoist doesn't sort server-side: every client applies the sorting saved on the view, and falls back to a documented default hierarchy when the view has none, which is what "Manual (default)" means in the app. The CLI read neither piece, so a saved filter listed in an order no other Todoist client shows. Reads the saved options with `api.getViewOptions()` and orders the results with `sortTasks`, both added to the SDK by @gnapse in Doist/todoist-sdk-typescript#665 and #666. What lives here is the part the SDK asks the caller to supply: the vocabulary `--sort` speaks, the mapping from a saved view to that vocabulary, the sidebar layout it wants as a lookup, and the guess at whether a filter query is date-driven, which picks between the priority-first and date-first hierarchies. Adds `--sort default|priority|date|deadline|date-added|name|project|assignee| workspace|none` and `--sort-order asc|desc`, resolved flag first, then saved view, then Todoist default. `--sort none` returns the raw API order. Each section of a comma-separated filter sorts on its own, and `--json` / `--ndjson` come back in the same order as the pretty output. Closes #473 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
craigcarlyle
force-pushed
the
craigcarlyle/fix-issue-473
branch
from
August 19, 2026 20:03
c74a731 to
fdbe654
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
td filter viewnow orders tasks the way the Todoist apps order them, instead of printing whatever order the API returnedapi.getViewOptions()and orders the results withsortTasks, both from@doist/todoist-sdk@14.1.0--sort default|priority|date|deadline|date-added|name|project|assignee|workspace|noneand--sort-order asc|desc, resolved flag first, then saved view, then Todoist default--sort nonereturns the raw API order--json/--ndjsonin the same order as the pretty outputQuery:andURL:Why the order was wrong
GET /tasks/filterreturns storage order, roughly date added ascending, which is why the reporter found that setting their filter to "date added asc" made the app match the CLIsorted_byon the view and then by a documented defaultWhat stays in the CLI
@gnapse put the comparators and the view-options reader in the SDK (Doist/todoist-sdk-typescript#665 and #666), so this PR carries only what
sortTasksasks its caller for:buildProjectOrder, which lays the sidebar out as theprojectOrdermap the SDK wants. It has no helper for this, so every consumer builds its ownqueryUsesDates, which decides between the priority-first and date-first hierarchies from the filter query--sortspeaks, and the mapping from a saved view onto itThe ordering tests here cover that handoff. The comparators are tested in the SDK, so re-testing them through this wrapper would only break on his refactors.
Not addressed
grouped_byis still ignored, so a filter grouped by date or label renders flat. That overlaps with feat: expose view options (board/list, group by, sort) — SDK supports view_options_set but the CLI never calls it #462Test plan
sorted_by: PRIORITYrenders p1 first--sort nonereproduces the pre-fix order--sort assigneeacross a workspace filter with 13 assignees sorts A-Z with unassigned last, and--sort-order descreverses itCloses #473