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>
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
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
- 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
- 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
- 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)
- Add WeekStartDay and DefaultDueTime fields to Config struct
- Set defaults: week_start_day=monday, default_due_time=empty
- Add GetWeekStart() and GetDefaultDueTime() helper methods
- Update ParseDate() to use configured week start day
- All tests passing