Skip to content

Latest commit

 

History

History
199 lines (153 loc) · 12.8 KB

File metadata and controls

199 lines (153 loc) · 12.8 KB

Aura — Implementation Plan

Phased checklist. Work top-to-bottom within each phase; check items off as they land. Stack reference: .agent/context/stack.md · Architecture: docs/architecture.md


Phase 1 — Cargo workspace & CI ✓

  • Create Cargo.toml workspace at repo root with members: crates/aura, crates/aura-core, plugins/rtk-gains
  • crates/aura-core — library crate (data layer, plugin runner, config, state); no UI deps
  • crates/aura — binary crate (GPUI app, tray icon, modal); depends on aura-core
  • plugins/rtk-gains — standalone binary crate (the RTK Gains plugin)
  • Add .rustfmt.toml (defaults) and clippy.toml
  • Add .github/workflows/ci.yml: cargo fmt --check, cargo clippy -- -D warnings, cargo test --workspace
  • Add .gitignore (standard Rust + target/)
  • Smoke-test: cargo build --workspace passes

Phase 2 — Configuration & state ✓

Crate: aura-core

  • Define AgentConfig and AgentKind (enum: ClaudeCode, Codex) in config.rs
  • Define PluginConfig (name + command path) in config.rs
  • Define AppConfig (agents list, plugins list, display defaults) — deserialize from TOML via serde + toml
  • Implement AppConfig::load(path) — reads ~/.config/aura/config.toml; creates default file on first run
  • Write default config template (two Claude Code profiles + RTK plugin entry)
  • Define AppState (active profile name) in state.rs
  • Implement AppState::load() / AppState::save() — reads/writes ~/.local/share/aura/state.json via serde_json
  • Unit tests: round-trip serialize/deserialize for both config and state
  • Smoke-test: binary loads config and prints active profile

Phase 3 — JSONL data engine (Claude Code adapter) ✓

Crate: aura-core — mirrors the ML8 / AT5 / Wp6 logic from claude /usage

  • Define UsageSnapshot struct: input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, sessions, active_days, longest_session_secs, current_streak, longest_streak, peak_hour, favorite_model, per_model: Vec<ModelUsage>, daily_tokens: Vec<DailyModelTokens> — all fields needed to render Overview + Models panels
  • Define Period enum: AllTime, Last7Days, Last30Days
  • Implement list_session_files(config_path)readdir on projects/ subdirectories, collect all *.jsonl paths including subagents/ subdirs
  • Implement scan_jsonl_files(files, from_date, to_date) → raw aggregated data:
    • Skip file by mtime < from_date (cheap OS check before opening)
    • Parse each assistant entry: timestamp, message.model, message.usage.*
    • Skip synthetic models (model name <synthetic>)
    • Accumulate per-model: inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens
    • dailyModelTokens[date][model] += input + output only (matches /usage)
    • Track session stats: start/end timestamps per file → duration, message count
    • Track hourCounts from session start hours
  • Implement streak computation from daily activity dates (matches WM4 logic)
  • Implement ClaudeCodeReader::snapshot(config, period):
    • Last7Days / Last30Days: call scan_jsonl_files with date range; return UsageSnapshot from results
    • AllTime: load stats-cache.json as baseline, find JSONL files newer than lastComputedDate, merge delta, append today's JSONL on top
  • Define AgentReader trait with fn snapshot(&self, period: Period) -> Result<UsageSnapshot>
  • Implement AgentReader for ClaudeCodeReader
  • Unit tests:
    • Fixture JSONL files covering multi-model, multi-day, cache tokens, synthetic model filtering
    • Assert snapshot(Last7Days) totals match hand-computed values
    • Assert AllTime merges cache + delta correctly
    • Assert streak computation for gaps, current streak reaching today
  • Integration smoke-test: run against real ~/.claude/projects/ — outputs 119,845 tokens / 5 sessions / 2 active days / favorite model claude-opus-4-7

Phase 4 — File watcher (live updates) ✓

Crate: aura-core

  • Add notify-debouncer-mini = "0.7" dependency (pairs with notify 8.2; stable; built-in debouncing)
  • Implement ProjectsWatcher wrapping notify::RecommendedWatcher on {config_path}/projects/
  • Expose channel via try_recv() / recv_timeout() returning Vec<PathBuf> of changed *.jsonl files (non-JSONL events filtered out)
  • Implement read_jsonl_since(path, offset): returns parsed new entries + new byte offset; respects partial trailing lines mid-write
  • Deferred to Phase 6UsageSnapshot merge logic lives with the UI consumer; for now the UI calls snapshot() after watcher events (mtime fast-skip already keeps this cheap)
  • Debounce: 500ms quiet-window coalescing handled by notify-debouncer-mini
  • Unit tests: file create fires event, non-JSONL ignored, rapid writes coalesced, read_jsonl_since byte-offset correctness incl. partial trailing line

Phase 5 — Plugin system ✓

Crate: aura-core

  • Define PluginPanel struct: title: String, lines: Vec<PluginLine>, error: Option<String>
  • Define PluginLine: label: String, value: String, highlight: bool
  • Implement PluginRunner::run(config: &PluginConfig) -> PluginPanel:
    • Spawn subprocess via std::process::Command
    • 500ms timeout via std::thread + mpsc::recv_timeout
    • Read stdout, parse JSON into PluginPanel
    • On non-zero exit, timeout, missing binary, or bad JSON: return PluginPanel with error set (never panics, never returns Err)
  • Unit tests: valid JSON, missing binary, timeout, bad JSON, non-zero exit (5 cases)

Plugin: plugins/rtk-gains

  • Uses rtk gain -a --format json (covers summary + daily + monthly in one call)
  • Outputs PluginPanel JSON with: tokens saved today / this month / all-time, savings rate, commands intercepted
  • Graceful fallback if rtk is missing or fails (emits error panel)
  • Unit tests: format_thousands formatting, real rtk gain JSON parsing

Phase 6 — GPUI modal (UI) ✓ (needs interactive visual verification)

Crate: aura

  • Add gpui = "0.2" (crates.io release); builds on Linux X11/Wayland
  • AuraView holding: config, state, active_profile, active_period, active_tab, snapshot, plugin_panels, error
  • Borderless 520×640 window, dark background (Zed-ish palette), monospace font
  • Profile picker — pills in the header row; click switches profile, persists to state, refreshes snapshot
  • Period selector — three pills (All time / Last 7 days / Last 30 days); active pill in accent color
  • Overview panel — 2-col grid of stat cards: Favorite model, Total tokens, Sessions, Longest session, Active days, Peak hour, Current/Longest streak
  • Models panel — daily token bar chart + per-model rows with horizontal % bar
  • Loading / error states — "Loading…" placeholder; red error message replaces body when snapshot fails
  • Plugin panels — RTK gains rendered as title + key/value lines with highlight + error variant
  • Tab switching (Overview ↔ Models) via accent-underlined header
  • Click Aura ⟳ title to manually refresh
  • Active profile saved to state on selection change
  • Deferred — wire ProjectsWatcher to GPUI background executor for auto-refresh. Manual refresh via title click works today.
  • Manual test required — UI rendering can't be verified from headless CLI. Launch with cargo run -p aura to confirm layout

Phase 7 — System tray integration ✓ (basic; tooltip+click integration deferred)

Crate: aura

  • Add tray-icon = "0.24" with default-features = false, features = ["gtk"] (drops libxdo system dep)
  • Programmatic 32×32 RGBA placeholder icon (purple/violet square); real SVG asset is a follow-up
  • Tray installed at startup with tooltip "Aura — Agent Usage Reporter"; handle held by main fn so it persists for app lifetime
  • Best-effort install: warns and continues if no tray host is available
  • Deferred — dynamic tooltip ("Aura · profile · tokens today") and click-to-toggle-window. Requires bridging tray-icon's event channel into the GPUI main loop; left as a future iteration since main flow is "window opens on launch"

Phase 8 — Codex adapter ✓

Crate: aura-core

  • Stub CodexReader returning UsageSnapshot::default()
  • reader::make_reader(agent) factory dispatches on AgentKind (ClaudeCode → real, Codex → real)
  • codex_scan module — walks {config_path}/sessions/{YYYY}/{MM}/{DD}/rollout-*.jsonl
    • Parses session_meta (session id + start ts), turn_context (active model), and event_msg/token_count (per-turn last_token_usage)
    • Attributes each last_token_usage delta to the most-recent turn_context.model
    • Splits Codex's input_tokens (which includes cached) into input_tokens = input − cached and cache_read_tokens = cached; cache_write_tokens = 0 (Codex has no separate creation bucket)
    • mtime-based fast skip for files outside the requested date window
  • CodexReader::snapshot reuses claude_code::build_snapshot (with no cache baseline) for Last7Days / Last30Days / AllTime
  • Default AppConfig ships a Codex profile so it works out of the box
  • Unit tests: file discovery, single-session token attribution, mid-session model switch, pre-context token_count skipped, date-range filter, multi-session aggregation, empty dir
  • Future — incremental scanning + watcher integration for live updates (today the UI scans on demand)

Phase 9 — Packaging & install ✓

  • justfile with targets: build, run, test, lint, install, install-plugin-rtk, uninstall, start/stop/status/logs
  • Systemd user unit packaging/aura.service (graphical-session.target, restart-on-failure, inherits DISPLAY/WAYLAND_DISPLAY)
  • install.sh — checks prereqs, builds release, installs binaries to ~/.local/bin/, drops the systemd unit
  • Updated README.md Installation section with system deps, one-shot install, manual install, and common commands
  • Manual test required — verify on a clean session: ./install.sh, systemctl --user enable --now aura, tray icon appears, app launches

Phase 11 — Windows support ✓

  • Cargo gating: tray-icon falls through to its Win32 backend on Windows (no gtk feature needed); the existing "not Linux/BSD" target table covers this
  • Windows Credential Manager read/write for Claude Code OAuth (Claude Code-credentials, user = %USERNAME%) via keyring crate (windows-native backend); falls back to .credentials.json for legacy installs
  • scripts/install.ps1 — copies binaries to %LOCALAPPDATA%\Programs\Aura and drops a Startup-folder Aura.lnk for autostart
  • install.sh detects MSYS/MinGW/Cygwin and points users at install.ps1
  • justfile adds install-windows, uninstall-windows, start-windows, stop-windows, status-windows
  • Release CI matrix builds x86_64-pc-windows-msvc (stable) and aarch64-pc-windows-msvc (experimental); Windows artifacts are zipped (not tarballed)
  • README documents Windows config path (%APPDATA%\aura\config.toml), install flow, and Credential Manager caveat
  • Manual test required — first-launch verification on x86_64 + aarch64 Windows hosts; code-signing setup once a Windows Authenticode cert is available

Phase 10 — macOS support ✓

  • Cross-platform Cargo gating for tray-icon (gtk feature only on Linux/BSD)
  • objc2-app-kit override of NSApplicationActivationPolicyAccessory (menu-bar app, no Dock icon) since GPUI hard-codes Regular
  • Keychain read/write for Claude Code OAuth credentials (Claude Code-credentials, account = $USER) via security-framework; falls back to .credentials.json for legacy installs
  • packaging/com.aura.agent-usage.plist LaunchAgent (RunAtLoad, KeepAlive on crash)
  • packaging/macos/Info.plist with LSUIElement=true
  • scripts/build-macos-app.sh assembles Aura.app (optional codesign via CODESIGN_IDENTITY)
  • install.sh and justfile dispatch on uname -s (Linux: systemd, macOS: launchd + .app)
  • Release CI builds and packages Aura.app + plist for both darwin targets; experimental: true dropped
  • README documents macOS install, config path, Gatekeeper quarantine, Keychain caveat
  • Manual test required — first-launch verification on Intel + Apple Silicon, codesign / notarization setup once an Apple Developer ID is available

Deferred / roadmap

  • Light theme
  • Historical usage charts in the modal (beyond ASCII)
  • Plugin authoring guide + example repo
  • Plugin registry (aura plugin install <name>)
  • Custom command agents (BYOA)
  • macOS: Developer-ID codesign + notarization (needs Apple account)