Commit Graph

69 Commits

Author SHA1 Message Date
joakim 32cc05a546 feat: add task history via log command and info integration
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>
2026-02-19 13:56:55 +01:00
joakim 7aaaa86a0a feat: add annotations, undo system, and schema updates
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>
2026-02-19 13:54:58 +01:00
joakim 6fb8a40a43 feat: add --dry-run flag to action commands
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>
2026-02-19 13:47:39 +01:00
joakim b02c40f716 feat: improve CLI output with relative dates, rich feedback, and recurring task info
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>
2026-02-19 13:44:56 +01:00
joakim 779da6ddfd feat: IMP-13 — add version command with build-time variables
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>
2026-02-19 13:38:25 +01:00
joakim f57baee6bc fix: IMP-4/5/6 — parser allowlist, delete ID resolution, consistent errors
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>
2026-02-19 13:37:33 +01:00
joakim a551f50cef ui updates 2026-02-18 23:16:00 +01:00
joakim f05d6e154e gradient gutters 2026-02-17 21:57:34 +01:00
joakim 4dfef88f19 refactor: use Vite built-in DEV flag instead of VITE_OAUTH_ENABLED
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>
2026-02-17 21:19:34 +01:00
joakim d51c6da18d feat: replace mock mode with real backend dev mode
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>
2026-02-17 17:07:34 +01:00
joakim 80ea17227d fix: prevent nil-panic on server and improve OAuth callback handling
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>
2026-02-17 16:40:53 +01:00
joakim c5a963bfd9 fix: make LoadConfig read-only to prevent panic on read-only filesystems
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>
2026-02-16 00:04:54 +01:00
joakim b3c30738bd fix(web): minor UI refinements across header, pills, swipe, and settings
- 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>
2026-02-15 14:59:58 +01:00
joakim 3bb2ef2759 feat: add JSON serialization, urgency field, and snake_case API contract
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>
2026-02-15 14:58:34 +01:00
joakim 924b66bc64 docs: add opal-task REST API reference
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 14:58:01 +01:00
joakim d86501e4e6 feat(web): use CSS grid-areas layout with anchor-positioned report picker
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>
2026-02-15 14:08:41 +01:00
joakim 78881e1b07 feat: add parse endpoint, refactor recurring tasks, and improve web task completion
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>
2026-02-14 23:49:20 +01:00
joakim 0352c22b4f feat(web): add theme system with Obsidian, Paper, and Midnight themes
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>
2026-02-14 22:36:07 +01:00
joakim 6c2fc6960a feat(web): add fade-out animation for checkbox task completion
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>
2026-02-14 17:41:28 +01:00
joakim 5e829320cf feat(web): rewrite home page as single-screen CLI-passthrough orchestrator
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>
2026-02-14 17:35:07 +01:00
joakim a6cd0ea41d feat(web): update TaskItem and TaskList for single-screen design
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>
2026-02-14 17:30:02 +01:00
joakim ac0fd6c72f feat(web): add SwipeAction touch gesture component
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>
2026-02-14 17:29:14 +01:00
joakim 2f83e8fe2f feat(web): add InputBar and PropertyPills components
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>
2026-02-14 17:28:48 +01:00
joakim 5ff75453bc feat(web): add Header and ReportPicker components
Header shows active report name (left) with dropdown chevron and gear
icon linking to /settings (right). ReportPicker uses native Popover API
with 11 reports grouped into Common, Time-based, Recurring, and Archive.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 17:28:06 +01:00
joakim 40b1f51f64 refactor(web): remove old routes and BottomNav for single-screen redesign
Delete /tasks/new, /tasks/[uuid], /projects, /tags routes and BottomNav
component. Simplify layout to slot-only with 100dvh flexbox. Remove
nav-height CSS variable and .page padding rules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 17:27:23 +01:00
joakim 83a9689e47 feat(web): add parse and report API endpoints with store methods
Add tasks.parse() and tasks.listByReport() to the API layer, and
loadReport() and parseAndCreate() to the tasks store with mock mode
support for the CLI-passthrough redesign.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 17:25:17 +01:00
joakim 7c97440366 fix: use PersistentPreRun instead of OnInitialize for proper command isolation
Replace cobra.OnInitialize with PersistentPreRun pattern to fix initialization
issues with setup and server commands. This follows Cobra best practices and
allows subcommands to properly override initialization behavior.

Problem:
- OnInitialize runs globally before command parsing
- os.Args check for 'setup' was fragile and broke with flags
- Setup wizard failed on server: 'unable to open database file: no such file
  or directory'

Solution:
- Use rootCmd.PersistentPreRun for initialization (inherited by all commands)
- setup and server commands override with their own PersistentPreRun
- Directory overrides still applied correctly in all cases
- Removes fragile os.Args parsing

Benefits:
- Works regardless of flag order
- Follows Cobra's intended design patterns
- Only 3 files modified (root.go, setup.go, server.go)
- Commands that need custom init (setup/server) simply override
- All other commands get automatic initialization
- Cleaner, more maintainable code

Testing:
- ✓ opal setup works without initialization errors
- ✓ opal list initializes database correctly
- ✓ First-run detection still works
- ✓ Directory overrides work with flags in any position
- ✓ Server command handles its own initialization
2026-01-06 22:18:09 +01:00
joakim 6a4fdb6850 fix: show default directories before asking to customize in setup wizard
Display the default config and data directories before prompting
the user if they want to customize them. This gives better context
for the decision.

Before: Only showed directories if user chose not to customize
After: Shows directories first, then asks if user wants to change them
2026-01-06 21:54:21 +01:00
joakim 140d9f7f25 feat: add interactive setup wizard for first-run configuration
Implement a comprehensive setup wizard to improve onboarding and
configuration experience for both personal and server deployments.

Key features:
- Interactive wizard with profile selection (personal/server/custom)
- Quick setup mode with sensible defaults
- First-run detection with helpful welcome message
- Directory configuration with validation
- Server OAuth/JWT configuration with auto-generation
- Environment file creation for server deployments
- Template generators for systemd service and env files

New commands:
- opal setup              # Interactive wizard
- opal setup --quick      # Quick setup with defaults
- opal setup --profile    # Use specific profile
- opal setup --show-systemd  # Show systemd template
- opal setup --show-env   # Show environment file template

Implementation:
- internal/wizard/prompts.go: Reusable prompt utilities
- internal/wizard/profiles.go: Profile definitions and templates
- cmd/setup.go: Main setup command implementation
- cmd/root.go: First-run detection and welcome message
- internal/engine/config.go: ConfigExists() and IsFirstRun() helpers

User experience:
- On first run, shows welcome message suggesting 'opal setup'
- Non-intrusive - creates defaults automatically if skipped
- Wizard guides through all configuration options
- Server setup includes OAuth/JWT configuration
- Environment file created with proper permissions (0600)
- Clear next steps displayed after completion
2026-01-06 21:49:13 +01:00
joakim 5d01c9f564 refactor: implement configurable directory structure with XDG support
Separate configuration from data storage and make paths configurable
via environment variables and command-line flags. This improves
Unix/Linux compliance and supports both development and production
deployments.

Key changes:
- Separate config dir (opal.yml) from data dir (database, logs)
- Support XDG Base Directory specification
- Add --config-dir and --data-dir flags
- Environment variables: OPAL_CONFIG_DIR, OPAL_DATA_DIR, OPAL_DB_PATH
- Smart fallback: /etc/opal, /var/lib/opal -> ~/.config/opal, ~/.local/share/opal
- Server mode validates required OAuth/JWT environment variables
- Update naming from 'jade' to 'opal' throughout
- Update systemd service name to 'opal.service'
- Add migration guide in README

Default paths:
- Config: /etc/opal (fallback: ~/.config/opal)
- Data: /var/lib/opal (fallback: ~/.local/share/opal)

Files modified:
- internal/engine/config.go: New directory resolution logic
- internal/engine/database.go: Auto-create data directory
- cmd/root.go: Add global flags for directory overrides
- cmd/server.go: Add configuration validation
- cmd/sync.go, internal/sync/*: Use new path helper functions
- tests: Update to use directory overrides
- docs: Update deployment guide and README
2026-01-06 20:46:29 +01:00
joakim 7ea78d3b54 docs: add comprehensive deployment guide and Caddy configuration
- Create detailed deployment documentation
- Add Caddyfile.example with security headers and API proxy
- Document SystemD service setup for Go API
- Include database backup strategy
- Add troubleshooting guide
- Document OAuth configuration steps
- Add build and deployment commands
- Test production build successfully (340KB static site)
2026-01-06 16:19:34 +01:00
joakim 8145645252 feat(pwa): complete PWA configuration
- Generate PWA icons in multiple sizes (72-512px)
- Create favicon.svg with app branding
- Add offline.html fallback page
- Update app.html with PWA meta tags
- Add theme-color and apple-touch-icon
- Configure viewport-fit for mobile notches
- PWA manifest already configured in vite.config.js
- Service worker caching already configured
2026-01-06 16:17:57 +01:00
joakim e8c6dd3930 feat(frontend): implement task CRUD functionality
- Create TaskItem component with checkbox, meta info, tags
- Create TaskList component with loading/empty states
- Update home page with task list and filter toggle (pending/completed)
- Add new task form with description, project, priority, due date, tags
- Add task detail placeholder page
- Implement task toggle (complete/uncomplete)
- Add filter bar to switch between pending and completed tasks
- Support for optimistic UI updates and offline queueing
- Visual indicators for priority, due dates, overdue tasks
- Mobile-optimized list items with proper touch targets
2026-01-06 16:16:44 +01:00
joakim 6b146c16a8 feat(frontend): add core UI components and pages
- Add global CSS with mobile-first styling and CSS custom properties
- Create base UI components: Button, Input, Checkbox, Select
- Add BottomNav component with icons for Tasks, Projects, Tags, Settings
- Update app layout to include BottomNav and auth handling
- Create Settings page with API key input and sync controls
- Create auth pages: /auth/login (OAuth) and /auth/callback
- Add placeholder pages for Projects and Tags
- Implement manual API key authentication for MVP testing
- Add logout functionality and user info display
- Support for safe-area-inset (mobile notches)
2026-01-06 16:14:24 +01:00
joakim d99e158a8c feat(frontend): add API client and Svelte stores
- Create API client with auto-retry and token refresh support
- Add comprehensive API endpoints for tasks, tags, projects, sync, and auth
- Implement authStore for authentication state management
- Implement tasksStore with optimistic updates and offline queue
- Add derived stores for filtered task views (pending, completed, by project)
- Implement syncStore for managing sync state and queue
- Add client ID generation and persistence for sync tracking
2026-01-06 15:45:13 +01:00
joakim 41795d1827 feat(frontend): setup frontend foundation
- Install dependencies: date-fns, @vite-pwa/sveltekit, workbox-window
- Configure Vite with PWA support and manifest
- Update SvelteKit config for static adapter with fallback
- Create environment files (.env, .env.example, .env.production)
- Create directory structure for lib, components, routes
- Add JSDoc type definitions for Task, User, AuthTokens, etc.
- Add utility functions: storage (localStorage wrapper), uuid, dates, sync-queue
- Configure PWA with icons, theme colors, and caching strategies
2026-01-06 15:43:39 +01:00
joakim 4eb18388db feat(backend): add OAuth2/JWT authentication support
- Add OAuth2 client for Authentik integration
- Implement JWT token generation and validation
- Add refresh token support with database storage
- Update database schema with oauth_subject, oauth_provider, and refresh_tokens table
- Create auth package with config, jwt, oauth, and token management
- Add OAuth endpoints: /auth/login, /auth/callback, /auth/refresh, /auth/logout
- Update AuthMiddleware to support both JWT and API key authentication
- Add user helper functions for OAuth user creation and retrieval
- Add .env.example with OAuth configuration template

API keys still work for CLI compatibility while JWT tokens support web/mobile clients.
2026-01-06 15:42:03 +01:00
joakim e506d76e6a docs: add Authentik OAuth2 setup guide for opal-web 2026-01-06 15:39:29 +01:00
joakim 5b660c3c1c Fix working set IDs to match display order
BREAKING CHANGE: BuildWorkingSet() now accepts []*Task instead of *Filter

Problem:
- Working set IDs were assigned based on database query order (unsorted)
- Reports displayed tasks in sorted order (by urgency)
- Result: IDs didn't match displayed task positions (ID 1 wasn't first task)

Solution:
- Changed BuildWorkingSet() to accept pre-sorted task slice
- Reports now pass sorted tasks to BuildWorkingSet()
- IDs are assigned sequentially to match display order (1, 2, 3...)

Behavior:
- Reports rebuild working set on every execution with fresh IDs
- Task operations (done, modify, info) use saved working set IDs
- After completing a task, re-running report renumbers remaining tasks

Example:
  Before:  opal list shows ID 1 = low urgency task (wrong)
  After:   opal list shows ID 1 = highest urgency task (correct)

Tested scenarios:
✓ List report: IDs 1-N match urgency order
✓ Next report: IDs 1-5 match top urgent tasks
✓ Task completion: IDs renumber correctly after removal
✓ Multiple operations: Use saved working set (correct behavior)
✓ Different reports: Each builds own sequential IDs
2026-01-06 14:54:01 +01:00
joakim 8f6db4672a Implement urgency system with TaskWarrior-inspired calculation
- Add urgency calculation based on multiple factors:
  * Due date (linear scale: overdue=12.0, today=10.0, week=6.0, 2weeks=2.0)
  * Priority (H=6.0, M=3.9, D=1.8, L=0.0)
  * Age (0-2.0 over 365 days)
  * Active status (+4.0 boost)
  * Waiting status (-3.0 penalty)
  * Tags (+1.0 with count modifier)
  * Project assignment (+1.0)
  * Configurable urgent tag (default 'next', +15.0)

- Replace priority column with urgency in all reports
  * Display as decimal with 1 decimal place
  * 4-tier color coding: ≥10 (bright red), ≥5 (red), ≥2 (yellow), <2 (cyan)
  * Minimal format color-coded by urgency

- Add default urgency sorting to all reports
  * list, minimal, active, ready, overdue reports sort by urgency
  * newest/oldest keep date-based sorting

- Implement 'next' report
  * Shows most urgent ready tasks
  * Configurable limit (default 5)
  * Only includes tasks ready to work on (no future wait/scheduled)

- Add urgency display to info command
  * Shows urgency score alongside priority

- All urgency coefficients configurable via config
  * Adjusted defaults for Opal's simpler model (no blocking/annotations)
  * Configurable urgent tag name (not hardcoded to 'next')

Priority order maintained: High > Medium > Default > Low
2026-01-06 14:32:44 +01:00
joakim 1c3186a342 feat: Add visual progress indicators to sync operations
- Implement ProgressReporter interface with InteractiveProgress and NoOpProgress
- Add real-time progress bars using go-pretty/progress library
- Track 6 sync phases: connection test, queue push, pull, parse, apply, and server push
- Add --quiet flag to suppress progress output
- Auto-detect TTY to disable progress when piped/redirected
- Show task-level progress during apply phase with descriptions
- Display percentage complete and elapsed time for each phase
2026-01-05 23:20:27 +01:00
joakim 59861bc3bf Implement report system and fix template task filtering
- Fix template task filtering bug: templates now hidden from all reports
  except 'template' and 'all' reports, even when using custom filters
- Add support for status:template filter to explicitly show templates
- Implement comprehensive report system with 12 predefined reports:
  * active - Started tasks
  * all - All tasks including templates
  * completed - Completed tasks
  * list - Pending tasks (default)
  * minimal - Pending tasks in minimal format
  * newest - Most recent pending tasks
  * oldest - Oldest pending tasks
  * overdue - Overdue tasks
  * ready - Tasks ready to work on
  * recurring - Pending recurring instances
  * template - Recurring template tasks
  * waiting - Hidden/waiting tasks
- Replace list command with report-based architecture
- Add configurable default_report option (defaults to 'list')
- Add minimal display format (ID + description only)
- Support flexible syntax: 'opal <report> [filters]' or 'opal [filters] <report>'
- Add 'opal reports' command to list all available reports
2026-01-05 21:17:07 +01:00
joakim f5f7bc3ad7 feat: Complete key:value format implementation and fix tag sync
Implement complete key:value format parsing for change log entries and fix
critical tag synchronization issue from server to client.

Key Changes:

1. Shared Key:Value Parser (NEW: internal/engine/parser.go)
   - Created ParseKeyValueFormat() for both edit and sync operations
   - Supports flexible whitespace: 'key:value' and 'key: value'
   - Handles comment skipping for edit files
   - Consolidates parsing logic (DRY principle)

2. Database Triggers - Tags Support (internal/engine/database.go)
   - Added tags to track_task_create trigger
   - Added tags to track_task_update trigger
   - Tags sorted alphabetically via SQL ORDER BY
   - Format: 'tags: alpha,bravo,charlie'

3. Task Creation - Tag Update Fix (internal/engine/task.go)
   - CreateTaskWithModifier() now triggers update after adding tags
   - Ensures tags appear in change log (UPDATE entry)
   - Fixes missing tags in initial CREATE entries

4. Edit Command - Use Shared Parser (cmd/edit.go)
   - Replaced custom parseEditedFile() with shared ParseKeyValueFormat()
   - Added tag sorting in parseTags()
   - Removed ~30 lines, improved maintainability

5. Sync Client - Complete Implementation (internal/sync/client.go)
   - NEW: applyChangeDataToTask() - parses all fields from change log
   - NEW: Helper functions for status, priority, tag parsing
   - FIXED: parseChanges() - sort by timestamp+ID before grouping
   - Added parent/child task ordering (prevents FK violations)
   - Enhanced tag sync in merge loop with task reload
   - Specific validation error messages per field

Critical Bug Fix:
- When CREATE and UPDATE have same timestamp, old code kept CREATE (no tags)
- New code sorts by ID as tiebreaker, ensuring UPDATE (with tags) is used
- Verified: Server->client tag sync now works correctly

Validation:
- Description must not be empty (both edit and sync)
- Recurrence validated (not negative, max 100 years)
- All timestamps parsed correctly (Unix epoch)
- Tags sorted alphabetically in all contexts

Testing:
- Fresh pull from server:  All tags present
- API-created tasks:  Tags sync correctly
- Local->server->client round-trip:  No data loss
- Same-second CREATE+UPDATE:  Correct entry processed
- Parent/child tasks:  Correct ordering

Files Changed:
- NEW: internal/engine/parser.go (+44 lines)
- Modified: internal/engine/database.go (+10 lines)
- Modified: internal/engine/task.go (+8 lines)
- Modified: cmd/edit.go (-25 lines net)
- Modified: internal/sync/client.go (+280 lines)
- Modified: srv/README.md (+1 line)

Total: +318 lines added, -25 removed, net +293 lines

This completes Phase 6: Full bidirectional sync with complete tag support.
2026-01-05 18:56:17 +01:00
joakim 4c54814eb5 docs: Phase 5 - Comprehensive deployment documentation
- Created detailed srv/README.md with:
  - Quick start guide
  - SystemD service setup instructions
  - Reverse proxy configuration (Caddy & Nginx)
  - Complete API endpoint reference
  - Client configuration examples
  - Troubleshooting guide
  - Security considerations
  - Future enhancement roadmap
- Updated main README.md with server & sync features
- Added sync command quick reference
- Documented offline support and conflict resolution
2026-01-05 16:21:09 +01:00
joakim 40c09d6a8a feat: Phase 4 - Offline & merge enhancements
- Added 'opal sync merge' command for initial database merge
- Support for merge strategies: prefer-local, prefer-server, smart (default)
- Offline queue already implemented in Phase 2
- Conflict warning display already implemented in Phase 2-3
- Full offline mode support with automatic queueing when server unreachable
2026-01-05 16:19:49 +01:00
joakim 944d628ca1 feat: Phase 3 - CLI sync commands
- Created comprehensive sync command suite:
  - 'opal sync init' - Configure sync with server (URL, API key)
  - 'opal sync status' - Show sync configuration and queue status
  - 'opal sync now' - Bidirectional sync with conflict resolution
  - 'opal sync up' - Push local changes to server
  - 'opal sync down' - Pull server changes to local
  - 'opal sync log' - View conflict resolution log
- Added interactive prompts for init (URL and API key)
- Automatic client ID generation (UUID)
- Display user-friendly sync results with emojis
- Support for viewing queued offline changes
- Integration with config system for persistent sync settings
2026-01-05 16:19:00 +01:00
joakim e6710eb19f feat: Phase 2 - Sync infrastructure
- Created sync client for communicating with API server
- Implemented conflict resolution strategies (last-write-wins, server-wins, client-wins)
- Added offline change queue for queuing changes when server is unreachable
- Implemented merge logic for local and remote task lists
- Added conflict logging to sync_conflicts.log
- Created bidirectional sync with pull/push operations
- Extended Config struct with sync settings (URL, API key, client ID, strategy, offline queue)
- Added SyncResult display with user-friendly output
- Sync handlers already implemented in Phase 1 (GetChanges, PushChanges)
2026-01-05 16:17:18 +01:00
joakim ba0cfc08e3 feat: Phase 1 - Core API server with authentication
- Added database schema for users, api_keys, sync_state, change_log, and sync_config
- Implemented API key generation and validation with bcrypt hashing
- Created Chi-based REST API server with endpoints for:
  - Task CRUD operations (create, read, update, delete)
  - Task actions (complete, start, stop)
  - Tag management (list, add, remove)
  - Projects listing
  - Health check endpoint
- Added middleware for authentication and CORS
- Implemented change log tracking with triggers (key:value format)
- Added configurable change log retention (default 30 days)
- Created server CLI commands (opal server start, opal server keygen)
- Dependencies added: golang.org/x/crypto/bcrypt, github.com/go-chi/chi/v5
2026-01-05 16:14:49 +01:00
joakim 9bde1aefea Migrate table formatting to go-pretty for proper UTF-8 and ANSI handling
Replace manual string-based table formatting with jedib0t/go-pretty/v6/table
library to fix alignment issues with Norwegian characters (æøå) and ANSI
color codes.

Changes:
- Migrate FormatTaskList() to use go-pretty table
- Migrate FormatTaskDetail() to use clean table format
- Migrate FormatProjects() and FormatTagCounts() for consistency
- Remove truncate() function (no longer needed)
- Configure StyleLight with Unicode box-drawing characters
- Set proper column widths and alignment

Fixes:
- Priority column now center-aligned under 'Pri'
- Norwegian characters (æøå) display and align correctly
- Tags column properly aligned
- Description field truncates at 40 chars with proper UTF-8 handling
- All ANSI color codes handled automatically
- Consistent formatting across all table views

All existing color functions work unchanged. UTF-8 and ANSI codes are
handled automatically by go-pretty's width calculation.
2026-01-05 13:41:46 +01:00
joakim 1d55f04a1f Fix recurring task modifier application bug
When creating recurring tasks, modifiers (due, wait, priority, etc) were
not being applied because AttributeOrder was not copied to the temporary
modifier. This caused all date attributes to be ignored.

Refactored addRecurringTask to:
- Create task structs directly instead of using CreateTask (avoiding
  premature saves)
- Use ApplyToNew() instead of Apply() for modifiers before first save
- Properly copy AttributeOrder when building the temporary modifier
- Save template and instance once with all fields correctly set

This ensures recurring tasks now properly have due dates, wait dates,
and other modifiers applied when created via 'opal add' or batch import.
2026-01-05 11:18:43 +01:00