# Ouijit documentation Every page below is also served on its own: https://ouijit.com/docs/.md (for example https://ouijit.com/docs/cli.md). Index with per-page summaries: https://ouijit.com/llms.txt # Getting started Ouijit is a task and terminal manager for running coding agents in parallel. Each task gets its own git worktree and terminal, lifecycle hooks launch your agent with the right context, and a session-aware CLI lets agents drive the board back. From download to an agent working on your first task takes about a minute. Reading this from inside an Ouijit terminal? You're an agent with `ouijit` on `PATH` and its reference in your system prompt. The [Agents page](https://ouijit.com/docs/agents/) is your contract, and every page here is plain Markdown at `/docs/.md`. ## Requirements - **macOS** 13+ (Apple Silicon or Intel) or **Linux** x64 - **Git** 2.20+ on `PATH` - An agent CLI you already use: `claude`, `codex`, `pi`, or `opencode` The downloads are self-contained. Nothing else is required to run the app. ## Download and install - [macOS (Apple Silicon)](https://github.com/ouijit/ouijit/releases/latest/download/ouijit-darwin-arm64.dmg) - [macOS (Intel)](https://github.com/ouijit/ouijit/releases/latest/download/ouijit-darwin-x64.dmg) - [Linux (x64)](https://github.com/ouijit/ouijit/releases/latest/download/ouijit-linux-x64.zip) On macOS, open the DMG and drag **Ouijit.app** to Applications. On Linux, unzip the archive and put the binary somewhere on your `PATH`. ## Your first agent task 1. **Add a project.** Click the **+** button in the sidebar and select a folder. If it isn't a git repository yet, Ouijit offers to run `git init` in place. You land on the [kanban board](https://ouijit.com/docs/kanban/). 2. **Describe the task.** Press `⌘N` (`Ctrl+N` on Linux), give the task a name, and write the prompt you'd give your agent. Paste in screenshots if you have them. 3. **Drag the card to In Progress.** Ouijit creates a branch and an isolated [worktree](https://ouijit.com/docs/worktrees/), opens a terminal in it, and asks what to run. Enter: ``` claude "$OUIJIT_TASK_DESCRIPTION" ``` **Save** remembers it as the project's [start hook](https://ouijit.com/docs/hooks/), so from now on every card you drag starts an agent on its own copy of the repo. 4. **Watch it work.** The card shows whether the agent is thinking or waiting on you, and Ouijit chimes when a turn ends. Press `⌘D` to [review the diff](https://ouijit.com/docs/diff/), leave notes on changed lines, and drag the card to In Review when it's ready. Repeat from step two, in parallel: each task runs in its own worktree, so agents never trip over each other. ## Where to go next - [Command palette](https://ouijit.com/docs/palette/): jump between everything with `⌘K`. - [Panels](https://ouijit.com/docs/panels/): put the agent's plan, its dev server, and a runner beside its terminal. - [Sandbox](https://ouijit.com/docs/sandbox/): run a task in a Lima VM or under nono when you don't trust the code. - [Pull requests](https://ouijit.com/docs/pull-requests/): review and merge without leaving Ouijit. ## Build from source Building needs Node.js 20+ (the repo pins 22) and C/C++ build tools for the native modules (better-sqlite3, node-pty, koffi): - **macOS:** `xcode-select --install` - **Linux:** `sudo apt install build-essential python3` (Debian/Ubuntu) ``` git clone https://github.com/ouijit/ouijit.git cd ouijit npm install npm start ``` ## Projects folder New projects are created in `~/Ouijit/projects` by default. Change it under **App Settings → Projects**. If projects still live in the old folder, Ouijit asks whether to move them (tasks, hooks, and settings stay attached), forget them (files stay on disk), or leave them where they are. ## Check your environment Ouijit checks your environment at startup: git, the agent CLIs (`claude`, `codex`, `pi`, `opencode`), and the tools behind optional features (`gh`, Lima, nono). The **Help & setup** dialog shows each result, and dialogs that need a missing tool say so inline. ## Update Ouijit Ouijit checks for updates at launch and hourly. On macOS the update downloads in the background and a dialog offers to restart when it is ready. On Linux a notice links to the release page. After an update, a **What's New** dialog shows that version's release notes once. Turn automatic checks off under **App Settings → Updates**. --- # Worktrees Every task gets its own git worktree: an isolated directory on its own branch. Work on several tasks at once without branch switching, stashing, or agents overwriting each other. ## How tasks map to worktrees Git worktrees let one repository check out several branches into separate directories at the same time. Ouijit creates one per task when the task starts, so each piece of work has its own directory and branch. ``` ~/Ouijit/worktrees/ my-project/ T-42/ # Worktree for task 42 T-43/ # Worktree for task 43 ``` ## Task lifecycle A task has one of four statuses: **To Do**, **In Progress**, **In Review**, **Done**. Each event below changes the status and can run a [hook](https://ouijit.com/docs/hooks/). | Event | Status | What Ouijit does | | --- | --- | --- | | **Create** | → To Do | Adds a task card to the To Do column. | | **Start** | To Do → In Progress | Creates a branch (e.g. `T-42`) and a worktree at `~/Ouijit/worktrees//T-42`, opens a terminal in it, runs the `start` hook. | | **Continue** | In Review or Done → In Progress | Opens a new terminal in the existing worktree, runs the `continue` hook. | | **Review** | → In Review | Runs the `review` hook. | | **Done** | → Done | Runs the `done` hook. | | **Delete** | Task removed | Moves the worktree to the trash, deletes the branch. | ## Merge targets Each task has a merge target, the branch its work is destined for. It defaults to the repository's main branch and drives the default [diff base](https://ouijit.com/docs/diff/). Set it per task to chain a task off a parent branch: ``` ouijit task set-merge-target 43 T-42 ``` ## Copy-on-write cloning Ouijit clones worktrees with copy-on-write (APFS on macOS, btrfs/XFS reflinks on Linux): - Worktree creation is fast regardless of repository size. - Disk usage is proportional to your changes, not the full repository. - Gitignored files (dependencies, build caches) come along, so the worktree is ready to run. ## Choose a creation mode Each project picks how new worktrees are populated, under **Project Settings → Worktree**. | Mode | What lands in the worktree | When to pick it | | --- | --- | --- | | **Quick start** (default) | Tracked files plus a copy-on-write clone of every gitignored path (`node_modules`, `.env`, build caches). | You want a worktree that runs immediately, with no setup step. | | **Clean checkout** | Just `git worktree add`. Only tracked files appear. | You want a reproducible setup driven by your [start hook](https://ouijit.com/docs/hooks/) (e.g. `npm ci`), or you don't want gitignored secrets copied across tasks. | Sandboxed tasks always use clean checkout, regardless of this setting. Provision the VM-side environment with the sandbox setup hook. ## Inspect worktrees manually Ouijit manages worktrees for you, but they are standard git worktrees: ``` # List all worktrees git worktree list # From inside a worktree, check which branch it's on git branch --show-current ``` --- # Kanban board A four-column board for tasks. Dragging a card changes its status, creates worktrees, and fires hooks. ![Kanban board with agent terminals attached to task cards](https://ouijit.com/assets/screenshots/kanban.png) ## Columns The four columns map to the four task statuses: **To Do**, **In Progress**, **In Review**, **Done**. Dragging a card between columns changes its status, which can fire a [hook](https://ouijit.com/docs/hooks/). Dragging from To Do to In Progress creates the task's branch and worktree. See [Task lifecycle](https://ouijit.com/docs/worktrees/#task-lifecycle) for the full mapping. ## Create a task Click the **+** button on a column header, or press `⌘N` (`Ctrl+N` on Linux). A task takes a name and an optional prompt for the agent. Paste images into the prompt and the agent can read them when the task runs. Each task gets an auto-incrementing number (`T-1`, `T-2`) used for branch names and worktree directories. ## Act on a task Right-click a task card for its context menu: - **Open in**. A terminal in the task's worktree, a [sandboxed](https://ouijit.com/docs/sandbox/) terminal for each installed backend (**Lima VM** or **nono**), your editor, or the OS file manager. - **Rename**. Edit the task's name inline. - **Move to Done / Reopen**. Complete an active task or reopen a finished one. - **Delete**. Remove the task, its worktree, and its branch. If a task has running terminals, they appear at the top of the menu so you can jump straight to them. With the [GitHub surface](https://ouijit.com/docs/pull-requests/) enabled, cards show a badge for their linked pull request, and the menu adds **Create pull request** or **Pull request #N**. ## Shells outside tasks Terminals that don't belong to a task (a plain project shell) get their own strip on the board, so nothing you have running is hidden behind a card. --- # Terminals Terminals are cards in a stack. The active one is in front; the rest peek out behind it so you can see what's running. ## Card stack Each terminal runs your default shell in the task's worktree. Click a background card to bring it forward. When an agent runs in a card, its status (thinking or idle) shows on the card even when it is behind others. See [Harnesses](https://ouijit.com/docs/harnesses/). ## Keyboard shortcuts | Shortcut | Action | | --- | --- | | `⌘K` / `Ctrl+K` | Open the [command palette](https://ouijit.com/docs/palette/) | | `⌘I` / `Ctrl+I` | New terminal | | `⌘W` / `Ctrl+W` | Close terminal | | `⌘1`\-`⌘9` / `Ctrl+1`\-`Ctrl+9` | Select terminal by position | | `⌘⇧←` / `Ctrl+Shift+Left` | Previous stack page | | `⌘⇧→` / `Ctrl+Shift+Right` | Next stack page | | `⌘T` / `Ctrl+T` | Show or hide the kanban board | | `⌘N` / `Ctrl+N` | New task | | `⌘P` / `Ctrl+P` | Open the runner [panel](https://ouijit.com/docs/panels/) | | `⌘D` / `Ctrl+D` | Toggle the [diff panel](https://ouijit.com/docs/diff/) | ## Home view The home view shows every live terminal across all projects in one stack, grouped by project. Click a group's tab to bring it forward. Terminals that don't belong to a registered project appear as **Shell**. Press `⌘T` there to group by tag instead, and `⌘I` to open a shell in your home directory. When nothing is running, the home view offers to [resume your last session](https://ouijit.com/docs/resume/) and lists recent tasks so you can pick up where you left off. ## Tag terminals Tag a terminal from the tag button on its card. Tags belong to the task, so every terminal of the task carries them. Use tags to slice what's running: - **Project view**. A filter pill next to the toolbar buttons shows only matching cards. It appears once any session carries a tag. - **Home view**. The tag control groups sessions by tag, or filters to one tag. Untagged sessions group under **Untagged**. Manage tags from the shell or an agent with [`ouijit tag`](https://ouijit.com/docs/cli/). --- # Panels Attach panels to a terminal as tabs: a runner for long-running commands, web previews, and rendered markdown files. A plan can open right beside the agent working from it. ## Open a panel Click the **+** button in a terminal's header. The menu lists **Run** (when a [run hook](https://ouijit.com/docs/hooks/) is configured), each project script, **Web Preview**, and **Markdown File**. Panels open as tabs; a terminal can hold several of each kind. Tabs shrink like browser tabs when crowded, and clicking the active tab collapses the panel back to the bare terminal. Each panel can split beside the terminal or go full width. Agents and scripts open panels through the CLI: `ouijit markdown add` and `ouijit preview add` attach to the terminal they run in. Adding a panel that is already open activates it instead of duplicating it. ## Runner A runner is a child terminal that executes the project's run hook or a named script, with a status dot on its tab (running, success, error). Kill leaves the tab in a stopped state with a Restart button; closing the tab removes it. Press `⌘P` to jump to the runner, or start one if none is open. By default, launching a runnable that is already running in the task focuses the existing instance. Check **Restart if it's already running in the task** on a script or the run hook to kill and relaunch instead. The restart is scoped to the task's worktree, so parallel tasks never evict each other's runners. ## Web preview An embedded browser for the app you're building. It has back, forward, reload/stop, and an editable URL field. Open one empty and type a URL, point it at a dev server the runner started, or let an agent surface its work with `ouijit preview add http://localhost:3000`. ![Agent terminal beside a web preview of the app it is building](https://ouijit.com/assets/screenshots/preview.png) ## Markdown A read-only rendered view of a markdown file, for plans, notes, and TODO lists shared between you and an agent. It re-renders when the file changes on disk, so a plan the agent edits updates live. Mermaid code fences render as diagrams, code blocks get syntax highlighting, and file paths that exist in the worktree become links that open in your [editor](https://ouijit.com/docs/hooks/) at the right line. ![Agent terminal beside a rendered markdown plan](https://ouijit.com/assets/screenshots/markdown.png) --- # Command palette Press `⌘K` (`Ctrl+K` on Linux) to jump to any terminal, project, task, or pull request. ![Command palette over the kanban board](https://ouijit.com/assets/screenshots/palette.png) ## What Enter does A task is always one row. Its state decides the action: | Row | Action | | --- | --- | | Task with a live terminal | **Focus terminal** | | Task with a worktree but no terminal | **Open worktree**: opens a shell there | | Unstarted task | **Start task**: creates the branch and worktree, moves it to In Progress, opens a shell. No hook runs. | | Terminal | **Focus terminal**, switching project and view as needed | | Project | **Switch project** | | Pull request | **Open pull request** in the [panel](https://ouijit.com/docs/pull-requests/) | A task's live terminals nest under its row, each selectable on its own. Pull requests appear for the active project when the [GitHub surface](https://ouijit.com/docs/pull-requests/) is on; a pull request already linked to a task shows as that task instead. ## Search by anything Type any part of a name, a task number (`T-42` or `42`), a PR number, a branch, a tag, or a project name. Items you open often rise toward the top. With an empty query the palette shows a **Recent** group first, then terminals, projects, tasks, and pull requests. --- # Diff review Every task terminal carries a diff of its worktree, with word-level highlighting and notes that flow into the agent's prompt. Review the work without leaving the terminal it happened in. ![Worktree diff beside the agent terminal, with a note being written on a changed line](https://ouijit.com/assets/screenshots/diff.png) ## Open the diff Click the **Diff** button in the terminal header (it shows the change size) or press `⌘D`. The panel lists changed files in a tree, renders per-file diffs with syntax highlighting, and marks the exact changed words within each line. Panel state survives app restarts via [session resume](https://ouijit.com/docs/resume/). ## Pick a base The comparison always ends at the working tree. The picker in the panel header chooses where it starts: - **Uncommitted changes**: everything since your last commit. - **The task's merge target**, the repository's main branch, or the branch's upstream. - **Any local or remote branch**, searchable by name. Picking a remote ref fetches it first, and a freshness button next to the picker shows the fetch age and refetches on click. A branch comparison shows the task's own changes, not what has landed on the target since. The chosen base is remembered per worktree. ## Leave notes for the agent Hover a line and click **+**, or drag across several lines, and write a note. Notes are kept with the worktree until you hand them over: the floating island at the bottom of the panel lists them, and **Send** pastes them into the terminal's prompt as one block, quoted code and all, unsent. You press Enter. **Copy** puts the same block on the clipboard for any other destination. Notes anchor to the code, not the line number. As the agent edits, each note follows its text to its new location; a note whose text is gone is dropped. A note outside the current comparison is kept and flagged **not in this comparison**. --- # Pull requests An experimental GitHub surface: a pull request inbox, review comments staged locally until you send them, and merging. It drives the `gh` CLI, so `gh auth login` is the only setup. ## Turn it on Enable **GitHub** under **Project Settings → Experimental**. It needs the [GitHub CLI](https://cli.github.com) 2.48.0 or newer on `PATH`, signed in via `gh auth login`, and an `origin` remote on GitHub. Ouijit never stores a token; every GitHub call goes through `gh` on the host, and sandboxed terminals never see your credentials. ## Work the inbox The panel lists open pull requests in three groups: **Needs your review**, **Authored**, and **Everything else**, with an issues tab beside them. A pull request opens into **Summary**, **Timeline**, and **Code** tabs: checks, review threads with resolve and reply, and per-file diffs with a **Viewed** state that resets when the head changes. Browsing a pull request never checks anything out. ## Stage a review Review comments start as local drafts. Nothing reaches GitHub until you press **Comment**, **Approve**, or **Request changes**, which submits the drafts as one review. Drafts re-anchor when the pull request's head moves; one that can no longer be placed is flagged rather than deleted, and blocks submission until you discard or rewrite it, because GitHub would reject the whole review. Agents stage drafts through the CLI, and each draft shows its origin so you can tell who wrote what before sending: ``` ouijit pr draft add 42 --file src/api.ts --line 88 \ --origin claude --body "this can throw when the token is missing" ``` See [Agents](https://ouijit.com/docs/agents/) for the full agent review workflow. ## Merge Open pull requests get a merge menu with **Merge**, **Squash**, and **Rebase**, plus a delete-branch toggle. Unmet requirements are listed; when GitHub grants you admin bypass, a separate **Merge without meeting requirements** entry maps to `gh pr merge --admin`. ## Connect pull requests and tasks Ouijit links a task to the pull request on its branch automatically, so a PR the agent opened mid-session still shows up on the card. The other direction works too: - **Check out as task** creates a task with a worktree at the pull request's head, with the PR body as its prompt and the PR's base branch as its merge target. - **Create pull request** on a task card pushes the branch and runs `gh pr create`, so your repository's PR template applies. - A task created from an issue links back, and its pull request closes the issue. --- # Sandbox Run any task's terminals and hooks inside a sandbox. Two backends: a Lima VM that mounts only the task's work, or nono's kernel-level access limits in place on the host. ## Choose a backend Sandboxing is a per-terminal choice. Right-click a task and use **Open in → Lima VM sandbox** or **Open in → nono sandbox**; an entry appears for each backend installed on your system. Plain **Open in → Terminal** runs on the host, unsandboxed. Every terminal and [hook](https://ouijit.com/docs/hooks/) opened for that task then runs under the chosen backend. A sandboxed terminal carries an outlined status dot and a backend suffix on its label. Each backend has its own configuration under **Project Settings → Sandbox**. ## Lima VM Lima-sandboxed terminals run inside a Linux VM, on a parallel git branch, in a worktree that contains only tracked files. ### What it does For a Lima-sandboxed task, Ouijit creates a second git worktree at `~/Ouijit/sandbox-views//T-N-sandbox` on a child branch `s/`. Because it is a plain `git worktree add`, only tracked files exist there: no `.env`, `node_modules`, build caches, or anything else gitignored. That sandbox-view directory is what gets mounted into a [Lima](https://lima-vm.io) VM as the agent's working directory. Your real worktree is never mounted. When the agent commits inside the VM, the commit lands on `s/`. A host-side watcher fast-forwards your branch to match. If you committed in parallel on the host, the fast-forward fails and Ouijit surfaces the divergence. ### How it works 1. **Open a Lima sandbox terminal** from a task's **Open in** menu. Every terminal and hook for that task then runs in the VM. 2. **First terminal**. Ouijit lazily creates an Ubuntu 24.04 VM via Lima on the first sandboxed spawn for a project. One VM per project, reused across that project's sandboxed tasks. 3. **Spawn progress**. The dialog shows each step: checking VM status, creating or starting, SSH connection, provisioning, launching the shell. First boot takes 1-2 minutes; later spawns reuse the running VM. 4. **Shell sessions** run inside the VM via `limactl shell`, with the working directory set to the sandbox-view path. 5. **VM lifecycle and resources**. Manage the VM from **Project Settings → Sandbox → Lima VM**: see status, create, start, stop, recreate, open a console, and edit the merged Lima YAML config (memory, CPU, disk, mounts). Resource changes prompt a recreate. ### Mount surface Only two host paths are exposed to the VM: | Host path | In guest | Why | | --- | --- | --- | | `~/Ouijit/sandbox-views//` | Read/write | The agent's tracked-files-only worktree. | | `/.git/` | Read-only base, with read/write overlays at `objects/`, `refs/`, `logs/`, `worktrees/` | Commits need to write packs, refs, and reflogs. `hooks/`, `config`, and `packed-refs` stay read-only. | Your home directory, source tree, system binaries, and any other host paths are not mounted at all. ### Isolation properties - **Gitignored content is not in the mount**. `.env`, `.npmrc`, `secrets/`, `node_modules`, and build artifacts never leave the host. - **Host toolchains stay separate**. darwin-arm64 `node_modules`, Rust `target/`, and Python `.venv` don't collide with the Linux guest. - **Parallel sandboxed tasks** on the same project share the VM but work in separate sandbox-view directories. - **`.git/hooks/` and `.git/config` are read-only** in the mount, so writes from the VM that would land there fail with `EROFS`. - **No `ouijit` CLI in the VM.** The binary isn't installed; the `/api/*` routes return 403 for sandbox-scoped tokens. Only the `/hook` endpoint accepts them. #### Caveats - **Committed files are always visible.** The sandbox view is populated from the index, so anything you committed lives there regardless of `.gitignore`. To remove: `git rm --cached ` and rotate the secret. - **Commits round-trip through fast-forward.** Parallel commits on the host make the fast-forward fail; Ouijit surfaces the divergence. - **The VM has no git credentials.** Pushes happen from the host. Commits made inside the VM ride the next host-side push. - **The VM's home directory is out of scope.** The isolation above applies to mounts. Files copied into the VM's home (`~/.netrc`, `~/.git-credentials`) aren't covered. ### Customize the VM VMs start from a clean Ubuntu 24.04 image with `bash`, `git`, `curl`, `wget`, `nodejs`, `npm`, `python3`, and `build-essential` pre-installed. Defaults are 4 GiB memory, 2 CPUs, 10 GiB disk. Resource limits, extra packages, additional mounts, and provisioning scripts are controlled by the Lima YAML config in **Project Settings → Sandbox → Lima VM**. The editor shows the merged effective config and lets you write overrides; Ouijit prompts you to recreate the VM when changes require it. See the [Lima configuration reference](https://lima-vm.io/docs/config/) for the available keys. ### Requirements Ouijit bundles `limactl`; there is nothing extra to install. - **macOS**. Apple's Virtualization.framework (vz) with vzNAT networking. - **Linux**. QEMU with user-mode networking. ### Limitations - First boot downloads an Ubuntu cloud image and provisions packages. It needs internet and takes 1-2 minutes. - GPU passthrough is not supported. - macOS-specific tooling (Xcode, Swift) is not available inside the Linux VM. - Docker-in-VM requires additional Lima configuration. - The VM home directory is ephemeral. Anything outside the mounted sandbox-views and `.git` overlays is lost on recreate. ## nono [nono](https://github.com/nolabs-ai/nono) runs a task's commands in place on the host worktree under kernel-level access limits (Seatbelt on macOS, Landlock on Linux). There is no VM and no file sync, so it starts instantly. Compared to the Lima VM it is a lighter boundary: a kernel access policy on a host process rather than a separate machine. nono is experimental. Turn it on under **Project Settings → Experimental**; the **nono sandbox** entry then appears in a task's **Open in** menu. ### What it does Terminals and hooks run on the real worktree, wrapped by `nono`. Filesystem access is limited to the worktree and the repository's git data, with the same read/write overlays at `objects/`, `refs/`, `logs/`, `worktrees/`, so commits land while `hooks/` and `config` stay read-only. Ouijit derives these grants from the task's worktree path at spawn time. Network is allowed by default; the local hook-server port stays reachable so agent status still reports back. ### Configuration Under **Project Settings → Sandbox → nono**: - **Additional folders**. Extra paths granted read/write beyond the worktree and git data, e.g. a shared cache or a monorepo sibling. - **Block outbound network**. Deny outbound connections. The hook-server port stays open so agent status keeps working. - **Allowed ports**. Extra localhost ports to keep reachable, e.g. a dev server on `3000`. ### Requirements Ouijit bundles the `nono` binary and the agent packs it uses; there is nothing extra to install. - **macOS**. Seatbelt (`sandbox-exec`). - **Linux**. Landlock, which needs kernel 5.13 or newer. --- # Session resume Quitting saves the session. The next launch offers to bring its terminals back in their worktrees, panels included. ![Resume banner listing the previous session's terminals](https://ouijit.com/assets/screenshots/resume.png) ## What gets saved Ouijit continuously snapshots your open terminals: their project, task, worktree, sandbox backend, order, labels, and each terminal's panels (runner, previews with their URLs, markdown files), split sizes, and diff panel state. Closing the last terminal clears the snapshot. ## Resume On the next launch, the home view shows a **Resume last session** banner summarizing what was open, expandable to the full list. **Resume** reopens every terminal in its worktree with its panels; **Dismiss** clears the snapshot. Terminals whose project or task no longer exists, or whose task is Done, are skipped. Restored terminals come back as plain shells. No hook fires, so resuming never launches a fresh agent session on its own. To pick an agent conversation back up, use your harness's own resume (for example a `continue` hook running `claude -c`). --- # Themes Choose system, light, dark, or one of five presets, or define your own theme as design-token overrides. Themes restyle the whole app, terminals and their ANSI palette included. ![The same board in dark, Dracula, Sepia, and light themes](https://ouijit.com/assets/screenshots/themes.png) ## Pick a theme Under **App Settings → Appearance**, choose **System** (follows the OS appearance live), **Light**, **Dark**, or a theme. Hovering an option previews it without saving. Five presets ship with the app: | Preset | id | Base | | --- | --- | --- | | Dracula | `dracula` | dark | | Tokyo Night | `tokyo-night` | dark | | Matrix | `matrix` | dark | | Hot Pink | `hot-pink` | dark | | Sepia | `sepia` | light | The theme is global, not per-project, and changes apply live. ## Write a custom theme A theme is a set of design-token overrides on a `dark` or `light` base. Add one under **App Settings → Appearance → Custom themes**, or from the shell: ``` ouijit theme save '{"id":"my-theme","name":"My Theme","base":"dark","tokens":{"--color-accent":"#ff2d55"}}' ouijit theme use my-theme ``` Any token is overridable: surface and text colors, accent, the full 16-color ANSI palette, diff colors, and shadows. The presets returned by `ouijit theme list` show the full token vocabulary; saving a theme with a preset's id overrides that preset, and deleting your copy restores it. --- # Hooks Five lifecycle hooks define how tasks launch and what runs alongside them: `start`, `continue`, `run`, `review`, and `done`. Configure them per project. ![Project settings with hooks and scripts](https://ouijit.com/assets/screenshots/settings.png) ## Lifecycle hooks Configure these from the kanban column headers or Project Settings. | Hook | When it runs | Example | | --- | --- | --- | | **Start** | Task moves from To Do to In Progress | `npm install && claude "$OUIJIT_TASK_DESCRIPTION"` | | **Continue** | Reopening a task already In Progress | `claude -c` | | **Run** | The Run button, `⌘P`, or a runner [panel](https://ouijit.com/docs/panels/) opens | `npm run dev` | | **Review** | Task moves to In Review | `gh pr create --fill` | | **Done** | Task moves to Done | `git push origin HEAD` | The most common setup wires the start hook to an agent: `claude "$OUIJIT_TASK_DESCRIPTION"` launches Claude Code with the task's prompt the moment the task starts. ## Hook environment Hooks receive the task's context as environment variables: ``` OUIJIT_PROJECT_PATH=/path/to/my-project OUIJIT_WORKTREE_PATH=~/Ouijit/worktrees/my-project/T-42 OUIJIT_TASK_BRANCH=T-42 OUIJIT_TASK_NAME="Add user auth" OUIJIT_TASK_DESCRIPTION="implement oauth login" OUIJIT_HOOK_TYPE=start ``` These come on top of the variables every Ouijit terminal gets. See [CLI → Environment variables](https://ouijit.com/docs/cli/#environment-variables). ## Editor The editor command opens a task's worktree in your editor, from **Open in → Editor** on a task or terminal. It is configured like a hook (type `editor`, e.g. `code` or `hx`) but is not part of the task lifecycle; it runs on demand. The command runs inside a task terminal, so terminal editors like Helix and Vim get a real TTY, while GUI editors detach and leave a shell behind. File links in [markdown panels](https://ouijit.com/docs/panels/) use the same editor to open files at a specific line. --- # Harnesses Claude Code, Codex, Pi, and OpenCode get live status indicators and learn the `ouijit` CLI automatically. No configuration required. ## What you get Run `claude`, `codex`, `pi`, or `opencode` in any Ouijit terminal and two things happen: - **Live status**. The terminal card shows whether the agent is thinking or idle, visible across the whole stack. You can tell which sessions need attention without bringing each one forward. - **CLI awareness**. The agent knows the [`ouijit` CLI](https://ouijit.com/docs/cli/), so it can create tasks, advance the board, stage review comments, and open panels from the shell. See [Agents](https://ouijit.com/docs/agents/). This applies only in terminals Ouijit opens, including [sandboxed](https://ouijit.com/docs/sandbox/) ones. Everywhere else these tools behave as they always do. ## Notifications When an agent finishes a turn, Ouijit plays a sound. If the window isn't focused, you also get an OS notification. Turn the sound off under **App Settings → Sound**. --- # Agents Agents are first-class users of Ouijit. This page is the contract an agent can rely on inside an Ouijit terminal, and the workflows it should use to drive the app. ## What an agent receives Every [supported harness](https://ouijit.com/docs/harnesses/) gets the `ouijit` CLI reference appended to its system prompt automatically, from `~/.config/Ouijit/ouijit-cli-reference.md`. It covers the commands in these docs plus behavioral notes: JSON output on stdout, errors as `{"error": "..."}` on stderr with a non-zero exit, and the flags for headless status changes. ## Environment Every Ouijit terminal has these set. An agent can read them directly: | Variable | What it is | | --- | --- | | `OUIJIT_API_URL` | Base URL of the local REST API the CLI talks to. Unset outside Ouijit terminals, so scripts can detect the environment. | | `OUIJIT_API_TOKEN` | Bearer token scoped to this terminal session. | | `OUIJIT_PTY_ID` | This terminal's id. `ouijit task current` and the panel commands resolve through it. | [Hooks](https://ouijit.com/docs/hooks/#hook-environment) additionally receive `OUIJIT_TASK_*` context. ## Drive the board An agent working a task should resolve its own task first, then advance it when the work is committed: ``` TASK=$(ouijit task current | jq -r .taskNumber) ouijit task set-status "$TASK" in_review --skip-hook ``` By default a status change opens that column's hook dialog in the GUI. When no human is at the keyboard, pass `--run-hook` (run the configured hook immediately), `--skip-hook` (no hook), or `--hook-command ""` (a one-off command). The same flags apply to `task start` and `task create-and-start`. ## Open panels beside yourself An agent can put its plan or its running app next to its own terminal. Both commands default to the current terminal via `OUIJIT_PTY_ID`: ``` ouijit markdown add ./plan.md ouijit preview add http://localhost:3000 ``` ## Review without posting For anything that belongs to a pull request review, agents must use `ouijit pr`, not `gh`. `gh` posts to GitHub immediately under the user's name; `ouijit pr draft` stages comments locally and the user sends the review themselves. Reading with `gh pr diff` is fine; posting with `gh` is not. ``` PR=$(ouijit task current | jq .githubPrNumber) gh pr diff $PR # read it ouijit pr draft add $PR --file src/api.ts --line 88 \ --origin claude --body "this can throw when the token is missing" ``` Pass `--origin` with your name so the user can tell agent comments apart before sending, and `--body -` to read multi-line bodies from stdin. Anchor each comment to a line that appears as an added line in the diff, by its new-file line number: GitHub rejects the whole review at submit time if any comment points outside the diff. ## Inside a sandbox The API surface shrinks under a [sandbox](https://ouijit.com/docs/sandbox/): - **Lima VM**. The `ouijit` binary is not installed in the VM, and the `/api/*` routes return 403 for sandbox-scoped tokens. Only the `/hook` status endpoint is reachable, so agent status still reports, but the CLI workflows above are host-only. - **nono**. The terminal runs on the host, so `ouijit` stays on `PATH` with access scoped to the task's own data. Pull request drafts written from a sandbox are stamped with a `sandbox` origin the caller cannot forge. ## Machine-readable docs - **Markdown pages**. Every docs page is served as plain Markdown at `https://ouijit.com/docs/.md`, for example [/docs/cli.md](https://ouijit.com/docs/cli.md). - **Full docs in one file**. [ouijit.com/llms-full.txt](https://ouijit.com/llms-full.txt) concatenates every page. - **Index with summaries**. [ouijit.com/llms.txt](https://ouijit.com/llms.txt) lists each page with a one-line summary. --- # CLI Drive Ouijit from any terminal it spawns: tasks, hooks, scripts, tags, pull request drafts, themes, and panels, all from the shell. ## Where it lives The `ouijit` command is auto-installed into every terminal Ouijit opens, host and sandboxed alike. It talks to the running app over a localhost API authenticated by a per-session token, so it only works from inside an Ouijit terminal. ## Output format Every successful command writes a single JSON value to `stdout` and exits 0. Errors write `{"error": "..."}` to `stderr` and exit non-zero. Pipe to `jq` to extract fields: ``` ouijit task list | jq '.[] | select(.status == "in_progress") | .name' ``` ## Project detection The CLI detects the active project from your current directory by walking up to the nearest git root. Inside a worktree it follows the `.git` file back to the main repository. Override with `--project `. ## Tasks Statuses are `todo`, `in_progress`, `in_review`, and `done`. ``` ouijit task list # array of tasks ouijit task get 5 # single task ouijit task current # task owning this terminal ouijit task create "Fix login bug" ouijit task create "Refactor auth" --prompt "Extract middleware" ouijit task start 5 # creates worktree, sets in_progress ouijit task start 5 --branch custom-name ouijit task start 5 --run-hook # run the configured start hook, no dialog ouijit task start 5 --skip-hook # spawn the terminal, run no hook ouijit task start 5 --hook-command "claude" # one-off command instead of the configured hook ouijit task create-and-start "Add 2FA" # create + start in one step ouijit task create-and-start "Add 2FA" --prompt "..." --branch custom --skip-hook ouijit task spawn "Add 2FA" # alias for create-and-start ouijit task set-status 5 in_review # fires the review hook (dialog by default) ouijit task set-status 5 in_review --run-hook # run the review hook, no dialog ouijit task set-status 5 in_review --skip-hook ouijit task set-status 5 done --hook-command "npm run deploy" ouijit task bulk-set-status done 5 6 7 # set status on many in parallel ouijit task set-name 5 "Better name" ouijit task set-description 5 "More detail" ouijit task set-merge-target 5 develop ouijit task delete 5 ``` `--run-hook`, `--skip-hook`, and `--hook-command` are mutually exclusive. By default, a status change opens that column's hook dialog in the GUI, just like dragging a card; use these flags for headless runs where no one is at the keyboard to dismiss it. They apply to `task start` and `create-and-start` (the start hook) and to `set-status` into `in_progress`, `in_review`, or `done` (the continue, review, and done hooks). `bulk-set-status` takes the same flags. Setting status to `todo` runs no hook and rejects these flags. `ouijit task current` resolves the task owning the current terminal via the `OUIJIT_PTY_ID` env var that Ouijit sets on every spawn: ``` TASK=$(ouijit task current | jq -r .taskNumber) ouijit task set-status "$TASK" in_review ``` ## Hooks Manage the [lifecycle hooks](https://ouijit.com/docs/hooks/) and the editor command. Hook types are `start`, `continue`, `run`, `review`, `done`, `editor`. ``` ouijit hook list ouijit hook get start ouijit hook set start --name "Install & agent" --command 'npm install && claude "$OUIJIT_TASK_DESCRIPTION"' ouijit hook set run --name "Dev server" --command "npm run dev" --restart-if-running ouijit hook delete done ``` `--restart-if-running` applies to the run hook only: relaunch instead of focusing an already-running instance. ## Tags Tag tasks to [group and filter](https://ouijit.com/docs/terminals/) their terminals. ``` ouijit tag list # all tags across projects ouijit tag list --task 5 # tags on one task ouijit tag add 5 bug ouijit tag remove 5 bug ouijit tag set 5 bug priority urgent # replace all tags ``` ## Scripts Scripts are saved shell commands that show up in the [panel menu](https://ouijit.com/docs/panels/) and the CLI. Unlike hooks, they don't fire on lifecycle events. ``` ouijit script list ouijit script set --name "Lint" --command "npm run lint" ouijit script set --name "Dev" --command "npm run dev" --restart-if-running ouijit script run Lint # by name ouijit script run # by id ouijit script run Tests --task 5 # run inside task 5's worktree ouijit script delete ``` `script run` streams the command's output through your terminal and exits with the command's exit code. `script set` upserts by name, so re-running with the same `--name` updates that script. ## Pull requests Inspect [pull requests](https://ouijit.com/docs/pull-requests/) and stage review drafts. Drafts stay local until the user sends the review from the app. ``` ouijit pr list # open PRs, grouped review/yours/others ouijit pr view 42 # one PR with threads, timeline, checks ouijit pr link 42 --task 5 # link a PR to a task ouijit pr draft list 42 ouijit pr draft add 42 --file src/api.ts --line 88 --body "typo" --origin claude ouijit pr draft add 42 --file src/api.ts --line 88 --body - # body from stdin ouijit pr draft add 42 --file src/api.ts --start-line 80 --line 88 --body "range" ouijit pr draft discard 42 ``` `--body -` reads the body from stdin, the safe path for multi-line text. `--origin` names the author shown beside the draft. `--side` defaults to `RIGHT` (the new file); anchor comments to lines that appear as added lines in the diff. ## Markdown panels Open markdown files as [panels](https://ouijit.com/docs/panels/) on a terminal. The pty id defaults to the current terminal. ``` ouijit markdown add ./plan.md # uses current OUIJIT_PTY_ID ouijit markdown add ./plan.md pty_abc123 # explicit pty id ouijit markdown list ouijit markdown remove ./plan.md ``` ## Web preview panels ``` ouijit preview add http://localhost:3000 # uses current OUIJIT_PTY_ID ouijit preview add http://localhost:3000 pty_abc123 ouijit preview list ouijit preview remove http://localhost:3000 ``` ## Themes [Themes](https://ouijit.com/docs/themes/) are global, not project-scoped. ``` ouijit theme list # preference, presets, custom themes ouijit theme use dracula ouijit theme use system ouijit theme save '{"id":"my-theme","name":"My Theme","base":"dark","tokens":{"--color-accent":"#ff2d55"}}' ouijit theme save --file my-theme.json ouijit theme delete my-theme ``` ## Projects ``` ouijit project list # every project registered with the app ``` ## Environment variables Ouijit sets these on every terminal it spawns: | Variable | What it is | | --- | --- | | `OUIJIT_API_URL` | Base URL for the local REST API the CLI talks to. | | `OUIJIT_API_TOKEN` | Bearer token scoped to this terminal session. | | `OUIJIT_PTY_ID` | Unique id for this terminal session. Used by `task current` and the panel commands (`markdown`, `preview`). | Hooks get task context on top of these. See [Hooks → Hook environment](https://ouijit.com/docs/hooks/#hook-environment) for the `OUIJIT_TASK_*` variables.