Files
logseq/docs/cli/logseq-cli.md

468 lines
36 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Logseq CLI (Node)
The Logseq CLI is a Node.js program compiled from ClojureScript that connects to a db-worker-node server managed by the CLI. When installed, the CLI binary name is `logseq`.
## Build the CLI
```bash
clojure -M:cljs compile logseq-cli
pnpm db-worker-node:release:bundle
```
`pnpm db-worker-node:release:bundle` compiles and bundles `db-worker-node` with Vite, and writes a standalone runtime to `dist/db-worker-node.js` plus an asset manifest at `dist/db-worker-node-assets.json` (which may contain an empty `assets` array when no extra files are required).
## db-worker-node lifecycle
`logseq` manages `db-worker-node` automatically. You should not start the server manually. The server binds to localhost on a random port and records that port in the repo lock file.
Desktop + CLI shared semantics:
- Electron desktop and CLI are expected to use the same `db-worker-node` and lock-file protocol for a graph.
- Disk SQLite under `~/logseq/graphs` is the source of truth; OPFS periodic export is not part of the desktop primary write path.
- If a daemon already exists for the graph and reports the same revision as the requester, CLI reuses it via lock-file discovery instead of starting a second writer.
- If the discovered daemon reports a different revision, startup stops that exact server, starts a replacement from the requester's bundled runtime, and only returns a usable endpoint after the replacement reports the expected revision.
- A proven revision mismatch may be stopped across owner sources for the same repo/root-dir. Matching-revision servers keep the normal owner boundary.
- If lock ownership is invalid or stale, startup cleans stale lock state before retrying.
- Lock metadata includes an `owner-source` value (`cli`, `electron`, `unknown`) and lifecycle actions enforce owner boundaries.
- `server stop` and `server restart` are owner-aware: CLI can only stop/restart servers it owns (or legacy `unknown` ownership).
- If lock is missing but a matching orphan `db-worker-node` process still exists for the same repo/root-dir, startup performs orphan cleanup before retrying.
## Run the CLI
```bash
node ./dist/logseq.js graph list
```
You can also use npm link to make ./dist/logseq.js globally available, run:
```
npm link
```
If installed(or linked) globally, run:
```bash
logseq graph list
```
## Configuration
The CLI config file defaults to `<root-dir>/cli.edn`, where `root-dir` defaults to `~/logseq`.
Supported keys include:
- `:graph` - The current active graph. Set this with `graph switch`.
- `:root-dir` - CLI root directory. Default is `~/logseq`.
- Graph data directory is derived as `<root-dir>/graphs`.
- Config file default is derived as `<root-dir>/cli.edn`.
- Server list file default is derived as `<root-dir>/server-list`.
- Graph directories under `<root-dir>/graphs` are user-facing graph names e.g. `demo` and do not start with `logseq_db_`.
- `:output-format` - Format for output. Default is `:human`. Use `:json` or `:edn` for scripting.
- `:list-title-max-display-width` - For `:human` output, the max display width for TITLE column, defaulting to `40`.
- `:http-base` - Http base domain for sync service. Interact with this via `sync config`.
- `:ws-url` - Websocket url for sync service. Interact with this via `sync config`.
- `:timeout-ms` - Request timeout in milliseconds.
- `:login-timeout-ms` - Login callback timeout. Defaults to 5 minutes.
- `:logout-timeout-ms` - Logout callback timeout. Defaults to 2 minutes.
- `:custom-queries` - Map of custom queries which are run with `query --name`. See [below for more](#custom-queries).
CLI global flags take precedence over environment variables, which take precedence over the config file. Here is a mapping of these three:
| Config key | Environment variable | Global flag |
| --- | --- | --- |
| :graph | $LOGSEQ_CLI_GRAPH | --graph |
| :root-dir | $LOGSEQ_CLI_ROOT_DIR | --root-dir |
| :output-format | $LOGSEQ_CLI_OUTPUT | --output |
| :timeout-ms | $LOGSEQ_CLI_TIMEOUT_MS | --timeout-ms |
| :login-timeout-ms | $LOGSEQ_CLI_LOGIN_TIMEOUT_MS | n/a |
| :logout-timeout-ms | $LOGSEQ_CLI_LOGOUT_TIMEOUT_MS | n/a |
Legacy notes:
* Migration note: If you previously used `~/.logseq/cli-graphs` or `~/.logseq/cli.edn`, pass `--root-dir` and/or `--config` to continue using equivalent custom locations.
* `:e2ee-password` in `cli.edn` is ignored and removed silently during config read/update. Use `sync start --e2ee-password` or `sync download --e2ee-password` instead.
* `cli.edn` no longer persists cloud auth tokens. CLI login state is stored separately in `~/logseq/auth.json`.
### Custom Queries
Custom queries are defined in `:custom-queries` of a config file. This config is a map with the key as a query name and the value as a map with the following keys:
* `:query` - Required datalog query as a vector. Queries can use built-in rules from `logseq.db.frontend.rules` by appending `%` to the `:in` part of a query. See `logseq.cli.command.query` for example queries.
* `:doc` - Optional doc string describing the query.
* `:inputs` - Optional vector of inputs where each input is a map. Valid keys for the map are `:name` and `:default`. This defines positional arguments to a query e.g. the arguments a user passes map to these inputs and the `:in` bindings in a `:query`.
## Authentication
Use `logseq login` to authenticate the current machine with Logseq cloud.
- `logseq login` starts a temporary callback server at `http://localhost:8765/auth/callback`, opens a browser to the Logseq Cognito Hosted UI, exchanges the returned authorization code, and writes `~/logseq/auth.json`.
- `logseq logout` removes `~/logseq/auth.json`, opens a browser to the Cognito Hosted UI logout endpoint, and completes the browser logout flow at `http://localhost:8765/logout-complete`.
- Sync commands still pass an in-memory runtime `:auth-token` to db-sync, but that token is now resolved from `auth.json` instead of `cli.edn`.
Default auth file: `~/logseq/auth.json`
Auth file contents include the persisted Cognito `id-token`, `access-token`, `refresh-token`, `expires-at`, `sub`, `email`, and `updated-at` values needed for headless refresh.
Verbose logging:
- `--verbose` enables structured debug logs to stderr for CLI option parsing and db-worker-node API calls.
- `sync download` can stream realtime progress lines to stdout when progress is enabled; debug previews remain truncated.
Timeouts:
- `--timeout-ms` continues to control request timeout behavior for CLI transport.
- Login callback timeout is controlled separately by `:login-timeout-ms` / `LOGSEQ_CLI_LOGIN_TIMEOUT_MS` and defaults to 5 minutes.
- Logout callback timeout is controlled separately by `:logout-timeout-ms` / `LOGSEQ_CLI_LOGOUT_TIMEOUT_MS` and defaults to 2 minutes.
## Commands
Graph commands:
- `graph list` - list all db graphs
- `graph create --graph <name>` - create a new db graph and switch to it
- Fails with `graph-exists` if a local graph with the same name already exists
- `--enable-sync` creates the graph, switches to it, uploads it to Logseq Sync, and starts sync in one command
- `--e2ee-password <password>` is accepted only with `--enable-sync` and uses the same password verification path as `sync upload` and `sync start`
- `graph switch --graph <name>` - switch current graph
- `graph remove --graph <name>` - remove a graph
- `graph validate --graph <name>` - validate graph data
- `graph info [--graph <name>]` - show graph metadata (defaults to current graph)
- `graph export --type edn|sqlite --file <path> [--graph <name>]` - export a graph to EDN or SQLite
- EDN export also accepts `--include-timestamps`, `--exclude-built-in-pages`, and `--exclude-namespaces <csv>`
- `--exclude-namespaces` trims CSV tokens, ignores empty tokens, removes duplicates, and can reduce backend export validation strictness
- SQLite export writes the snapshot directly to the destination path through `db-worker-node` instead of round-tripping a base64 payload through the CLI
- EDN-only export flags are rejected when `--type sqlite` is selected
- `graph import --type edn|sqlite --input <path> --graph <name>` - import a graph from EDN or SQLite (new graph only)
- `graph backup list` - list backup snapshots under `<root-dir>/graphs/<graph>/backup`
- `graph backup create [--graph <name>] [--name <label>]` - create a backup snapshot for the selected graph
- `graph backup restore --src <backup-name> --dst <graph-name>` - restore one backup snapshot into a new graph
- `graph backup remove --src <backup-name>` - delete one backup snapshot
For any command that requires `--graph`, if the target graph does not exist, the CLI returns `graph not exists` (except for `graph create`). `graph import` and `graph backup restore` fail if the target graph already exists.
Backup scope note:
- `graph backup create` copies only `db.sqlite`.
- `search-db.sqlite` and `client-ops-db.sqlite` are intentionally excluded.
- Desktop graph backups use the same `<root-dir>/graphs/<graph>/backup/<backup-name>/db.sqlite` layout, so the CLI can list, restore, and remove Desktop-created graph backups when it uses the same root directory.
Server commands:
- `server list` - list running db-worker-node servers
- `server cleanup` - manually terminate revision-mismatched CLI-owned db-worker-node servers discovered from lock files in the current root-dir
- `server start --graph <name>` - start db-worker-node for a graph
- `server stop --graph <name>` - stop db-worker-node for a graph
- `server restart --graph <name>` - restart db-worker-node for a graph
- `doctor [--dev-script]` - run runtime diagnostics for `db-worker-node.js`, `root-dir` permissions, and running server readiness (`--dev-script` checks `static/db-worker-node.js` explicitly)
Auth commands:
- `login` - authenticate this machine and create/update `~/logseq/auth.json`
- `logout` - remove persisted CLI auth from `~/logseq/auth.json`
Debug commands:
- `debug pull --id <db-id> [--graph <name>]` - pull a raw entity by db id with selector `[*]`
- `debug pull --uuid <uuid> [--graph <name>]` - pull a raw entity by block UUID lookup
- `debug pull --ident <db-ident> [--graph <name>]` - pull a raw entity by `:db/ident` lookup
- `--ident` must be a strict EDN keyword (for example `:logseq.class/Tag`)
- exactly one of `--id`, `--uuid`, or `--ident` is required
- if `--graph` is omitted, CLI falls back to current graph config
Utility commands:
- `completion <zsh|bash>` - generate shell completion script to stdout
- `example <command-or-prefix...>` - show runnable command examples for a command path or command prefix (phase 1 covers Graph Inspect and Edit commands)
- exact selector example: `logseq example upsert page`
- prefix selector example: `logseq example upsert`
- `skill show` - print built-in `logseq-cli` skill markdown to stdout
- always prints raw markdown text, even when `--output json` or `--output edn` is set
- `skill install` - install built-in skill to `./.agents/skills/logseq-cli/SKILL.md`
- `skill install --global` - install built-in skill to `~/.agents/skills/logseq-cli/SKILL.md`
Setup for zsh (add to `~/.zshrc`):
```bash
autoload -Uz compinit && compinit
eval "$(logseq completion zsh)"
```
Setup for bash (add to `~/.bashrc`):
```bash
eval "$(logseq completion bash)"
```
Skill command examples:
```bash
# Print markdown content to stdout
logseq skill show
# Install for current working directory
logseq skill install
# Install for current user home directory
logseq skill install --global
```
Server ownership behavior:
- `server stop` and `server restart` can return `server-owned-by-other` if the daemon was started by another owner source.
- `server start` can return `server-start-timeout-orphan` when lock creation times out and orphan matching processes are detected.
- Normal connection startup compares the discovered server revision with the local requester revision. Missing revision is treated as mismatch.
- On revision mismatch, startup attempts one graceful-first restart of the exact discovered server for the same repo/root-dir, even if the stale server has a different owner source. If the replacement still reports a different revision, startup fails fast and does not return an incompatible endpoint.
- Manual `server stop` and `server restart` keep owner protections; cross-owner stop is only used by the automatic revision-mismatch startup path after the target mismatch is proven.
- `server list` human output includes both `OWNER` and `REVISION` columns.
- `server list` prints a compatibility warning in human output when any server revision string is not exactly equal to the local CLI revision string.
- `server cleanup` remains a manual maintenance command. It checks discovered servers in the current root-dir, treats `revision != local CLI revision` (including missing revision) as mismatch, and attempts graceful-first termination only for `:owner-source :cli` targets.
- `server cleanup` structured output includes `checked`, `mismatched`, `eligible`, `skipped-owner`, `killed`, and `failed` summaries.
- Structured output (`--output json|edn`) includes per-server `revision` data but does not include human warning text.
Sync commands:
- `sync status --graph <name>` - show db-sync runtime state for a graph daemon
- `sync start --graph <name> [--e2ee-password <password>]` - start db-sync websocket client for a graph
- `sync stop --graph <name>` - stop db-sync client on a graph daemon
- `sync upload --graph <name>` - upload local graph snapshot to remote
- `sync download --graph <name> [--progress true|false] [--e2ee-password <password>]` - download remote graph `<name>` into a same-name local graph directory
- `sync asset download --graph <name> --id <asset-db-id>` - request one remote asset download by the `ID` shown by `list asset`
- `sync asset download --graph <name> --uuid <asset-uuid>` - request one remote asset download by asset block UUID
- `sync remote-graphs [--graph <name>]` - list remote graphs visible to the current login context
- `sync ensure-keys [--graph <name>]` - ensure user RSA keys for sync/e2ee
- `sync grant-access --graph <name> --graph-id <uuid> --email <email>` - grant encrypted graph access to a user
- `sync config set [--graph <name>] ws-url|http-base <value>` - set non-auth db-sync runtime config key
- `sync config get [--graph <name>] ws-url|http-base` - get non-auth db-sync runtime config key
- `sync config unset [--graph <name>] ws-url|http-base` - remove non-auth db-sync runtime config key
Sync start behavior:
- `sync start --e2ee-password <password>` verifies the password against user encrypted private key before persisting it.
- Verification and persistence run in worker-side sync crypt logic (shared with desktop/web interaction paths).
- Wrong `--e2ee-password` fails fast and does not overwrite a previously stored encrypted password payload.
Sync upload behavior:
- `sync upload` requires `--graph <name>`.
- The CLI starts or reuses that graph's `db-worker-node`, applies the current sync config, and uploads the local snapshot only after the worker has resolved a usable remote graph id.
- If the local graph already has sync metadata, upload reuses the stored remote `graph-id`.
- If the local graph does not have a stored remote `graph-id`, upload first lists visible remote graphs and reuses an exact same-name match when one exists.
- If no same-name remote graph exists, upload creates a new remote graph and persists the returned remote metadata locally before snapshot transfer.
- Successful upload persists graph identity metadata locally in both client-op state and graph KV (`logseq.kv/graph-uuid`, `logseq.kv/graph-remote?`, and `logseq.kv/graph-rtc-e2ee?`) so CLI and web upload/bootstrap flows stay aligned.
- Fresh uploads default to encrypted remote graph creation unless local sync metadata explicitly marks the graph as non-e2ee. In headless CLI mode, run `logseq login` first so refresh-token based E2EE password persistence can be used by follow-up `sync start`/`sync download` flows.
- `sync upload` returns a real error instead of false success when login state, remote graph bootstrap, or snapshot upload fails.
- Common upload failures include missing/invalid CLI login state, missing `http-base`, remote graph creation failure, snapshot upload failure, and local DB/worker startup failure.
- Troubleshooting: after a successful upload, run `graph info --graph <name> --output json` and confirm `data.kv.logseq.kv/graph-uuid` is present. If it is missing, rerun `sync upload` for the same graph to trigger identity backfill.
Sync download behavior:
- `sync download` requires `--graph <name>`.
- If a local graph with the same name already exists, the CLI returns `graph-exists`.
- If no remote graph with that name exists, the CLI returns `remote-graph-not-found`.
- `sync download` starts `db-worker-node` in create-empty mode so local startup does not write `db-initial-data` before snapshot import.
- The final snapshot download/import invoke uses a command-specific long-running timeout (30 minutes by default) rather than the generic short-request timeout path.
- Progress streaming uses db-worker-node SSE `/v1/events` and shared `:rtc.log/download` events.
- `--progress` defaults to `true` for human output.
- For structured output (`--output json|edn`), progress is auto-disabled unless explicitly overridden with `--progress true`.
- `--progress false` always suppresses progress streaming.
- If the target graph DB is not empty at download time, the CLI returns `graph-db-not-empty` and aborts before import.
- For e2ee remote graphs, provide `--e2ee-password` on `sync download` (or persist once via `sync start --e2ee-password`).
- If e2ee password is required but missing, `sync start`, `sync download`, and `sync status` return `e2ee-password-not-found` with a hint to provide `--e2ee-password`.
Sync asset download behavior:
- `sync asset download` requires `--graph` and exactly one of `--id` or `--uuid`.
- `--id` selects the asset node by the Datascript db/id shown as `ID` in `list asset` human output.
- `--uuid` selects the asset block UUID for scripts that already track UUIDs.
- The command requires sync to already be running for the graph. If the graph's sync client is not active, it returns `sync-not-started` with a hint to run `logseq sync start --graph <name>` first.
- The command uses the existing worker asset request API (`:thread-api/db-sync-request-asset-download`) and returns immediately after the worker accepts the enqueue request.
- Before enqueueing, the CLI checks the local `assets/<asset-uuid>.<asset-type>` file. If the file exists and its checksum matches asset metadata, the command reports `download-requested? false` and skips the request.
- If the local file exists but its checksum mismatches, the command reports `checksum-status mismatch`, prints a mismatch hint in human output, and requests a re-download.
- The first version does not accept `--e2ee-password`; persist E2EE password state with existing `sync start` or `sync download` flows before requesting asset download.
- Structured output includes asset identity and status fields such as `asset-id`, `asset-uuid`, `asset-type`, `download-requested?`, `checksum-status`, `skipped-reason`, and `hint` when applicable. It intentionally omits local filesystem paths.
Sync config persistence:
- `sync config set/unset` writes non-auth sync config to the CLI config file selected by `--config`.
- If `--config` is not provided, the default config path is `~/logseq/cli.edn`.
- `sync config get` reads from that same config source.
- `:e2ee-password` is not part of sync config and is silently ignored when found in legacy `cli.edn`.
- Cloud auth is persisted separately in `~/logseq/auth.json`.
E2EE password persistence locations:
- Browser runtime stores refresh-token-encrypted password payload in IndexedDB secret storage.
- Node runtime stores refresh-token-encrypted password payload at `~/logseq/e2ee-password`.
Inspect and edit commands:
- `list page [--expand] [--limit <n>] [--offset <n>] [--sort <field>] [--order asc|desc]` - list pages (defaults to `--sort updated-at --order desc`)
- `list tag [--expand] [--limit <n>] [--offset <n>] [--sort <field>] [--order asc|desc]` - list tags (defaults to `--sort updated-at --order desc`)
- `list property [--expand] [--limit <n>] [--offset <n>] [--sort <field>] [--order asc|desc]` - list properties (defaults to `--sort updated-at --order desc`; `TYPE` and `CARDINALITY` are included by default even without `--expand`; missing schema cardinality is treated as `one`)
- `list task [--status <status>] [--priority <low|medium|high|urgent>] [-c|--content <text>] [--fields <csv>] [--limit <n>] [--offset <n>] [--sort <field>] [--order asc|desc]` - list task nodes tagged with `#Task` (supports both pages and blocks; defaults to `--sort updated-at --order desc`)
- `--status` is validated at runtime using values from the current graph; invalid values return an error that includes available values from that graph.
- `list node [--tags <csv>] [--properties <csv>] [--fields <csv>] [--limit <n>] [--offset <n>] [--sort <field>] [--order asc|desc]` - list ordinary nodes (pages and blocks) filtered by tags/properties (supports selector forms id/uuid/ident/name; at least one of `--tags` or `--properties` is required; defaults to `--sort updated-at --order desc`)
- `--tags` and `--properties` use **all-of** semantics, and when both are present they are combined with **AND**.
- CSV tokens are trimmed and empty tokens are ignored; if a provided filter becomes empty after normalization, CLI returns `invalid-options`.
- `list asset [--fields <csv>] [--limit <n>] [--offset <n>] [--sort <field>] [--order asc|desc]` - list nodes tagged with `#Asset` (`:logseq.class/Asset`; defaults to `--sort updated-at --order desc`)
- `upsert block --content <text> [--target-page <name>|--target-id <id>|--target-uuid <uuid>] [--pos first-child|last-child|sibling]` - create blocks; defaults to todays journal page if no target is given
- `upsert block --blocks <edn> [--target-page <name>|--target-id <id>|--target-uuid <uuid>] [--pos first-child|last-child|sibling]` - insert blocks via EDN vector
- `upsert block --blocks-file <path> [--target-page <name>|--target-id <id>|--target-uuid <uuid>] [--pos first-child|last-child|sibling]` - insert blocks from an EDN file
- `upsert block --id <id>|--uuid <uuid> [--content <text>] [--target-id <id>|--target-uuid <uuid>|--target-page <name>] [--pos first-child|last-child|sibling] [--update-tags <edn-vector>] [--update-properties <edn-map>] [--remove-tags <edn-vector>] [--remove-properties <edn-vector>]` - update and/or move a block
- `--status` is not supported on `upsert block`; use `upsert task --status ...` for task status updates.
- `upsert page --page <name> [--update-tags <edn-vector>] [--update-properties <edn-map>] [--remove-tags <edn-vector>] [--remove-properties <edn-vector>]` - create (or update by page name) a page
- `upsert page --id <id> [--update-tags <edn-vector>] [--update-properties <edn-map>] [--remove-tags <edn-vector>] [--remove-properties <edn-vector>]` - update a page by id (cannot be combined with `--page`)
- `upsert task --content <text> [--target-page <name>|--target-id <id>|--target-uuid <uuid>] [--pos first-child|last-child|sibling] [--status <status>] [--priority <low|medium|high|urgent>] [--scheduled <datetime>] [--deadline <datetime>] [--no-status] [--no-priority] [--no-scheduled] [--no-deadline]` - create a task block and ensure `#Task` is attached
- `upsert task --page <name> [--status <status>] [--priority <low|medium|high|urgent>] [--scheduled <datetime>] [--deadline <datetime>] [--no-status] [--no-priority] [--no-scheduled] [--no-deadline]` - create/update a task page and ensure `#Task` is attached
- `upsert task --id <id>|--uuid <uuid> [--status <status>] [--priority <low|medium|high|urgent>] [--scheduled <datetime>] [--deadline <datetime>] [--no-status] [--no-priority] [--no-scheduled] [--no-deadline]` - update an existing node and ensure `#Task` is attached
- `--status` is validated at runtime using values from the current graph; invalid values return an error that includes available values from that graph.
- generic task tag/property mutation options are not supported on `upsert task`; use `upsert block --update-tags/--update-properties/--remove-tags/--remove-properties` when needed.
- for the same field, set and clear options are mutually exclusive (for example: `--status todo --no-status` is invalid).
- `upsert asset --path <asset-file-path> [--content <text>] [--target-page <name>|--target-id <id>|--target-uuid <uuid>] [--pos first-child|last-child|sibling]` - create an asset node, add `#Asset`, set asset metadata (`type`, `size`, `checksum`), and copy the local file into graph `assets/` as `<block-uuid>.<ext>`
- `upsert asset --id <id>|--uuid <uuid> [--content <text>]` - update an existing asset node title; target node must be tagged with `#Asset`
- create mode requires `--path`; update mode rejects `--path`.
- `upsert tag --name <name>` - create or upsert a tag by name
- `upsert tag --id <id> [--name <name>]` - validate a tag by id; when `--name` is provided, rename that tag id (no-op if normalized name is unchanged)
- `upsert tag --id <id> --name <name>` conflicts: returns `tag-name-conflict` when target name is a non-tag page, and `tag-rename-conflict` when target name is another existing tag
- `upsert property --name <name> [--type <type>] [--cardinality one|many] [--hide true|false] [--public true|false]` - create or update a property by name
- `upsert property --id <id> [--type <type>] [--cardinality one|many] [--hide true|false] [--public true|false]` - update a property by id
- `move --id <id>|--uuid <uuid> --target-id <id>|--target-uuid <uuid>|--target-page <name> [--pos first-child|last-child|sibling]` - move a block and its children (defaults to first-child)
- `remove --id <id>|--uuid <uuid>|--page <name>` - remove blocks (by db/id or UUID) or pages
- `search block --content <query>` - search blocks by `:block/title` (case-insensitive substring)
- `search page --content <query>` - search pages by `:block/name` (case-insensitive substring)
- `search property --content <query>` - search properties by `:block/title` (Property entities only)
- `search tag --content <query>` - search tags by `:block/title` (Tag entities only)
- `qsearch <query>` - search QMD Markdown Mirror output and render matched Logseq blocks grouped by page in human output; uses the current graph when `--graph` is omitted
- `query --query <edn> [--inputs <edn-vector>]` - run a Datascript query against the graph
- `query --name <query-name> [--inputs <edn-vector>]` - run a named query (built-in or from `cli.edn`)
- `query list` - list available named queries
- `show --page <name> [--level <n>]` - show page content tree
- Use `--page-hierarchy true` to display child pages connected through page hierarchy instead of normal page content blocks.
- `show --uuid <uuid> [--level <n>]` - show block tree
- `show --id <id> [--level <n>]` - show block tree by db/id
Help output:
```
Subcommands:
list page [options] List pages
list tag [options] List tags
list property [options] List properties
list task [options] List tasks
list node [options] List nodes
list asset [options] List assets
upsert block [options] Upsert block
upsert page [options] Upsert page
upsert task [options] Upsert task
upsert asset [options] Upsert asset
upsert tag [options] Upsert tag
upsert property [options] Upsert property
move [options] Move block
remove [options] Remove block or page
search block [options] Search blocks by title
search page [options] Search pages by name
search property [options] Search properties by title
search tag [options] Search tags by title
show [options] Show tree
```
Options grouping:
- Help output separates **Global options** (apply to all commands) and **Command options** (command-specific flags).
Version output:
- `logseq --version` prints:
```
Build time: <timestamp>
Revision: <commit>
```
Output formats:
- Global `--output <human|json|edn>` applies to all commands
- Output formatting is controlled via global `--output`, `:output-format` in config, or `LOGSEQ_CLI_OUTPUT`.
- Global `--profile` enables stage timing output to **stderr**. This is for debugging latency and does not change command stdout payloads.
- Human profile output is rendered as a tree (similar to `show` output style). The elapsed time column is printed at the far left with fixed width for alignment, for example:
```text
146ms command=list status=ok
stages
146ms └── cli.total
4ms ├── cli.parse-args
139ms └── cli.execute-action
129ms └── transport.invoke:thread-api/cli-list-pages
```
- Human output is plain text. List/search commands render tables with a final `Count: N` line. For list and search subcommands, the ID column uses `:db/id` (not UUID). If `:db/ident` exists, an `IDENT` column is included. `list property` includes dedicated `TYPE` and `CARDINALITY` columns; `list node`/`list asset` include a dedicated `TYPE` column (page/block) and page context columns for blocks. Search table columns are `ID` and `TITLE`. For `list page|tag|property|task|node|asset` in human output, the `TITLE` column is display-width-aware (CJK-safe), defaults to max width `40`, and truncates overflow with ``; set `:list-title-max-display-width` in `cli.edn` to override. JSON/EDN outputs keep full titles (no truncation). Block titles can include multiple lines; multi-line rows align additional lines under the `TITLE` column. Times such as list `UPDATED-AT`/`CREATED-AT` and `graph info` `Created at` are shown in human-friendly relative form. Errors include error codes and may include a `Hint:` line. Use `--output json|edn` for structured output.
- `example` human output includes `Selector`, `Matched commands`, and `Examples` sections. Structured output (`json`/`edn`) includes `selector`, `matched-commands`, `examples`, and `message` fields under `data`.
- `skill show` always prints raw markdown text to stdout, regardless of `--output` mode.
- `sync download` progress lines are streamed to stdout only when progress is enabled. In `json`/`edn` mode, progress is disabled by default unless `--progress true` is provided.
- JSON machine output preserves namespaced keyword semantics:
- Namespaced keyword keys are emitted as canonical string keys in `namespace/name` form (for example `:block/title` -> `"block/title"`).
- Unqualified keyword keys remain plain strings (for example `:status` -> `"status"`).
- Keyword values are emitted as strings with namespace text preserved when present (for example `:db.cardinality/one` -> `"db.cardinality/one"`).
- UUID values are emitted as strings.
- For `list property`, `TYPE` and cardinality are returned in default output (without `--expand`) for human and structured (`json`/`edn`) formats.
- Human output renders cardinality as `one` or `many` in the `CARDINALITY` column.
- JSON keeps namespaced key/value form via `"db/cardinality"` and values like `"db.cardinality/one"` or `"db.cardinality/many"`.
- EDN keeps `:db/cardinality` (for example `:db.cardinality/one` or `:db.cardinality/many`).
- When a property omits schema cardinality, CLI treats it as default `one`.
JSON key migration (flat -> namespaced):
| Old JSON key path | New JSON key path |
| --- | --- |
| `data.items[].title` | `data.items[].block/title` |
| `data.items[].id` | `data.items[].db/id` |
| `data.items[].type` | `data.items[].logseq.property/type` |
| `data.items[].cardinality` | `data.items[].db/cardinality` |
| `data.root.children[]` | `data.root.block/children[]` |
- `upsert page`, `upsert block`, `upsert task`, and `upsert asset` return entity ids in `data.result` for JSON/EDN output, and include ids in human output.
- Human example:
```text
Upserted page:
[123]
```
- Human example:
```text
Upserted blocks:
[201 202]
```
- JSON example: `{"status":"ok","data":{"result":[123]}}`
- EDN example: `{:status :ok, :data {:result [123]}}`
- `doctor` output includes overall status (`ok`, `warning`, `error`) and per-check rows for `db-worker-script`, `data-dir`, `running-servers`, and `server-revision-mismatch`. For scripting, `--output json|edn` keeps the structured check payload.
- Common doctor failures and warnings:
- `doctor-script-missing`: `db-worker-node.js` runtime target is missing (default target: `dist/db-worker-node.js`; use `doctor --dev-script` to check `static/db-worker-node.js`).
- `doctor-script-unreadable`: script path exists but is not a readable file.
- `data-dir-permission`: configured data dir is not readable or writable.
- `doctor-server-not-ready`: one or more lock-discovered servers are still in `:starting` state (warning).
- `doctor-server-revision-mismatch`: one or more discovered servers use a different revision than the local CLI revision (warning). Follow the printed remediation command for each affected graph: `logseq server restart --graph <name>`.
- If bundled runtime startup fails with missing-module or missing-file errors, rebuild with `pnpm db-worker-node:release:bundle` and confirm `dist/db-worker-node.js` exists and every path listed in `dist/db-worker-node-assets.json` is present next to it.
- `query` human output returns a plain string (the query result rendered via `pr-str`), which is convenient for pipelines like `logseq query ... | xargs logseq show --id`.
- `qsearch` human output is page grouped. Each group renders the page id/title as the tree root and the matched blocks below it with the same tree-style id column and block details used by `show`, including visible tags and properties. When colors are enabled, query terms are highlighted case-insensitively after the final human text is rendered. JSON and EDN output keep the structured `:items`, `:missing-ids`, and `:qmd` payload.
- Built-in named queries currently include `block-search`, `task-search`, `recent-updated`, `list-status`, and `list-priority`. Use `query list` to see the full set for your config.
- Show output resolves block reference UUIDs inside text, replacing `[[<uuid>]]` with the referenced block content. Nested references are resolved recursively up to 10 levels to avoid excessive expansion. For example: `[[<uuid1>]]` → `[[some text [[<uuid2>]]]]` and then `<uuid2>` is also replaced.
- When `show` targets an ordinary block (`--id` or `--uuid`), human output prepends one breadcrumb line (`page > ... > nearest parent`) above the root block line. Each segment is display-width truncated to `24` with ``.
```text
Project Alpha > Milestone 2026 > API rollout
5137 Implement retry policy for upload worker
5138 ├── Add timeout backoff guard
5139 └── Add deterministic retry test
```
- `show` tree lines print `:db/id` as the first column:
```text
id1 block1
id2 ├── b2
id3 │ └── b3
id4 ├── b4
id5 │ ├── b5
id6 │ │ └── b6
id7 │ └── b7
id8 └── b8
```
Troubleshooting:
- If authenticated sync commands fail with missing or invalid local auth, run `logseq logout` and then `logseq login` again.
- You can also manually remove `~/logseq/auth.json` and repeat `logseq login`.
Examples:
```bash
node ./dist/logseq.js login
node ./dist/logseq.js graph create --graph demo
node ./dist/logseq.js graph export --type edn --file /tmp/demo.edn --graph demo
node ./dist/logseq.js graph import --type edn --input /tmp/demo.edn --graph demo-import
node ./dist/logseq.js graph backup create --graph demo --name nightly
node ./dist/logseq.js graph backup list
node ./dist/logseq.js graph backup restore --src demo-nightly-20260101T000000Z --dst demo-restore
node ./dist/logseq.js graph backup remove --src demo-nightly-20260101T000000Z
node ./dist/logseq.js upsert block --target-page TestPage --content "hello world"
node ./dist/logseq.js move --uuid <uuid> --target-page TargetPage
node ./dist/logseq.js search block --content "hello"
node ./dist/logseq.js show --page TestPage --output json
node ./dist/logseq.js show --page Foo --page-hierarchy true
node ./dist/logseq.js debug pull --graph demo --ident :logseq.class/Tag --output json
node ./dist/logseq.js server list
node ./dist/logseq.js doctor
node ./dist/logseq.js doctor --dev-script
node ./dist/logseq.js doctor --output json
node ./dist/logseq.js --graph demo sync start --e2ee-password "my-secret"
node ./dist/logseq.js --graph demo sync download --e2ee-password "my-secret"
node ./dist/logseq.js logout
```