11 KiB
Logseq CLI list node Subcommand Implementation Plan
Goal: add a new list node subcommand to the existing logseq-cli surface so users can list ordinary nodes (both blocks and pages) filtered by tags and/or properties.
Architecture: keep the current logseq-cli -> command parse/build -> transport/invoke -> db-worker-node thread-api flow and implement filter query logic in db-worker helpers.
Architecture: reuse existing identifier resolution patterns from current CLI commands (especially tag/property resolution in add.cljs) instead of introducing a second resolver system.
Tech Stack: ClojureScript, babashka.cli, Datascript query/datoms, promesa, current formatter/command framework.
Related:
/Users/rcmerci/gh-repos/logseq/docs/agent-guide/078-logseq-cli-task-subcommands.md/Users/rcmerci/gh-repos/logseq/docs/agent-guide/062-cli-list-default-sort-updated-at.md/Users/rcmerci/gh-repos/logseq/docs/cli/logseq-cli.md
Problem statement
Current CLI has list page, list tag, list property, and list task, but no generic list node path for querying ordinary content nodes by tag/property constraints.
Users need a first-class command that can return both pages and blocks, with script-friendly filter options:
--tags <tag1>,<tag2>,<tag3>--properties <prop1>,<prop2>,<prop3>
Each filter item must support id / uuid / ident / block-name, and command execution must require at least one filter (--tags or --properties) to avoid returning unbounded node sets.
Current baseline from implementation
1) List command framework is centralized and reusable
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/list.cljs already provides:
list-common-spec(--expand,--fields,--limit,--offset,--sort,--order)- shared helpers (
apply-sort,apply-offset-limit,apply-fields) - per-subcommand field maps and execute functions
This is the natural place to add list-node-spec, list-node-field-map, and execute-list-node.
2) db-worker list read logic already lives in one namespace
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/common/db_worker.cljs holds list-pages, list-tags, list-properties, and list-tasks.
/Users/rcmerci/gh-repos/logseq/src/main/frontend/worker/db_core.cljs exposes them via :thread-api/cli-list-* endpoints.
A new list-nodes helper and :thread-api/cli-list-nodes should follow this same pattern.
3) Existing resolver utilities already cover most selector forms
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/add.cljs already contains reusable resolution logic:
- tags:
resolve-tag-entity/resolve-tags(supports id/uuid/ident/name through typed inputs) - properties:
resolve-property-identifierswith:allow-non-built-in? true(supports id/ident/name)
Gap to close for this plan:
- property selector by
uuidis not currently supported in property resolver path and should be added for parity with the requested contract.
4) Command wiring and formatting are explicit and easy to extend
- Command dispatch/build/validation currently enumerates list commands in
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/commands.cljs. - Human output table formatting for list commands is in
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/format.cljs. - Command parser and behavior tests already exist in:
/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/common/db_worker_test.cljs/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/format_test.cljs
Command contract
CLI syntax
logseq list node --graph <name> [--tags <csv>] [--properties <csv>] [common list options]
Required filters
At least one of the following must be present:
--tags--properties
If both are missing, return :invalid-options with a clear message:
list node requires at least one of --tags or --properties
Filter options
--tags
--tags <tag1>,<tag2>,<tag3>
Each tag token supports:
id(numeric)uuidident(keyword form)block-name(tag page name)
--properties
--properties <prop1>,<prop2>,<prop3>
Each property token supports:
id(numeric)uuidident(keyword form)block-name(property page name)
Filter semantics
- CSV tokens are trimmed; blank tokens are ignored.
- After normalization, empty filter lists are invalid.
--tags a,bmeans node must contain all resolved tag selectors.--properties x,ymeans node must contain all resolved property selectors.- If both are provided, result must satisfy both groups (logical AND).
Node scope
list node returns ordinary content nodes of two kinds:
- pages
- blocks
Planned implementation should exclude schema-definition entities (for example, tag/property definition pages) so output focuses on content nodes.
Optional options (aligned with existing list commands)
list node should support the same common list options already used by existing list subcommands:
--expand--fields <csv>--limit <n>--offset <n>--sort <field>(defaultupdated-at)--order asc|desc
Proposed sortable/field keys for list node
MVP field map:
id->:db/idident->:db/identtitle->:block/titleuuid->:block/uuidtype->:node/type("page"or"block")page-id->:block/page-id(for blocks)page-title->:block/page-title(for blocks)created-at->:block/created-atupdated-at->:block/updated-at
Design details
A) CLI parsing and validation (list.cljs + commands.cljs)
- Add
list-node-field-mapandlist-node-specinlist.cljs. - Add new command entry:
[["list" "node"] :list-node ...]
- Add CSV parser helper for selector options (
--tagsand--properties). - Extend
invalid-options?handling solist nodeenforces required filter presence without changing behavior of existing list subcommands. - Wire
:list-nodethroughcommands.cljsparse/build/execute branches.
B) Selector resolution strategy
Resolve filters in CLI layer before invoking db-worker:
-
Tags:
- normalize token to typed selector (id/uuid/ident/name)
- resolve via existing tag resolver flow in
add.cljs - pass resolved tag ids to db-worker
-
Properties:
- extend resolver support to include uuid-based property lookup
- resolve to canonical property idents
- pass resolved property idents to db-worker
This keeps db-worker focused on DB filtering, while CLI owns user-facing selector parsing/errors.
C) db-worker query strategy (db_worker.cljs)
Add list-nodes function with options shape like:
{:tag-ids [...]
:property-idents [...]
:expand true|false}
Implementation outline:
- Build candidate ordinary-node set (pages + blocks, excluding schema-definition entities).
- Apply tag intersection filter using
:block/tagsrefs against resolved tag ids. - Apply property-presence intersection filter by checking node datoms for each resolved property ident.
- Return minimal list item by default; include richer payload when
:expand true. - Include
:node/typeand block page context fields for downstream formatting/fields selection.
D) thread-api exposure (db_core.cljs)
Add and wire:
:thread-api/cli-list-nodes->cli-db-worker/list-nodes
E) Output formatting (format.cljs)
Add list-node formatter branch with dedicated columns (including TYPE) and title width behavior identical to other list commands.
F) Completion/docs/e2e alignment
Update:
- command docs (
docs/cli/logseq-cli.md) - command summary/help tests
- optional completion tests if option specs affect completion surface
- CLI e2e inventory/cases for new command coverage
Step-by-step implementation plan (TDD first)
- Add RED parser tests for
list nodecommand recognition and option parsing. - Add RED validation tests asserting
list nodefails without--tagsand--properties. - Add RED tests for CSV parsing/normalization behavior for both filters.
- Add RED resolver tests for tag/property selectors covering id/uuid/ident/block-name.
- Add RED db-worker tests for node filtering semantics (all-of tags, all-of properties, combined AND).
- Implement resolver extensions (including property uuid support).
- Implement
list-node-spec, command entry, action build, and execute path. - Implement
cli-list-nodesthread-api andlist-nodesdb-worker helper. - Add/adjust formatter for human list-node output and structured output shape.
- Update command docs and e2e inventory/cases.
- Run focused tests, then full lint/test.
Files expected to change
Core implementation:
/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/list.cljs/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/command/add.cljs(property uuid resolution support)/Users/rcmerci/gh-repos/logseq/src/main/logseq/cli/commands.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/src/main/logseq/cli/format.cljs
Tests:
/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/commands_test.cljs/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/common/db_worker_test.cljs/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/format_test.cljs/Users/rcmerci/gh-repos/logseq/src/test/logseq/cli/completion_generator_test.cljs(if option completion changes)
Docs/e2e:
/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 failing tests before implementation changes.
Focused verification commands:
bb dev:test -v logseq.cli.commands-testbb dev:test -v logseq.cli.common.db-worker-testbb dev:test -v logseq.cli.format-testbb dev:test -v logseq.cli.completion-generator-test(if touched)bb -f cli-e2e/bb.edn test --skip-build(if e2e specs changed)bb dev:lint-and-test
Risks and mitigations
Risk: property selector normalization can be ambiguous between ident and block-name.
Mitigation: define deterministic precedence (id -> uuid -> explicit keyword ident -> block-name fallback) and lock with tests.
Risk: filtering large graphs by multiple property idents can be slow.
Mitigation: use set-intersection style filtering and limit payload in non-expand mode.
Risk: “ordinary node” definition drift (schema entities leaking into output).
Mitigation: encode explicit exclusion rules in db-worker tests.
Acceptance criteria
logseq list nodeexists and is documented.- It returns both block and page nodes (ordinary content nodes).
--tagsaccepts comma-separated selectors and supports id/uuid/ident/block-name.--propertiesaccepts comma-separated selectors and supports id/uuid/ident/block-name.- At least one of
--tagsor--propertiesis required; otherwise command returns:invalid-options. - Common list options (
--expand,--fields,--limit,--offset,--sort,--order) are supported. - Unit and e2e coverage is updated and green.
Open question
No blocking question for this phase.