Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ A tab's auto-title is, by default, the pane's live **foreground process name** (

### Control CLI (`macterm`)

A bundled CLI (issue #107) controls the running app over a Unix socket at `<App Support>/control.sock` (per build flavor; `MACTERM_BENCHMARK_DATA_DIR` relocates it with the rest of app data). Built as the `MactermCLI` tool target — `PRODUCT_NAME` is `macterm` but `PRODUCT_MODULE_NAME` must stay `MactermCLI` (a `macterm` module would collide case-insensitively with the app's `Macterm.swiftmodule` in the shared products dir). A post-build script copies it to `Contents/Resources/bin/macterm`; the app prepends that dir to `PATH` and exports `MACTERM_SOCKET` before any shell spawns, so `macterm` works inside every pane.
A bundled CLI (issue #107) controls the running app over a Unix socket at `<App Support>/control.sock` (per build flavor; `MACTERM_BENCHMARK_DATA_DIR` relocates it with the rest of app data). Full grammar + protocol reference in `docs/cli.md`. Built as the `MactermCLI` tool target — `PRODUCT_NAME` is `macterm` but `PRODUCT_MODULE_NAME` must stay `MactermCLI` (a `macterm` module would collide case-insensitively with the app's `Macterm.swiftmodule` in the shared products dir). A post-build script copies it to `Contents/Resources/bin/macterm`; the app prepends that dir to `PATH` and exports `MACTERM_SOCKET` before any shell spawns, so `macterm` works inside every pane. Verbs: `status`; `project list/create/select`; `tab list/new/select/close`; `pane list/split/focus/close/run`; `grid RxC`; `session list/info/kill`; `layout apply/save`. New panes/tabs take `--run CMD` (spawn-time `initial_input`, the layout `run:` path); `pane run` types into an *existing* live shell via `GhosttyTerminalNSView.sendText` (the paste path — `ghostty_surface_key` with `.text`). Every pane's env carries `MACTERM_SESSION` (its restart-stable session name) so a bare `macterm pane split` inside a pane self-targets; close verbs demand explicit targets and return a typed `busy` error instead of staging UI confirmation dialogs (headless callers can't click).

- **Wire protocol** (`ControlProtocol.swift`, compiled into both targets so the codec can't drift): one request per connection; newline-terminated JSON both ways; client half-closes after sending. Requests are `{v, id, command, args}` with `command` namespaced `noun.verb` (`pane.list`); responses echo `id` with `{ok, data}` or `{ok:false, error:{code, message, action?}}` — `action` is a human recovery hint. Errors use snake_case codes (`not_found`, `busy`, `no_surface`, `starting`…).
- **Server** (`ControlSocketServer`): dedicated thread polling a non-blocking listen fd (20ms) — never a blocking `accept()` on a queue shared with `stop()`, which would starve shutdown. Per-connection dispatch hops to `@MainActor`. Starts in `applicationDidFinishLaunching` (skipped under xctest); the handler attaches later in `installResponders` when AppState exists — early requests get a `starting` error. `FD_CLOEXEC` on the listen fd so spawned shells can't hold the socket past quit.
Expand Down
Loading
Loading