Files
logseq/docs/agent-guide/027-logseq-cli-update-command.md
2026-03-12 15:12:33 +08:00

8.0 KiB

Logseq CLI Update Command (Move Refactor)

Summary

Introduce a new update CLI command that subsumes the current move command and adds tag/property updates. The new command keeps all existing move capabilities/options, makes move targets optional (no target means no move), and adds update/remove semantics for tags and properties. The plan is grounded in current logseq-cli parsing/execution and db-worker-node outliner ops.

Goals

  • Replace move command behavior with update while keeping all current move options and semantics.
  • Allow update to move blocks and/or update tags/properties in one command.
  • Support new options:
    • --update-tags, --update-properties
    • --remove-tags, --remove-properties
  • Reuse tag/property parsing and resolution rules from add block:
    • Identifiers accept db/id, db/ident, block/title.
    • --update-tags and --update-properties accept the same EDN format as add block --tags/--properties.
    • --remove-tags and --remove-properties accept EDN vectors of tag/property identifiers.

Non-goals

  • Changing db-worker-node APIs or introducing new outliner ops.
  • Expanding update beyond blocks (no page-level update scope in this iteration).
  • Changing move semantics or target resolution.

Background: Current Move Command

  • CLI move implementation lives in src/main/logseq/cli/command/move.cljs.
  • It requires a source (--id or --uuid) and a target (--target-id, --target-uuid, or --target-page).
  • Execution uses :thread-api/apply-outliner-ops with [:move-blocks ...].
  • Move output formatting in src/main/logseq/cli/format.cljs uses format-move-block.

Proposed Behavior (Update Command)

Required/Optional Inputs

  • Source is required: one of --id or --uuid.
  • Move targets are optional:
    • --target-page, --target-uuid, --target-id, --pos are optional.
    • If no target is provided, no move occurs and the command only updates tags/properties.
    • If a target is provided, move executes with the same semantics as today.
  • Update/remove options are optional:
    • --update-tags (EDN vector, same as add block --tags)
    • --update-properties (EDN map, same as add block --properties)
    • --remove-tags (EDN vector of tag identifiers)
    • --remove-properties (EDN vector of property identifiers)

Validation Rules

  • Only one source selector is allowed: --id or --uuid.
  • Only one target selector is allowed: --target-id, --target-uuid, or --target-page.
  • --pos sibling is only valid when the target is a block (not a page), same as move.
  • At least one of the following must be provided: target (move) or update/remove options.
  • --update-tags and --update-properties accept the same EDN grammar and validations as in add.
  • --remove-tags and --remove-properties must be non-empty vectors (EDN), with identifiers validated similarly to add.

Execution Semantics

  • Resolve source block to db/id using existing move helpers.
  • If move target provided, resolve target entity using existing move helpers and compute pos opts.
  • Tag/property updates use current outliner ops (no new db-worker changes):
    • Add/update tags: [:batch-set-property [block-ids :block/tags tag-id {}]] for each tag.
    • Add/update properties: [:batch-set-property [block-ids property-id value {}]].
    • Remove tags: [:batch-delete-property-value [block-ids :block/tags tag-id]].
    • Remove properties: [:batch-remove-property [block-ids property-id]], with property resolution aligned to add (built-in/public property rules).
  • Combine operations into a single apply-outliner-ops call when possible to keep updates atomic and reduce roundtrips.

Design and Implementation Plan

1) Create update command module

  • New file: src/main/logseq/cli/command/update.cljs.
  • Start from move.cljs and expand the spec to include update/remove tag/property options.
  • Extract shared helpers from move.cljs (e.g., resolve-source, resolve-target, pos->opts, invalid-options?) into a small shared namespace or move into update.cljs.

2) Reuse tag/property parsing and resolution from add

  • Refactor src/main/logseq/cli/command/add.cljs to expose reusable helpers for:
    • parse-tags-option, parse-properties-option (for update)
    • resolve-tags, resolve-properties (for update)
    • Tag/property identifier normalization functions if needed for remove vectors
  • Keep the parsing behavior exactly consistent with add block.
  • Add new helper in add (or a shared namespace) to parse remove vectors:
    • parse-tags-vector-option (vector of tag identifiers)
    • parse-properties-vector-option (vector of property identifiers)

3) Update top-level command registry

  • Add update to src/main/logseq/cli/commands.cljs table and summary groups.
  • Remove move from the command registry and help output (no alias/compat).

4) Update parse/validation logic

  • In finalize-command (src/main/logseq/cli/commands.cljs):
    • Add update-specific validation for sources, targets, and update/remove options.
    • Reuse update-command/invalid-options?.
    • Allow missing target when update/remove options are present.
    • Keep error messaging aligned with existing CLI patterns.

5) Implement update action building

  • build-action returns a combined action with:
    • :type :update-block
    • :id/:uuid for source, optional target selectors, :pos default first-child when move is requested
    • :update-tags, :update-properties, :remove-tags, :remove-properties
  • execute-update:
    • Resolve source; resolve target only when move is requested.
    • Build a vector of outliner ops, in order: move (if present), then remove tags/properties, then update/add tags/properties (order can be adjusted if needed for predictable results).
    • Use :thread-api/apply-outliner-ops once with all ops.

6) Update output formatting

  • src/main/logseq/cli/format.cljs:
    • Add format-update-block to describe move + updates in a concise line.
    • Update dispatcher to handle :update-block.

7) Tests

  • Unit tests in src/test/logseq/cli/commands_test.cljs:
    • Parsing: update accepts --id/--uuid, optional target, and new options.
    • Validation: missing source, invalid target selector combinations, invalid pos, invalid EDN options.
    • Ensure that update without target but with update/remove options is accepted.
  • Format tests in src/test/logseq/cli/format_test.cljs for :update-block.
  • Integration tests in src/test/logseq/cli/integration_test.cljs:
    • Move-only update should behave the same as current move.
    • Update tags/properties on an existing block.
    • Remove tags/properties and validate via show or query.

Open Questions

  • If only --pos is provided without any target selector, return the error: --pos is only valid when a target is provided.

Risks / Edge Cases

  • If tag/property parsing rules diverge from add, user experience becomes inconsistent. Refactor shared parsing to avoid drift.
  • Combining move and property updates in one apply-outliner-ops call needs to preserve correct operation order; keep move first unless property updates depend on position.
  • Ensure non-page validation for source and block target remains intact when refactoring.

Implementation Checklist (Concrete File Touches)

  • Add: src/main/logseq/cli/command/update.cljs.
  • Update: src/main/logseq/cli/commands.cljs (table, validation, action dispatch).
  • Update: src/main/logseq/cli/command/core.cljs (top-level summary group list to include update).
  • Update: src/main/logseq/cli/format.cljs (formatting for update).
  • Update: src/test/logseq/cli/commands_test.cljs.
  • Update: src/test/logseq/cli/format_test.cljs.
  • Update: src/test/logseq/cli/integration_test.cljs.

Verification

  • Run unit tests: bb dev:test -v logseq.cli.commands-test.
  • Run format tests: bb dev:test -v logseq.cli.format-test.
  • Run CLI integration tests (move/update subset): bb dev:test -v logseq.cli.integration-test.
  • Optional full suite: bb dev:lint-and-test.