The terminal AI agents can drive — 67 MCP methods across 11 namespaces. macOS / Linux / Windows, local-first, $0, MIT.
SaferSkills independently audited Unterm (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
The terminal AI agents can drive.
Cross-platform terminal (macOS / Linux / Windows) built on a customized WezTerm engine, with one design bet: the terminal itself is controllable from the outside by any AI agent over MCP. Claude Code, Cursor, Aider, Continue, your own scripts — they all get the same JSON-RPC surface to spawn shells, run commands, read pane state, capture screenshots, change settings, and record sessions.
The other 2026 terminals each pick a different side: Warp embeds AI inside a closed cloud (Oz), Ghostty stays out of your way and lets you bring your own tools, iTerm2 is Mac-only. Unterm picks the third side — terminal as MCP-controllable surface, deliberately keep AI features out of the terminal, let external agents grip it through the API.
Practical implications:
▼ overlay is intentionally minimal — six quick actions and a link to the Web Settings page.unterm-cli lang set <code>.~/.unterm/instances/<name>.json. Agents that drive several windows at once enumerate that directory.Built on top of the WezTerm engine for renderer / font / TUI / SSH / mux work, with a thin product layer on top.
Pre-built artifacts are published on GitHub Releases:
https://github.com/zhitongblog/unterm/releases
| Platform | Artifact |
|---|---|
| macOS | Unterm-macos-<version>.dmg (universal arm64+x86_64, signed + notarized) |
| Linux | unterm-<version>.deb or Unterm-<version>-x86_64.AppImage |
| Windows | Unterm-<version>-x64.msi or Unterm-windows-<version>.zip |
Double-click Unterm-macos-<version>.dmg, then drag Unterm.app onto the Applications shortcut. The DMG is signed with a Developer ID and Apple- notarized, so Gatekeeper opens it on first launch without warnings.
Finder integration is bundled in the DMG. After the first launch, Finder's right-click menu can show Open in Unterm for folders and files; if macOS doesn't refresh the extension immediately, run Repair Finder Integration.app from the DMG once.
sudo apt install ./unterm-<version>.deb
untermOther distros — use the AppImage:
chmod +x Unterm-<version>-x86_64.AppImage
./Unterm-<version>-x86_64.AppImageRun the MSI installer; it places unterm.exe in Program Files\Unterm and creates a Start Menu shortcut.
~/.unterm/instances/<name>.json; ~/.unterm/active.json points at the current foreground instance for single-target agents.cmd.exe spawns are wrapped to set the code page; no more mojibake on Chinese filenames.~/.unterm/scrollback.json, TERM_PROGRAM overridable via ~/.unterm/compat.json, background update poller writes ~/.unterm/update_check.json.The full Unterm docs live at https://unterm.app/docs/:
unterm-cli subcommands, flags, exit codes~/.unterm/This README is the short version. The site is the long version.
127.0.0.1:<auto-port> (default 19876) — JSON-RPC over TCP, auth-token gated. Method namespaces: session, exec, screen, signal, orchestrate, proxy, workspace, capture, policy, system, server, instance, profile.127.0.0.1:<auto-port> (default 19877) — open in any browser via unterm-cli settings open or the Settings (Web) item in the ▼ menu. Tailwind-styled SPA, supports all 9 languages, keyboard + mouse.$HTTPS_PROXY, falls back to scanning common local ports. The single proxy.json toggle is {"enabled": true|false} — no manual URL configuration needed.~/.unterm/screenshots/, on the system image clipboard, and the path on the text clipboard.capture.scrollback re-renders a pane's entire history into one tall PNG headlessly (exact fonts/theme, streaming-encoded, works while occluded); capture.window_scroll long-shots another app's window by synthesizing wheel events and stitching frames via row-hash matching with sticky-header/footer detection (macOS). Both also in the ▼ menu and unterm-cli screenshot --scrollback / --scroll-app.KEY=value / 40+ char hex/base64 patterns are masked). Recordings are stored in the project directory under <cwd>/.unterm/sessions/<date>/<tab>-<time>.md, or in ~/.unterm/sessions/_orphan/ when no writable project context.▼ button:Bind a window to a coherent developer identity — GitHub PAT, AWS keys, npm token, git author, SSH keys — all in one shot. New window for a different identity. The chip in the tab bar tells you which one you're in. Secrets live in the OS-native vault (Keychain / Credential Manager / Secret Service), never in ~/.unterm/.
unterm-cli profile create "Work — Acme"
unterm-cli profile set-secret "Work" GITHUB_TOKEN
unterm-cli profile spawn "Work" # → new window bound to Work
unterm-cli profile set-default "Work" # plain `unterm` now binds to Work
unterm-cli profile import # scans gh/aws/npm/ssh/docker/gcloud/netrc
# for existing credentials, read-onlyInside a profile-bound shell:
$ env | grep UNTERM_PROFILE
UNTERM_PROFILE=work-acme
# GITHUB_TOKEN, GIT_AUTHOR_NAME, AWS_*, etc. all set from the profileFull docs: unterm.app/docs/profiles.
Every running Unterm process is one instance with a NATO-phonetic name: alpha, bravo, charlie, … zulu. The first window claims alpha, the second bravo, etc. When all 26 are taken at once, the next one wraps to alpha2. Names are easy to pronounce and AI agents handle them right — no UUIDs, no ports in your head.
Each instance writes its metadata (mcp_port, http_port, auth_token, pid, started_at, version, platform) to ~/.unterm/instances/<name>.json. Agents that need to drive a specific window enumerate that directory and pick by id, cwd, or title. For single-target agents, ~/.unterm/active.json points at the most recently launched live instance, and ~/.unterm/server.json mirrors that same record for backward compat.
The MCP instance.* namespace exposes this directly: instance.list, instance.info, instance.set_title, instance.focus. See the multi-instance docs for examples and the discovery protocol.
The unterm-cli binary exposes the full Unterm product surface, transparently routing to the local MCP server. Read ~/.unterm/server.json (or any file under ~/.unterm/instances/) for current ports + auth.
# Settings + Web UI
unterm-cli settings open # open the Web Settings page
unterm-cli theme list / set <id> # standard / midnight / daylight / classic / notion-dark / notion-light
unterm-cli lang list / set <code> / current # en-US / zh-CN / zh-TW / ja-JP / ko-KR / de-DE / fr-FR / it-IT / hi-IN
# Proxy
unterm-cli proxy status # auto-detect health
unterm-cli proxy nodes / switch <name> / disable / env
# Sessions / panes
unterm-cli session list # list panes in active/latest instance
unterm-cli instance list # discover alpha/bravo/... windows
unterm-cli --instance bravo session list # pin a command to one window
unterm-cli session create [--cwd DIR] [-- CMD] # spawn a new tab
unterm-cli session record start [--id N]
unterm-cli session record stop [--id N]
unterm-cli session export [--id N] [-o FILE]
unterm-cli sessions list [--project SLUG]
unterm-cli sessions read <session-id>
# Screenshots
unterm-cli screenshot [--include-window] [-o FILE]
# Long screenshot of a pane's ENTIRE scrollback (headless re-render -> tall PNG)
unterm-cli screenshot --scrollback [--pane N] [--max-rows N] [-o FILE]
# Long screenshot of ANOTHER app's window: scroll + stitch (macOS)
unterm-cli screenshot --scroll-app Safari [--scroll-title SUBSTR] [--max-frames N] [-o FILE]Pass --json to any subcommand for raw JSON-RPC output (suitable for scripts). Pass --lang <code> to override the locale for one invocation. Pass --instance <id> (or set UNTERM_INSTANCE=<id>) when several Unterm windows are open and you need a deterministic target.
Multi-instance discovery is exposed over MCP rather than as a separate CLI list command — call instance.list against any running Unterm's MCP port, or just ls ~/.unterm/instances/.
Unterm makes every AI coding agent on the machine aware of it, so they can drive the terminal without manual setup. On first launch (per version) the GUI runs unterm-cli setup-ai, which detects installed agents — Claude Code, Codex, Gemini CLI, Cursor, Windsurf, OpenCode — and, for each:
unterm MCP server into the agent's global config (merging into existing config, never clobbering), so the agent can list/run/read/screenshot the real terminal the moment it starts;CLAUDE.md / AGENTS.md / GEMINI.md) so even an agent that never loads the MCP server knows Unterm is here.The registered bridge (unterm-cli mcp-stdio) self-discovers the live instance at connect time, so a static registration keeps working across restarts and multiple windows. Agents that connect also receive a usage brief via the MCP initialize instructions field.
unterm-cli setup-ai # detect agents + register (idempotent; safe to re-run)
unterm-cli setup-ai --dry-run # show what would change, write nothing
unterm-cli setup-ai --no-context # register the MCP server only, don't touch context files
unterm-cli setup-ai --remove # undo: strip the server entry + context block from every agentUser config lives at:
| Platform | Location |
|---|---|
| macOS | ~/.unterm/ |
| Linux | ~/.unterm/ |
| Windows | %USERPROFILE%\.unterm\ |
Files:
| File | Purpose | |
|---|---|---|
server.json | Active instance's MCP/HTTP ports + auth token + pid (auto, mirrors the active instance for back-compat) | |
active.json | Pointer at the current foreground instance id (auto, updated only when previous active dies) | |
instances/<name>.json | Per-instance metadata (NATO id, ports, token, pid, started_at, version, platform) | |
auth_token | Legacy mirror of the active auth token (for back-compat) | |
proxy.json | `{"enabled": true | false}` — URLs auto-detected |
theme.json | Active theme id | |
lang.json | Persisted locale override | |
compat.json | {"term_program": "..."} override for $TERM_PROGRAM | |
scrollback.json | Override the default scrollback line count | |
update_check.json | Background update-poller state (last check, latest seen version) | |
onboarded.json | First-run flags (which ▼ items have been seen) | |
recording.json | Recording config (redaction patterns, etc.) | |
sessions/ | Recording metadata index (per-project subdirs) | |
screenshots/ | Region screenshots (PNG) |
Prereqs: a recent stable Rust toolchain. Linux additionally needs the system deps in get-deps.
make build # all binaries (debug)
make check # static checks
make test # tests
make clean-release-artifacts # remove local dmg/msi/zip/deb/AppImage packagesBuild a release for the current platform:
cargo build --release -p unterm -p unterm-cli -p unterm-mux -p strip-ansi-escapesBuild platform packages:
# macOS — universal .app + zip (run on macOS)
ci/deploy.sh
# Linux — .deb
ci/deploy.sh
# Linux — AppImage
ci/appimage.sh
# Windows — staged release tree + zip
bash ci/deploy.sh
# Windows — MSI (requires WiX 6 at .\.tools\wix.exe — install via `dotnet tool install --tool-path .\.tools wix --version 6.0.1`)
pwsh -File ci/build-msi.ps1macOS code-signing + notarization is local-only (no CI step) so the Developer ID .p12 private key never has to leave your Mac. One-time setup, on the Mac that holds the cert:
xcrun notarytool store-credentials UntermNotary \
--apple-id <your-apple-id> --team-id 6NQM3XP5RFUnterm release tags may use either minor tags (v0.50) or patch tags (v0.50.0). Use the tag form that matches the changelog and package version for the release. Cut a tag only when a coherent batch of fixes / features is ready to ship.
git tag -a vX.Y.Z -m "Unterm vX.Y.Z" && git push origin vX.Y.Z
make release-mac # build universal + sign + notarize + uploadmake release-mac reads the tag from git describe --exact-match HEAD, builds universal x86_64+aarch64 binaries, calls ci/sign-macos.sh with NOTARY_PROFILE=UntermNotary, then gh release uploads the resulting DMG to the matching GitHub Release. After local validation/upload, run make clean-release-artifacts to remove root-level release packages while keeping build caches intact.
CI on every PR runs cargo check against macOS, Linux, and Windows. Tagged pushes (vX.Y or vX.Y.Z) trigger the release-linux and release-windows workflows that publish those two platforms' artifacts to GitHub Releases. macOS sits out of CI by design — see above.
This repository is the main Unterm project:
https://github.com/zhitongblog/unterm
Unterm includes modified WezTerm components. Upstream WezTerm remains a separate project by Wez Furlong and contributors.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.