API handlers were populating SetAttributes directly without appending to
AttributeOrder. Since Apply() only iterates AttributeOrder, all updates
via PUT/POST were silently dropped — causing edits to revert and tasks
to disappear on reload.
Adds Modifier.Set() helper, safety net in Apply()/ApplyToNew(), adds
description and status to applyNonDateAttribute, and fixes the
recurrence->recur key mismatch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Also fix selectedTask going stale after store updates by syncing
it from the store subscription instead of manual patching.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add --hard flag to `opal delete` for permanent removal and a new
`opal clean` command to bulk-purge soft-deleted tasks with optional
--older duration filter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bypass Execute() preprocessing for __complete/__completeNoDesc so Cobra's
built-in completion handles shell TAB without creating tasks. Add root
ValidArgsFunction for flexible syntax (e.g. "opal 1 de<TAB>" → delete),
attribute value completions (status:pending, priority:H, date synonyms),
and NoSpace directive on key: completions to avoid trailing space.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract shared code that was duplicated across functions:
- taskJSON struct (MarshalJSON/UnmarshalJSON) to package-level type
- scanTask(scanner) helper for GetTask/GetTasks (~70 identical lines)
- monthNames map for parseMonthName/parseDayAndMonth
- applyNonDateAttribute helper for Apply/ApplyToNew
- resolveDisplayID calls replace inline loops in FormatTaskListWithFormat
Replace O(n²) bubble sorts with sort.Slice in all four report sort
functions (sortByUrgency, NewestReport, NextReport, OldestReport).
Remove dead code: formatTimeWithColor (unused, also used time.Now()
instead of timeNow()), getCurrentTimestamp (unnecessary wrapper).
Remove ~20 comments that restated the next line of code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add migration v2: source column on change_log to distinguish local
vs sync-originated entries, preventing the echo loop where synced
tasks get re-pushed as local changes
- PushChanges handler now skips save when server version is newer
- Client PushChanges/pushQueuedChanges collect and report marshal errors
instead of silently dropping them
- De-duplicate getLocalChanges/getLastSyncTime into exported sync
package functions
- Fix logConflict winner detection via pointer identity instead of
fragile UUID+timestamp comparison
- Fix sync down to actually parse, save, and tag-sync pulled changes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Type headers as Record<string, string> in apiRequest (client.js)
- Annotate SyncResult on result object, cast catch vars to any (sync.js)
- Widen sync.push param to Partial<Task>[] (endpoints.js)
- Fix parse() return type to reflect {task?: Task} shape (endpoints.js)
- Narrow add() param from Partial<Task> to Task (tasks.js)
- Cast parseAndCreate result to Task (tasks.js)
- Type tasksByProject grouped object as Record<string, Task[]> (tasks.js)
svelte-check now reports 0 errors and 0 warnings.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Deduplicate API_BASE (was defined in both client.js and endpoints.js)
- Extract EMPTY_STATE and persist() helper in auth store (DRY)
- Extract updateByUuid() in tasks store, normalize to .map() pattern
- Remove unused getQueueSize(), Select.svelte, and lib/index.js
- Modernize uuid.js to prefer crypto.randomUUID()
- Strip ~60 redundant comments that restated self-evident code
No behavior changes. Build passes, pre-existing type errors unchanged.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add fly transition back to Toast component and move cleanup from
onDestroy to onMount return for SSR safety. Remove debug console.logs
from page orchestrator.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 33 new test functions covering annotations, undo system, history
formatting, relative date display, and weekday parsing pipeline. Fix ISO
date parsing to use ParseInLocation instead of Parse to respect the
parser's timezone context.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
onDestroy runs during SSR in Svelte 5, causing document reference
errors. The onMount return function only runs on client teardown.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The $: reactive block runs during SSR component init. Use afterUpdate
with a mounted guard instead so document is only accessed client-side.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire BottomSheet+TaskDetail, undo Toast, ConfirmDialog for delete,
and start/stop handlers into +page.svelte.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- SwipeAction: bidirectional with onSwipeRight/onSwipeLeft, dual backgrounds
- TaskItem: onTap for bottom sheet, onStartStop, active border + pill
- TaskDetail: full field layout with inline editing, action buttons
- TaskList: passes onTap and onStartStop through to TaskItem
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add generic BottomSheet, Toast, and ConfirmDialog components.
Add startTask/stopTask optimistic methods to tasks store.
Add --color-active-bg/text theme tokens for all three themes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dedicated command that sets status back to pending and clears End time.
Unlike undo, works on any completed task regardless of when it was
completed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use GenZshCompletion (native zsh) instead of the old bash-wrapper method.
Use GenBashCompletionV2 for modern bash completions. Fix help text to show
proper fpath-based zsh installation instead of `source`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add completion command for bash/zsh/fish/powershell generation. Organize
help text using Cobra command groups (Task Commands, Reports, Other).
Register dynamic ValidArgsFunction on filter-accepting commands to
suggest +tag and project:name completions from the database.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add engine/history.go with GetTaskHistory and diff-style FormatTaskHistory
that compares consecutive change_log entries to show only what changed.
Add cmd/log.go command for full task history. Integrate last 5 history
entries into FormatTaskDetail (info view) as a "Recent Changes" section.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add annotations as JSON column on tasks table with Annotate/Denotate
methods and CLI commands. Add undo system backed by change_log with
lightweight undo_stack table (capped at 10 entries). All mutating CLI
commands (add, done, delete, modify, start, stop) now record undo
entries. Undo restores prior task state from change_log data.
Schema changes (in v1 migration):
- annotations TEXT column on tasks
- undo_stack table
- annotations field in change_log triggers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a persistent --dry-run flag that shows matched tasks without
performing mutations. Supported on done, delete, modify, start, and stop
commands. Also fixes preprocessArgs to skip flag-like args when
identifying commands, preventing flags from being treated as filters.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add relative date formatting (today, tomorrow, in 3d, etc.) for list and
detail views. Add structured feedback helpers for add/complete/delete
operations showing display IDs and parsed modifiers. Change Complete() to
return spawned recurring instance so callers can display recurrence info.
Add AppendTask to working set for immediate display ID assignment.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
opal version (and opal --version) prints version, commit, and build
date. Values are set via ldflags at build time; defaults to "dev" for
local builds. VERSION file added at 0.1.0.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
import.meta.env.DEV is already true during `bun run dev` and false in
production builds, so a separate env var is unnecessary.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add --dev flag to `opal server start` that disables auth (injects
userID=1 for all requests) and exposes a /auth/dev-session endpoint,
so the frontend can develop against a real backend without OAuth
config. Remove VITE_MOCK_MODE and all mock data/branches from the
frontend stores. Add scripts/dev.sh to start both services locally.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Load config eagerly during server startup so sortByUrgency never
hits a nil config. Add nil-guard in BuildUrgencyCoefficients as
belt-and-suspenders defense. Fix OAuth callback to support both
GET and POST, and resolve issuer URLs properly with path.Dir.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
LoadConfig() tried to create directories and write opal.yml as a side
effect of loading config. On the server (where /etc/opal is in systemd
ReadOnlyPaths), this failed, returning nil. All internal GetConfig()
callers discarded the error, passing nil to BuildUrgencyCoefficients()
which panicked on nil dereference.
Redesign the config system with layered, read-only loading:
- Defaults (always present) → YAML file (if exists) → OPAL_ env vars
- LoadConfig never writes to the filesystem or returns nil
- File creation moved to explicit InitConfig() for CLI first-run/setup
- SaveConfig uses yaml.Marshal instead of manual field-by-field Viper
calls, eliminating the three-place maintenance burden
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove ThemeSwitcher from header (already accessible via settings)
- Increase pill padding and font size for better tap targets
- Guard non-cancelable touchmove preventDefault in SwipeAction
- Restyle settings page with grid-area layout and inline sign-out button
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix latent API bug where multi-word fields (RecurrenceDuration, ParentUUID,
CreatedAt) serialized as PascalCase, breaking the frontend. Add explicit
snake_case json tags and custom MarshalJSON/UnmarshalJSON on Task, Status,
and APIKey to emit unix timestamps and string status codes.
Add Urgency float64 as a derived field on Task, populated via
PopulateUrgency helper in all handlers before serialization. The report
engine's sortByUrgency now also retains the computed score.
Frontend updated with urgency type, color-coded badge in TaskItem, and
mock data values.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace flexbox layout with CSS grid using named grid-areas for responsive
content containment. Gutters collapse naturally on small screens via
min(--content-max-width, 100%). Anchor ReportPicker to its trigger button
using CSS anchor positioning instead of fixed viewport offsets.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract CreateRecurringTask into engine package for reuse by both CLI
and API. Add POST /tasks/parse endpoint for CLI-style input parsing.
Remove FK constraint on change_log to preserve history after task
deletion. Update web frontend to filter completed tasks from view and
add mock mode support for development.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three holistic design directions with CSS custom properties, a theme
store persisted to localStorage, and a live switcher in both the header
(cycle button) and settings page (card selector). Also fixes checkbox
checkmark alignment and adds back navigation from settings.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a task is completed via checkbox tap, it fades out and collapses
before being removed from the list, matching the swipe-to-complete
animation behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace multi-page task management with single-screen layout: Header
with report picker at top, scrollable TaskList in the middle, and
InputBar with property pills fixed at the bottom. Owns state for
active report, task loading, input parsing, and task completion.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TaskItem: remove onClick navigation, wrap in SwipeAction for
swipe-to-complete, update priority colors (H=red, M=amber, L=gray,
default=hidden), add due-today amber color.
TaskList: accept activeReport prop for context-aware empty states,
replace onToggle/onTaskClick with onComplete, make scrollable with
flex:1 and overflow-y:auto.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements right-swipe-to-complete with angle-based lock-in (horizontal
must exceed 2x vertical), 100px threshold, green checkmark background
reveal, and CSS transition for snap-back and completion animation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
InputBar provides fixed-to-bottom text input with Enter to submit,
blur-delay (150ms) for pill interaction, and cursor-aware text
insertion. PropertyPills shows 8 modifier pills (Due, Pri, Project,
Tag, Recur, Scheduled, Wait, Until) on input focus.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>