onyx-tasks/PLAN.md
Claude 4459b339ff
Add output examples to CLI usage sections
Enhanced CLI Usage Examples in Phase 1 and Phase 2 with realistic
output examples showing:

Phase 1 (Core & CLI):
- Init command with success messages
- Add tasks with UUID confirmation and due date display
- List command showing task status, counts, and due dates
- Complete/edit/delete with confirmation messages
- Config and sort commands with feedback

Phase 2 (WebDAV Sync):
- Interactive setup with prompts and keychain confirmation
- Push/pull with file upload/download progress
- Automatic two-way sync with change indicators
- Status command showing connection, last sync, and pending changes

All examples now show:
- Command prompts ($)
- Realistic UUIDs
- Success indicators (✓)
- Progress/status information
- Colored output representation (checkmarks, arrows)
- Helpful feedback messages

Makes it much clearer what users can expect from the CLI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 21:23:41 +00:00

23 KiB

Bevy Tasks - Project Plan

Vision

A local-first, cross-platform tasks application inspired by Google Tasks. Built with Rust for high performance and true native support across Windows, Linux, macOS, iOS, and Android.

Core Principles:

  • Local-First: Your data, your folder, your control
  • Fast: Sub-second startup, instant response
  • Cross-Platform: Single codebase, all platforms

Data Format: Tasks stored as markdown files with YAML frontmatter (Obsidian-compatible) Storage: User selects folder location (e.g., ~/Documents/Tasks, ~/Dropbox/Tasks) Sync: Optional WebDAV for cross-device synchronization Architecture: Backend/frontend separation with CLI-first development


Resources


Phase 1: Core Library & CLI MVP

Goal: Build and validate the backend with a functional CLI

Why CLI First?

  • Test backend thoroughly before GUI complexity
  • CLI useful for power users and automation
  • Clean API boundaries
  • Easy to write comprehensive tests

Architecture

Cargo Workspace Structure

bevy-tasks/
├── Cargo.toml                    # Workspace definition
├── PLAN.md
├── README.md
├── crates/
│   ├── bevy-tasks-core/          # Core library (backend)
│   ├── bevy-tasks-cli/           # CLI frontend
│   └── bevy-tasks-gui/           # GUI frontend (Phase 3+)
└── docs/

Data Model

Tasks are stored as individual .md files with YAML frontmatter:

---
id: 550e8400-e29b-41d4-a716-446655440000
status: backlog
due: 2025-11-15T14:00:00Z
created: 2025-10-26T10:00:00Z
updated: 2025-10-26T12:30:00Z
parent: 550e8400-e29b-41d4-a716-446655440001
---

Task description and notes go here in **markdown** format.

- Can include lists
- Rich formatting
- Links, etc.

TaskStatus values:

  • backlog - Task not yet completed
  • completed - Task is done

In-Memory Model:

Task {
    id: Uuid,
    title: String,              // Derived from filename
    description: String,              // Markdown content
    status: TaskStatus,         // Backlog or Completed
    due_date: Option<DateTime>,
    created_at: DateTime,
    updated_at: DateTime,
    parent_id: Option<Uuid>,    // For subtasks
}

enum TaskStatus {
    Backlog,     // Not yet completed
    Completed,   // Done
}

TaskList {
    id: Uuid,
    title: String,              // Derived from folder name
    tasks: Vec<Task>,           // Ordered according to sort_order preference
    created_at: DateTime,
    updated_at: DateTime,
    sort_order: SortOrder,      // How to sort: Manual or ByDueDate
}

enum SortOrder {
    Manual,      // Use task_order from .listdata.json
    ByDueDate,   // Group by due_date, then sort by task_order within groups
}

AppConfig {
    local_path: PathBuf,
}

File System Structure

~/Documents/Tasks/           # User-selected folder
├── .metadata.json           # Global: list ordering, last opened list
├── My Tasks/                # Task list folder
│   ├── .listdata.json       # List metadata: task order, id, timestamps
│   ├── Buy groceries.md
│   └── Call dentist.md
└── Work/                    # Another task list
    ├── .listdata.json
    ├── Review PRs.md
    └── Team meeting prep.md

.metadata.json (root level):

{
  "version": 1,
  "list_order": ["list-uuid-1", "list-uuid-2"],
  "last_opened_list": "list-uuid-1"
}

.listdata.json (per list):

{
  "id": "list-uuid-1",
  "created_at": "2025-10-26T10:00:00Z",
  "updated_at": "2025-10-27T14:30:00Z",
  "sort_order": "manual",
  "task_order": [
    "task-uuid-1",
    "task-uuid-2",
    "task-uuid-3"
  ]
}

Sort Order Options:

  • "manual" - Tasks ordered by hand (uses task_order array)
  • "by_due_date" - Tasks automatically sorted by due date (tasks without due dates appear at end)

When sort_order is "manual", the task_order array defines the sequence. When sort_order is "by_due_date", tasks are grouped first and then sorted within each group by task_order.

App Configuration (separate from task data):

  • Windows: %APPDATA%/bevy-tasks/config.json
  • Linux: ~/.config/bevy-tasks/config.json
  • macOS: ~/Library/Application Support/bevy-tasks/config.json

Core Library API

pub struct TaskRepository {
    storage: Box<dyn Storage>,
}

impl TaskRepository {
    pub fn new(tasks_folder: PathBuf) -> Result<Self>;
    pub fn init(tasks_folder: PathBuf) -> Result<Self>;

    // Task operations
    pub fn create_task(&mut self, list_id: Uuid, task: Task) -> Result<Task>;
    pub fn get_task(&self, id: Uuid) -> Result<Task>;
    pub fn update_task(&mut self, task: Task) -> Result<()>;
    pub fn delete_task(&mut self, id: Uuid) -> Result<()>;
    pub fn list_tasks(&self, list_id: Uuid) -> Result<Vec<Task>>;

    // List operations
    pub fn create_list(&mut self, name: String) -> Result<TaskList>;
    pub fn get_lists(&self) -> Result<Vec<TaskList>>;
    pub fn delete_list(&mut self, id: Uuid) -> Result<()>;

    // Task ordering (modifies .listdata.json)
    pub fn reorder_task(&mut self, list_id: Uuid, task_id: Uuid, new_position: usize) -> Result<()>;
    pub fn get_task_order(&self, list_id: Uuid) -> Result<Vec<Uuid>>;

    // Sort preference (modifies .listdata.json)
    pub fn set_sort_order(&mut self, list_id: Uuid, sort_order: SortOrder) -> Result<()>;
    pub fn get_sort_order(&self, list_id: Uuid) -> Result<SortOrder>;
}

pub trait Storage {
    fn read_task(&self, list_id: Uuid, task_id: Uuid) -> Result<Task>;
    fn write_task(&mut self, list_id: Uuid, task: &Task) -> Result<()>;
    // ... more methods
}

Dependencies

Workspace Cargo.toml:

[workspace]
members = [
    "crates/bevy-tasks-core",
    "crates/bevy-tasks-cli",
    "crates/bevy-tasks-gui",
]
resolver = "2"

[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
uuid = { version = "1.0", features = ["serde", "v4"] }
chrono = { version = "0.4", features = ["serde"] }
anyhow = "1.0"
tokio = { version = "1.40", features = ["full"] }

bevy-tasks-core/Cargo.toml:

[package]
name = "bevy-tasks-core"
version = "0.1.0"
edition = "2024"

[dependencies]
serde = { workspace = true }
serde_json = "1.0"
serde_yaml = "0.9"        # YAML frontmatter
pulldown-cmark = "0.12"   # Markdown parsing
uuid = { workspace = true }
chrono = { workspace = true }
directories = "5.0"
anyhow = { workspace = true }

[dev-dependencies]
tempfile = "3.0"

bevy-tasks-cli/Cargo.toml:

[package]
name = "bevy-tasks-cli"
version = "0.1.0"
edition = "2024"

[[bin]]
name = "bevy-tasks"
path = "src/main.rs"

[dependencies]
bevy-tasks-core = { path = "../bevy-tasks-core" }
clap = { version = "4.5", features = ["derive", "env"] }
colored = "2.0"
indicatif = "0.17"
anyhow = { workspace = true }
tokio = { workspace = true }

Features

  • Cargo workspace setup
  • Data models (Task, TaskList, AppConfig)
  • Markdown file I/O with YAML frontmatter parsing
  • Local storage implementation
  • Repository pattern and public API
  • CLI: init command (user selects folder)
  • CLI: add command (create tasks)
  • CLI: list command (view tasks)
  • CLI: complete command (mark done)
  • CLI: delete command (remove tasks)
  • CLI: edit command (modify tasks)
  • Two sort modes: manual ordering and by due date
  • CLI: sort command (switch between manual/by-due-date)
  • Comprehensive unit and integration tests (>80% coverage)

CLI Usage Examples

# First run: initialize tasks folder
$ bevy-tasks init ~/Documents/Tasks
✓ Initialized tasks folder at ~/Documents/Tasks
✓ Created default list "My Tasks"

# Or use a cloud-synced folder
$ bevy-tasks init ~/Dropbox/Tasks
✓ Initialized tasks folder at ~/Dropbox/Tasks
✓ Created default list "My Tasks"

# Add tasks
$ bevy-tasks add "Buy groceries" --list "Personal"
✓ Created task "Buy groceries" (550e8400-e29b-41d4-a716-446655440000)

$ bevy-tasks add "Review PR #123" --list "Work" --due "2025-11-15"
✓ Created task "Review PR #123" (7f3a9c21-b8d2-4e5f-9a1c-3d8e7f6a2b1c)
  Due: 2025-11-15

# List all tasks
$ bevy-tasks list
My Tasks (3 tasks)
  [ ] Buy groceries
  [ ] Call dentist
  [] Pay bills

Work (2 tasks)
  [ ] Review PR #123 (due: 2025-11-15)
  [ ] Team meeting prep

# List tasks in specific list
$ bevy-tasks list --list "Work"
Work (2 tasks)
  [ ] Review PR #123 (due: 2025-11-15)
  [ ] Team meeting prep

# Complete a task
$ bevy-tasks complete 550e8400-e29b-41d4-a716-446655440000
✓ Completed task "Buy groceries"

# Edit a task
$ bevy-tasks edit 7f3a9c21-b8d2-4e5f-9a1c-3d8e7f6a2b1c
# Opens editor with task file
✓ Updated task "Review PR #123"

# Delete a task
$ bevy-tasks delete 550e8400-e29b-41d4-a716-446655440000
✓ Deleted task "Buy groceries"

# Change folder location later
$ bevy-tasks config set-folder ~/new/location
✓ Updated tasks folder location to ~/new/location
✓ Migrated 15 tasks from ~/Documents/Tasks

# Sort order
$ bevy-tasks sort manual --list "Work"
✓ Set sort order to "manual" for list "Work"

$ bevy-tasks sort by-due-date --list "Personal"
✓ Set sort order to "by due date" for list "Personal"

Deliverables

  • bevy-tasks-core library with stable API
  • Functional CLI that can manage tasks
  • Data persists as Obsidian-compatible .md files
  • Well-tested backend (>80% coverage)
  • Documentation for core library API

Development Setup

# Clone and build
git clone <repository-url>
cd bevy-tasks
cargo build

# Run tests
cargo test -p bevy-tasks-core

# Run CLI
cargo run -p bevy-tasks-cli -- init ~/test-tasks
cargo run -p bevy-tasks-cli -- add "Test task"
cargo run -p bevy-tasks-cli -- list

Phase 2: WebDAV Sync (Backend + CLI)

Goal: Enable cross-device synchronization via CLI

Architecture

WebDAV Integration

Add WebDAV support to bevy-tasks-core:

// Update AppConfig
AppConfig {
    local_path: PathBuf,             // User-selected tasks folder (required)
    webdav_url: Option<String>,
    webdav_credentials: Option<Credentials>,
    last_sync: Option<DateTime>,
    // Note: list_order and last_opened_list in .metadata.json at root of tasks folder
}

// Add sync methods to TaskRepository
impl TaskRepository {
    pub fn sync_push(&mut self) -> Result<SyncResult>;
    pub fn sync_pull(&mut self) -> Result<SyncResult>;
    pub fn sync_status(&self) -> Result<SyncStatus>;
}

Sync Strategy

  • Trigger: On app start (if connected), background timer (every 5 min), on modification (debounced)
  • Conflict Resolution: Last-write-wins with timestamp
  • Offline Support: Queue operations when offline, sync when online

Authentication

Primary: Platform Keychain via keyring crate

  • Store WebDAV username + password in system keychain
  • Key format: com.bevy-tasks.webdav.{server-domain}
  • Works on: Windows (Credential Manager), macOS (Keychain), Linux (Secret Service), iOS/Android (Keystore)

Fallback: Encrypted local storage if keychain unavailable

Dependencies

Add to bevy-tasks-core/Cargo.toml:

reqwest = { version = "0.12", features = ["json", "rustls-tls"] }
keyring = "3.0"
# TODO: Evaluate dav-client or implement custom WebDAV

Features

  • WebDAV client implementation in core library
  • Credential storage (platform keychain)
  • Bi-directional sync (push/pull)
  • Conflict resolution (last-write-wins)
  • Offline queue for pending operations
  • CLI: sync --setup command
  • CLI: sync --push command
  • CLI: sync --pull command
  • CLI: sync --status command
  • Progress indicators for sync operations

CLI Usage Examples

# Setup WebDAV
$ bevy-tasks sync --setup
WebDAV URL: https://nextcloud.example.com/remote.php/dav/files/username/Tasks
Username: myuser
Password: ********
✓ WebDAV credentials saved to system keychain
✓ Connection verified

# Push local changes to WebDAV server
$ bevy-tasks sync --push
Syncing to https://nextcloud.example.com/...
  Uploading My Tasks/.listdata.json
  Uploading My Tasks/Buy groceries.md
  Uploading Work/Review PR #123.md
✓ Pushed 3 files to WebDAV server

# Pull changes from WebDAV server
$ bevy-tasks sync --pull
Syncing from https://nextcloud.example.com/...
  Downloading Work/Team meeting notes.md
  Downloading Personal/Call mom.md
✓ Pulled 2 files from WebDAV server

# Automatic two-way sync
$ bevy-tasks sync
Syncing with https://nextcloud.example.com/...
  ↑ Uploading My Tasks/New task.md
  ↓ Downloading Work/Updated task.md
  = No changes for 15 files
✓ Sync complete

# Check sync status
$ bevy-tasks sync --status
WebDAV Server: https://nextcloud.example.com/remote.php/dav/files/username/Tasks
Status: Connected
Last sync: 2025-10-27 14:32:15
Local changes: 2 files modified
Remote changes: 0 files modified

Deliverables

  • Working WebDAV sync in backend
  • CLI can sync with remote WebDAV server
  • Reliable conflict resolution
  • Tested with Nextcloud, ownCloud

Phase 3: GUI MVP (Desktop)

Goal: Build graphical interface on desktop platforms

Architecture

Frontend Framework: egui

Decision: Use egui (immediate mode GUI) for MVP

Why egui?

  • Fast development with rich built-in widgets
  • Excellent text editing support out of the box
  • Small binary size (~2-3MB stripped)
  • Fast startup time (100-200ms target)
  • Mature and stable
  • Simple immediate-mode API
  • Cross-platform (desktop AND mobile)
  • Easy integration with bevy-tasks-core

GUI Crate Structure

crates/bevy-tasks-gui/
├── Cargo.toml
├── src/
│   ├── main.rs           # App entry point
│   ├── app.rs            # egui app setup
│   ├── ui/
│   │   ├── mod.rs
│   │   ├── screens/
│   │   │   ├── task_list.rs
│   │   │   ├── task_detail.rs
│   │   │   └── settings.rs
│   │   └── components/
│   │       ├── task_item.rs
│   │       ├── task_input.rs
│   │       └── list_selector.rs
│   └── state.rs          # UI state management
├── assets/
│   ├── fonts/
│   └── icons/
└── README.md

First Run Experience

  • Show folder picker dialog on first launch
  • User selects where to store tasks (e.g., ~/Documents/Tasks)
  • No default hidden directories
  • Remember choice in app config

App Configuration (Phase 3+)

Update AppConfig to include UI preferences:

AppConfig {
    local_path: PathBuf,             // From Phase 1
    webdav_url: Option<String>,      // From Phase 2
    webdav_credentials: Option<Credentials>,
    last_sync: Option<DateTime>,
    theme: Theme,                    // NEW: light/dark mode
    window_size: Option<(u32, u32)>, // NEW: remember window size
    last_opened_list: Option<Uuid>,  // NEW: restore last view
}

Dependencies

bevy-tasks-gui/Cargo.toml:

[package]
name = "bevy-tasks-gui"
version = "0.1.0"
edition = "2024"

[dependencies]
bevy-tasks-core = { path = "../bevy-tasks-core" }
anyhow = { workspace = true }

# egui for Phase 3-6
eframe = "0.31"  # egui framework with native windowing
egui = "0.31"    # Core egui library

Performance Strategy

Startup Sequence:

  1. Initialize eframe window (< 50ms)
  2. Load config from disk (< 20ms)
  3. Render empty UI (first frame < 100ms)
  4. Load current task list in background
  5. Update UI as tasks load
  6. Start WebDAV sync in background (if configured)

Target: < 200ms cold start on desktop

Optimizations:

  • Lazy data loading (load visible tasks first)
  • Background operations for sync
  • Efficient file I/O (stream large files)
  • Minimal dependencies

Features

  • egui framework integration
  • Folder picker dialog on first launch
  • Basic task list view
  • Create new tasks
  • Edit existing tasks
  • Delete tasks
  • Mark tasks complete/incomplete
  • Settings screen (change folder, WebDAV config)
  • Sync status indicators
  • Desktop support (Windows, Linux, macOS)

Deliverables

  • Functional desktop GUI app
  • Sub-200ms startup time
  • Clean, minimal UI
  • Feature parity with CLI

Build & Release

Distribution:

  • Linux: AppImage, .tar.gz
  • macOS: DMG
  • Windows: MSI, portable .exe

CI/CD: GitHub Actions for automated builds


Phase 4: Mobile Basic Support

Goal: Get app running on iOS and Android ASAP, validate architecture

Why Early Mobile?

  • De-risk mobile builds early in development
  • Test cross-platform architecture sooner
  • Validate egui on mobile
  • Get mobile-specific feedback early
  • Can dogfood on mobile while building desktop features

Architecture

Mobile Build Setup

iOS:

  • Use Xcode for builds
  • Target: aarch64-apple-ios
  • Bundle identifier: com.bevy-tasks

Android:

  • Use Android SDK/NDK
  • Build with cargo-apk or cargo-ndk
  • Min SDK: 26 (Android 8.0)

egui Mobile Adaptation

Touch Support:

  • egui has basic touch support
  • Add larger touch targets (44pt minimum)
  • Test on real devices

File System Access:

  • iOS: App sandbox documents directory + file picker
  • Android: Scoped storage + SAF (Storage Access Framework)

First Run on Mobile

  • Show folder picker on first launch
  • Suggest locations: Documents, iCloud Drive (iOS), Google Drive (Android)
  • User selects folder, path stored in preferences

Platform-Specific Code

#[cfg(target_os = "ios")]
mod ios {
    // iOS-specific file picker, etc.
}

#[cfg(target_os = "android")]
mod android {
    // Android-specific file picker, etc.
}

Features

  • iOS build pipeline setup (Xcode project)
  • Android build pipeline setup (Gradle/NDK)
  • Basic egui mobile adaptation
  • Simple test UI (even just buttons for CRUD)
  • File system access on iOS
  • File system access on Android
  • Folder picker for mobile
  • Basic task CRUD on mobile
  • Test on real devices

Deliverables

  • App launches on iOS
  • App launches on Android
  • Can create and view tasks on mobile
  • Validates cross-platform architecture
  • Foundation for future mobile polish

Distribution

  • iOS: .ipa for TestFlight (early access)
  • Android: .apk (direct install / sideloading)

Note: This phase prioritizes getting mobile working, even with a simple UI. Polish comes in Phase 6.


Phase 5: GUI Advanced Features (Desktop + Mobile)

Goal: Feature parity with Google Tasks across all platforms

Features

Desktop & Mobile

  • Multiple task lists (folders)
  • Switch between lists
  • Subtasks support
  • Due dates with date picker
  • Rich markdown editor for task notes
  • Move tasks between lists
  • Change storage folder location in settings
  • Search functionality
  • Theme selection (light/dark mode)

Desktop-Specific

  • Drag & drop reordering
  • Keyboard shortcuts
  • Multiple windows (optional)

Mobile-Specific

  • Swipe gestures (swipe to complete, swipe to delete)
  • Pull-to-refresh
  • Touch-optimized UI elements
  • Larger touch targets

Deliverables

  • Full-featured task manager on all platforms
  • Polished UX on desktop
  • Touch-optimized UX on mobile
  • Consistent feature set across platforms

Phase 6: Mobile Polish & Platform-Specific Features

Goal: Native mobile experience and deep platform integration

Features

iOS-Specific

  • Share extension (share to tasks)
  • iOS widgets (home screen, lock screen)
  • Siri shortcuts
  • Haptic feedback
  • iOS-native gestures
  • App icon badge with task count
  • Quick capture via 3D touch / long press
  • iCloud Drive integration

Android-Specific

  • Share target (share to tasks)
  • Android widgets (home screen)
  • Quick settings tile
  • Haptic feedback
  • Material Design guidelines
  • Google Drive integration

Both Platforms

  • Background sync on mobile
  • Push notifications for due dates
  • Notification actions (complete from notification)
  • App shortcuts
  • Platform-specific animations

Deliverables

  • Native-feeling mobile apps
  • Deep platform integration
  • Mobile-specific features

Distribution

App Store Distribution:

  • iOS: Apple App Store
  • Android: Google Play Store
  • Android: F-Droid (FOSS store)

Phase 7: Advanced Features & Imports

Goal: Differentiate from Google Tasks, add unique features

Features

Google Tasks Importer

  • Import from Google Tasks (via API or export)
  • Migrate tasks, lists, due dates, notes
  • Preserve task hierarchy and order
  • Easy onboarding for Google Tasks users

Advanced Task Management

  • Recurring tasks (tasks that automatically uncomplete and reschedule)
    • When completed, task automatically returns to backlog
    • Due date updates by specified interval (e.g., +1 day, +1 week, +1 month)
    • Intervals: daily, weekly, monthly, yearly, custom (e.g., "every 3 days")
    • Optional: limit number of repetitions or end date
    • Stored in frontmatter: recurs: "daily", recurs_until: "2026-01-01"
  • Task templates (save common tasks)
  • Bulk operations (select multiple, bulk edit)
  • Full-text search across all tasks
  • Filters and smart lists (e.g., "Due this week")
  • Statistics and insights (completion rate, etc.)

Integration & Automation

  • Calendar integration (view tasks in calendar)
  • Email to task (send email to create task)
  • Voice input (speech-to-text for tasks)
  • URL schemes / deep links
  • Zapier integration (optional)

Collaboration (Optional)

  • Share lists with other users
  • Collaborative editing
  • Comments on tasks
  • Activity log

Customization & Polish

  • Custom themes and color schemes
  • Advanced animations (consider Bevy migration)
  • Plugin system for extensions (optional)
  • Custom fonts
  • Export/import (backup/restore to .zip)

Optional: Bevy Migration

If you want game-like polish after Phase 7:

  • Migrate GUI from egui to Bevy
  • Full control over animations and rendering
  • Unique, polished look beyond standard apps
  • Backend (bevy-tasks-core) stays identical
  • Only rewrite bevy-tasks-gui crate

Deliverables

  • Polished, delightful UX
  • Unique features not in Google Tasks
  • Easy migration path from Google Tasks
  • Distribution to all app stores

Final Distribution

All Platforms:

  • F-Droid (FOSS Android)
  • Flathub (Linux Flatpak)
  • Google Play Store (Android)
  • Apple App Store (iOS and macOS)
  • Microsoft Store (Windows)
  • Direct downloads (all platforms)

License

GNU General Public License v3.0 (GPL-3.0)

This project is free and open-source software licensed under GPL v3.


Last Updated: 2025-10-27 Document Version: 3.0 Status: Ready to Implement - Milestone-Driven Plan