GUI & Terminal UI for Claude Code Sessions, Agent Teams & Dev Workflows
The GUI is the primary way to use clash; the TUI is the terminal-native fallback mode.
Install • Features • Usage • GUI • Workflows • TUI keys
-
One place for every agent session — list, start, attach, rename, stash, resume, reload and kill Claude Code and OMP (oh-my-pi) sessions, grouped by status (Active / Done / Fail / External) with live three-layer status detection (hooks, PTY screen analysis, transcripts). See OMP sessions.
-
Two frontends, one core — a desktop GUI (Tauri; the primary mode) and a keyboard-driven TUI. Both are backed by the same in-process PTY daemon, and several instances can run side by side.
-
Desktop workspace (GUI) — cmux-style workspaces, freely nested, drag-to-split resizable panes, GPU-rendered terminals, shell terminals, embedded browser tabs, 12 themes, a searchable font picker, and a layout restored exactly on relaunch. See GUI.
-
Workflows (GUI) — one item per piece of work, taken through plan → review → implement → diff review → (optional) PR by agents, with you approving each step:
- line comments on the diff;
- repeatable agent review rounds (plan, code, plan-vs-changes);
- explanations of the plan and of the change;
- a suggested next step on every item, with an optional autopilot;
- multi-repo PRs and a PR dashboard;
- sharing to Slack / Discord / Jira.
See Workflows.
-
Attention inbox (GUI) — one ordered list (
⌘I) of everything waiting on you: approvals, dead agents, errors, decisions, unanswered PR threads, finished turns. -
Files (GUI) — a file explorer (
⌘E) on the focused session's folder: git status on every file, fuzzy find (⌘P), syntax-highlighted / Markdown / image previews, and@-mentioning a file into the session. See Files. -
Queued follow-ups — type the next instruction while an agent works; clash delivers it the moment the session is idle at its prompt, never to a tool-approval question.
-
Takeover of outside sessions —
claudeprocesses started elsewhere show up as wild rows; one confirm moves their conversation under clash. -
Git worktrees — start a session in an isolated worktree for parallel branches; diffs, PR detection and open in IDE (Cursor, VS Code, Zed, JetBrains, vim…) per session.
-
Open externally — send one or all sessions to panes / tabs / windows of your terminal (tmux, iTerm2, WezTerm, kitty…) with a planned layout.
-
Teams & tasks — create and edit Claude Code agent teams and their members, tasks and per-agent inboxes; jump from a running member to its session.
-
Scratches — an IntelliJ-style tree of free-form notes and folders, opened in your editor of choice.
-
Session presets — reusable directory / worktree / prompt / setup-script templates, per project or global (Superset-compatible).
-
Layered configuration — one
config.tomlshared by both frontends, with project and env overrides, live reload andclash configto inspect it. -
Self-updating —
clash update,:updateor the GUI's ⟳ Update clash updates both binaries in place. -
Guided tour in both frontends,
--debuglogging, and UI state persisted across restarts.
curl -fsSL https://raw.githubusercontent.com/defgenx/clash/main/install.sh | bashThis installs the TUI (clash) from the latest release. For the GUI, build
from a clone with make install (below).
Custom install path:
CLASH_INSTALL_DIR=~/.local/bin curl -fsSL https://raw.githubusercontent.com/defgenx/clash/main/install.sh | bashcargo install --git https://github.com/defgenx/clash.git # the TUI onlyOr from a clone — installs both the TUI and the GUI
(override paths with INSTALL_DIR=~/.local/bin / APP_DIR=~/Applications):
make install # or: make install-tui / make install-guiThe TUI installs as the clash binary in INSTALL_DIR. The GUI installs
as a regular desktop application, discoverable like any other app:
- macOS —
Clash.appin/Applications(falls back to~/Applicationswhen not writable): Spotlight, Launchpad, Dock. Aclash-guisymlink lands inINSTALL_DIRfor terminal launching. - Linux —
clash-guibinary plus an XDGclash.desktoplauncher entry and icon (system-wide under/usr/local/shareas root, per-user under~/.local/shareotherwise).
- Claude Code CLI (
claude), and/or OMP (omp, oh-my-pi) for OMP sessions git(worktrees, diffs) and, for every PR feature, the GitHub CLIgh- Building from source: a recent stable Rust (1.82+); for the GUI on Linux, the webkit2gtk / GTK development packages (see GUI)
clash # Start the TUI (reads from ~/.claude)
clash-gui # Start the GUI
clash --data-dir ~/.claude # Custom data directory
clash --claude-bin /path/to/claude # Custom CLI path
clash --debug # Enable debug logging
clash update # Update clash and clash-gui to the latest release
clash config [--path|--defaults|--validate|--schema] # Inspect configuration
clash attach <session-id> # Attach to a session owned by a running clash (used by external panes)
clash daemon # Run the session daemon standalone (normally in-process)
clash --versionAt every launch, clash (re)writes its lifecycle hooks to ~/.claude/clash/hooks/ (plus the OMP status extension) for instant status detection; the first launch also shows a guided tour. Replay it anytime with :tour. clash passes that hook file to every session it spawns (claude --settings …), so it registers nothing in your own settings files — see docs/hooks.md.
clash detects session status through three layers (in priority order):
- Hooks — Claude Code lifecycle events (
PermissionRequest,Stop,SessionStart, etc.) write instant status updates - Daemon PTY — screen content analysis pattern-matches the terminal for prompts, approval dialogs, and thinking indicators
- JSONL baseline — conversation log heuristics (last entry type, stop reasons, timing)
| Icon | Status | Meaning |
|---|---|---|
◆◇ |
Prompting | Claude needs tool approval — blinking diamond |
◉ |
Waiting | Awaiting your next prompt |
◌◎◉ |
Thinking | Reasoning / generating — pulsing circle |
⠋⠙⠹… |
Running | Executing tools — braille spinner |
○◔◑◕● |
Starting | Session just spawned — filling circle |
✗ |
Errored | Session crashed shortly after starting |
○ |
Stashed | Exited or inactive |
✓ |
Done | A subagent that finished |
Each row in the sessions list may carry a single-character prefix indicating where its underlying Claude process lives:
| Prefix | Source | Meaning |
|---|---|---|
| (none) | Daemon | clash spawned and manages the PTY — attach inline with a |
⊞ |
External | clash spawned the process in another pane/tab/window via o/O |
🌿 |
Wild | A claude process started outside clash. Press a to take over: one confirm, then clash kills the outside process (SIGTERM, SIGKILL after 2s) and attaches to its conversation under the daemon (--resume <id>) |
The Wild detection runs in the background every ~2s. clash surfaces every wild claude PID that started after this clash launched under the EXTERNAL section — pre-existing claudes from before clash booted are intentionally hidden, the section is for things spawned during this session. Each wild process is dynamically associated with a conversation: exact evidence first (--resume <id> / --session-id <id> in argv, or — rarely — the .jsonl held open as an fd), otherwise the most recently modified conversation in the process's working directory. The association is re-evaluated on every scan, so it always tracks the latest conversation. Only a bare claude in a directory with no conversation on disk at all (typically the few seconds before a brand-new conversation's JSONL appears) shows as a PID-keyed row with takeover disabled. Press d to drop a wild row: clash signals the PID directly (SIGTERM, SIGKILL after 5s if still alive and still claude). The row also disappears on the next scan tick once the process exits, so closed/stopped claudes never linger. List the section in isolation with :external. The GUI behaves the same way: clicking a wild row (or its ⚡ button) confirms, takes over, and opens the terminal.
The GUI is the primary way to use clash; the TUI remains fully supported as the
terminal-native mode. It is a Tauri 2 desktop app (gui/) sharing the TUI's
core: the session pipeline, the in-process PTY daemon and the protocol. Both
can run side by side, each instance owning its own sessions. Everything in this
section except Files and Workflows also exists in the TUI.
The first launch opens a guided tour, a spotlight walkthrough of workspaces, starting a session, sessions, the inbox, workflows, scratches, teams, files, tabs and panes, the terminal and settings. Replay it from Settings → ▶ Show the tour.
- Workspaces (cmux-style) each own a pane layout and their sessions:
⌘Ncreates one,⌘1–9switches, and⌘⇧Ror a double-click on the chip renames it.⌘⇧Wor the chip's×closes it; the last one can't be closed.- The sidebar is scoped to the active workspace, plus an UNASSIGNED group for sessions no workspace has claimed (opening one claims it).
- Search (
/,⌘F) is global; results from other workspaces carry a⌘nbadge.
- Split panes nest freely, like iTerm:
⌘Dsplits the focused pane (beside it when it is wide, below it when it is tall) and⌘⇧Dcloses it.- Drag a tab — from the strip or a pane's title bar — onto any pane: drop it on an edge to split that pane on that side, or in the middle to show it there (swapping with the pane it came from). Dragging a pane's tab moves the pane.
⌘⇧↩(or a double-click on the pane title) zooms.⌘⌥←/→cycles focus.- Drag a divider to resize; the layout persists per workspace.
- Tabs: the active tab is always the content of the focused pane. The
+ghost tab opens a shell terminal, a browser tab or a new session; right-click an empty pane for the same menu.- Double-click a label to rename; middle-click or
⌘Wcloses. - Closing an agent tab stashes the session (stopped, conversation kept resumable); choose Detach in the tab menu to leave it running instead.
- Double-click a label to rename; middle-click or
- On relaunch clash restores where you were: workspace, tabs, layout, focused pane and browser tabs. Stashed sessions resume the moment you click them.
- Status: the sidebar shows status sections with animated status labels
(PROMPTING / THINKING / RUNNING / WAITING / STARTING / STASHED / ERRORED /
DONE), and each tab gets a colored dot. Every row carries a CC / OMP
agent badge.
- STASHED means resumable: sessions are stashed when clash starts, because a transcript can't say whether its process still lives.
- Sessions whose conversation Claude Code has deleted (after ~30 days) stop being listed.
- New session (
+ New session,⌘T):- pick a directory (prefilled from the default directory or the focused project, with a 📁 picker);
- pick a preset, optionally a git worktree, and the agent (Claude Code or OMP; one whose binary doesn't resolve is greyed out).
- Session actions: each row's
⋯menu (or right-click) offers rename, ⟳ reload, details, stash, kill, open PR, queue or cancel a follow-up, and take over (for wild rows). - External claudes started outside clash are listed under
⚡ EXTERNAL. Clicking one takes it over after a confirm: the outside process is killed and its conversation opens under clash. - Section buttons: every section header has
✕(kill the whole group, one confirm). The topbar's ⏸ all stashes every running session. - ⟳ Reload hot-restarts a session on the newest
claudebinary, resuming its latest conversation. It is on rows, tabs and section headers, and⌘Rreloads the focused session. Busy sessions are skipped by group reloads, and an individual reload asks first. - Details panel (ⓘ): status, branch, project, CWD and summary, plus Ports, Open in IDE and Open in browser (the PR, the diff on GitHub, or the repo). Conversation, Subagents and Diff open as full tabs.
- A session whose output mentions a GitHub PR gets a green
⇄ PR #nchip.
- Terminals are xterm.js with GPU (WebGL) rendering; a lost GL context is reacquired automatically.
- Shell terminals: the topbar button picks among the machine's shells, and
⌘⇧Treopens the last one. Closing the tab kills the shell. - Keyboard:
Shift+Enterinserts a newline in agent sessions.⌘C/⌘Vcopy and paste (Ctrl+Shift+C/Von Linux); plainCtrl+Cinterrupts.⌘Kclears the terminal.
- Selecting text: Claude Code captures the mouse, so ⌥-drag (Shift on Linux) to select text. Right-click selects a word.
- International layouts: ⌥ composes characters (
{,[on AZERTY). ⌥ sends Esc (Meta) keeps ⌥+letter as Meta. - Notifications: a desktop alert fires when a session starts waiting or
errors (not while the window is focused). Any process can raise one with
printf '\e]777;notify;Title;Body\a'(OSC 9 / 777).
- Browser tabs are first-class tabs.
⌘⇧Bopens a blank one in its own split. - Full chrome: back/forward, reload/stop, an address bar taking URLs or searches, copy URL, open in the system browser and DevTools.
- While focused:
⌘Lfocuses the address bar,⌘Rreloads, and⌘+/⌘-/⌘0zoom. target="_blank"links open a new clash tab.
How links open is one setting, ask (default), in clash or system browser. It applies to every link clash opens: terminal URLs, PR buttons and chips, listening ports, and links in rendered plans and reviews. A link opened in clash goes to a new split beside the current session.
The Files panel (⌘E, or the folder button in the topbar) is a file
explorer docked to the right of the panes.
- Root: it shows the focused session's folder and follows you as you switch sessions. 📌 pins the current folder; the folder button shows any other one (pinned). Click the folder name to reveal it in Finder.
- Tree: folders load as you expand them and remember what was open per
folder. Git-ignored files are hidden (◌ shows them, dimmed),
.gitnever appears, and the panel refreshes every few seconds while it is open. - Git status: the header shows the branch and ahead/behind counts. Files
are marked Modified, Added, Deleted, Renamed, Untracked or
! (conflict), and a collapsed folder holding changes shows
•. - Find (
⌘P, or type in the filter box): fuzzy search over every file git knows about (tracked and untracked, not ignored); outside a repository it walks the folder, skipping dot-folders andnode_modules/target-style trees. Matched letters are highlighted. - Keyboard:
↑/↓move,→/←open and close a folder,↵previews,⌥↵mentions the file in the session,Escclears the search. - Preview: clicking a file opens it in a preview pane beside the session.
Code is syntax-highlighted with line numbers, Markdown is rendered (mermaid
included) with a Source toggle, and images are shown. Binary files and
text over 2 MB are not previewed.
- Clicking another file replaces the preview (its tab is in italics).
Double-click,
↵or 📌 Keep keeps a tab open.
- Clicking another file replaces the preview (its tab is in italics).
Double-click,
- Actions (right-click a row, or the preview's toolbar): Mention in
session types
@pathinto the session (relative to its folder), Open in editor… (the scratch editor picker), copy the absolute or relative path or the name, reveal in Finder, and for folders New terminal here and Show as root. Dragging a row into a terminal types its path.
The TEAMS section lists real, user-managed teams, each with a live n/m
running count; Claude Code's per-session session-<id> teams are hidden.
- A team's panel lists its members with run indicators and model chips. Left-click a running member to jump to its session, or a stopped one to open its inbox.
- Right-click a member to edit its model, agent type, prompt or name, remove it, or open its inbox. + Add member adds one.
- Tasks can be created, have their status cycled (click the badge), get an owner, or be deleted.
- The name and description are click-to-edit, and the panel live-refreshes.
The inbox (sidebar tray icon, ⌘I) answers "what needs me?" as one list
across every workspace and project, ordered by urgency, then by how long each
has waited:
- sessions holding a tool-approval prompt;
- workflow agents that died mid-round;
- errored sessions;
- workflow items parked on a decision;
- PRs with open review threads;
- sessions waiting for your next message.
The count turns red only when something is blocked. Clicking a row jumps to it.
Queue follow-up… (a running session's ⋯ menu, the inbox, or f in the
TUI) delivers a multi-line prompt to that session the moment it is idle at its
input prompt. ⧖n on the row shows pending prompts; click it (or F in the
TUI) to review or cancel one.
It is safe to leave running:
- delivery waits for two consecutive idle samples and never goes to a tool-approval question;
- the text arrives as one bracketed paste plus one Enter;
- each session holds at most 20 queued prompts;
- the queue is in memory for that clash instance;
- every delivery is announced by a toast.
The sidebar's collapsible SETTINGS section is grouped and filterable (type "cursor" or "font"), and every terminal setting applies live:
| Group | Settings |
|---|---|
| Appearance | Theme — 12 palettes (below) |
| Paths | default session directory · scratch directory · workflows directory · claude binary · OMP binary · default agent (Claude Code / OMP) |
| Workflows · agents | workflow agent (ask / Claude Code / OMP) · lead model · next-step assist (suggest / autopilot / off) · delegation (team / solo) · subagent model · OMP model · PR skill · forge · skill updates |
| Workflows · sharing | who posts to Jira (agent / clash) · Jira skill · Jira site, email, API token · who posts to Slack/Discord (agent / clash) · chat skill · Slack and Discord webhooks |
| Workflows · notifications | notify decisions (off / Slack / Discord) |
| Terminal · text | font family (searchable picker) · size · weight · bold weight · line height · letter spacing |
| Terminal · cursor | style · unfocused style · bar width · blink |
| Terminal · colors | minimum contrast ratio · bold in bright colors |
| Terminal · scroll & input | scrollback · scroll speed · smooth scroll · copy on select · right-click selects word · ⌥ sends Esc · toast on bell |
| clash | how links open · desktop notifications · attention count in the title · confirm before kill · refresh interval · default shell · TUI launcher terminal |
Theme, terminal and link settings are the GUI's own (gui-state.json).
Everything else lives in the shared config.toml, so the TUI
agrees.
Themes recolor the chrome and the terminals together:
| Dark | Light |
|---|---|
| clash dark (default) · Tokyo Night · Catppuccin Mocha · Nord · Dracula · One Dark · Gruvbox Dark · Solarized Dark | clash light · Catppuccin Latte · Solarized Light · GitHub Light |
The font picker lists the installed families, each previewed in its own face and tagged mono or proportional. Monospace only is on by default, and Custom… takes a full CSS font stack.
The sidebar and details panel are drag-resizable. The WORKFLOWS / SCRATCHES / TEAMS sections have a draggable divider and their own scrollbars.
The TUI badge in the sidebar header launches the clash TUI in a detected terminal (Terminal, iTerm2, WezTerm, kitty, Alacritty, Ghostty, Warp, GNOME Terminal, Konsole, xterm, tmux). It turns gold while a TUI is running.
- ⟳ Update clash (below the settings), like
clash updateand:update, updates both binaries. Progress shows in the footer version label. - A Restart dialog stashes every session, relaunches the new binary, and logs
the relaunch in
clash.log. - Symlinked installs are followed. On macOS the binary inside
Clash.appis replaced, its version bumped and the bundle re-signed.
| Key | Action |
|---|---|
⌘T / ⌘⇧T |
new session / new shell terminal (last-used shell) |
⌘N · ⌘1–9 · ⌘⇧R · ⌘⇧W |
new / switch / rename / close workspace |
⌘D / ⌘⇧D |
split / close the focused pane |
⌘⇧↩ · ⌘⌥←/→ |
zoom pane · cycle pane focus |
⌘W |
close the active tab (agent: stash, shell: kill) |
⌘B |
toggle the sidebar |
⌘E / ⌘P |
toggle the Files panel / find a file |
⌘⇧B |
new browser tab |
⌘F or / |
search |
⌘I |
inbox |
⌘K |
clear the terminal |
⌘R |
reload the focused session (in a browser pane: reload the page) |
⌘L · ⌘+/⌘-/⌘0 |
browser: address bar · zoom |
Esc |
close the new-session dialog / clear the search |
Text fields support ⌘A / ⌘C / ⌘X / ⌘V and forward delete (fn+⌫; ⌥ =
word, ⌘ = to end of line). Dialogs take Enter / Esc, and composers send with
⌘↵.
cargo build --release # builds BOTH binaries: clash and clash-gui
./target/release/clash-guiOn Linux the build needs the Tauri system packages:
libwebkit2gtk-4.1-dev libgtk-3-dev librsvg2-dev libxdo-dev. The GUI is
self-contained: there is no external daemon and no node build step, since the
frontend in gui/dist/ is vendored and embedded in the binary.
A pipeline manager for AI-assisted development: one item per piece of work, taken from idea to plan to code to PR by Claude Code (or OMP) agents, with you approving every step. Everything is plain files, so an item's whole history stays readable outside clash. The agent-side file contract and the design behind each rule are in docs/workflows.md. Workflows are GUI-only.
The + button on the WORKFLOWS section asks how the item starts:
| Mode | Starts at | Use it when |
|---|---|---|
| Full workflow | draft |
you have an idea: an agent plans, you approve, it implements |
| From a plan I already have | plan-review |
the plan exists: paste it, point at a markdown file, or pick a scratch note. No planning agent runs |
| Review only | diff-review |
the code is already written: give a PR (URL or number) or a local branch and get just the review loop |
Give it a title, a project and a repo (picked from your open sessions and existing projects, or Browse…), and optionally a description of the goal. The planning agent reads the description first, which saves half the requirements questions.
Review only resolves the PR through gh, checks its branch out (reusing an
existing worktree of it), and diffs against the PR's own base. A PR URL is
looked up in its own repository, so a link to another repo is refused by name
rather than resolved to whatever local PR shares the number. No plan is written
and no draft-PR stage runs, since the PR isn't clash's.
Planning reads the repo in place. The first round that writes code gets its own git worktree and branch, unless the directory isn't a git repository or the item is set to work in place.
draft → planning → plan-review → changes-requested → implementing → diff-review → pr-draft → pr-ready → done, plus abandoned from anywhere and
reviewing while an agent review round runs.
- The PR stages are optional: approving a diff can close the item outright, for repos that merge straight to their default branch.
- A pipeline stepper at the top of every item shows the mode's stages, where the item is, and how many rounds it has been through.
- Decision stages (plan review, diff review, PR draft) badge the sidebar, fire a desktop notification and appear in the attention inbox.
- An item follows its primary PR: merged on GitHub →
done; flipped to ready for review on GitHub →pr-readyon the next refresh.
Every item ends with an action bar in three labeled zones:
- This step · stays here — reviews, explanations, opening the PR or the session.
- Continue · moves the item — the decisions that advance the pipeline.
- Item · back, park, share — the item as a whole.
Above the zones, a Suggested next strip names the one button to press now and why, along with what it already checked:
- a review round came back and isn't applied yet → ↻ Apply
- comments are still open → ✎ Request changes…
- PR threads are waiting on you → ⇄ Answer PR threads
- this iteration's plan or code has no review yet → ⌕ Plan review / ⌕ Code review
- nothing has compared the change with the plan → ⇄ Compare plan vs changes
- otherwise → Approve
It only ever points at a button that is on the bar, and it moves the highlight
there. The agents advise too, through a **Next:** line at the end of every
review round and of the executor's hand-off, but clash's own checks rank first.
"Reviewed" means reviewed at this iteration: after a fix round, the code is
due for review again.
workflows.assist sets how far it goes (Settings → Workflows · agents):
suggest(the default) highlights the step.autopilotalso starts it after each agent hand-back, but only steps that decide nothing: a review, a plan-vs-changes round, or applying a round that said apply. It stops before every approval, PR flip, change request and GitHub post, and after 3 steps on an item until you click something there.offgives the plain bar.
| Tab | Holds |
|---|---|
| Plan | the current plan.md, rendered; Edit plan.md opens it in your editor |
| ◫ Revisions | every plan version (one per applied review round), any version's full text, and ⇄ Changes between any two |
| Diff | the change, with line comments; its source picker can also show a linked PR's diff (view-only) |
| Change requests | your notes, one section per round — what the next agent round reads first |
| ↩ Hand-off | the workflow agent's note from its last phase: what it did, what it is unsure about, what it tested, what it suggests next |
| Agent reviews (n) | every review round's verdict and findings, with jump chips, opening on the latest |
| ◫ Plan explained / ◫ Changes explained | the explainer's two documents (see below) |
| ⇄ Plan vs changes | the latest plan-vs-changes comparison |
| Timeline | one newest-first feed of every change round (your note, the plan diff, the plan as it stood, the code diff) and every review round |
| ⚙ Settings | per-item knobs: title, description, diff base, agent CLI, PR skill, default interaction mode, work in place, session-name prefix, Jira ticket — plus the item's facts |
Workflow sessions are named by item and job (Auth refactor · implement,
· plan review r2, · code review r1, · explain plan, · plan vs changes r1,
· answer PR comments), so the sessions list says what each agent is doing.
Hover any diff line and press + to leave a GitHub-style comment. Threads
support reply, edit, resolve, won't fix and park (kept, but not sent with
the next round). The ↑/↓ buttons in the diff header step through open comments,
expanding collapsed files on the way. Comments re-anchor by content when the
diff moves between iterations, and are never dropped: one that can't be placed
lands in an orphan tray.
✎ Request changes… opens a composer. The note is appended verbatim to
review.md and is the first thing the next round reads, so it is effectively
that round's prompt. The composer offers:
- a What to change / Why / Out of scope template;
- Insert review findings… from any review round;
- a preview of exactly what will be recorded;
- the queued comments as rows you can untick (park), jump to or delete;
- After recording: record only, or launch the fix round now, choosing how it runs and, optionally, a different executor skill.
⌘↵ sends, and a dismissed composer keeps its draft.
Recording a round freezes the iteration first (diff, plan and comments under
history/), so every round has a before and after. Where the request lands
decides what the agent may touch:
- At plan review, the round revises the plan.
- Everywhere else, it changes the code and never rewrites
plan.md. To amend the plan, use ↩ Move back to… → plan-review first.
When the item has a PR, the fix round pushes so the PR follows.
⌕ Plan review (at plan review) and ⌕ Code review (diff review and both PR
stages) hand the item to a reviewer agent. A round is a side trip: the item goes
to reviewing and comes back exactly where it started. Rounds are unbounded,
and nothing advances until you approve. Rounds are numbered per target, so the
first code review is Code review 1 however many plan reviews came before it.
The launch composer shows the round's whole shape before anything spends tokens:
| Choice | Options |
|---|---|
| Which change? | multi-repo items only: this repo's own diff and/or any of the item's PRs, in one round |
| Depth | code and drift rounds: deep (default; traces callers, invariants and existing tests) or standard |
| Findings | keep local (default) or also post to the PR — one review with line comments, never an approval; on a draft PR the summary goes as a comment, since GitHub refuses reviews on drafts |
| Interaction | ask when it starts (default), interactive, or autonomous |
| Apply when done | let a round that says "apply" start the fix round by itself |
Interactive or autonomous is your call. An interactive round asks its open questions in one batch, each with a recommended answer. It then walks you through its findings before writing anything: you keep, drop or regrade each one, and plan issues come with lettered options. It asks before any trivial fix or PR post. An autonomous round decides alone and reports at the end. The executor phases start with the same question.
Code findings arrive as diff comments graded BLOCKER/RISK/GAP/NIT,
which you triage like your own. The verdict and plan findings go to
Agent reviews. When a round finishes, a toast and a strip on the item say
what it concluded and what it published. ↗ Post round N to PR shares an
existing round later, with no new review. A reviewer may fix only trivial
mechanical issues, after asking, and never rewrites what it reviews. While a
round runs, approval and the comment editor are locked. End round always
unlocks them, so a crashed reviewer can't wedge an item; a dead executor gets
⚠ Relaunch agent.
Every round ends with **Apply:** yes|no — reason (are the findings worth a
revision round?) and **Next:** <action> — reason. clash shows the call on the
button: ↻ Apply plan review 2 → revise plan · recommended (or not needed).
One click composes the note from the round's findings, records it as the next
change round and launches the agent. Edit the note first… opens the composer
pre-filled, for "apply 1a and 3b". Until a round is applied, the item header
says not applied yet and the stage's own Approve steps back.
◫ Explain plan reads plan.md and the code it will land in, and explains
what the implementation is going to do before it exists. ◫ Explain changes
reads the diff and explains what the change did. Each run writes two forms:
- a written walk-through with mermaid diagrams;
- one hand-drawn page of boxes and arrows (the parts, the repos, what is new), rendered sandboxed with scripts off.
The tab opens on the drawing. ◫ Diagram / ☰ Write-up switches form, and ⤢ gives the drawing the whole tab. An explainer judges nothing, runs alongside the item's other agents without blocking it, and can be told what to focus on.
⇄ Compare plan vs changes reads plan.md and the diff together, to answer
the question neither review can: did we build what we agreed to? Every
divergence has:
- a direction: missing, extra or different;
- a grade on consequence, not size: intended, benign or an issue.
A test or migration the plan promised and the change skipped is an issue.
Issues arrive as diff comments, so ↻ Apply or Request changes turns them into a
fix round. Drift resolved by amending the plan is reported instead, because a
fix round never rewrites plan.md: move the item back to plan review for that.
Launching it offers to refresh any explanation written for an older iteration
alongside it, pre-ticked; it runs in parallel, and the comparison doesn't wait
for it.
↩ Move back to… lists the stages behind the current one, with what each is for. Clicking a passed stage in the stepper does the same. Only the stage moves: the plan, diff, comments, PR and rounds all stay, and no agent runs. A finished or abandoned item offers ↩ Reopen at diff review.
- Create draft PR… offers two ways to write the description: from the plan (free, instant) or by the agent from the real diff (spends tokens). A branch that was never pushed is pushed first.
- ✓ Mark PR ready flips the draft once you have validated it. ✓ PR is ready → PR ready records a PR already flipped on GitHub. ✓ Mark done closes the item at either PR stage.
- ⇄ Answer PR threads launches an agent that reads every review thread, fixes the trivial ones with commits, replies in each thread, and queues the rest as comments for you. The button shows how many threads are open (a thread is settled only when your reply is its last word, so findings clash itself posted count until they get a decision) and says all answered when nothing is.
- PR skill (Settings → Workflows, default
hivebrite-engineering:github-pr,nonedisables, per-item override in ⚙ Settings): agent-written PRs go through that skill, so they follow your org's titles, templates and ticket links. workflows.forge(auto|github|none):autodetects GitHub from the origin remote (GitHub Enterprise included).nonehides every PR feature.- A command that needs a PR the item doesn't know asks for its URL, attaches it and retries. A review round can run locally instead.
🔗 Link a PR… attaches PRs from other repositories. They show as chips in the item header and refresh with the primary. Only the primary PR moves the item's status; an item with only linked PRs closes when all of them merge. Once an item has several PRs, every PR action asks which ones (any subset, with an all/none toggle):
| Action | Pre-ticked |
|---|---|
| Open PRs (n)… — the first in a split pane, the rest as browser tabs | all |
| ✓ Mark PR ready… — drafts only; only the primary moves the item | the primary |
| ↗ Post round N to PR… | the primary |
| ⌕ Code review — one round over several diffs; a linked PR's findings are posted to that PR | this repo's own diff |
| ⇄ Answer PR threads… | every PR with open threads |
Every PR chip also has a right-click menu with that PR's actions. The ⇄ PR dashboard (button on the WORKFLOWS section) lists every item holding a PR across all projects, decisions first and merged last.
↗ Share… composes one document from the item (summary, plan, rounds, verdicts, open comments, diff) from three presets with per-section checkboxes. The preview is the payload. It can go to:
- the clipboard;
- a
.mdor self-contained.htmlfile; - Slack or Discord;
- a Jira ticket as one comment. The key is pre-filled from the item's remembered ticket or detected in its title or branch.
Who posts a share is a per-destination setting (jira_transport /
chat_transport):
- An agent session (the default) posts it through a skill you name, or else with whatever tooling that session has connected.
- clash itself posts over HTTPS with the webhook or Jira credentials.
Neither route is a fallback for the other, and each button names its route.
Notify decisions (notify_webhook, off by default) announces every item an
agent parks at a decision stage on the configured webhook. Your own clicks
never post.
-
Agent CLI: workflow sessions run on Claude Code or OMP.
workflows.agentdefaults toaskat every step and can be fixed per item. See OMP sessions. -
Lead and subagents: every Claude workflow session runs on one pinned lead model (
workflows.lead_model, defaultclaude-opus-5-5). Underworkflows.delegation = team(the default), the lead may split work across parallel subagents onworkflows.subagent_model(defaultclaude-sonnet-5-5). It sizes the round first: small work stays with the lead, and large work gets explorers, implementers in waves of disjoint files, or one reviewer per area, followed by verification.solokeeps everything in one session. Details. -
Skills: the agent side is five skills embedded in the binary:
clash-workflow— the executor: plans, implements, opens PRs, writes the hand-off;clash-plan-review— the plan reviewer;clash-code-review— the code reviewer;clash-explain— the explainer;clash-drift-review— plan vs changes.
Startup installs missing skills and refreshes the ones you never edited. clash asks (Keep my edits / Overwrite) only when an upgrade would replace a skill you edited by hand;
general.skills_updatepins the answer. The ☰ button on the WORKFLOWS section lists every installed skill and badges the ones clash manages.
~/.claude/clash/workflows/<project>/<item>/ (or workflows_dir) holds:
meta.jsonandplan.md, withplan-history/for its versions;review.md(your decisions) andagent-review.md(the reviewers' rounds);annotations.json;explain-plan.*,explain-diff.*anddrift.*;handoff.mdandexplainers.json;history/<NNN>/round snapshots.
The contract for what agents may write, and when, is docs/workflows.md.
| Key | Action |
|---|---|
j / k |
Select next / previous |
g / G |
Jump to first / last |
Enter |
Drill in |
Esc |
Go back |
r |
Refresh |
q / Ctrl+C |
Quit (with confirmation; Ctrl+C again while shutting down force-quits) |
In dialogs: y / n (or Esc) answer a confirmation; pickers take j/k,
Enter, Esc; text fields support the usual readline keys (Ctrl+A/E/U/K/W,
Alt+B/F/D). The tour advances with Enter/Space/→ and is skipped with
Esc/q; the help overlay scrolls with j/k and closes with ?/Esc/q.
| Key | Description |
|---|---|
: |
Command mode — :teams, :sessions, :tour, :update, :quit |
/ |
Fuzzy filter |
? |
Context help |
| Key | Action |
|---|---|
Enter / i |
Open the session detail |
a |
Attach (inline terminal); on a 🌿 wild row: take over and attach (one confirm) |
p |
View git diff |
e |
Open project in IDE (auto-detect + picker) |
f |
Queue a follow-up prompt — delivered when the session is next idle |
F |
Cancel a queued follow-up (picker when several are pending) |
o |
Open in new pane / tab / window |
O |
Open ALL running sessions (smart layout) |
c / n |
New session (directory, name, worktree?, then agent: claude/omp) |
s |
Stash / unstash session (stop process, keep in registry) |
w |
Spawn session in a git worktree |
Tab |
Expand / collapse subagents |
A |
Cycle section filter (All/Active/Done/Fail/External; Fail is skipped under :active) |
S |
Stash / unstash ALL sessions (with confirmation) |
d |
Drop session |
D |
Drop ALL sessions |
| Key | Action |
|---|---|
Enter / i |
Open the team's detail (its members and tasks from there) |
c |
Create team |
R |
Rename team (moves its config + tasks) |
d |
Delete team |
e |
Edit team description |
m |
Add member (name → agent type → model) |
x |
Remove member (picker) |
A status bar at the bottom shows session name, project, and git branch. The PTY is resized to fit above the bar.
| Key | Action |
|---|---|
Ctrl+B |
Detach (works across all terminal encodings) |
| Everything else | Forwarded to the session's agent (claude or omp), mouse scroll included |
| Key | Action |
|---|---|
j / k |
Scroll |
s / Enter |
Subagents |
t |
Linked team (the Teams list when none is linked) |
m |
Team members (all agents when no team is linked) |
p |
View git diff |
a |
Attach |
o |
Open in new pane / tab / window |
w |
Spawn a session in a git worktree |
e |
Open in IDE |
d |
Drop |
| Key | Action |
|---|---|
Enter |
Subagent detail |
a |
Attach to the parent session |
o |
Open the parent session in a new pane / tab / window |
e |
Open in IDE |
p |
View git diff |
f / F |
Queue / cancel a follow-up for the parent session |
| Key | Action |
|---|---|
j / k |
Scroll diff content |
n / p |
Next / previous file |
r |
Refresh diff |
Esc |
Go back |
Opening a team scopes the Agents and Tasks views to that team. Its member list
marks members whose session is currently running with ● (○ otherwise); the
Agents view says active / idle. Per-member edits are commands run
from a team view — :member model <name> [model], :member type <name> <type>,
:member prompt <name> <text>, :member rename <old> <new>.
| Key | Action |
|---|---|
Enter / a |
View agents (team-scoped) |
t |
View tasks (team-scoped) |
s |
View lead session |
e |
Edit team description |
m |
Add member (name → agent type → model) |
x |
Remove member (picker) |
R |
Rename team |
d |
Delete team |
The Tasks view is scoped to the current team.
| Key | Action |
|---|---|
Enter |
View task detail |
c |
Create task |
s |
Cycle status (pending → in-progress → completed → …) |
a |
Assign owner (picker of the team's members) |
d |
Delete task |
In a task's detail view, s cycles its status and d deletes it (with
confirmation).
Reach the Scratches view with :scratch (also :notes). Scratches are an
IntelliJ-style "Scratches and Consoles" tree: notes and folders you can
nest, rename, and reorganize. Folders sort first; the tree is shown indented
with an expand/collapse caret.
| Key | Action |
|---|---|
a / c / n |
New scratch — created inside the selected folder (or alongside the selected note, else at the root) |
A |
New folder (same placement rule) |
Enter |
Open a file in an editor (picker), or expand/collapse a folder |
e |
Open the selected note in an editor (picker) |
r |
Rename the selected file or folder |
m |
Move the selected file or folder into another folder (picker; choose / (root) to move it back to the top level) |
y |
Copy the entry's path to the clipboard (picker: absolute path, path relative to the scratch root, or file name) — IntelliJ-style "Copy Path/Reference…" |
d |
Delete the selected entry (folders are removed recursively, with confirmation) |
Scratches are plain files and folders under ~/.claude/clash/scratch/ by
default; override the location with scratch_dir in config.toml or the GUI
Scratch directory setting (which writes the same key, so the TUI honors it
too). The tree auto-refreshes when the scratch directory changes on disk
(a note saved from an editor, the GUI, a git pull…) via a filesystem watcher
that follows the configured directory. The editor picker lists installed IDEs
(Cursor, VS Code, Zed, JetBrains, …) and terminal editors (vim, nvim, emacs,
nano, helix, micro); terminal editors open in a tab/pane, GUI editors launch
alongside.
y copies an entry's path to the system clipboard: it uses the platform
clipboard tool (pbcopy/wl-copy/xclip/xsel/clip) for local copies and
also emits an OSC 52 escape, so it works over SSH and in clipboard-capable
terminals (iTerm2, kitty, WezTerm, Ghostty, tmux with set-clipboard on).
In the GUI, scratches live in a collapsible Scratches sidebar section that
renders the same tree: click a folder to expand/collapse it, click a note to
open it, and use the section's + button (or a folder's right-click menu) to
create notes and folders. Drag and drop any note or folder onto another
folder — or onto empty space to move it back to the root — to reorganize.
Right-click any entry to copy its path (absolute path, path relative to the
scratch root, or file name — handy for pasting into a Claude session), rename,
or delete it. The tree auto-refreshes when
the scratch directory changes on disk (a note saved from an editor, the TUI, a
git pull…) via a filesystem watcher; the section's ⟳ button forces a
manual re-list.
| Command | Action |
|---|---|
:teams |
Navigate to Teams view (each view name also works singular: :team, :session, …) |
:sessions |
Navigate to Sessions view |
:agents |
Navigate to Agents view |
:tasks |
Navigate to Tasks view |
:subagents |
Navigate to Subagents view |
:inbox |
Show the selected/drilled-in agent's inbox |
:prompts |
Navigate to Prompts view |
:scratch / :notes |
Navigate to Scratches view |
:create team <name> |
Create a new team |
:rename team <old> <new> |
Rename a team |
:delete team <name> |
Delete a team (also :remove team) |
:member model <member> [model] |
Set a member's model (current team; empty = inherit) |
:member type <member> [type] |
Set a member's agent type (empty = general-purpose) |
:member prompt <member> <text> |
Set a member's system prompt |
:member rename <old> <new> |
Rename a member |
:create task <team> <subject> |
Create a task |
:new [path] |
Spawn a new session (default agent) |
:new --agent <claude|omp> <path> |
Spawn a new Claude Code or OMP session (the path is required for the agent to stick) |
:new --preset <name> |
Spawn session from a preset |
:diff |
View git diff for current session |
:rename <name> |
Rename session (from detail view) |
:active / :all / :external (:wild) |
Filter sessions (active only / all / wild + external only) |
:tour (:guide) |
Replay guided tour |
:config |
Show the config.toml path |
:reload (:reload-config) |
Re-read config.toml now (it is watched, so this is only ever a nudge) |
:update (:upgrade) |
Update clash |
:quit (:q) |
Exit |
clash runs OMP (oh-my-pi, omp) sessions next to Claude
Code ones; the choice is made per session (the GUI's new-session dialog, the
TUI's last new-session prompt, or :new --agent omp <path>). Every workflow
step — plan, implement, change rounds, reviews, answering PR comments,
explanations, drift, shares — follows one setting: the item's ⚙ Settings tab,
else workflows.agent (ask by default, or claude / omp). A fixed agent
runs every step unasked; ask asks at every start — a picker for one-click
starts, a Run on row in the review and change-request composers —
pre-selecting the agent the item last ran on, which is also what a relaunch
or an auto-applied round uses without asking. An agent whose binary does not
resolve is greyed out with the reason, in these pickers and in the
new-session dialog, and the backend refuses it before any set-up.
An OMP session is the same row as any other — the GUI badges every row
with its agent (CC for Claude Code, OMP) — and
everything built around sessions applies:
- Identity and resume. clash creates the transcript itself with
omp --resume ~/.omp/agent/sessions/<bucket>/<timestamp>_<id>.jsonl, so the file's uuid is the session id exactly likeclaude --session-id. A resume reopens that file (omp appends in place — no fork to chase). - Status. A clash-owned extension (
~/.claude/clash/hooks/omp-status.js, loaded withomp -e) writes the same status files as the Claude hook, from omp's own events — a tool-approval prompt or anaskquestion reads as prompting./newand/resumeinside omp re-key the row like/clear. - Skills. The five
clash-*skills are also installed into~/.omp/agent/skills/at startup (when that directory exists) and again before every OMP workflow launch, so an omp installed since startup still has the skills its kickoff names. - Workflows. OMP workflow sessions launch with
workflows.omp_model(empty = omp's own default model) instead ofworkflows.lead_model.
Settings: general.omp_bin, general.default_agent, paths.omp_dir,
workflows.agent, workflows.omp_model. Details: docs/hooks.md.
One file, shared by the TUI and the GUI. Find it with clash config --path
(~/.config/clash/config.toml on Linux, ~/Library/Application Support/clash/config.toml on macOS, %APPDATA%\clash\config.toml on Windows).
clash config # the merged config, annotated with where each value came from
clash config --path # just the path
clash config --defaults # the full annotated default file, ready to copy lines out of
clash config --show-effective # same as bare `clash config`
clash config --validate # check the file; exits non-zero on an error
clash config --schema # JSON Schema, for taplo / Even Better TOML completionThe flags are mutually exclusive. Bare clash config exits 1 on a parse error
(after printing the defaults it fell back to).
Later layers win, key by key:
| Layer | Where | Notes |
|---|---|---|
| defaults | in the binary | clash config --defaults prints them |
| user | clash config --path |
what the GUI Settings panel writes |
| project | <repo>/.clash/config.toml (found by walking up from the cwd) |
restricted (see below) |
| environment | CLASH_<SECTION>_<KEY> |
any key, e.g. CLASH_SESSIONS_REFRESH_SECS=5 |
A project config may set only paths.claude_dir, paths.scratch_dir,
paths.workflows_dir, [actions] and notifications.hooks. It deliberately
cannot change claude_bin, omp_bin or any other key — clash spawns
processes, so a cloned repo must not be able to decide which binary runs.
Rejected keys and unknown CLASH_* variables are reported by
clash config --validate, not silently ignored (CLASH_LOG_RETENTION_HOURS
is the one CLASH_* variable that is not a config key).
schema_version = 2
[general]
claude_bin = "claude" # name on PATH, or an absolute path
skills_update = "ask" # when an upgrade ships skills you edited: ask | all | keep
omp_bin = "omp" # OMP (oh-my-pi) binary for OMP sessions
default_agent = "claude" # agent new sessions pre-select: claude | omp
debounce_ms = 200 # filesystem-watcher debounce
[paths]
claude_dir = "" # empty = ~/.claude
omp_dir = "" # OMP agent dir; empty = ~/.omp/agent
scratch_dir = "" # empty = <claude_dir>/clash/scratch
workflows_dir = "" # empty = <claude_dir>/clash/workflows
[sessions]
default_cwd = "" # prefill for a new session; empty = home
confirm_kill = true # ask before killing (stash never asks)
refresh_secs = 2 # GUI session-list poll cadence
[terminal]
shell = "" # in-app terminals; empty = $SHELL
tui_terminal = "" # TUI launcher target; empty = auto-detect
[notifications]
enabled = true
title_attention = true # "clash (2!)" in the window title
[workflows]
agent = "ask" # agent CLI workflow sessions run on: ask | claude | omp (per-item override)
omp_model = "" # --model for OMP workflow sessions; empty = omp's default
lead_model = "claude-opus-5-5" # --model for every Claude workflow session (the lead)
delegation = "team" # team: lead fans work out to parallel subagents | solo
assist = "suggest" # suggest: highlight the next step | autopilot: also start it when it decides nothing | off
subagent_model = "claude-sonnet-5-5" # model those subagents run on; empty = inherit the lead's
pr_skill = "hivebrite-engineering:github-pr" # skill the PR phase opens PRs with; "none" disables
forge = "auto" # code forge for PR features: auto | github | none
slack_webhook = "" # Slack incoming webhook for sharing + notifications
discord_webhook = "" # Discord webhook for sharing + notifications
notify_webhook = "off" # announce decision states: off | slack | discord
jira_base_url = "" # Jira site URL for share → Post to Jira; empty disables
jira_email = "" # Jira account email (API-token auth)
jira_api_token = "" # Jira API token (id.atlassian.com → Security)
jira_transport = "agent" # who posts a share to Jira: agent (a session) | clash (direct, with the jira_* keys)
chat_transport = "agent" # who posts a share to Slack/Discord: agent | clash (the webhooks)
jira_skill = "" # skill the agent Jira route goes through; empty = the session's own tooling
chat_skill = "" # skill the agent chat route goes through; empty = the session's own tooling
[[ides]] # extra editors offered when opening a project or note
name = "VS Code"
command = "code"
terminal = false
description = "" # optionalThe GUI's 20 xterm-rendering settings (font, cursor, scrollback, scroll, link handling) and its theme stay in the GUI's own store — the TUI can't apply them. Everything above is read by both.
- Edits apply live. The config directory is watched; a change by hand, by
the GUI, or by another clash instance is picked up without a restart.
:reloadforces it.general.claude_bin,general.omp_bin,general.debounce_msandpaths.omp_dirtake effect on restart, and clash says so rather than pretending otherwise. - A typo never loses your settings. A parse error keeps the last good values
in memory, reports the failure with
line:column, and blocks writes until you fix it — so the next GUI toggle can't overwrite your file with defaults. - Your comments and unknown keys survive a save. Writes edit the parsed document and touch only the keys that changed, so comments, key order, and any key this version doesn't know (including one a newer clash wrote) round-trip intact.
- Concurrent instances are safe. Several clash processes run by design; the whole read-modify-write happens under an advisory lock, so two of them editing different settings can't drop each other's change.
See docs/configuration.md for the full reference —
every property's metadata, the layer contract, and the migration behaviour.
clash reads directly from Claude Code's filesystem:
~/.claude/
├── projects/{name}/
│ ├── sessions-index.json # Session index with summaries
│ ├── {session-id}.jsonl # Conversation log
│ └── {session-id}/subagents/ # Subagent transcripts
├── teams/{name}/config.json # Team config + members
│ # (Claude's auto session-* teams are hidden)
├── teams/{name}/inboxes/{agent}.json # Per-agent inboxes
└── tasks/{team-name}/{id}.json # Tasks
Outside its own ~/.claude/clash/ directory, clash writes only where you ask
it to — team configs, inboxes and tasks when you edit them — plus its five
embedded workflow skills under ~/.claude/skills/<name>/ (with a
.clash-skills.json manifest). It registers nothing in your Claude settings
files; older versions' entry in ~/.claude/settings.local.json is withdrawn at
startup. OMP sessions are read from ~/.omp/agent/sessions/.
clash also maintains its own state in ~/.claude/clash/:
~/.claude/clash/
├── hooks/status-hook.sh # Lifecycle hook script
├── hooks/settings.json # Hook registration, passed to `claude --settings`
├── hooks/omp-status.js # OMP status extension, loaded with `omp -e`
├── status/{session-id} # Instant status from hooks
├── names/{session-id} # Session display names
├── project-names/{encoded-cwd} # Project-to-name mapping
├── sessions.json # Session registry (+ sessions.json.bak)
├── ui_state.json # Persisted UI state (nav, selection, filters) — saved
│ # continuously so any exit resumes where you were
├── scratch/ # Scratch notes — a nested tree of
│ ├── {name}.md # free-form text files and
│ └── {folder}/{name}.md # user-created folders
├── workflows/<project>/<item>/ # Workflow items (see Workflows → Storage)
└── share/ # Payloads handed to share sessions
Outside ~/.claude, in the platform's config and data directories
(~/.config/clash and ~/.local/share/clash on Linux, ~/Library/Application Support/clash on macOS):
config.toml # the shared configuration (see Configuration)
presets.json # global session presets
gui-state.json # GUI workspaces, layout and GUI-only settings
clash.log # log, rotated after 24h (CLASH_LOG_RETENTION_HOURS)
daemon-<pid>.sock # one per running instance; `clash attach` finds the owner
Presets are reusable templates for session creation. When presets are available, pressing n shows a picker; otherwise the manual flow is used (directory →
name → worktree → agent).
{
"presets": {
"backend-fix": {
"description": "Backend bugfix workflow",
"directory": "./",
"worktree": true,
"setup": ["./.clash/setup-backend.sh"],
"teardown": ["./.clash/teardown.sh"]
},
"frontend-feature": {
"description": "New frontend feature",
"directory": "./frontend",
"worktree": false
}
}
}Same format as project presets. Project presets override global presets with the same name.
If .superset/config.json exists, it appears as a synthetic "superset" preset with the setup and teardown fields mapped directly.
| Field | Type | Description |
|---|---|---|
description |
string | Shown in the preset picker |
directory |
string | Working directory (relative or absolute) |
prompt |
string | Initial prompt sent to the session (GUI) |
worktree |
bool? | true/false = auto, omit = ask |
setup |
string[] | Scripts to run after session creation |
teardown |
string[] | Scripts to run before session drop |
Setup scripts receive CLASH_ROOT_PATH and CLASH_SESSION_ID env vars. Each
script has a 30s timeout. setup/teardown run from the TUI; the GUI applies a
preset's directory, worktree and prompt.
clash follows The Elm Architecture (TEA) with clean architecture layers:
User Input → Action → reducer() → (State', Effects) → execute_effects() → draw()
(pure) (infrastructure IO)
| Layer | Purpose |
|---|---|
| Domain | Entities, port traits — no dependencies |
| Application | State, actions, effects, pure reducer, pure workflow/session logic |
| Adapters | Input mapping, view rendering |
| Infrastructure | Event loop, filesystem, in-process PTY daemon, session refresh, config, hooks, windowing, git/gh/forge, skills, self-update, TUI widgets |
Two frontends share that core: the TUI (src/main.rs) and the GUI — a Tauri 2
app (gui/src-tauri) whose frontend is plain JS in gui/dist with its pure
modules tested under gui/tests. CLAUDE.md maps every subsystem.
cargo test --all-targets # Rust tests
node --test "gui/tests/*.test.js" # GUI frontend tests
cargo clippy -- -D warnings # Lint (as CI runs it)
cargo fmt --check # FormattingReleases are automatic — push with conventional commits (feat:, fix:) and CI handles the rest.
MIT