{"slug": "sauron-a-free-and-open-source-terminal-development-environment", "title": "Sauron: A free and open source terminal development environment", "summary": "Matt Olson released Sauron 0.1.0, a free and open-source terminal development environment (TDE) for macOS that manages multiple coding agents across multiple projects using tmux sessions, with features like background agents, worktrees, and commit attribution. The app, available as a signed and notarized DMG for Apple silicon Macs on macOS 15 or later, requires tmux, git, and at least one agent CLI such as Claude or Codex.", "body_md": "A \"super terminal\" desktop app for running a lot of coding agents across a lot of projects, built on tmux.\n\nI like running agents in the terminal. Claude Code, Codex, Opencode, Picode, local AI, cloud providers, etc. What I did not like was keeping track of fifteen of them across six repositories. Which ones are waiting on me? What did that one agent commit? Where was I on this project three weeks ago? Sauron is the thing I built to answer those questions without giving up the terminal. It is not quite an IDE and it is not quite a terminal. I call it a TDE (terminal development environment).\n\nIt is free and open source and always will be!\n\n**Download:** [Sauron-0.1.0-arm64.dmg](https://github.com/mlolson/sauron/releases/latest/download/Sauron-0.1.0-arm64.dmg)\nfor Apple silicon Macs on macOS 15 or later, signed and notarized. Other builds are on the\n[releases page](https://github.com/mlolson/sauron/releases). It needs `tmux`, `git`, and at least\none agent CLI such as `claude` or `codex` on your PATH.\n\nEvery session Sauron starts is a tmux session. Quit the app and they keep running. Reboot and\nresume them. Attach from any terminal with `tmux attach` (there is a button that copies the\ncommand). Nothing you run is trapped inside the app. Sauron can also see sessions that are started in external terminal. The principal is low coupling between Sauron and your tmux sessions.\n\nSauron does not include skills, a task framework, a planning routine, or instructions for your\nagents. It does not put anything in your repository except a `post-commit` hook, an entry in\n`.git/info/exclude`, and the ignored `.sauron/` directory that entry covers. An agent is a profile: an executable and its arguments. Claude and\nCodex come preconfigured; add anything else that runs in a terminal, including a plain shell.\nRun agents the way you already do, with the flags you already use. Sauron just keeps track of them.\n\nRun Claude, Codex, Openrouter, and a local model side by side under the same project. Fork a session into\na worktree. Hand a session's work to a *different* agent:\nSauron writes a briefing from the recent transcript, the session's commits, and the state of\nthe working tree, and starts the other agent with it. No model in the loop, so it takes a\nfraction of a second. Hit a usage limit on one vendor, carry on with another.\n\nSauron allows you to define \"background\" agents and attach them to any project. Background agetns run headless in a fresh worktree on its own branch, manually, on a schedule, or after commits with a cooldown, and it runs whether or not the app is open. When it finishes, its commits wait on the agent's page with a diff viewer and Merge. Or they can run on your main repo and just do whatever.\n\nA `post-commit` hook records which session was behind each commit, so an agent's work is\nnever just \"Matt Olson, 4d ago\" in the log. Each session shows its last commit under its name\nin the sidebar. The Commits pane lists everything a session committed, with the diff, or\nswitches to the whole project so you can see who did what across every agent and branch.\nIt works for you too: commits you make by hand inside a Sauron terminal are attributed to that\nterminal. The hook only ever exits 0, so it cannot break a commit.\n\n- **Project summaries.** A built-in background agent rewrites a short status for each project\nafter commits: what it is, what changed, what is open or blocked. It lives in`<project>/.sauron/status.json` , kept out of git, where your other agents can read it too.\n- **Sessions you started elsewhere.** Claude and Codex sessions launched from a terminal show\nup under their project with a live transcript. Import one to bring it under Sauron.\n- **Worktrees.** Start a session in a fresh worktree on its own branch so parallel agents do\nnot step on each other. The header shows the branch and checkout and updates as they change.\n- **A supervisor agent** , off by default. A long-lived agent you chat with that can start and\nmessage workers through the CLI.\n\nElectron, TypeScript, React, xterm.js, node-pty, built with electron-vite. Sessions run inside tmux; commit attribution lives in a SQLite database alongside the app's config.\n\nRequires Node 22+, pnpm, and on the machine: `tmux`, `git`, `claude` (Codex optional).\n\n```\npnpm install          # also rebuilds node-pty for Electron\npnpm dev              # hot-reloading development run\nscripts/app.sh             # build and launch the unpackaged dev build, detached\nscripts/app.sh --packaged  # build and launch dist/mac-arm64/Sauron.app (proper name and icon; slower)\nscripts/install-launcher.sh  # installs `sauron-dev` on PATH and ~/Desktop/Sauron.app, both of which run scripts/app.sh\npnpm test             # Vitest\npnpm typecheck\npnpm package          # unsigned Sauron.app in dist/\n```\n\n`pnpm dist` builds a signed, notarized DMG (and zip) into `dist/`. It needs two things set up\nonce on the machine that builds:\n\n1. A **Developer ID Application** certificate in the login keychain, from Xcode > Settings >\nAccounts > Manage Certificates, or from developer.apple.com.\n2. Notarization credentials stored as a keychain profile named `sauron-notary` :\nIt asks for an app-specific password, made at appleid.apple.com.\n\n```\nxcrun notarytool store-credentials sauron-notary --apple-id you@example.com --team-id TEAMID\n```\n\nThe script refuses to run without both. `scripts/dist.sh --unsigned` builds the same DMG\nunsigned for a local check; other Macs will only open that one via right-click > Open.\nSigning uses the hardened runtime with the entitlements in `build/entitlements.mac.plist`\n(JIT and unsigned executable memory for Electron, library validation off for node-pty).\n\n- **⌘O** add a project,**⌘,** preferences,**⌘K** quick switcher,**⌘⇧]** /**⌘⇧[** next and\nprevious live session. Right-click projects, sessions, and the supervisor for actions.\n- Add a project by dragging a git repository onto the window, pressing ⌘O, or from the shell:\n`open -a Sauron ~/code/my-repo` (packaged app) or`scripts/sauron projects add ~/code/my-repo` .\n- The supervisor's home is `~/Library/Application Support/Sauron/master` ; Sauron writes both`CLAUDE.md` and`AGENTS.md` there so agents of either convention receive its instructions.\n- Logs: `~/Library/Logs/Sauron/main.log` (Preferences > Reveal Logs).\n- `docs/QA.md` is the manual checklist;`scripts/smoke.sh` is the end-to-end check against a\nrunning app.`TODOS.md` tracks open items.\n\n`scripts/sauron` talks to the running app over `~/Library/Application Support/Sauron/sauron.sock`.\nThe supervisor agent uses the same commands to drive workers.\n\n```\nscripts/sauron ping\nscripts/sauron projects [add <dir>]\nscripts/sauron sessions [--project <name|id>]\nscripts/sauron launch --project <name|id> [--tool claude|codex|shell|<profile>] [--prompt \"...\"] [--worktree [<branch>]]\nscripts/sauron send --session <id> --text \"...\"\nscripts/sauron stop | resume | close | hide --session <id>\nscripts/sauron fork --session <id>\nscripts/sauron handoff --session <id> --tool <profile>\nscripts/sauron rename --session <id> --title \"...\"\nscripts/sauron worktrees --project <name|id> [remove --path <dir> [--force]]\nscripts/sauron status refresh --project <name|id>   # run the Project summarizer now\nscripts/sauron status set --project <name|id> --summary \"...\" [--details \"...\"] [--update \"...\"] [--todo \"...\"]\nscripts/sauron status get|set ...                # also work with the app closed, straight from the project's status file\nscripts/sauron job run|list|merge|discard ...    # background agent runs; `sauron tick` is what launchd calls every minute\nscripts/sauron master                            # start the supervisor\nscripts/sauron select --project <name|id> [--job <agent id>] | --session <id> | --document <path>\n```\n\nSet `SAURON_TOOL_CLAUDE`, `SAURON_TOOL_CODEX`, `SAURON_TOOL_TMUX`, or `SAURON_TOOL_GIT` to override\na tool's path; the value `none` simulates a missing tool.\n\nPreferences > Agent profiles lets you add any interactive CLI agent by giving it an id, name,\nexecutable, and arguments. Arguments are stored as an array and support `{prompt}`, `{cwd}`,\n`{sessionId}`, and `{sauronBin}` placeholders. If `{prompt}` is absent, an initial prompt is\nappended as the final argument. Custom agents run in the same persistent tmux terminals as the\nbuilt-ins and can be handoff targets; transcript discovery, hooks, forking, and resume remain\nbuilt-in integrations for Claude and Codex.\n\nAn agent profile may define `forkCommand` as an argument array. It supports the same placeholders\nplus `{sourceSessionId}`. Fork and Import only appear for sessions with a known CLI session id\nwhose profile has a non-empty fork command. Claude and Codex include fork commands by default.\n\nClaude and Codex are themselves editable profiles. Their default profile arguments bypass permission prompts; remove those arguments in Preferences if you want the CLIs to prompt normally.\n\nThe supervisor agent is selected separately in Preferences. Its extra arguments can select a\nmodel (for example `--model opus` for a Claude profile).\n\nThe Project summarizer's prompt is `jobs/project-summarizer.md` beside `config.json`, seeded on\nfirst launch and then yours to edit. It supports `{projectName}`, `{projectId}`, `{projectPath}`,\n`{branch}`, `{jobName}` and `{previousSummaryUpdatedAt}`. Its trigger and cooldown are edited like\nany background agent's, globally in Preferences or per project with **Configure…**.\n\nWhen a project is added, Sauron installs a `post-commit` hook into it (an existing hook is kept\nand chained to). The hook inherits the environment of whatever invoked git, so it sees\n`SAURON_SESSION_ID` exactly when the commit came from a managed session, and reports the commit\nto the app over the control socket; otherwise it does nothing. Every path in the hook exits 0,\nso a Sauron problem can never fail a commit. Linked worktrees share the main checkout's hooks,\nso one install covers every worktree Sauron creates. Removing a project removes the hook and\nrestores whatever it displaced.", "url": "https://wpnews.pro/news/sauron-a-free-and-open-source-terminal-development-environment", "canonical_source": "https://github.com/mlolson/sauron/", "published_at": "2026-09-08 22:50:24+00:00", "updated_at": "2026-09-08 23:04:00.131221+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-tools"], "entities": ["Matt Olson", "Sauron", "tmux", "Claude Code", "Codex", "Openrouter", "Picode"], "alternates": {"html": "https://wpnews.pro/news/sauron-a-free-and-open-source-terminal-development-environment", "markdown": "https://wpnews.pro/news/sauron-a-free-and-open-source-terminal-development-environment.md", "text": "https://wpnews.pro/news/sauron-a-free-and-open-source-terminal-development-environment.txt", "jsonld": "https://wpnews.pro/news/sauron-a-free-and-open-source-terminal-development-environment.jsonld"}}