# 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 open a folder, create one, or clone from GitHub. If the folder isn't a git repository yet, Ouijit offers to run `git init` in place. 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 under nono's access limits 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`, `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. | ## 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 (**nono** or **Custom**), 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. ## Read it through a lens ![A worktree diff regrouped into named parts, with one file appearing in two of them](https://ouijit.com/assets/screenshots/lens.png) A lens is a named instruction you keep with the project — "lead with what could break in production" — that an agent applies to one change. Pick one from the picker above the file list and it reads the diff and answers with the parts of the change: a titled group, a line on why those files belong together, and the hunks that make it up. The file tree becomes one way of reading among several. The rail and the document follow the same order, and each part folds like a folder. A file that spans two parts appears in both, showing only the hunks that part claims. Whatever no part claims is still shown, under a heading at the end: a lens reorders and splits a change, but never hides any of it. A lens goes out of date when the change under it moves. The picker says so and offers to run it again, and on a worktree diff the older grouping stays on screen until you do — anything it no longer accounts for moves to that trailing group. Add and edit lenses under **Lenses** in project settings, or from the picker itself. Writing one needs Claude Code or Codex installed; where both are, you choose which runs. ## 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 tracker surface: a pull request inbox, review comments staged locally until you send them, merging, and an issues list fed by GitHub, Linear, or both. It drives the `gh` CLI, so `gh auth login` is the only setup for the GitHub half. ## 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 GitHub token; every GitHub call goes through `gh` on the host, and sandboxed terminals never see your credentials. ## Add Linear Enable **Linear** beside it, then paste a personal API key into **App Settings → Linear**. Make one in Linear under **Settings → API**: **Read** and **Create comments** is enough to read issues and comment, and **Write** additionally buys the control that moves an issue. Limiting the key to one team is fine, and narrows what Ouijit can see. One key serves every project, since a key reads one workspace. A project in a second workspace can keep its own under **Project Settings → Linear**, which is also where the field appears when there is no key yet — clearing it puts that project back on the app-wide one. Keys are encrypted by your OS keychain and kept out of the renderer, the REST API and every sandboxed terminal. On a machine with no keychain — Linux without a keyring — Ouijit refuses to store one and reads `LINEAR_API_KEY` from your environment instead. Ouijit then works out which issues are this project's. Workspaces that label issues under a `repo` label group — the convention other Linear integrations already write — are matched against your `origin` remote and connected without being asked. Otherwise a row above the list asks once which team to use. Change it, or disconnect the project from Linear entirely, under **Project Settings → Linear**. For an issue to move on its own when its pull request opens and merges, connect **Linear's GitHub integration** — in Linear, per repository. Without it nothing syncs, and the state control on the issue is the way to move it. ## 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. ## Work the issues The Issues tab is one list, ordered by what wants your attention: **Triage**, **In progress**, **Assigned to you**, **Current cycle**, then the repository's open issues. Each group is answered by whichever source can answer it — an `ENG-231` and a `#299` sit side by side under one **Assigned to you** — and a group with nothing in it does not render, so a project with one tracker sees one tracker's groups. An issue opens in the same chrome a pull request does, with what its source knows: assignee, priority, cycle, estimate and team for a Linear issue, assignees and labels for a GitHub one. A Linear issue carries a state control that moves it through its team's workflow — the fastest way to move one, and the only way where Linear is not watching the repository. Under a key without **Write** the control is absent. Comments post when you send them. A comment written by an agent through the CLI waits above the box with its origin beside it, until you press **Send**. A workspace running Linear's own GitHub sync has each piece of work as a Linear issue _and_ a mirrored GitHub issue, so it appears twice — once under Linear's groups, once under the repository's. ## 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. ## Read it through a lens The Code pane takes the same [lenses](https://ouijit.com/docs/diff/) the worktree diff does, from the picker above the file list: an agent reads the change and answers with its parts, and the rail and the document both follow that order instead of the file tree. Read marks follow the parts. A file split across three of them is ticked a part at a time, and counts as read once every part of it has been. A lens written for an earlier head is dropped rather than shown against hunks a force-push has moved; the picker offers to run it again. An agent can write one itself with `ouijit pr lens set`, described under [the CLI](https://ouijit.com/docs/cli/). ## 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. - A task created from a Linear issue starts on the branch name Linear suggested, and the pull request opened from it carries the issue identifier in its body. Linear reads either one, so the issue moves to In Review and then to Done as the pull request does — provided Linear's GitHub integration is connected to the repository. --- # Sandbox Run any task's terminals and hooks inside a sandbox. Two backends: nono's kernel-level access limits in place on the host, or a launcher of your own. ## Choose where things run **Hooks.** Start, continue, review, and done each have a sandbox setting beside their command: Host, nono, or Custom. Moving a task runs that column's [hook](https://ouijit.com/docs/hooks/#sandbox) where it is set to run, and the hook dialog can change it for that one run. **Runners.** Run and scripts from a terminal's **+** menu, and `⌘P`, run where the project's **Default environment** setting says: Host unless you change it, under **Project Settings → Sandbox**, or from **Run in** at the top of the menu. **Terminals.** Right-click a task and use **Open in → nono sandbox** or **Open in → Custom sandbox**. **Open in → Terminal** runs the continue hook where it is set to run. A sandboxed terminal carries an outlined status dot and a backend suffix on its label; a sandboxed runner's tab dot is outlined too. A backend is offered once it can run: nono once **Use nono** is on, Custom once its command is set. Each has its own configuration under **Project Settings → Sandbox**. If the chosen backend cannot start, the terminal says why and Ouijit raises an error. It never runs the command on the host instead, and a restored terminal whose backend is gone fails the same way. ## 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). It is a kernel access policy on a host process rather than a separate machine, so it starts instantly with no file sync. ### 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: `.git` is read-only apart from read/write overlays at `objects/`, `refs/`, `logs/`, and `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. ## Custom **Read this first.** A plain task terminal carries a host-scoped `OUIJIT_API_TOKEN`. A launcher you run by hand inside one passes that token into the sandbox, where an agent can rewrite hooks and this very setting, so the next spawn runs its command on the host. Either `unset OUIJIT_API_TOKEN` in a hand-run launcher (status reporting stops), or configure the launcher here so Ouijit spawns it with a sandbox-scoped token. The Custom backend runs a task's terminals under a launcher you provide: a reviewed script in your team's control, with its own pinned sandbox binary, committed policy, and preflight checks. Ouijit grants nothing itself; the launcher owns the boundary. Set the command under **Project Settings → Sandbox → Custom** or with `ouijit sandbox-command set`. ### What Ouijit passes Ouijit runs ` [args] -- [shell args]` with the working directory set to the task worktree and the full terminal environment: `OUIJIT_API_URL` and a **sandbox-scoped** `OUIJIT_API_TOKEN`, `OUIJIT_PTY_ID`, `OUIJIT_WRAPPER_DIR`, `OUIJIT_CLI_PATH` (when the bundled CLI is present), `OUIJIT_SHELL_INTEGRATION_DIR`, and the shell integration's `ZDOTDIR`. The launcher's argv is exactly the configured command, split on whitespace with shell-style quoting. Nothing is expanded except a leading `~/` on the launcher path, and nothing is added except the `--` and the shell. Everything Ouijit would otherwise have granted is exported as advisory hints. They are computed on the host from the project path and Ouijit's own directories, never from the worktree, and a launcher is free to ignore them. | Variable | What Ouijit's other backends do with it | | --- | --- | | `OUIJIT_SANDBOX_WORKTREE` | The task worktree, read+write. | | `OUIJIT_SANDBOX_GIT_DIR` | The repository's `.git`, read-only. | | `OUIJIT_SANDBOX_GIT_WRITABLE_DIRS` | Colon-separated `objects`, `refs`, `logs`, `worktrees` under it, writable so commits land while `hooks/` and `config` stay read-only. | | `OUIJIT_SANDBOX_HOOK_PORT` | The localhost port that must stay reachable for agent status to report back. | | `OUIJIT_SANDBOX_CACHE_DIR` | A per-project package-manager cache outside any worktree, read+write. Shared by every task of the project. | | `OUIJIT_SANDBOX_WRAPPER_DIR` | Ouijit's agent shims and CLI reference, read-only. Its `bin/` is first on `PATH`. | | `OUIJIT_SANDBOX_CLI_DIR` | The bundled `ouijit` CLI the shim runs, read-only. Set only when the bundled CLI is present. | ### What the launcher is responsible for - Exec the argv after `--` with the inherited environment intact: at least `PATH`, `ZDOTDIR`, every `OUIJIT_*` variable, `TERM`, `HOME`, `SHELL`, and the locale. Shell integration, the status dot, and the `ouijit` and agent shims are all carried by the environment; a launcher that scrubs it starts a bare shell. - Own the boundary. Ouijit adds no `--allow` or `--read` flags. - Keep the hook port reachable if you block the network. - Exit non-zero to refuse. Whatever it wrote to stderr appears in the terminal, the exit line prints red, and Ouijit raises a "sandbox failed to start" notice. It never falls back to an unsandboxed shell. ### Where the launcher may live An absolute path or a bare name resolved on `PATH`. Relative paths are refused, and so is any absolute path inside the worktree or the spawn directory: the sandboxed agent can write there, and a launcher it can edit is one it can turn off. Keep a versioned launcher in the repo if you like, but install it from a reviewed checkout to somewhere the sandbox cannot write, such as `~/.local/bin`, and point the setting there. If your `PATH` contains relative entries, use an absolute path. --- # 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. ## Sandbox Start, continue, review, and done each choose where they run: on the host, or in a [sandbox](https://ouijit.com/docs/sandbox/). An agent started with permission checks off can run sandboxed while a deterministic done script stays on the host. The hook dialog shows the setting and can change it for one run. ## 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. OpenCode 2 runs on a private server in each Ouijit terminal instead of the shared background service, so its session ends when you close the terminal. ## 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`. The hook runs in its own [sandbox](https://ouijit.com/docs/sandbox/) setting unless `--sandbox host|nono|custom` says otherwise. ## 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 default to `ouijit pr` over `gh`. `gh` posts to GitHub the moment it runs, under the user's own account; `ouijit pr draft` stages comments locally and the user sends the review themselves. Reading with `gh pr diff` is always fine, and a `gh` write the user asks for by name — `gh pr create`, say — still goes through, as them. ``` 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. A [Linear](https://ouijit.com/docs/pull-requests/) comment works the same way, and for the same reason. Nothing an agent runs moves an issue — that is a control on the issue, pressed by someone looking at it. ``` ISSUE=$(ouijit task current | jq -r .linearIssueIdentifier) ouijit linear view $ISSUE # read it ouijit linear draft add $ISSUE --origin claude \ --body "the retry loop drops the last error" ``` ## Inside a sandbox The API surface shrinks under a [sandbox](https://ouijit.com/docs/sandbox/): - **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. - **Custom**. The launcher the project supplies owns the boundary, so whether `ouijit` is reachable at all is up to it. The token is scoped the same way as under nono, and a draft written through it carries the same `sandbox` origin. ## 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 start 5 --run-hook --sandbox custom # run it in a sandbox other than the hook's own 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. `--sandbox host|nono|custom` overrides the hook's own [sandbox](https://ouijit.com/docs/sandbox/) setting for that run, with or without the flags above. With the dialog, it is what the dialog preselects. `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 set continue --name "Agent" --command "claude -c" --sandbox custom ouijit hook delete done ``` `--restart-if-running` applies to the run hook only: relaunch instead of focusing an already-running instance. `--sandbox nono|custom` applies to the start, continue, review, and done hooks: the sandbox the hook runs in. `hook set` replaces the whole hook, so leaving it out runs the hook on the host. ## Sandbox command The launcher for the [Custom sandbox](https://ouijit.com/docs/sandbox/#custom) backend. Host terminals only; a sandboxed session gets 403. ``` ouijit sandbox-command get ouijit sandbox-command set "$HOME/.local/bin/ouijit-sandbox --strict" ouijit sandbox-command clear ``` ## 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 ouijit pr lens get 42 # the lens shown in the Code pane ouijit pr lens set 42 --body - # grouping as JSON on stdin ouijit pr lens clear 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. `pr lens set` takes a [lens](https://ouijit.com/docs/diff/) an agent wrote itself: a head sha and the parts of the change, each naming paths and new-file line ranges. A range touching any line of a hunk claims the whole hunk, so approximate ranges are fine, and omitting a file's ranges claims all of it. Run `ouijit pr lens set --help` for the shape. ## Linear issues Read the [Linear issues](https://ouijit.com/docs/pull-requests/) this project is scoped to, link one to a task, and stage a comment. Comments stay local until the user sends them from the app, and nothing here moves an issue — that is a control on the issue itself. ``` ouijit linear list # this project's issues, in the panel's groups ouijit linear view ENG-214 # one issue with its comments and states ouijit linear link ENG-214 --task 5 # link an issue to a task ouijit linear draft list ENG-214 ouijit linear draft add ENG-214 --body "the retry loop drops the last error" --origin claude ouijit linear draft add ENG-214 --body - # body from stdin ouijit linear draft discard ENG-214 ``` A task created from an issue carries its identifier, so `ouijit task current | jq .linearIssueIdentifier` is the issue an agent is working on. ## 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.