Skip to content
Merged
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
52 changes: 48 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,56 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this

## [Unreleased]

This release turns Dispatch from a TUI-only tool into a scriptable CLI: most session operations are now available as non-interactive commands with JSON output, and the TUI gains a git status overlay plus several navigation and organization features.

### Added
- **Configurable auto-refresh** — set `auto_refresh_seconds` (also in the settings panel) to tune the session-list poll interval, or set it to `0` to turn polling off and refresh only with `r` or reindex
- **Machine-readable doctor output** — `dispatch doctor --json` prints diagnostics as a single JSON object so scripts and CI can parse them instead of scraping text
- **Search query argument** — pass a search string on the command line (`dispatch auth` or `dispatch fix auth bug`) to launch the TUI with the search box pre-filled and the list already filtered

#### CLI commands
- **`dispatch open`** — resume a session by ID, alias, or short ID prefix. Supports `--last` (most recent), `--print` (print the resume command without launching), `--stdin` (batch resume piped IDs), `--mode` (inplace, tab, window, pane), scoped resume (`--repo`, `--branch`, `--folder`, `--current` to auto-detect from the working directory), and per-launch `--agent`, `--model`, and `--yolo` overrides
- **`dispatch new`** — start a fresh session from the CLI in the current or a given directory, with the same per-launch overrides as `open`
- **`dispatch stats`** — session totals and breakdowns by repository, branch, and host type, with `--json`, `--csv`, `--calendar` (activity heatmap), `--top`, and date/scope filters
- **`dispatch search`** — non-interactive session search with `--json`, `--table`, and `--ids` / `--format ids` output, `--deep` search, and repo/branch/date/limit filters
- **`dispatch export`** — save a session as Markdown, JSON, or HTML, with `--redact` (mask secrets), `--stdout`, and batch export by `--repo`, `--branch`, or `--query`
- **`dispatch config`** — `list`, `get`, `set`, `unset` (reset to default), `edit` (open in your editor), and `path`
- **`dispatch tags`** — list every tag in use with per-tag session counts
- **`dispatch tag <id>`** — add, remove, or set tags on a session (`--add`, `--remove`, `--set`, `--json`)
- **`dispatch notes`** — get, set, or clear a session's local notes
- **`dispatch views`** — list named views and switch the active view
- **`dispatch aliases`** — list every session alias
- **`dispatch compare`** — compare two sessions side by side (metadata, files, refs), with `--json`
- **`dispatch info <id>`** — print a concise summary of one session
- **`dispatch watch`** — monitor attention state across sessions; stream transitions or take a one-shot `--once` snapshot, with `--json` and `--interval`
- **`dispatch prune`** — report and remove stale config entries for sessions that no longer exist (`--apply`, `--json`)
- **`dispatch man`** — generate a roff man page
- **`dispatch completion`** — bash, zsh, fish, and PowerShell scripts, with dynamic completion of session aliases and config keys
- **`dispatch doctor --json`** — machine-readable diagnostics; doctor also reports the detected Copilot CLI version and stored session count

#### TUI
- **Git status overlay** (`i`) — shows the git status of the folder a session is mapped to: current branch and upstream, push/pull stats (commits ahead to push, behind to pull), working-tree counts (staged, modified, untracked, deleted, conflicts), and a scrollable changed-file list. The same push/pull stats appear inline on each session row and in the preview pane
- **Open linked reference** (`b`) — open a session's linked pull request, issue, or commit on github.com
- **Frecency sort** — rank sessions by a blend of launch frequency and recency
- **Session tags** (`#`) — attach comma-separated tags and filter with the `tag:` search token
- **Session aliases** (`A`) — give a session a short alias and resume it with `dispatch open <alias>`
- **Fullscreen preview zoom** (`z`) — view the preview pane fullscreen
- **Configurable list columns** — choose which optional columns (repo, folder, turns, host) appear in the session list
- **Jump navigation** — `g` / `Home` jump to top, `G` / `End` jump to bottom
- **Date search tokens** — `after:` and `before:` narrow the list by session activity date
- **Waiting notifications** — optional desktop notification when a session enters the waiting state
- **Configurable keybindings** — remap any key via `config.json`
- **Split-pane launch** — open sessions in a tmux split when running inside tmux
- **Open working directory** (`O`) — open the selected session's working directory in the system file manager (Explorer, Finder, or the Linux file manager)
- **Bulk hide and favorite** — with sessions marked via `Space`, `h` and `*` now apply to the whole selection instead of just the cursor session
- **Bulk hide and favorite** — with sessions marked via `Space`, `h` and `*` apply to the whole selection instead of just the cursor session
- **Search query argument** — pass a search string on the command line (`dispatch auth` or `dispatch fix auth bug`) to launch the TUI with the search box pre-filled and the list already filtered

#### Configuration
- **`DISPATCH_CONFIG`** — environment variable to override the config file path
- **Configurable auto-refresh** — set `auto_refresh_seconds` (also in the settings panel) to tune the session-list poll interval, or set it to `0` to turn polling off and refresh only with `r` or reindex

### Changed
- Upgraded Go and website dependencies to latest

### Fixed
- Hardened the swarm CLI commands: `open` scoped resume now picks the most recently active match, `open --current` errors instead of resuming an unrelated session when nothing can be detected, `watch` counts interrupted sessions separately, `stats --csv` neutralizes spreadsheet formula injection, `prune` refuses to apply against an empty store, and `tag` resolves ID prefixes

## [v0.13.0] — 2026-06-30

Expand Down
43 changes: 41 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Dispatch reads your local Copilot CLI session store and presents every past sess
- **Copy resume command** (`Y`) — copy the selected session's full resume command to the system clipboard. With a multi-select active, copies one resume command per selected session, one per line
- **Open working directory** (`O`) — open the selected session's working directory in the system file manager (Explorer on Windows, Finder on macOS, the default file manager on Linux)
- **Open linked reference** (`b`) — open the selected session's linked pull request, issue, or commit on github.com in your browser. Picks the pull request first, then the issue, then the commit
- **Git status overlay** (`i`) — show the git status of the folder the selected session is mapped to: current branch and upstream, push/pull stats (commits ahead to push, behind to pull), working-tree counts (staged, modified, untracked, deleted, conflicts), and a scrollable list of changed files. The same push/pull stats also appear inline on each session row and in the preview pane's git section
- **Four launch modes** (`Enter` / `t` / `w` / `e`) — in-place, new tab, new window, split pane (Windows Terminal, or tmux when running inside a tmux session) with per-session overrides
- **Multi-session open** (`Space` / `L` / `a` / `d`) — select multiple sessions with Space, launch all at once with L, select/deselect all with a/d. Shift+↑/↓ for range selection, Ctrl+click and Shift+click for mouse selection. With a selection active, `h` (hide), `*` (favorite), and `Y` (copy resume command) apply to every selected session at once
- **Attention indicators** — colored dots showing real-time session status: working (blue, executing tools), thinking (cyan, generating response), compacting (magenta, context compaction), waiting (purple), active (green), stale (yellow), interrupted (orange ⚡), idle (gray). Jump to next waiting session with `n`, resume interrupted sessions with `R`, filter by status with `!`
Expand Down Expand Up @@ -338,6 +339,17 @@ dispatch compare 0a1b2c3d 9f8e7d6c --json

The output shows metadata differences (summary, branch, turn count), files that appear in only one session, ref differences, and checkpoint title lists. Use `--json` for machine-readable output. Session IDs accept the same prefix shorthand as `open` and `export`.

### Info

Print a concise summary of one session with `dispatch info <id>`:

```sh
dispatch info 0a1b2c3d
dispatch info 0a1b2c3d --json
```

The summary covers the session's repository, branch, working directory, host type, turn and file counts, timestamps, tags, alias, and any linked refs. Use `--json` for scripting. The session ID accepts the same prefix shorthand as `open`.

### Aliases

List every session alias with `dispatch aliases`:
Expand Down Expand Up @@ -414,6 +426,17 @@ The query can be passed as a positional argument or with `--query`. Filters mirr

Each JSON result includes `id`, `summary`, `cwd`, `repository`, `branch`, `created_at`, `updated_at`, `turn_count`, and `file_count`. No JSON matches prints `[]`; no ID matches prints nothing. Both exit 0. Invalid flags or an unreadable store exit non-zero with a message on stderr.

### Man

Generate a roff man page for `dispatch` with `dispatch man` and write it wherever your system keeps manual pages:

```sh
dispatch man > dispatch.1
man ./dispatch.1
```

The page targets section 1 (user commands) and mirrors the built-in usage: commands, flags, environment variables, and examples. It takes no flags and writes to standard output.

### Key Bindings

#### Navigation
Expand Down Expand Up @@ -503,13 +526,29 @@ Dates accept `YYYY-MM-DD` or full RFC3339 timestamps (e.g. `after:2024-01-15` or
| `v` | View plan in preview pane |
| `o` | Toggle conversation sort order (oldest/newest first) |
| `c` | Copy session ID to clipboard |
| `O` | Open session working directory in file manager |
| `g` | Open linked PR, issue, or commit in browser |
| `Y` | Copy resume command to clipboard |
| `PgUp` / `PgDn` | Scroll preview |
| `r` | Refresh session store |
| `,` | Open settings panel |

#### Session Actions

| Key | Action |
|---|---|
| `i` | Git status overlay for the selected session's folder |
| `b` | Open linked PR, issue, or commit in browser |
| `O` | Open working directory in file manager |
| `F` | Open a file the session touched |
| `D` | Compare two selected sessions |
| `T` | Activity timeline in the preview pane |
| `V` | Switch named view |
| `X` | Export selected sessions to Markdown |
| `m` | Edit note on current session |
| `C` | Copy working directory path to clipboard |
| `y` | Copy preview text to clipboard |
| `x` | Expand or collapse all groups |
| `:` | Open the command palette |

#### Time Range (when search is not focused)

| Key | Action |
Expand Down
31 changes: 31 additions & 0 deletions internal/tui/screenshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,37 @@ func (c *captureCtx) captureFeatures(subDir string) []Screenshot {
addOverlay("cmd-palette", m)
}

// ── Git status overlay ───────────────────────────────────────────
{
m := newBase()
m.gitStatusView.SetStatus(platform.GitStatus{
Dir: "/home/me/code/dispatch",
Exists: true,
IsRepo: true,
Branch: "feature/git-status",
Upstream: "origin/feature/git-status",
HasUpstream: true,
Ahead: 2,
Behind: 1,
Staged: 1,
Modified: 3,
Untracked: 2,
Deleted: 1,
Files: []platform.GitFileStatus{
{Code: "M ", Path: "internal/tui/model.go"},
{Code: " M", Path: "internal/tui/components/gitstatusview.go"},
{Code: " M", Path: "internal/platform/gitstate.go"},
{Code: " D", Path: "internal/tui/old_preview.go"},
{Code: "??", Path: "docs/specs/git-status/spec.md"},
{Code: "??", Path: "docs/specs/git-status/test-plan.md"},
},
})
m.gitStatusView.SetSize(m.width, m.height)
m.state = stateGitStatusView
m.recalcLayout()
addOverlay("git-status", m)
}

return shots
}

Expand Down
Binary file added web/public/screenshots/campbell/git-status.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 31 additions & 10 deletions web/src/pages/cli.astro
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,27 @@ import CliTable from '../components/CliTable.astro';
<div id="commands" class="shortcut-group">
<h3>Commands</h3>
<CliTable rows={[
{ name: 'help', description: 'Show usage information and exit.' },
{ name: 'version', description: 'Print the version number and exit.' },
{ name: 'open <id>', description: 'Resume a session by ID, alias, or short prefix. Flags: --last, --print, --stdin, --mode, --repo, --branch, --folder, --current, --agent, --model, --yolo.' },
{ name: 'new [dir]', description: 'Start a new session from the CLI, with --mode/--agent/--model/--yolo overrides.' },
{ name: 'search [query]', description: 'Non-interactive session search with --json, --table, --ids, --deep, and filters.' },
{ name: 'stats', description: 'Session totals and breakdowns, with --json, --csv, --calendar, --top.' },
{ name: 'export <id>', description: 'Export a session as Markdown, JSON, or HTML, with --redact, --stdout, and batch by --repo/--branch/--query.' },
{ name: 'info <id>', description: 'Print a concise summary of one session.' },
{ name: 'compare <a> <b>', description: 'Compare two sessions side by side, with --json.' },
{ name: 'tags', description: 'List all tags with per-tag session counts.' },
{ name: 'tag <id>', description: 'Add, remove, or set tags on a session (--add, --remove, --set, --json).' },
{ name: 'notes', description: "Get, set, or clear a session's local notes." },
{ name: 'views', description: 'List named views and switch the active view.' },
{ name: 'aliases', description: 'List all session aliases.' },
{ name: 'watch', description: 'Monitor attention state across sessions (--once, --json, --interval).' },
{ name: 'prune', description: 'Report and remove stale config entries (--apply, --json).' },
{ name: 'config', description: 'Read and set preferences (list, get, set, unset, edit, path).' },
{ name: 'doctor', description: 'Print setup diagnostics, with --json.' },
{ name: 'completion <shell>', description: 'Generate a completion script (bash, zsh, fish, powershell).' },
{ name: 'man', description: 'Generate a roff man page.' },
{ name: 'update', description: 'Check GitHub Releases for a newer version and upgrade in-place.' },
{ name: 'version', description: 'Print the version number and exit.' },
{ name: 'help', description: 'Show usage information and exit.' },
]} />
</div>

Expand All @@ -46,6 +64,7 @@ import CliTable from '../components/CliTable.astro';
<CliTable rows={[
{ name: 'DISPATCH_DB', description: 'Path to a custom session store database.' },
{ name: 'DISPATCH_SESSION_STATE', description: 'Path to a custom session state directory.' },
{ name: 'DISPATCH_CONFIG', description: 'Path to a custom config file (overrides the default location).' },
{ name: 'DISPATCH_LOG', description: 'Path to a log file. Enables debug logging when set.' },
]} />
</div>
Expand All @@ -55,21 +74,23 @@ import CliTable from '../components/CliTable.astro';
<pre><code>{`# Launch the TUI
dispatch

# Resume the most recent session
dispatch open --last

# Search sessions in table form
dispatch search auth --table

# Print stats as JSON
dispatch stats --json

# Check for updates
dispatch update

# Try with demo data
dispatch --demo

# Use a custom database
DISPATCH_DB=~/my-sessions.db dispatch

# Enable debug logging
DISPATCH_LOG=/tmp/dispatch.log dispatch

# Reset config and reindex
dispatch --clear-cache
dispatch --reindex`}</code></pre>
DISPATCH_DB=~/my-sessions.db dispatch`}</code></pre>
</div>
</div>

Expand Down
28 changes: 28 additions & 0 deletions web/src/pages/features.astro
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,34 @@ import Carousel from '../components/Carousel.astro';
</div>
</section>

<!-- Git Status Overlay -->
<section class="section" id="git-status">
<div class="feature-section">
<div class="feature-text">
<h2>Git Status Overlay</h2>
<p>
Press <kbd>i</kbd> on any session to see the full Git status of the
folder it is mapped to, without leaving the TUI. Git commands run
under a bounded timeout so the interface never blocks.
</p>
<ul>
<li>Current <strong>branch</strong> and <strong>upstream</strong> ref</li>
<li><strong>Push/pull stats</strong> — commits ahead to push and behind to pull</li>
<li><strong>Working-tree counts</strong> — staged, modified, untracked, deleted, and conflicts</li>
<li>A scrollable <strong>changed-file list</strong> with status codes</li>
<li><kbd>c</kbd> copies a plain-text summary; <kbd>Esc</kbd> closes the overlay</li>
</ul>
<p>
The same push/pull stats also appear inline on each session row and
in the preview pane's Git section.
</p>
</div>
<div class="feature-screenshots">
<Screenshot src="one-half-light/git-status.png" alt="Git status overlay showing branch, upstream, push and pull counts, working-tree changes, and a changed-file list" caption="Git status overlay for a session's folder" />
</div>
</div>
</section>

<!-- Search Tokens -->
<section class="section" id="search-tokens">
<div class="feature-section">
Expand Down
Loading