An animated pet that keeps an eye on your coding agents inspired by coucou. It runs as a desktop app on macOS, Windows and Linux (any desktop environment) and as a plugin for the Noctalia shell (v5). It watches Claude Code, Codex CLI, GitHub Copilot CLI, Antigravity CLI, Gemini CLI, opencode and pi, side by side.
- Live: every session, step by step. What the agent reads, edits and runs, as a rail of steps (✓ done, ◌ running, ✗ failed) and a viewer with the diff of the file being edited (line numbers, red/green lines, syntax colors, typed out as it happens), the terminal of a command (with how long it has been running), or the file it read. Steps come faster than you can read them, so the viewer lets each edit be typed out and each command be seen before moving on, skips the reads in between, and never falls more than three steps behind. When a turn finishes the pet does a happy little jump.
- Search, flag, export: the live rail only ever shows a session's last few steps, but a search
box above it queries that session's full stored history (hundreds of steps, kept beyond the
current turn and beyond the session ending — see
sushi::history) by tool name, label, diff or command text. Any step, live or found by search, can be flagged "needs review" (with an optional note), which persists across restarts and shows up on the live rail too. The export button (orsushi export SESSION_ID) writes the session's steps as a markdown file — the same bounded diffs/output the viewer already shows, never a complete/git-applyable patch — to~/.cache/sushi/exports/, and prints/shows the resulting path. - Approve from the notch: permission requests pop up with a preview of what the agent wants to do and
Allow / Deny buttons. One click, back to work. With Claude Code, when it asks you a question
(
AskUserQuestion) its options show up as buttons and your choice goes straight back; a plan (ExitPlanMode) can be read and approved (with or without auto-accepted edits) or sent back with "Keep planning". When an agent waits in its own terminal with nothing to answer in the notch, the pet calls you all the same. - Ask anything: a built-in chat tab that runs one of your agents headless with no tools (Claude Code, Copilot, pi or Codex, your choice in the plugin settings).
- Feed it a file: drop a file on the pet and it swallows it; the chat opens with the file attached, so the
next question is about it. An audio file (wav, mp3, flac, ogg, m4a, aac, opus, wma) is transcribed first, with
whisper.cpp running fully offline — set
transcribe_model_pathto a GGML/GGUF model to turn this on; the transcript is also kept next to the audio file as a.txt. - Usage: plan limits for every agent that has them — Claude Code's 5-hour/weekly windows, Codex's same shape
when logged in with a ChatGPT account, Copilot's monthly quotas and Antigravity's per-model windows, each
fetched straight from that agent's own account, side by side — plus Claude's context window trend and a
token/estimated-cost comparison across every agent ("not tracked yet" for the agents nothing reads transcripts
for today, never a fabricated number), and a day-by-day cost chart (
usage_history, persisted so it survives a restart, unlike the rest ofusagewhich is rebuilt from transcripts). Budget alerts (budget_alertsinconfig.json) raise a one-shot pet reaction (a distinct sound and a worried blip) the moment a plan window or a daily token/cost budget crosses a configured threshold —budget_alerts_by_cwdadds the same check per project (keyed by a session's exactcwd), shown as its own row in the Usage tab, for when different repos have very different costs. - External hooks: run a command and/or POST a small JSON body to a URL (
hooksinconfig.json) when a session starts or ends, starts waiting for you, or a turn finishes — e.g. kick off a build, a staging deploy, or update an internal dashboard. Fire-and-forget: a slow or failing script never blocks the daemon. - Policy flags: configured rules (
policiesinconfig.json) flag a matching tool call (e.g. aBashcommand containingrm -rf) in the live viewer for every agent, even one it auto-approved. Real enforcement — genuinely forcing Claude Code to ask or refuse — uses its own nativepermissions.ask/permissions.denyinstead (claude_permissionsinconfig.json, merged in bysushi install --agent claude --write): see Policy flags vs. real enforcement for why the other agents only get the flag, not the block. - Focus mode: a quiet, compact pet (no sounds, no idle fidgets, but the status badge stays visible) during configured hours, or forced on/off with the moon button next to the pet's emotes (app only; the Noctalia plugin follows the configured schedule).
- A pet with a personality: 30 characters (nigiri, maki, ramen, bao, dango, sake, taiyaki, ramune…), breathing, blinking, eyes that glance at whatever you hover, 30+ emotes and idle quirks, a nap after a while, a greeting on launch and 31 little sounds. Each character also has its own temperament (energetic, calm, shy, fancy, sleepy, cozy, feisty, classic…): it fidgets more or less often, leans toward a few favorite quirks, and flavors a handful of lines ("Ha! Done!" for the feisty wasabi, "Finished, calmly." for the calm tofu).
- Milestones with accessories: if you leave it on, a quiet day-streak counter and a step-watched counter in the Usage tab, and the pet wears small permanent pins for the highest tier it has ever earned on each track (bronze → silver → gold → platinum → diamond) — a flame for the streak, an award for steps watched. They are earned badges, not a live gauge: breaking a streak does not take the pin away.
| Claude Code | Codex CLI | GitHub Copilot | Antigravity CLI | Gemini CLI | opencode | pi | |
|---|---|---|---|---|---|---|---|
| Sessions, steps, diffs, command output | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Allow / Deny from the notch | ✓ | ✓ | ✓ | – | ✓ (unverified) | ✓ | opt-in (SUSHI_PI_APPROVE=1) |
| Questions and plans | ✓ | – | – | – | – | – | – |
| Context size and tokens | ✓ | – | – | – | – | – | – |
| Plan limits (5 h / weekly or monthly quota) | ✓ | ✓ (ChatGPT login) | ✓ (undocumented) | ✓ (unverified) | – | – | – |
| Built-in chat | ✓ | ✓ (read-only sandbox, not tool-free) | ✓ | – | – | – | ✓ |
| Connected through | hooks in ~/.claude/settings.json |
hooks in ~/.codex/hooks.json |
hook file ~/.copilot/hooks/sushi.json |
group in ~/.gemini/config/hooks.json |
hooks in ~/.gemini/settings.json |
plugin in ~/.config/opencode/plugins/ |
extension in ~/.pi/agent/extensions/ |
What has been checked against the real thing:
- Claude Code: the reference.
- GitHub Copilot CLI (1.0.89): the hook payloads in
src/agent/copilot.rswere recorded from the real CLI; Allow and Deny from the notch were run end to end (the allowed command ran, the denied one did not), and so was the chat. Copilot gives no id for tool calls, so a step is matched to its result by tool name. - Antigravity CLI (
agy1.2.14): watched only. The hook payloads insrc/agent/antigravity.rswere recorded from the real CLI, but aPreToolUsehook cannot approve: itsallowdoes not skip Antigravity's own confirmation (tried interactively and headless; reported upstream as #1053), so Allow / Deny from the notch is off. What Sushi does is flag the session as waiting while a command, a file write or a URL fetch runs, so the pet calls you to theagyterminal (it cannot tell whetheragywill really ask: with a permission already granted the flag lasts a moment). On Linux the hook reports theagyprocess, so the session goes away whenagyquits instead of waiting for hours. Its hook payloads carry no event name, so the event is told from the fields; the argument names ofview_file, the replace tools and the searches come from its documentation. No chat. - pi: the extension follows the docs shipped with pi 0.85 and was exercised against a stand-in for pi's API, not inside pi. The chat parser follows pi's JSON mode docs (the model configured here was offline).
- Codex (hooks and
codex exec --jsonas documented; theapply_patchshape is a best reading of the docs), opencode (plugin API as documented) and Gemini CLI (hooks as documented indocs/hooks/reference.md; whetherBeforeTool's"allow"really skips its own confirmation prompt is unverified, unlike Antigravity's confirmed bug — seesrc/agent/gemini.rs) have only been tested against their documented payloads.
Plan limits (src/limits/) are a separate, undocumented-API-per-agent problem from the hook payloads above, so
their own confidence levels, from most to least certain:
- Claude Code (
limits/claude.rs): Anthropic's own OAuth usage endpoint, the same one Claude Code itself calls — the reference, like everywhere else in this list. - Codex (
limits/codex.rs): verified by reading the open-sourcecodex-rsclient's own source (backend-client/src/client.rs,client/rate_limit_resets.rs, thecodex-backend-openapi-modelscrate) for the endpoint and every field name, and cross-checked againststeipete/CodexBar's independent notes — but never run against a real ChatGPT-login account. Only works when logged in with a ChatGPT account, not an API key (which has no plan window at all). - GitHub Copilot (
limits/copilot.rs): GitHub documents none of this. The endpoint and field names come from cross-checking a public quota-tracking gist againststeipete/CodexBar's notes, not from anything GitHub ships. The bigger uncertainty is the token: Copilot CLI's own OAuth token usually lives in the OS keychain, not a file Sushi can read portably, so it is taken from (in order)COPILOT_GITHUB_TOKEN/GH_TOKEN/GITHUB_TOKEN, theghCLI'shosts.yml, the macOS Keychain entrycopilot-cli, and finally Copilot CLI's own plaintext fallback (~/.copilot/config.json, used when no keychain is available). If none of those holds a usable token, it is just unavailable. - Antigravity (
limits/antigravity.rs): the least certain of the four. Antigravity reuses Google's internal Cloud Code Assist backend, and everything here — endpoint, request body, field names, credentials file — comes fromsteipete/CodexBar's reverse-engineered provider notes, whose own issue tracker shows this has broken acrossagyversions before (a required client header changed, a local CSRF token got enforced). Untested against a live account.
Expect to adjust a field or two if a version differs: please open an issue with what it sent.
Claude Code / Codex / Copilot ──hook JSON──▶ sushi-hook --agent <id> ─┐
opencode / pi ──plugin (integrations/*.ts) spawns the hook──┤
▼ unix socket
sushi daemon (Rust): one adapter per agent
│ state.json (runtime dir)
Noctalia plugin (Luau): bar widget + panel ◀──┤
Desktop app (Tauri): pet window + panel ◀─────┘ (watches the daemon over the same socket)
Allow / Deny ──▶ sushi approve|deny ──▶ answers the hook (or the plugin)
Each agent has an adapter (src/agent/) that turns its events and tool calls into one neutral shape
(a session, a step, a diff...) and turns your Allow / Deny back into what the agent expects.
The hook never blocks an agent: if the daemon is not running it exits at once. Permission requests
and questions wait for your answer (30 s by default, SUSHI_PERMISSION_TIMEOUT_SECS; a Claude Code
plan 300 s, SUSHI_PLAN_TIMEOUT_SECS), then the agent falls back to its own prompt. That prompt is on
screen the whole time, so you can always answer in the terminal instead; if you do, the card in the
notch disappears by itself. A plan nobody answered in time stays in the notch for reading.
Claude Code ignores a bare "allow" for a plan: the hook sends the plan back as updatedInput (and
setMode: acceptEdits for auto-accepted edits), as checked on Claude Code 2.1.287. Hooks installed
by an older Sushi give PermissionRequest 40 s: run sushi install --write again to raise it.
- Linux, macOS or Windows 10+ for the daemon, the hook and
sushi install(they use a Unix domain socket, which Windows 10 supports). The Noctalia plugin additionally needs Linux with Noctalia v5 (plugin API 21) running. - Where things live: Linux uses
$XDG_RUNTIME_DIR; macOS$TMPDIR/sushi-<uid>; Windows%LOCALAPPDATA%\sushi\run. Config and cache followXDG_*,%APPDATA%and%LOCALAPPDATA%. - Plan limits:
curl(built into Windows 10+ and macOS). On macOS the Claude login is read from the Keychain. - Autostart the daemon:
contrib/sushi.service(systemd),contrib/io.sushi.daemon.plist(macOS launchd), or on Windowsschtasks /Create /SC ONLOGON /TN Sushi /TR "%USERPROFILE%\.cargo\bin\sushi.exe daemon". - At least one of: Claude Code, Codex CLI,
GitHub Copilot CLI, Antigravity CLI (
agy), Gemini CLI, opencode, pi - Rust (
cargo) to build the tool;curl(plan limits) andsystemd(optional, for the user service) just(optional) for the shortcuts below
The justfile builds and installs each part; just alone lists the recipes.
| Recipe | What it does |
|---|---|
just install |
cargo install --path .: sushi and sushi-hook into ~/.cargo/bin |
just connect claude |
connect an agent (codex, copilot, antigravity, opencode, pi, all); just connect claude no only shows what it would write |
just restart |
restart the daemon: the systemd user service if it is enabled, otherwise a background sushi daemon (log in /tmp/sushi-daemon.log) |
just update |
install + restart, after pulling or editing the code |
just app / just app-run |
build the desktop app (target/release/sushi-app) / build and start it |
just bundle |
the app's installers (needs cargo install tauri-cli --locked) |
just ui-mock |
serve the interface with sample data, without Tauri or a daemon |
just plugin-link / just plugin-reload |
link and enable the Noctalia plugin / reload it after editing |
just all |
update + app |
just test |
tests/run.sh (see Development) |
just assets |
regenerate the icons and the sounds |
A first install is just install, just connect all (or only your agents), then just restart or the
service below, then just app or just plugin-link. The steps below do the same by hand.
git clone <repo-url> sushi && cd sushi
cargo install --path . # installs sushi and sushi-hook into ~/.cargo/bin (just install)Make sure ~/.cargo/bin is in your PATH. Then connect the agents you use (without --write it only
shows what it would do; whatever it replaces is backed up as <file>.bak-sushi-<time>):
sushi install --agent claude --write # hooks in ~/.claude/settings.json
sushi install --agent codex --write # hooks in ~/.codex/hooks.json
sushi install --agent copilot --write # hook file ~/.copilot/hooks/sushi.json
sushi install --agent antigravity --write # group in ~/.gemini/config/hooks.json
sushi install --agent gemini --write # hooks in ~/.gemini/settings.json
sushi install --agent opencode --write # plugin in ~/.config/opencode/plugins/sushi.ts
sushi install --agent pi --write # extension in ~/.pi/agent/extensions/sushi.ts
sushi install --agent all --write # all of the above(sushi install-hooks --write still works and means --agent claude.) It is safe to run again, and
running it again after an update brings Sushi's hooks up to date (e.g. their timeouts).
Agents read their hooks and plugins when a session starts: sessions that are already open need a restart.
pi has no permission prompt of its own, so Sushi only watches it; to allow or deny every tool call
from the notch, start pi with SUSHI_PI_APPROVE=1.
Try it in a terminal first:
sushi daemonor run it at login as a user service (recommended):
mkdir -p ~/.config/systemd/user && cp contrib/sushi.service ~/.config/systemd/user/
systemctl --user enable --now sushi.service
systemctl --user status sushi.service # check that it is runningThe service expects the binary in ~/.cargo/bin/sushi; edit ExecStart if you installed it elsewhere.
After installing a new version, restart the daemon (just restart, or systemctl --user restart sushi.service):
the running one keeps the old code.
app/ is a Tauri app: a small always-on-top pet window (click it to poke it and open
the panel, double click to nap or wake it up, drag it to move it, right click to pet it — hold it down
longer for a bigger cuddle — middle click to feed it whatever a file manager's "Copy" put on the clipboard)
and a tray icon to show or hide things and quit. It is
the same pet and panel as the Noctalia plugin (Live, Chat, Usage, Allow / Deny, sounds), plus a Settings tab.
It starts the daemon for you if none is running, so steps 1 and 2 are all it needs.
cargo run -p sushi-app --release # try it (just app-run)
cargo build -p sushi-app --release # target/release/sushi-app (just app)
cargo install tauri-cli --locked # once, to make an installer
cd app/src-tauri && cargo tauri build # .app / .dmg, .msi / .exe, .deb / .AppImage (just bundle)- Linux needs the web view libraries (Debian/Ubuntu:
libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev libgtk-3-dev libgtk-layer-shell-dev; Arch:webkit2gtk-4.1 libayatana-appindicator gtk-layer-shell). The always-on-top pet window and a transparent background need X11 or a compositor that allows them; on GNOME / Wayland the window behaves like a normal one, and the tray icon needs the AppIndicator extension. - Tiling compositors (niri and the like): the pet window is sized for what it can show before it opens and
has a fixed size, and on Linux the panel is a child of the pet window, so both open floating instead of taking
a column. The size follows the "Pet window shows" setting; on niri a new size applies the next time the app
starts. To drop niri's border and focus ring around the pet, add a window rule:
window-rule { match app-id="^sushi-app$"; border { off; }; focus-ring { off; }; }. - macOS hides the Dock icon (it is a menu bar app). Run
tools/make_icon.pyandcargo tauri iconfor an.icns. - Windows 10 or later (the daemon uses Unix sockets, which Windows supports from build 1803).
- The app finds the
sushibinary next to itself or in yourPATH, so keepcargo install --path .from step 1. - Settings (character, sounds, fidgets, nap delay, what the pet window shows, chat agent and model) are in the
panel's gear tab and stored in
sushi/app.jsonunder your config folder. - To try the interface without Tauri or a daemon, serve
app/ui(just ui-mock) and open/index.html?view=panel&mock=work(alsoview=pet,mock=ask|idle|down,tab=chat|usage|settings).
The plugin lives in plugin/. Link it where Noctalia looks for local plugins and enable it:
mkdir -p ~/.local/share/noctalia-plugin-dev
ln -s "$PWD/plugin" ~/.local/share/noctalia-plugin-dev/sushi
noctalia msg plugins enable scanna/sushi(just plugin-link does both.)
Then add the widget to a bar (the center group of the bar is the notch):
noctalia msg settings-open-widget <bar-name> # add "Sushi" (scanna/sushi:pet)If the plugin does not find the tool, set the path to the sushi binary in the plugin's settings
(default ~/.cargo/bin/sushi). Start a session of any connected agent and the pet shows up with it.
After editing the plugin's code, reload it: noctalia msg plugins disable scanna/sushi
then enable (Noctalia caches the scripts), or just plugin-reload.
| Where | What |
|---|---|
| Bar widget (Noctalia) / pet window (app) | the pet, the turn timer, files changed, the 5-hour plan usage; the tooltip has the full progress of the active session. Left click opens the panel, right click pets it; in the app, double click also naps or wakes it, and holding the right click longer gives it a bigger cuddle. Feeding a file: in the app, drop it on the pet window (or on the panel), or copy it in the file manager and middle-click the pet. Noctalia cannot take a file dragged from a file manager onto the bar at all, so there copying it and middle-clicking (or "Feed a file" in the panel) is the only way. |
| Panel · Live | steps on the left, viewer on the right. Click a step to look at it, click a session to pin it. The search box above the rail queries that session's full stored history, not just the last few steps; the flag glyph on a step marks it "needs review" (sticks around after a restart); the export glyph writes the session (or the current search results) as markdown. |
| Panel · Chat | type and press Enter. Stop and "new conversation" buttons at the bottom. A file fed to the pet shows above the field (✕ to drop it) and goes with the next message; the "Feed a file" button next to the emotes takes the file copied in the file manager. |
| Panel · Usage | limits, context trend, tokens, a day-by-day cost chart, and a budget bar per configured project (budget_alerts_by_cwd). |
| Keybinding | noctalia msg plugin scanna/sushi:state all tab chat then noctalia msg panel-open scanna/sushi:panel opens the panel on a tab (live, chat, usage). noctalia msg plugin scanna/sushi:state all feed /path/to/file feeds the pet a file (handy as a file manager action). |
| CLI | sushi chat "what does it do?" --file src/main.rs asks the chat about a file; sushi history SESSION_ID [--query TEXT], sushi flag SESSION_ID STEP_ID on|off [--note TEXT], sushi export SESSION_ID [--query TEXT] work the same steps the panel does. |
In Noctalia's plugin settings: character, idle quirks, nap delay, sounds, widget details,
close the panel after Allow / Deny, chat agent and chat model, the path to the sushi binary,
focus mode (enable it and set its hours/days) and whether milestones celebrate. The app has the
same settings in its own Settings tab, plus a quick "Focus now" toggle next to the pet's emotes (the
Noctalia plugin has no equivalent write-back, so there it only follows the configured schedule).
The daemon reads an optional ~/.config/sushi/config.json:
{
"show_code": true,
"context_window": 200000,
"context_windows": { "claude-sonnet-5": 1000000 },
"chat_agent": "claude",
"chat_model": "sonnet",
"chat_models": { "copilot": "auto" },
"claude_path": "claude",
"agent_paths": { "pi": "/opt/pi/bin/pi" },
"transcribe_model_path": "/opt/whisper/ggml-base.en.bin",
"whisper_path": "whisper-cli",
"model_prices": { "claude-sonnet": { "input": 3.0, "output": 15.0, "cache_read": 0.3, "cache_write": 3.75 } },
"budget_alerts": { "plan_percent": [80, 95], "daily_tokens": 0, "daily_cost_usd": 0, "daily_percent": [80, 95] },
"budget_alerts_by_cwd": { "/home/me/big-repo": { "daily_cost_usd": 5, "daily_percent": [80, 95] } },
"hooks": {
"on_session_start": { "cmd": "", "url": "" },
"on_session_end": { "cmd": "", "url": "" },
"on_waiting": { "cmd": "", "url": "" },
"on_turn_end": { "cmd": "notify-send Sushi 'turn finished'", "url": "https://example.com/hook" }
},
"policies": [
{ "tool": "Bash", "contains": "rm -rf", "level": "ask", "label": "destructive delete" }
],
"claude_permissions": { "ask": ["Bash(rm -rf*)"], "deny": [] }
}chat_agent is the agent the chat talks to (claude, copilot, pi or codex; the plugin setting wins),
chat_model is the model for Claude Code and chat_models the one per other agent (empty: the agent's default).
agent_paths says where an executable is when it is not on the PATH. A conversation is with one agent:
switching starts a new one. transcribe_model_path (empty by default: feeding audio is then refused like any
other binary file) points whisper-cli at a whisper.cpp
GGML/GGUF model to transcribe a fed audio file automatically; whisper_path says where that executable is
when it is not on the PATH under its own name.
model_prices (USD per million tokens, keyed by a prefix of the model id) drives the estimated cost
shown in the Usage tab — Anthropic's published list prices by default, override a model if they drift; this
is always an estimate, never a billed amount. budget_alerts raises a one-shot event (a distinct sound, a
worried blip) the first time a threshold is crossed: plan_percent against Claude's 5-hour/weekly windows,
daily_tokens / daily_cost_usd (0 = disabled) against everything tracked today, checked against
daily_percent. budget_alerts_by_cwd repeats the daily checks per project, keyed by the exact cwd a
session reports (no entry = no check for that project, never silently falling back to the global one); the
real per-day cost behind both (no more blending an all-time rate against today's token count) is kept in
usage_history (~/.cache/sushi/usage_history.json), which is also where the Usage tab's day-by-day chart
reads from.
hooks runs cmd (with SUSHI_EVENT, SUSHI_AGENT, SUSHI_SESSION_ID, SUSHI_SESSION_NAME, SUSHI_CWD,
SUSHI_STATUS in its environment) and/or POSTs a small JSON body to url, fire-and-forget in their own
thread: useful for triggering a build, a staging deploy, or updating an internal dashboard when an agent
finishes or starts waiting for you. Neither ever blocks the daemon, and there is still no inbound listener
besides the Unix socket.
policies flags a matching tool call (tool: an exact name or *; contains: a case-insensitive substring
of its command/path/url) in the live viewer, for every agent, with level ("ask" or "deny", display only
here) and label shown as its tooltip. See Policy flags vs. real enforcement
for claude_permissions, the only way Sushi can make an agent genuinely ask or refuse something it would
otherwise have auto-approved.
Set SUSHI_NO_LIMITS=1 to stop the daemon from asking for your plan limits.
policies (above) only flags a step for you to notice — it changes nothing about what the agent does, for
any agent. Actually forcing a confirmation or a refusal needs the agent's own permission engine, and today
only Claude Code has one Sushi can reliably drive: its permissions.ask / permissions.deny lists (native
syntax, e.g. Bash(rm -rf*)), which make Claude ask or refuse even when it would otherwise have
auto-approved. sushi install --agent claude --write merges claude_permissions.ask / .deny from
config.json into ~/.claude/settings.json's permissions block (existing entries are kept, nothing is
ever removed) — run it again after changing claude_permissions.
There is no equivalent for the other agents: inventing one would mean racing every single tool call against
the daemon over the hook (new latency on every call, for a rule that might never match), and it still
would not be reliable for Antigravity, whose hook already cannot force a decision either way (see its own
note below). So for Codex, Copilot, Antigravity, opencode, pi and Gemini CLI, policies is visibility only:
you will see the flag in the Live tab, but the tool call already ran.
- Code in the viewer: diffs, command output and file excerpts are published in
$XDG_RUNTIME_DIR/sushi/state.json, readable only by you and cleared on logout. Set"show_code": falseto publish titles only. - The durable step history (search, flags, export —
~/.cache/sushi/history.json) keeps the same bounded diffs/output as the viewer, just for longer: beyond the current turn and beyond the session ending, capped at 500 steps per session and 200 sessions. It respectsshow_codethe same way (nothing but titles when it's off), and an export writes a markdown file of it to~/.cache/sushi/exports/— both readable only by you, neither ever sent anywhere. - Plan limits use the OAuth token Claude Code stores in
~/.claude/.credentials.json, sent only toapi.anthropic.com(throughcurl, never on a command line). The endpoint is undocumented and may change. - The chat runs the chosen agent headless with no tools (Claude Code:
--tools ""and no MCP servers; Copilot: no tools available; pi:--no-toolsand no extensions), using that agent's own login. It counts against its plan like any other session (a Copilot chat message is one premium request) and keeps its history in~/.cache/sushi/. Codex has no tool-free mode: its chat runs in a read-only sandbox, so it could still read files. A fed file must be text, or audio transcribed bywhisper-clirunning locally (seetranscribe_model_pathabove — nothing audio ever leaves the machine): its content (up to 60,000 characters, 20,000 on Windows) goes into the message, and the log keeps only its name.
The 31 sounds in plugin/sounds/ are synthesized by tools/make_sounds.py (standard library only).
Run it again to regenerate them; --check only verifies them.
tests/run.sh # everything below, in one go (just test)It runs cargo test (unit tests, plus end-to-end tests that start a real daemon and the real hook in a
throwaway directory and walk through Allow, Deny, questions, plans and killed hooks), cargo clippy, the
syntax and noctalia plugins lint checks of the Luau scripts, the Luau tests in tests/lua/ (they run
the plugin's scripts against stand-ins for Noctalia's ui, noctalia and panel, so luajit is
needed) and the sound check.
The Luau code is written to run in Noctalia's plugin runtime; the UI is plain flex boxes (no canvas), so every character is a tree of rounded boxes moved with computed spacers.