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

134 lines
8.0 KiB
Markdown

# 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`.