Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ jobs:

- uses: pnpm/action-setup@v4
with:
version: 9
version: 10

- uses: actions/setup-node@v5
with:
node-version: 22
node-version: 24
cache: pnpm

- name: Install dependencies
Expand Down Expand Up @@ -117,7 +117,7 @@ jobs:
path: |
release/*.dmg
release/*.zip
release/*.yml
release/latest*.yml
if-no-files-found: error

build-linux:
Expand All @@ -129,11 +129,11 @@ jobs:

- uses: pnpm/action-setup@v4
with:
version: 9
version: 10

- uses: actions/setup-node@v5
with:
node-version: 22
node-version: 24
cache: pnpm

- name: Install dependencies
Expand All @@ -154,7 +154,7 @@ jobs:
name: Dash-linux-x64
path: |
release/*.AppImage
release/*.yml
release/latest*.yml
if-no-files-found: error

build-windows:
Expand All @@ -168,11 +168,11 @@ jobs:

- uses: pnpm/action-setup@v4
with:
version: 9
version: 10

- uses: actions/setup-node@v5
with:
node-version: 22
node-version: 24
cache: pnpm

- name: Install dependencies
Expand All @@ -193,10 +193,14 @@ jobs:
name: Dash-windows-x64
path: |
release/*.exe
release/latest*.yml
if-no-files-found: error

release:
needs: [build-mac, build-linux, build-windows]
# workflow_dispatch can run this workflow on any branch; without this
# guard such a run would publish v<package.json version> from that branch.
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: write
Expand Down
11 changes: 7 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ Renderer hot-reloads; main process changes require restart. Husky pre-commit run

Two-process Electron app, strict context isolation (nodeIntegration disabled).

- **Main** (`src/main/`): `entry.ts` → `main.ts` boots PATH fix, DB, hook server, IPC handlers, activity monitor, window.
- **Main** (`src/main/`): `entry.ts` → `main.ts` boots PATH fix, DB, hook server, IPC handlers, activity monitor, supervisor polling, window. `DASH_USER_DATA_DIR` and `DASH_DEV_URL` env vars point a second dev instance at its own data dir and Vite port (needed to run a checkout beside an installed Dash).
- **Task sessions** live under Claude Code's session supervisor, not under Dash: `SupervisorService` dispatches `claude --bg --name <task> …` in the worktree (resuming the task's recorded session id, or the newest transcript for a pre-supervisor task), records `tasks.job_id` / `session_id`, and `ptyManager` spawns `claude attach <jobId>` as the task's agent PTY. Killing an agent PTY only detaches; archive → `claude stop`, delete → `claude rm`, restart → stop + rm + re-dispatch with `--resume`. `SupervisorService.startPolling` reconciles `claude agents --json --all` into `ActivityMonitor` (hooks stay the instant signal; the listing is the truth for `waiting`/`error`/`stopped`) and pushes the listing to the renderer for the per-project "Other sessions" group.
- **Hooks**: written per worktree to `.claude/settings.local.json` (`ptyHookSettings.ts`), keyed by `?ptyId=<taskId>`. Commands read the HookServer port from `<userData>/hook-port` at runtime (the supervisor freezes the dispatch env into the job, so the port cannot live in the env). Any hook event newer than `MIN_CLAUDE_VERSION` must still be gated with `isClaudeVersionAtLeast` (an unknown key makes Claude Code drop the whole file).
- **Renderer** (`src/renderer/`): React SPA. State lives in **Zustand stores** under `src/renderer/stores/` (`settingsStore`, `projectsStore`, `uiStore`, `gitStore`, `runtimeStore`); components subscribe with selectors instead of receiving drilled props, and stores read each other via `getState()`. `App.tsx` is a thin composition root (layout + modals + bootstrap). Communicates via `window.electronAPI` (preload bridge, typed in `src/types/electron-api.d.ts`). **Selector caveat:** a selector that returns a _derived_ array/object (`.filter`/`.map`/object-literal) must be wrapped in `useShallow` (`zustand/react/shallow`) or it re-renders infinitely; plain `s => s.field` selectors are stable.
- **IPC**: `electronAPI.method()` → `ipcRenderer.invoke()` → handler in `src/main/ipc/` → `IpcResponse<T>` `{ success, data?, error? }`. Fire-and-forget via `send()` for ptyInput/resize/kill/snapshot-save.
- **Services** (`src/main/services/`): Stateless singletons with static methods.
Expand All @@ -56,10 +58,11 @@ Main process `entry.ts` rewrites at runtime: `@shared/*` → `dist/main/shared/*
## Data Storage

- **DB**: `~/Library/Application Support/Dash/app.db` (macOS) · `~/.config/Dash/app.db` (Linux)
- **Snapshots**: `~/Library/Application Support/Dash/terminal-snapshots/`
- **Worktrees**: `{projectPath}/../worktrees/{task-slug}/`
- **Snapshots**: `~/Library/Application Support/Dash/terminal-snapshots/` (shell and service tabs only; agent panes repaint on attach)
- **Hook port file**: `~/Library/Application Support/Dash/hook-port` while Dash runs
- **Worktrees**: `{projectPath}/.claude/worktrees/{task-slug}-{hash}/` (excluded via `.git/info/exclude`; legacy `{projectPath}/../worktrees/` tasks are migrated by `WorktreeMigrationService`)
- **UI state**: localStorage (active project/task, theme, keybindings, panel states, notification prefs)

## Requirements

Node.js 24 (`.nvmrc`), pnpm (`shamefully-hoist` in `.npmrc`), Claude Code CLI, Git. macOS arm64 or Linux x64.
Node.js 24 (`.nvmrc`), pnpm (`shamefully-hoist` in `.npmrc`), Claude Code CLI ≥ 2.1.257 (`MIN_CLAUDE_VERSION` in `src/main/services/claudeCli.ts`; task sessions refuse to start below it), Git. macOS arm64 or Linux x64.
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,24 @@

Desktop app for running [Claude Code](https://docs.anthropic.com/en/docs/claude-code) across multiple projects and tasks, each in its own git worktree.

The main idea: you open a project, create tasks, and each task gets an isolated git worktree with its own branch. Claude Code runs in a real terminal (xterm.js + node-pty) inside each worktree, so you can have multiple tasks going in parallel without branch conflicts.
The main idea: you open a project, create tasks, and each task gets an isolated git worktree with its own branch. Each task's Claude Code session runs under Claude Code's own session supervisor (`claude --bg`) inside that worktree and is shown in a real terminal (xterm.js + node-pty via `claude attach`), so you can have multiple tasks going in parallel without branch conflicts — and sessions keep running when you switch tasks, reload, or quit Dash.

![Dash screenshot](docs/screenshot.png)

## What it does

- **Project management** — Open any git repo as a project, or clone from a URL. Tasks are nested under projects in the sidebar. Drag-and-drop to reorder projects. Project overview dashboard shows all tasks, activity status, and quick actions.
- **Git worktrees** — Each task gets its own worktree and branch. A reserve pool pre-creates worktrees so new tasks start instantly (<100ms). Per-project setup scripts run automatically after worktree creation (e.g. `pnpm install`, copying `.env`).
- **Terminal** — Full PTY terminal per task. Sessions persist when switching between tasks (state is snapshotted and restored). Shift+Enter sends multiline input. File drag-drop pastes paths. Clickable file paths open in your IDE. 16 terminal themes.
- **Sessions that outlive the window** — Task sessions are owned by Claude Code's supervisor, not by Dash: switching tasks, reloading or quitting Dash only detaches the terminal, and opening the task attaches again with a recap. Sessions idle for about an hour are parked by Claude Code and resume on the next open. Sessions started outside Dash inside a project show up under "Other sessions" and can be attached, stopped, removed or adopted as a task.
- **Terminal** — Full PTY terminal per task (`claude attach` in fullscreen mode; `←` on an empty prompt opens Claude Code's agent view, Esc leaves it, Ctrl+Z detaches). Shift+Enter sends multiline input. File drag-drop pastes paths. Clickable file paths open in your IDE. 16 terminal themes.
- **Shell drawer** — Separate shell terminal alongside the task terminal. Configurable position (left, right, or replacing main content).
- **File changes panel** — Real-time git status with staged/unstaged sections. Stage, unstage, discard per-file. Click to view diffs.
- **Diff viewer** — Full file or configurable context lines. Unified diff with syntax highlighting. Select lines to add inline comments and send them to the terminal.
- **Commit graph** — Visualize branch history with a DAG-style commit graph per project.
- **GitHub issues** — Search and link issues to tasks. Auto-posts branch comments on linked issues. PR link badge in task header.
- **Azure DevOps** — Search and link ADO work items to tasks. PR detection and branch comments. Per-project ADO configuration with PAT token storage.
- **Remote control** — Generate a QR code / URL to control a task's terminal from another device.
- **Activity indicators** — Busy (amber) and idle (green) status per task, with desktop notifications and sound alerts (chime, cash, ping, droplet, marimba).
- **Activity indicators** — Busy (amber), waiting, idle (green) and sleeping (grey) status per task, driven by Claude Code hooks and reconciled against `claude agents --json`, with desktop notifications and sound alerts (chime, cash, ping, droplet, marimba).
- **Editor integration** — Open changed files in your editor (Cursor, VS Code, Zed, Vim) with line navigation. Clickable file paths in terminal output.
- **Commit attribution** — Configurable co-author line on commits (default, none, or custom text).
- **Task archiving** — Archive inactive tasks to keep the sidebar clean; restore when needed.
Expand All @@ -34,7 +35,7 @@ Download the latest build from [Releases](https://github.com/syv-ai/dash/release

- Node.js 22+
- [pnpm](https://pnpm.io/)
- [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) (`npm install -g @anthropic-ai/claude-code`)
- [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) 2.1.257 or newer (`npm install -g @anthropic-ai/claude-code`, then `claude update`)
- Git

## Development setup
Expand Down Expand Up @@ -166,7 +167,9 @@ All keybindings are customizable in Settings > Keybindings.

- **Database**: `~/Library/Application Support/Dash/app.db` (macOS)
- **Terminal snapshots**: `~/Library/Application Support/Dash/terminal-snapshots/`
- **Worktrees**: `{project}/../worktrees/{task-slug}/`
- **Worktrees**: `{project}/.claude/worktrees/{task-slug}-{hash}/` (ignored via `.git/info/exclude`; pre-0.16 tasks at `{project}/../worktrees/` are offered a one-time move at launch)
- **Hook port**: `~/Library/Application Support/Dash/hook-port` while Dash runs — the per-worktree `.claude/settings.local.json` hooks read the hook server's port from it, so a session that keeps running after Dash quits no-ops instead of erroring
- **Sessions**: owned by Claude Code under `~/.claude/jobs/` (never read by Dash; `claude agents --json` is the interface)

## Acknowledgements

Expand Down
Loading
Loading