CLAUDE.md · git:20260807.10b1cef · 2026-08-07 · sha256 c03769e55972d289
CLAUDE.md git:20260807.10b1cefA
Immutable. This exact content never changes and is served at /api/v1/blob/c03769e55972d289.
# workhub
workhub — a Tauri 2 desktop "All-in-one Dev Hub" (Rust backend +
React/TypeScript/shadcn-ui frontend), based on the devdeck codebase.
Task management for humans and AI agents (Claude Code / OpenCode) backed by a
dedicated Obsidian vault; repository management and a music player are planned
as later modules.
## Commands
```powershell
npm install # frontend dependencies (first time)
npm run tauri dev # run the app with hot reload (or: npm run tauri:dev)
npx tauri build --no-bundle # release build -> src-tauri/target/release/workhub.exe
npm run build # typecheck + build frontend only
# in src-tauri/
cargo test # unit tests (on the local windows-gnu toolchain use
# `cargo test --release` — debug test exes fail to
# load with STATUS_ENTRYPOINT_NOT_FOUND)
cargo fmt # format (CI enforces --check)
cargo clippy -- -D warnings # lint (CI enforces)
```
## Architecture
Backend (`src-tauri/src/`):
| Module | Responsibility |
|---|---|
| `lib.rs` / `main.rs` | Tauri builder, command registration |
| `commands.rs` | `#[tauri::command]` layer exposed to the frontend |
| `models.rs` | domain types (`Task`, `Project`, `Settings`, `Config`, ...) |
| `tasks.rs` | task Markdown parsing/writing, vault file watching, `_ai/index/tasks.json` |
| `git.rs` | git CLI integration (kept from devdeck for the repos module) |
| `actions.rs` | external launches (VS Code, terminal, AI agent with task context) |
| `storage.rs` | JSON persistence (`~/.workhub/config.json`; migrated from `%APPDATA%\workhub\` — see `storage::migrate_from_appdata`) |
| `ink/` | screen-annotation overlay: Alt double-press gesture + transparent draw window (`overlay.html` / `src/overlay/main.ts`) |
| `rawkey.rs` | the single, shared Raw Input keyboard listener behind `ink/` and `clips/` — registration is per-process, so features register a consumer here instead of their own listener (see `.claude/rules/global-keyboard-input.md`) |
| `clips/` | clibor-style snippet picker: bare double-tap Ctrl opens a popup (`clips.html` / `src/clips-popup/`) that pastes the picked snippet into the previously focused app |
| `paste.rs` | paste injection into the focused app (clipboard save/restore + synthesized Ctrl+V), shared by `voice.rs` and `clips/` |
| `update.rs` | self-update against GitHub Releases (disabled by default; no releases yet) |
Frontend (`src/`): React 19 + Tailwind v4 + shadcn/ui. `lib/api.ts` wraps
`invoke()` with types matching the Rust structs (snake_case fields; command
*parameters* are camelCase — Tauri converts).
Blocking work (git calls, vault scans) runs via
`tauri::async_runtime::spawn_blocking` in commands — never on the UI thread.
## Data model: the vault is the source of truth
Task data lives as Markdown + YAML frontmatter in a dedicated Obsidian vault
(dev vault: `C:/repos/workhub-vault`, template: `vault-template/`).
The app must:
- treat frontmatter as the schema (`id`, `title`, `status`, `assignee`,
`project`, `priority`, `model`, `due`, `tags`, `created`, `updated`);
- rewrite only the frontmatter keys it manages and **preserve the body** of
task files (human/AI-written prose);
- pick up external edits (Obsidian, AI agents) via file watching;
- English folder names in the vault are lowercase kebab-case (`tasks/`,
`knowledge/`, `_ai/`).
Design docs live in the owner's vault:
`C:/repos/workhub-vault/projects/workhub/`.
## Plugin marketplace: the repo is the plugin source
This repository doubles as a Claude Code plugin marketplace
(`.claude-plugin/marketplace.json` + `plugins/`) — the single source of
plugins for the workhub vault harness. Skills live in plugins, never in
`vault-template/`; the vault carries configuration only. Each plugin is
classified required/optional and user/project scope — see
[docs/plugins.md](docs/plugins.md) for the catalog and the placement rules
before adding or moving a skill.
## Workflow
- Do all development on a feature branch cut from `main`
(`feature/<short-name>`); open a PR into `main` when done. Don't commit
directly to `main`.
- Any change that alters app behavior bumps `version` in
`src-tauri/Cargo.toml` (semver) and adds a `CHANGELOG.md` entry in the same
PR.
## Rules
- Conventional Commits (`feat:`, `fix:`, `chore:`, `docs:`, `refactor:`).
- Before committing: `cargo fmt`, `cargo clippy -- -D warnings`,
`cargo test --release`, and `npm run build` (typecheck) must pass.
- Shell out to the `git` CLI for git operations; do not introduce libgit2.
- Keep the in-app **Help** tab (`src/components/help-view.tsx`) in sync: when
you change a user-facing operation or setup step that isn't discoverable from
the UI (ink shortcuts, quick capture, first-run setup), update its section
there too. See `.claude/rules/help-screen.md`.
<important>
- Config compatibility: `~/.workhub/config.json` (migrated on first run of
0.49.0+ from `%APPDATA%\workhub\config.json`) is read by every released
version — only add fields with `#[serde(default)]`, never rename or remove
existing ones.
- Keep `crate-type` in `src-tauri/Cargo.toml` as plain rlib (no cdylib) —
cdylib breaks windows-gnu debug builds ("export ordinal too large").
- Never write to the vault's human zone (`tasks/`, `projects/`, `knowledge/`)
in ways that destroy hand-written body content; frontmatter-only updates
must round-trip the body untouched.
</important>