A macOS-native development console for running and managing terminal coding agents — Codex, Claude Code, and more — in one libghostty-powered workspace.
Terminals · AI sessions · MCP servers · Skills · API tokens · Browser panes, unified in one place.
English · 简体中文
M2Terminal is a macOS-first local development console built for working alongside terminal-based coding agents. It pulls the things a developer normally spreads across many tools into one native workspace: terminal panes, Codex / Claude Code sessions, MCP servers, skills, API tokens, and freely-placeable browser panes.
The goal is to feel like a native terminal workspace, not an Electron dashboard wrapped around a shell:
- Terminal semantics belong to a mature engine. ANSI/VT parsing, the terminal state machine, scrollback, reflow, and font handling are owned by libghostty (Ghostty's terminal kernel, consumed through the
libghostty-vtC ABI). Swift/AppKit is a native host and input router — it does not implement a second terminal. - Processes and state belong to a Rust core. A Rust daemon/library owns PTY supervision, workspace/session lifecycle, MCP processes, SQLite metadata, config import/export, hooks, runbooks, and a local automation interface.
- M2Terminal owns the canonical config. The MCP/skills configuration is app-owned and exported to Codex- and Claude Code-compatible formats through adapters — never the other way around.
- Secrets stay in Keychain. Token values live only in the macOS Keychain; SQLite and exported config store references only, and plaintext secrets are never rendered into terminal command text.
⚠️ Pre-1.0 (currently0.2.2). Architecture and some behavior are still evolving; interfaces and storage formats may change.
- Native AppKit workspace shell: sidebar, tab strip, terminal groups, split panes, status bar, tab history.
- Default Metal grid renderer (
TerminalMetalGridView) that GPU-paints the libghostty-vtTerminalFrame; falls back to the AppKit grid when Metal is unavailable orM2TERMINAL_GPU_RENDER=0. Both have full parity: text (incl. color emoji and ligatures), selection, find, cursor, underlines, URL underlines, IME composition, and predicted local echo. - Full input-method (IME) support, soft-wrap-aware copy, URL detection with Command-Click, and command highlighting.
Ctrl+Tab/Ctrl+Shift+Tabcycle tabs in order within a workspace.
- Discover Codex and Claude Code sessions from their own local JSONL history, plus the current workspace's terminal output.
- Filter by provider, inspect transcripts, filter messages, copy the resume command, restore, and delete the source file.
- Restoring a session creates a new workspace tab and runs the provider's native resume command (
codex resume <id>/claude --resume <id>).
- SwiftUI settings surfaces: general settings, MCP servers, skills, API-token references, per-workspace enablement, agent environment templates, shortcuts, and trash.
- The canonical MCP/skills config is independent of any single agent's file format; exporters generate Codex- and Claude Code-compatible output with dry-run, diff, and drift detection.
- WKWebView-based, placeable as a workspace browser tab, a split pane, a separate app window, or delegated to the system browser.
- App-owned link routing and network policy: embedded host allowlist, always-external rules, HTTP host allowlist, and System / HTTP CONNECT / SOCKS / No Proxy modes with a connection self-check.
- Page summaries sent to agent panes carry an explicit untrusted-source boundary and are delivered via bracketed paste — never injected straight into terminal input.
- Lightweight git status in the workspace footer; Git Diff / Git Branches open as workspace tabs without growing the main window.
- Local branch management: checkout and new-branch creation from the current or a selected source branch.
- Closed tabs and a workspace trash retain enough metadata to restore.
- The Rust core exposes a Unix domain socket + JSON-RPC automation interface, plus CLI/runbook/hook workflows.
- A built-in app-control socket (
M2TERMINAL_APP_CONTROL=1) drives authorization-free acceptance flows viascripts/app-control.sh.
flowchart TD
App["Swift / AppKit workspace UI"]
SwiftUI["SwiftUI settings / management"]
TerminalHost["Terminal host views"]
GhosttyVT["libghostty-vt bridge"]
Browser["WKWebView panes / windows"]
CoreClient["Swift CoreClient"]
Core["Rust m2terminal-core"]
SQLite["SQLite metadata"]
Keychain["macOS Keychain"]
PTY["PTY processes"]
Agents["Codex / Claude Code / shell"]
MCP["MCP processes"]
Config["Canonical MCP/skills config"]
Exporters["Codex / Claude Code exporters"]
App --> SwiftUI
App --> TerminalHost
App --> Browser
TerminalHost --> GhosttyVT
App <--> CoreClient
CoreClient <--> Core
Core <--> SQLite
App <--> Keychain
Core <--> PTY
PTY <--> Agents
Core <--> MCP
Core <--> Config
Core --> Exporters
Key boundaries
- Terminal truth source:
libghostty-vtowns the active VT state and the formatted scrollback/search/snapshot text.TerminalOutputBufferandNSTextVieware fallback, selection/accessibility mirrors, and regression models — not the product terminal truth source. - Process / session ownership: the Rust core is the preferred owner of PTY lifecycle and session metadata; Swift local PTY paths remain fallback/test support.
- Secrets boundary: token values live only in Keychain; SQLite and exported config store references or non-secret metadata only. Agent launches pass a typed
TerminalLaunchEnvironment(env map + managed-key removals) straight to the PTY — never anenv KEY=valuecommand prefix.
See
docs/TECHNICAL_ARCHITECTURE.mdfor the target architecture anddocs/CURRENT_ARCHITECTURE.mdfor the current implementation snapshot.
| Layer | Technology | Notes |
|---|---|---|
| Primary language / UI shell | Swift · AppKit | Owns windows, splits, terminal host, keyboard handling, native macOS ergonomics |
| Settings / management UI | SwiftUI | Settings, forms, and management surfaces |
| Terminal engine | libghostty / libghostty-vt | ANSI/VT parsing, terminal state, fonts, reflow, scrollback |
| Terminal rendering | Metal (default) + AppKit fallback | GPU-paints the libghostty-vt TerminalFrame |
| Core orchestration | Rust (m2terminal-core) |
Process supervision, PTY, socket API, state machines, config, agent lifecycle |
| Browser | WKWebView | Native embedding with controllable pane/window placement |
| Secrets | macOS Keychain | Tokens never hit plaintext files |
| Metadata | SQLite | Workspace/session state, MCP/skills registries, logs |
| User config | TOML / JSON | Easy to edit and diff |
| Automation | Unix domain socket + JSON-RPC | Local-first, scriptable, reusable by CLI tools |
- macOS 14 (Sonoma) or newer
- Swift 6 toolchain (Xcode 16+ or an equivalent Swift toolchain)
- Rust toolchain (
cargo, forcrates/m2terminal-core) - libghostty-vt — a linkable library + headers. Build it from Ghostty source with the bundled script (requires Zig 0.15.2), or provide it yourself via
pkg-config(libghostty-vt/libghostty) or theLIBGHOSTTY_INCLUDE_DIR+LIBGHOSTTY_LIBRARY_PATHenvironment variables.
# 1) Clone
git clone https://github.com/MetaMortise/M2Terminal.git
cd M2Terminal
# 2) Build libghostty-vt from Ghostty source (requires Zig 0.15.2)
# Output lands in .build/libghostty-vt by default
scripts/build_libghostty_vt.sh
# 3) Build and sign the app bundle → .build/M2Terminal.app
scripts/build_app.sh
# 4) Run
open .build/M2Terminal.appRun
scripts/check_libghostty.shto verify the libghostty dependency is in place. If you already provide libghostty-vt viapkg-configor env vars, you can skip step 2.
| Path | Owner |
|---|---|
Sources/M2Terminal/ |
macOS app: AppKit workspace shell, SwiftUI management surfaces, terminal host, browser panes, app-control hooks, localization, user workflows |
Sources/M2Terminal/Core/ |
Swift client + models for the Rust core JSON-RPC boundary (split by workspace / config / session / PTY / automation) |
Sources/M2TerminalGhosttyShim/ |
C module boundary that links the Ghostty VT runtime into Swift |
crates/m2terminal-core/ |
Rust core daemon/library: SQLite metadata, workspace/session state, PTY supervision, MCP lifecycle, config import/export, hooks, runbooks, CLI |
Tests/M2TerminalTests/ |
Swift unit/integration coverage: terminal behavior, workspace UX, preferences, localization, app-control |
scripts/ |
Build, packaging, app-control, Ghostty runtime, and benchmark helpers |
docs/ |
Target/current architecture, TODOs, runbooks, hooks, packaging, audits, and problem records |
# Full Swift test suite
swift test
# A single test
swift test --filter TerminalMVPTests
# Rust core tests (when changing Rust code)
cargo test --manifest-path crates/m2terminal-core/Cargo.toml
# Pre-commit sanity check
git diff --checkTwo-gate validation discipline: every requirement passes two gates — (1) scripted validation (unit/integration tests or project scripts), and (2) real-app acceptance (launch the packaged app with M2TERMINAL_APP_CONTROL=1, then drive the user-facing flow via scripts/app-control.sh). See Requirement Completion Validation in AGENTS.md.
AGENTS.md— the source of truth for product direction, architecture, engineering discipline, and workflow.docs/README.md— documentation index.docs/TECHNICAL_ARCHITECTURE.md— target architecture and optimization rules.docs/CURRENT_ARCHITECTURE.md— current implementation snapshot and source ownership.docs/MODULES.md— module map and per-area validation expectations.docs/SCHEMA.md— SQLite table ownership and retention.docs/RUNBOOKS.md,docs/HOOKS.md,docs/PACKAGING_AND_UPDATES.md— automation, hooks, packaging, and updates.docs/problems/— durable records of non-trivial bugs, design conflicts, and their resolutions.
- Phase 1 — Proof of terminal workspace: native window, single terminal pane, PTY lifecycle, launch Codex/Claude Code, basic scrollback and input, basic settings window.
- Phase 2 — Agent workspace: multiple panes, session list, status detection, attention notifications, workspace restore metadata.
- Phase 3 — Browser & context routing: WKWebView pane, user-selectable placement, send browser URL/context to a selected agent.
- Phase 4 — Management Center: MCP/skills registries, Keychain token management, per-workspace enablement, canonical config, Codex/Claude Code exporters, dry-run/diff/drift detection.
- Phase 5 — Automation: local socket API, CLI, hooks, runbooks, scriptable agent/browser/workspace actions.
Current progress and open work: docs/TODO.md.
Released under the MIT License.
M2Terminal depends on Ghostty (also MIT-licensed) via libghostty-vt; building from source fetches Ghostty source, so please observe its license terms as well.