# Opal CLI: User Experience Improvements **Status:** Draft — awaiting feedback **Date:** 2026-02-18 --- ## Problem Statement Opal's CLI is functional and expressive, but several gaps in feedback, safety, and discoverability make daily use rougher than it needs to be. These improvements target the person who uses `opal` 10+ times a day — reducing friction, preventing mistakes, and surfacing information at the right moment. --- ## Proposed Improvements ### IMP-1: Undo / Uncomplete **Priority:** MUST **Noted in:** `opal-web/BUGS.md` (missing uncomplete feat) #### Problem Accidentally completing or deleting the wrong task has no quick recovery path. The only workaround is `opal edit ` and manually setting status back to `pending`. This is slow, error-prone, and requires knowing the UUID. #### User Stories **US-1.1** As a user, I want to uncomplete a task so that I can recover from accidental completions. - **Given** task 3 was just completed - **When** I run `opal 3 uncomplete` (or `opal undo`) - **Then** task 3 returns to `pending` status, its `end` timestamp is cleared, and it reappears in my default report **US-1.2** As a user, I want a generic `undo` that reverts my last action so that I don't need to know the exact reverse command. - **Given** I just ran `opal 5 delete` - **When** I run `opal undo` - **Then** task 5 is restored to its previous status #### Functional Requirements 1. FR-1.1: `opal undo` MUST revert the last mutating CLI action (done, delete, modify, add, start, stop). 2. FR-1.2: `opal uncomplete` MUST set a completed task back to pending and clear the `end` timestamp. 3. FR-1.3: Undo history SHOULD persist across CLI invocations (stored in a local undo log file or DB table). 4. FR-1.4: Undo SHOULD support at least the last 10 operations. 5. FR-1.5: `opal undo` MUST display what was reverted (e.g., `Undone: task 3 "Buy milk" restored to pending`). #### Design Decisions - **Scope:** Local only — undo does NOT propagate across sync boundaries. - **Undo `add`:** Deletes the created task entirely (hard delete, not soft). - **Multi-level:** `opal undo` can be called repeatedly to walk back through the last 10 operations (stack-based, LIFO). --- ### IMP-2: Better Feedback After `add` **Priority:** MUST #### Problem `opal add Buy groceries due:tomorrow +errand` prints: ``` Created task 8f3a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c ``` The UUID is meaningless for subsequent commands. The user can't confirm their modifiers were parsed correctly without running a separate `list` or `info`. #### User Stories **US-2.1** As a user, I want to see the display ID and parsed attributes after adding a task so that I can confirm it was created correctly and reference it immediately. - **Given** I run `opal add Buy groceries due:tomorrow +errand` - **When** the task is created - **Then** I see output like: ``` Created task 3 — "Buy groceries" Due: tomorrow (2026-02-19) Tags: errand Priority: default ``` #### Functional Requirements 1. FR-2.1: `add` MUST display the new task's display ID (not just UUID). 2. FR-2.2: `add` MUST echo back all parsed modifiers so the user can verify. 3. FR-2.3: For recurring tasks, `add` MUST show recurrence interval and the first instance's due date. 4. FR-2.4: The display ID shown MUST be valid for immediate use (e.g., `opal 3 done`). --- ### IMP-3: Show Matched Tasks in Confirmations **Priority:** MUST #### Problem Destructive commands (`done`, `delete`, `modify`) prompt with only a count: ``` About to complete 3 tasks. Proceed? (y/N): ``` The user can't verify *which* 3 tasks will be affected without cancelling and running a separate `list` command with the same filter. #### User Stories **US-3.1** As a user, I want to see the list of affected tasks before confirming a bulk action so that I can verify I'm not making a mistake. - **Given** I run `opal +errand done` - **When** 3 tasks match - **Then** I see: ``` About to complete 3 tasks: 1 Buy groceries due:tomorrow +errand 4 Return library books due:fri +errand 7 Pick up dry cleaning +errand Proceed? (y/N): ``` #### Functional Requirements 1. FR-3.1: `done`, `delete`, and `modify` MUST list matched tasks (ID, description, key attributes) before the confirmation prompt. 2. FR-3.2: If more than 10 tasks match, show the first 10 and note "...and N more". 3. FR-3.3: Single-task operations (e.g., `opal 3 done`) SHOULD still show the task description for verification but skip the y/N prompt. --- ### IMP-4: Fix `delete` Not Resolving Display IDs **Priority:** MUST (bug fix) #### Problem `delete` calls `engine.GetTasks(filter)` directly without loading the working set to resolve display IDs. This means `opal 3 delete` may not resolve ID 3 correctly, unlike `done` and `modify` which both load the working set. #### Functional Requirements 1. FR-4.1: `delete` MUST load the working set and resolve display IDs the same way `done` and `modify` do. 2. FR-4.2: All action commands (done, delete, modify, start, stop, info, edit) MUST use the same ID resolution path. --- ### IMP-5: Handle Colons in Descriptions **Priority:** MUST #### Problem `parseAddArgs` treats any argument containing `:` as a modifier. This silently drops or misparses descriptions containing colons: ``` opal add "Meeting: discuss Q3 goals" # "Meeting:" parsed as modifier opal add Fix bug in http://example.com # "http:" parsed as modifier ``` There's no escaping mechanism or error message — the description is silently truncated. #### User Stories **US-5.1** As a user, I want to include colons in task descriptions so that I can write natural language without worrying about parser conflicts. - **Given** I run `opal add "Meeting: discuss Q3 goals"` - **When** the task is created - **Then** the description is `Meeting: discuss Q3 goals` with no modifiers #### Functional Requirements 1. FR-5.1: Quoted strings MUST be treated as description text, not parsed for modifiers. 2. FR-5.2: Only tokens matching a known modifier pattern (`key:value` where `key` is a recognized attribute like `due`, `priority`, `project`, `recur`, `status`, `wait`, `scheduled`, `until`) SHOULD be treated as modifiers. 3. FR-5.3: Unknown `key:value` patterns SHOULD be treated as description text, not silently dropped. 4. FR-5.4: If a token is ambiguous, prefer treating it as description text. #### Design Decisions - **Allowlist approach:** Only recognized attribute keys (`due`, `priority`, `project`, `recur`, `status`, `wait`, `scheduled`, `until`) are treated as modifiers. All other `key:value` tokens are treated as description text. --- ### IMP-6: Consistent Error on No-Match **Priority:** SHOULD #### Problem Action commands behave inconsistently when no tasks match a filter: | Command | No-match behavior | Exit code | |----------|-------------------------------------|-----------| | `done` | Prints "No tasks matched." | 0 | | `delete` | Prints "No tasks matched." | 0 | | `modify` | Returns error "no tasks matched" | 1 | | `start` | (unknown — needs verification) | ? | | `stop` | (unknown — needs verification) | ? | #### Functional Requirements 1. FR-6.1: All action commands MUST return exit code 1 when no tasks match an explicit filter. 2. FR-6.2: All action commands MUST print to stderr (not stdout) when no tasks match, to support scripting. 3. FR-6.3: The message SHOULD be consistent: `Error: no tasks matched filter ""`. --- ### IMP-7: Recurring Task Feedback **Priority:** SHOULD #### Problem Completing a recurring task instance gives no indication about recurrence: ``` $ opal 3 done Completed 1 task(s). ``` The user doesn't know if a next instance was spawned, when it's due, or whether the recurrence is still active. #### User Stories **US-7.1** As a user, I want to see recurrence information when completing a recurring task so that I know the schedule is continuing. - **Given** task 3 is a recurring weekly task - **When** I run `opal 3 done` - **Then** I see: ``` Completed task 3 — "Weekly review" Next instance created — due: 2026-02-25 (in 7 days) ``` #### Functional Requirements 1. FR-7.1: Completing a recurring task instance MUST display whether a new instance was created and its due date. 2. FR-7.2: If no new instance was created (e.g., recurrence was cleared), the output MUST say so. 3. FR-7.3: `info` on a recurring instance SHOULD show the recurrence pattern and parent template UUID. --- ### IMP-8: Shell Completions **Priority:** SHOULD #### Problem No tab completion exists for commands, report names, project names, or tag names. For a CLI with 14 commands, 13 report names, and user-defined projects and tags, discoverability is poor. #### Functional Requirements 1. FR-8.1: `opal completion bash|zsh|fish` MUST generate shell completion scripts (cobra has built-in support for this). 2. FR-8.2: Completions SHOULD cover: commands, report names, `+tag` names, `project:` values, `priority:` values, and `status:` values. 3. FR-8.3: Dynamic completions for tags and projects SHOULD query the database. 4. FR-8.4: Setup instructions SHOULD be printed after `opal completion `. --- ### IMP-9: Relative Dates in CLI Reports **Priority:** SHOULD #### Problem CLI report tables likely show absolute dates (`2026-02-20`). When scanning a task list, relative dates ("in 2d", "yesterday", "3w ago") are faster to parse at a glance. The web UI already uses relative dates. #### Functional Requirements 1. FR-9.1: Due dates in reports MUST be shown as relative when within 14 days (e.g., "tomorrow", "in 3d", "2d ago"). 2. FR-9.2: Dates beyond 14 days SHOULD fall back to short absolute format (e.g., "Feb 28", "Mar 15"). 3. FR-9.3: `info` SHOULD show both absolute and relative (e.g., `Due: 2026-02-20 (in 2 days)`). 4. FR-9.4: Relative display COULD be togglable via config (`date_display: relative|absolute`). --- ### IMP-10: Dry-Run / Preview for Action Commands **Priority:** SHOULD #### Problem Before running `opal +errand done`, the user often runs `opal +errand list` first to preview. This is a two-step workflow that could be one step. #### Functional Requirements 1. FR-10.1: `done`, `delete`, `modify`, `start`, and `stop` SHOULD support a `--dry-run` flag that lists matched tasks without acting. 2. FR-10.2: Dry-run output MUST match the same format as the confirmation listing (IMP-3), followed by "Dry run — no changes made." 3. FR-10.3: Dry-run MUST exit with code 0 if tasks matched, 1 if none matched. --- ### IMP-11: Task Annotations **Priority:** SHOULD #### Problem There's no way to attach notes to a task after creation. For long-running tasks like "Debug auth issue" or "Research hosting options", users want to record progress without cluttering the description. #### User Stories **US-11.1** As a user, I want to annotate tasks with timestamped notes so that I can track progress and findings over time. - **Given** task 3 exists - **When** I run `opal 3 annotate "Traced to token expiry in middleware"` - **Then** the annotation is saved with a timestamp - **And** `opal 3 info` shows the annotation under the task details **US-11.2** As a user, I want to link a task to a jade-depo note so that I can associate detailed research or write-ups with a task. - **Given** task 3 exists and a jade-depo note "debug-auth-issue.md" exists - **When** I run `opal 3 annotate --note debug-auth-issue` (or similar) - **Then** the task stores a reference to the jade-depo note - **And** `opal 3 info` shows the linked note path #### Functional Requirements 1. FR-11.1: `opal annotate ""` SHOULD add a timestamped note. 2. FR-11.2: Annotations MUST be visible in `info` and `edit`. 3. FR-11.3: Annotations MUST sync via the existing change log / sync system. 4. FR-11.4: `opal denotate` COULD remove the most recent annotation. 5. FR-11.5: Annotations SHOULD support linking to jade-depo notes (exact mechanism TBD — flag, URI scheme, or convention like `note:slug`). #### Design Decisions - **Storage:** JSON text column (`annotations`) on the `tasks` table. Each annotation is a JSON object with `timestamp` and `text` fields. Stored as a JSON array, e.g.: ```json [ {"timestamp": 1708300000, "text": "Traced to token expiry in middleware"}, {"timestamp": 1708310000, "text": "note:debug-auth-issue"} ] ``` This keeps annotations co-located with the task, avoids schema complexity, and syncs naturally via the existing change_log triggers. #### Open Questions - Should annotations be searchable via filters (e.g., `opal annotation:token list`)? - Jade-depo integration: should `opal 3 annotate --note ` verify the note exists in jade-depo, or just store the reference loosely? Loose coupling is simpler but can lead to stale links. --- ### IMP-12: Task History **Priority:** COULD #### Problem The `change_log` table records every mutation for sync, but there's no user-facing way to view a task's history. Useful for understanding what changed, when, and debugging unexpected state. #### Functional Requirements 1. FR-12.1: `opal <id> log` COULD display the change history for a task. 2. FR-12.2: Output SHOULD show timestamp, change type, and what changed: ``` 2026-02-18 09:15 created "Buy groceries" priority:D 2026-02-18 10:30 modified priority: D → H 2026-02-18 14:00 completed ``` 3. FR-12.3: History MUST respect the existing change_log retention policy. 4. FR-12.4: History SHOULD be surfaced in `info` output (recent changes section) and available in `edit` as read-only comment lines. --- ### IMP-13: Version Command **Priority:** COULD #### Functional Requirements 1. FR-13.1: `opal version` (or `opal --version`) MUST print the build version. 2. FR-13.2: Version SHOULD be set at build time via `ldflags`, reading from a `VERSION` file in the repo root. 3. FR-13.3: Output SHOULD include version, commit hash, and build date. --- ## Out of Scope - **GUI/TUI redesign** — this document covers CLI UX only. - **New task attributes** (e.g., estimated effort, dependencies between tasks). - **Multi-user features** — opal is a personal/household tool. - **Plugin/hook system** — not needed at this stage. - **Web UI changes** — covered separately in `opal-web/REQUIREMENTS.md`. --- ## Priority Summary | ID | Improvement | Priority | Effort | |--------|--------------------------------------|----------|----------| | IMP-1 | Undo / uncomplete | MUST | Medium | | IMP-2 | Better `add` feedback | MUST | Low | | IMP-3 | Show matched tasks in confirmations | MUST | Low | | IMP-4 | Fix `delete` display ID resolution | MUST | Low | | IMP-5 | Handle colons in descriptions | MUST | Medium | | IMP-6 | Consistent no-match error behavior | SHOULD | Low | | IMP-7 | Recurring task feedback | SHOULD | Low | | IMP-8 | Shell completions | SHOULD | Medium | | IMP-9 | Relative dates in CLI reports | SHOULD | Low | | IMP-10 | Dry-run flag for actions | SHOULD | Low | | IMP-11 | Task annotations | SHOULD | Medium | | IMP-12 | Task history | COULD | Medium | | IMP-13 | Version command | COULD | Trivial |