11 KiB
Logseq CLI Task Subcommands Implementation Plan
Goal: Add task-oriented subcommands to the current logseq-cli command surface with list task and upsert task, where a task is any node tagged with #Task (:logseq.class/Task), including both blocks and pages.
Architecture: Reuse the existing logseq-cli -> transport/invoke -> db-worker-node thread-api flow, and keep command parsing/build/validation in CLI command namespaces with db read logic in db-worker helpers.
Architecture: Keep behavior aligned with current list/upsert/remove conventions, and avoid introducing a parallel command framework for tasks.
Tech Stack: ClojureScript, babashka.cli, promesa, Datascript pull/query, existing apply-outliner-ops mutation path in db-worker.
Related: Builds on 069-logseq-cli-search-subcommands.md, 071-logseq-cli-search-content-option.md, and docs/cli/logseq-cli.md.
Problem statement
Current CLI supports generic node operations (list page/tag/property, upsert block/page/tag/property, remove block/page/tag/property) but has no task-focused command path.
The product model already has built-in class :logseq.class/Task and task properties (status, priority, deadline, scheduled), so users currently need multiple generic commands or custom queries for common task workflows.
We need a first-class task command group that maps to existing implementation patterns and is script-friendly.
Current baseline from implementation
list commands already expose pagination and sort contracts and depend on db-worker helper functions via :thread-api/cli-list-*.
upsert block/page already supports tag/property mutation via --update-tags, --update-properties, --remove-tags, and --remove-properties.
remove commands already implement robust selector validation, name/id disambiguation, and best-effort multi-id behavior for blocks.
Built-in class :logseq.class/Task exists and is treated as a tag class with task properties in db bootstrap.
Command surface proposal (for discussion first)
This section is intentionally a proposal draft so we can converge on options before implementation.
1) list task options proposal
Recommended MVP options:
| Option | Type | Default | Notes |
|---|---|---|---|
--status |
status alias | none | Accept same aliases as current upsert block --status normalization. |
--priority |
`low | medium | high |
--content |
string | none | Case-insensitive substring filter on :block/title. |
--fields |
csv | command default | Same pattern as existing list commands. |
--limit |
long | none | Same as existing list. |
--offset |
long | none | Same as existing list. |
--sort |
enum | updated-at |
Proposed sort fields: updated-at, created-at, title, status, priority. |
--order |
`asc | desc` | asc |
Recommended default output fields:
id,title,status,priority,scheduled,deadline,updated-at,created-at.
Phase 2 optional filters:
--scheduled-after, --scheduled-before, --deadline-after, --deadline-before, and multi-status support.
2) upsert task options proposal
Recommended MVP options:
| Option | Type | Purpose |
|---|---|---|
--id |
long | Update an existing node as task by db/id. |
--uuid |
uuid | Update an existing node as task by UUID. |
--page |
string | Upsert a page task by page name. |
--content |
string | Create a block task with content. |
--target-id |
long | Block create target selector. |
--target-uuid |
uuid | Block create target selector. |
--target-page |
string | Block create target selector. |
--pos |
`first-child | last-child |
--status |
status alias | Set :logseq.property/status. |
--priority |
enum | Set :logseq.property/priority.*. |
--update-properties |
edn map | Advanced property mutation. |
--remove-properties |
edn vector | Advanced property removal. |
--update-tags |
edn vector | Optional extra tags beyond #Task. |
--remove-tags |
edn vector | Optional tag removal with guardrails for #Task. |
Recommended semantics:
upsert task always ensures :logseq.class/Task tag exists on the target node.
When selector is --id or --uuid, command runs in update mode and converts non-task node into task by adding :logseq.class/Task.
When selector is --page, command upserts page and ensures task tag plus task properties.
When selector is --content and no id/page selector is provided, command creates block task and supports current block targeting options.
Key validation proposal:
Only one of --id, --uuid, --page is allowed.
--content and --page cannot be combined.
--target-* and --pos are only valid for block-create path.
Task removal strategy:
Do not add remove task in this scope.
Use existing remove block and remove page commands for deletion.
Proposed architecture and files
Keep list task in list command flow and add db-worker helper method to avoid large query logic in CLI command layer.
Implement upsert task by reusing existing upsert/tag/property helper functions and apply-outliner-ops patterns.
Primary files to touch:
- /Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/list.cljs
- /Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/upsert.cljs
- /Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/commands.cljs
- /Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/format.cljs
- /Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/common/db_worker.cljs
- /Users/rcmerci/gh-repos/logseq/src/main/frontend/worker/db_core.cljs
- /Users/rcmerci/gh-repos/logseq/docs/cli/logseq-cli.md
- /Users/rcmerci/gh-repos/logseq/cli-e2e/spec/non_sync_inventory.edn
- /Users/rcmerci/gh-repos/logseq/cli-e2e/spec/non_sync_cases.edn
Testing Plan
I will follow @test-driven-development and add all failing tests before implementation code changes.
I will add parser and validation tests in commands_test.cljs for new command paths and option conflicts.
I will add command execution tests for new task logic in upsert_test.cljs and existing remove/list test files, or create focused task command test namespaces if coverage becomes too broad.
I will add db-worker behavior tests for task listing in db_worker_test.cljs.
I will add output formatting tests in format_test.cljs for human/json/edn task command outputs.
I will extend CLI e2e inventory and non-sync cases for list task and upsert task.
I will run focused tests first and then run bb dev:lint-and-test.
NOTE: I will write all tests before I add any implementation behavior.
Step-by-step implementation plan
-
Add command entries for
list taskandupsert taskwith proposed option specs and examples. -
Wire new command keywords into parse-time validation in commands.cljs.
-
Add build-action branches for new task commands and keep error codes aligned with existing style (
:missing-target,:invalid-options,:missing-page-name). -
Add execute branch wiring for new task command types in commands.cljs.
-
Implement
list taskdb-worker helper query in db_worker.cljs and expose it via new:thread-api/cli-list-tasksin db_core.cljs. -
Implement
execute-list-taskin list.cljs with existing sort/fields/offset/limit helpers. -
Implement
build-task-actionandexecute-upsert-taskin upsert.cljs by composing existing tag/property resolution utilities and task-specific normalization for status and priority. -
Add
formatsupport for new command result shapes in format.cljs. -
Update docs and CLI e2e coverage files once behavior is stable.
Edge cases to cover explicitly
A target node can exist but not have #Task, and command should fail with explicit type-mismatch style error instead of silently mutating or deleting wrong entities.
Task pages and task blocks must both be supported for list and upsert.
upsert task status aliases should match current upsert block --status normalization so scripts can reuse existing values.
--scheduled and --deadline are explicitly out of MVP and should be handled later via dedicated options or via --update-properties.
list task sort behavior must stay stable with deterministic tie-breaker by :db/id, consistent with current list commands.
Testing Details
I will verify parse/build/execute behavior at command layer and confirm db-worker helper output contracts without relying on ad hoc manual checks.
I will ensure tests assert behavior for both block task and page task paths, including conversion of non-task nodes during upsert when selector-based update is used.
I will include at least one e2e case that creates task via upsert task and lists it via list task in one flow.
Implementation Details
- Keep task identity canonical as
:block/tagscontaining:logseq.class/Task. - Reuse existing normalization helpers for status and property parsing to minimize duplicate parsing logic.
- Prefer extending existing command namespaces over introducing a second task-only command framework.
- Reuse existing list formatting utilities with task-specific column mapping.
- Keep error code naming aligned with current conventions to avoid inconsistent CLI UX.
- Keep JSON namespaced key behavior unchanged by routing through existing formatter normalization.
- Ensure command help examples include both block and page task use cases.
- Keep cli-e2e inventory and docs in lockstep with final option names.
- Defer non-MVP filters (
deadline/scheduledranges, multi-status) andupsert taskdedicated--scheduled/--deadlineoptions.
Question
No open questions.