IMP-5: Replace strings.Contains(arg, ":") heuristic with an allowlist of recognized attribute keys (ValidAttributeKeys). Colons in task descriptions (URLs, "Meeting: topic") are no longer misinterpreted as modifiers. Canonical key sets live in engine/keys.go and are shared across parseAddArgs, ParseFilter, and ParseModifier. ParseModifier now errors on unknown keys. IMP-4: delete command now loads the working set and resolves display IDs via GetTaskByDisplayID, matching the pattern used by done/modify. IMP-6: All action commands (done, delete, modify, start, stop) now return an error on no-match (stderr, exit 1). Previously done/delete printed to stdout and exited 0; start/stop had no check at all. Also adds requirements and design docs for the CLI UX improvements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
15 KiB
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 <uuid> 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(oropal undo) - Then task 3 returns to
pendingstatus, itsendtimestamp 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
- FR-1.1:
opal undoMUST revert the last mutating CLI action (done, delete, modify, add, start, stop). - FR-1.2:
opal <id> uncompleteMUST set a completed task back to pending and clear theendtimestamp. - FR-1.3: Undo history SHOULD persist across CLI invocations (stored in a local undo log file or DB table).
- FR-1.4: Undo SHOULD support at least the last 10 operations.
- FR-1.5:
opal undoMUST 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 undocan 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
- FR-2.1:
addMUST display the new task's display ID (not just UUID). - FR-2.2:
addMUST echo back all parsed modifiers so the user can verify. - FR-2.3: For recurring tasks,
addMUST show recurrence interval and the first instance's due date. - 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
- FR-3.1:
done,delete, andmodifyMUST list matched tasks (ID, description, key attributes) before the confirmation prompt. - FR-3.2: If more than 10 tasks match, show the first 10 and note "...and N more".
- 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
- FR-4.1:
deleteMUST load the working set and resolve display IDs the same waydoneandmodifydo. - 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 goalswith no modifiers
Functional Requirements
- FR-5.1: Quoted strings MUST be treated as description text, not parsed for modifiers.
- FR-5.2: Only tokens matching a known modifier pattern (
key:valuewherekeyis a recognized attribute likedue,priority,project,recur,status,wait,scheduled,until) SHOULD be treated as modifiers. - FR-5.3: Unknown
key:valuepatterns SHOULD be treated as description text, not silently dropped. - 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 otherkey:valuetokens 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
- FR-6.1: All action commands MUST return exit code 1 when no tasks match an explicit filter.
- FR-6.2: All action commands MUST print to stderr (not stdout) when no tasks match, to support scripting.
- FR-6.3: The message SHOULD be consistent:
Error: no tasks matched filter "<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
- FR-7.1: Completing a recurring task instance MUST display whether a new instance was created and its due date.
- FR-7.2: If no new instance was created (e.g., recurrence was cleared), the output MUST say so.
- FR-7.3:
infoon 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
- FR-8.1:
opal completion bash|zsh|fishMUST generate shell completion scripts (cobra has built-in support for this). - FR-8.2: Completions SHOULD cover: commands, report names,
+tagnames,project:values,priority:values, andstatus:values. - FR-8.3: Dynamic completions for tags and projects SHOULD query the database.
- FR-8.4: Setup instructions SHOULD be printed after
opal completion <shell>.
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
- FR-9.1: Due dates in reports MUST be shown as relative when within 14 days (e.g., "tomorrow", "in 3d", "2d ago").
- FR-9.2: Dates beyond 14 days SHOULD fall back to short absolute format (e.g., "Feb 28", "Mar 15").
- FR-9.3:
infoSHOULD show both absolute and relative (e.g.,Due: 2026-02-20 (in 2 days)). - 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
- FR-10.1:
done,delete,modify,start, andstopSHOULD support a--dry-runflag that lists matched tasks without acting. - FR-10.2: Dry-run output MUST match the same format as the confirmation listing (IMP-3), followed by "Dry run — no changes made."
- 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 infoshows 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 infoshows the linked note path
Functional Requirements
- FR-11.1:
opal <id> annotate "<text>"SHOULD add a timestamped note. - FR-11.2: Annotations MUST be visible in
infoandedit. - FR-11.3: Annotations MUST sync via the existing change log / sync system.
- FR-11.4:
opal <id> denotateCOULD remove the most recent annotation. - 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 thetaskstable. Each annotation is a JSON object withtimestampandtextfields. Stored as a JSON array, e.g.:This keeps annotations co-located with the task, avoids schema complexity, and syncs naturally via the existing change_log triggers.[ {"timestamp": 1708300000, "text": "Traced to token expiry in middleware"}, {"timestamp": 1708310000, "text": "note:debug-auth-issue"} ]
Open Questions
- Should annotations be searchable via filters (e.g.,
opal annotation:token list)? - Jade-depo integration: should
opal 3 annotate --note <title>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
- FR-12.1:
opal <id> logCOULD display the change history for a task. - 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 - FR-12.3: History MUST respect the existing change_log retention policy.
- FR-12.4: History SHOULD be surfaced in
infooutput (recent changes section) and available ineditas read-only comment lines.
IMP-13: Version Command
Priority: COULD
Functional Requirements
- FR-13.1:
opal version(oropal --version) MUST print the build version. - FR-13.2: Version SHOULD be set at build time via
ldflags, reading from aVERSIONfile in the repo root. - 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 |