diff --git a/README.md b/README.md index b9161b3b..1acd9d9d 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ What it does: - **Agent skill.** An installable skill (Help ▸ Install Agent Skill…) teaches Claude Code or Codex the control model and the `agtermctl` commands, so an agent running inside agterm can build its own layout, run overlays, manage windows, and show images inline without you explaining the API. - **Agent status.** A coding agent reports its state (active, blocked, or completed) onto its session's row, so you can see which of many running agents needs you. Status hooks for Claude Code, Codex, Pi, OpenCode, and other agents install from Help ▸ Install Agent Status Hooks…. +A lot of "does it have X?" questions have the same answer: bind X yourself. A `command` line in `keymap.conf` turns any shell line into a key chord, and an overlay gives an interactive program a real terminal over the session, so a file manager, a git UI, or a database browser is one line away. Bigger workflows become scripts, which is what the [cookbook](cookbook/) collects. + +You are not meant to write those lines by hand. Install the agent skill (Help ▸ Install Agent Skill…) and ask the agent in your session for what you want, and it writes the line with the right syntax, targeting, and PATH handling. [Extend agterm](https://agterm.com/docs#extend) shows that, and teaches enough of the model to read and change what comes back. + For the real terminal work, rendering, VT parsing, and shell I/O, `agterm` embeds [Ghostty](https://ghostty.org)'s engine (libghostty); everything above is `agterm`'s own. ![agterm](docs/screenshots/main.png) @@ -33,7 +37,7 @@ An agent's interactive prompt mid-session, with attention glyphs on the sessions ![Agent prompt](docs/screenshots/agent-prompt.png) -A file manager in a floating overlay over the active session: +The yazi file manager in a floating overlay over the active session, from one `command` line in `keymap.conf`: ![Floating overlay](docs/screenshots/floating-overlay.png) diff --git a/docs/backlog/edit-keymap-menu-hides-the-extension-mechanism.md b/docs/backlog/edit-keymap-menu-hides-the-extension-mechanism.md new file mode 100644 index 00000000..704f39d7 --- /dev/null +++ b/docs/backlog/edit-keymap-menu-hides-the-extension-mechanism.md @@ -0,0 +1,24 @@ +--- +worth: later +where: agterm/agtermApp+Menus.swift +added: 2026-08-20 +--- +# File ▸ Edit Keymap… does not read as "add features" + +The seeded `keymap.conf` is the best explanation of custom commands anywhere in the project. It documents +the `command` directive, the detached-no-TTY limit, the PATH rule, the context tokens, and ships the +`agtermctl session overlay open 'zsh -lc lazygit'` pattern (`agtermCore/Sources/agtermCore/ConfigPaths.swift:39-116`). +It is better than the public docs were before the `#extend` lesson, and a user reaches it only by opening +a menu item named after key configuration. + +Both menu entries name the mechanism rather than what it is for: `File ▸ Edit Keymap…` and +`Navigate ▸ Custom Commands`. A user looking for a file browser has no reason to open either. The docs +side of this is fixed (the lesson, the nav rename, the screenshot captions); the in-app affordance is not. + +Worth considering, none of it obviously right: wording that advertises extension rather than +configuration, a Help entry that points at the lesson, or seeding the palette with one commented example +so `Custom Commands` is not empty on a fresh install. All of it is AppKit/menu work whose cost should be +priced on its own, which is why it was kept out of the docs change. + +Surfaced while brainstorming the onboarding gap after a new user asked whether agterm has a file browser +and the answer turned out to be one keymap line. diff --git a/docs/backlog/seeded-lazygit-example-omits-target.md b/docs/backlog/seeded-lazygit-example-omits-target.md new file mode 100644 index 00000000..286e563e --- /dev/null +++ b/docs/backlog/seeded-lazygit-example-omits-target.md @@ -0,0 +1,29 @@ +--- +worth: later +where: agtermCore/Sources/agtermCore/ConfigPaths.swift:77 +added: 2026-08-20 +--- +# the seeded Lazygit example omits --target and can open its overlay elsewhere + +The starter `keymap.conf` ships: + +``` +command "Lazygit" ctrl+a>g agtermctl session overlay open 'zsh -lc lazygit' --socket "$AGT_SOCKET" +``` + +`TargetOptions.target` defaults to `active` (`agtermCore/Sources/agtermctlKit/Commands.swift:69`), and `active` +is resolved when the request reaches the server, not when the chord built its context. The custom command is +spawned detached and fire-and-forget (`agterm/Commands/CustomCommandRunner.swift:336`), so a session or window +switch between the keypress and delivery opens the overlay over whatever is selected by then. The command +reference already tells automated callers to pin it (`site/commands.html:379`), and a custom command has the +stable `$AGT_SESSION_ID` for exactly this. + +The fix is one flag: `--target "$AGT_SESSION_ID"`. The line is otherwise valid — `overlay open` does not +require a size flag, so omitting `--size-percent` correctly gives a full-size overlay. + +Worth doing because the `#extend` lesson now sends new users to **File ▸ Edit Keymap…**, where they read this +example next to the docs' pinned one and get two different answers. Kept out of the docs change (#PR) because +touching `ConfigPaths.swift` pulls in the full Swift gate run for a one-line seed-text improvement, and no +existing user is blocked by it. + +Found by codex while reviewing the paste lines in the `#extend` lesson. diff --git a/site/docs.html b/site/docs.html index 6dfa1273..dc5a5c3e 100644 --- a/site/docs.html +++ b/site/docs.html @@ -183,6 +183,7 @@ > Overview Install + Extend agterm
CONCEPTS Notifications Accessibility Customization + Custom commands & keys
SCRIPTING agtermctl reference @@ -204,7 +208,6 @@ class="hv9" >Cookbook ↗ - Customizing keys Ghostty config Agent status
@@ -551,6 +554,271 @@ + +
+

+ Extend agterm +

+

+ agterm ships a small set of features on purpose. A great many of the tools you might wish it shipped are one line + in keymap.conf, and the line + stays short because three pieces do the work: a + command turns any shell line + into a named action on a key chord, an overlay gives an interactive program its own temporary terminal over the + session, and any CLI you already have fills that slot. agterm never needs to know what the program is. +

+

+ So “does agterm have a file browser?” has no yes-or-no answer. You bind the file browser you like, in + one line. The four steps below each add one building block. +

+
+ You are not expected to write these by hand. Install the agent skill from + Help ▸ Install Agent Skill… and ask the agent in your session + for what you want; it knows the syntax, the command catalog, and the traps below. + Step 4 shows exactly that. The three steps before it are not the + long way round: they are what let you read the line the agent writes, change it later, and tell a working answer + from a plausible one. +
+

+ To edit the file yourself, open it with + File ▸ Edit Keymap… and apply your changes with + File ▸ Reload Keymap. +

+ +

+ 1. Act on the session you are in +

+
+# open the session's working directory in Finder +command "Open in Finder" ctrl+a>o open "$AGT_SESSION_PWD"
+

+ Read the line left to right: + command is the directive, the + quoted text is the name shown in the + Navigate ▸ Custom Commands palette, + ctrl+a>o is the chord that + fires it — Ctrl-A, then O — and everything after that is an ordinary shell line. The chord is + optional: leave it out and the action exists in the palette only. +

+

+ The building block is session context. Every custom command is handed the + session it fired from — its directory, name, id, and current selection — as + $AGT_* environment variables + and as {AGT_*} tokens + substituted into the line. That is what makes one binding work in every session instead of hard-coding a path. +

+ +

+ 2. Run an interactive program over the session +

+
+# the yazi file manager, floating over the session at 95% +command "Yazi" ctrl+a>y agtermctl session overlay open 'zsh -lc yazi' --size-percent 95 --target "$AGT_SESSION_ID" --socket "$AGT_SOCKET"
+

+ The building block is the overlay. A custom command on its own runs detached + with no terminal, which suits launching a GUI app but kills a TUI at once, since it has nothing to draw on. An + overlay is a real terminal, so yazi, + lazygit, + htop and the rest behave + normally in it. It opens in the session's own working directory, and it disappears when the program exits, leaving + the shell underneath untouched. +

+
+ Note zsh -lc rather than + zsh -c. The program an overlay runs inherits + agterm's own PATH, not your shell's, so a Homebrew binary needs a login shell to be found — or an absolute + path. Skip it and the overlay flashes open and vanishes, because the binary was never found. +
+

+ agtermctl is how the line asks + for that terminal: it is agterm's own command-line tool, and opening an overlay is one thing it can do. Step 3 + opens up the rest. +

+

+ --target "$AGT_SESSION_ID" is + worth understanding rather than copying. + agtermctl does not act on + agterm directly; it sends a request over a socket, and agterm resolves it on arrival. Without a target the request + means “whichever session is selected when this lands”, which is normally the one you pressed the chord + in — but not if you switched in between. The token pins it to the session that fired, so the overlay always + opens where you asked for it. +

+ +

+ 3. Reach the rest of the app +

+
+# a second session in this directory, alongside this one, left in the background +command "Session here" ctrl+a>n agtermctl session new --cwd "$AGT_SESSION_PWD" --workspace "$AGT_WORKSPACE_ID" --window "$AGT_WINDOW_ID" --no-select --socket "$AGT_SOCKET"
+

+ The building block is the control API. Overlays are one entry in a much + larger catalog: the same + agtermctl creates and renames + sessions, splits panes, moves windows, opens the native picker, reads a session's text back, and posts + notifications. Every state it sets it also reads back, which is what lets a script build a whole layout rather than + fire a single action. +

+

+ The extra flags are the same idea as + --target above, applied to + every axis this command can drift on. A bare + session new lands in + whichever window is frontmost and whichever workspace is current when the request arrives, and it selects the new + session; the ids pin all three to where the chord was pressed, and + --no-select leaves you where + you were. Keymap lines never wrap, so this one is long: one directive is always one line. +

+

+ Past a certain size the line becomes a script, and that is where the + cookbook starts: pick a + project from anywhere and open it in its own workspace, park a set of workspaces and bring them back later, or have + each tab resume its own agent conversation after a restart. Those are not one-liners, and the recipes do not + pretend otherwise. +

+ +

+ 4. Have the agent write it +

+

+ This is the normal way to add things, not a shortcut for people who dislike config files. Install the agent skill + from Help ▸ Install Agent Skill…, then ask the agent running + in one of your sessions: +

+
+ add a custom command that opens yazi in a 95% overlay, bound to ctrl+a then y +
+

+ The skill carries the keymap syntax, the full command catalog, the context tokens, and the PATH rule, so the agent + writes a working line instead of guessing at one. The skill is what makes the difference: an agent without it will + still answer, plausibly and wrongly. It is shown last rather than first because the three steps above are what let + you tell those two apart, and what let you change the line afterwards without asking again. +

+ +

+ From here, Custom commands & keys has the full keymap syntax and + the bindable actions, the command reference lists every + agtermctl command with its + arguments, and the cookbook + collects worked recipes to copy and edit. +

+

- -
+ +

- agtermctl reference + Custom commands & keys

+

+ agterm reads a user-editable, kitty-flavored keymap file at + ~/.config/agterm/keymap.conf. It rebinds built-in menu shortcuts and defines custom shell commands bound to keys (and listed in the action + palette). The file is optional — a commented starter is written on first launch, and the directory can be changed in + Settings ▸ Key Mapping. Three verbs; blank lines and + # comments are ignored. +

-

- Looking for the full list? Every command, with its arguments, return values, and errors, lives on the - Command reference → -

+ # rebind a built-in to a chord (mods joined by +)
map + cmd+shift+l   toggle_split
map + ctrl+shift+k  command_palette

# or to several alternatives, joined by | with no spaces around it
map + cmd+t|ctrl+a>t  toggle_scratch

# define custom commands ("name" shows in the palette; chord optional)
command + "Open in Zed"  cmd+shift+e  open -a Zed {AGT_SESSION_PWD}
command + "Lazygit"      ctrl+a>g     agtermctl session overlay open 'zsh -lc lazygit' + --socket {AGT_SOCKET}
command + "Deploy"               ./deploy.sh

# one system-wide chord that summons the quick terminal from any application
global-hotkey ctrl+opt+space
-

- agterm can be driven from a script over a local unix-domain socket through the companion CLI, - agtermctl. This is for - fire-and-forget scripting that manages workspaces and sessions, injects text, invokes control actions, and polls a - control-event feed for status and lifecycle changes. There is no terminal-output streaming. +

+ A chord is modifier words (ctrl, cmd, + opt, + shift) joined by + + and a base key (a single + character or tab/space/return/delete/left/right/up/down). A Shift-typed symbol is written + shift+<base> + (e.g. shift+/ for + ?, + shift+= for + +) — the base key, not the + shifted symbol. Custom commands may also use a leader sequence + (ctrl+a>g), and their chord + must include a modifier — a bare key can't shadow a plain terminal key.

-

- To open a terminal at a directory without the CLI, - open -a agterm <path> — or - right-click a folder in Finder and choose Open With ▸ agterm. agterm adds a - session in that directory to the last-active window. This works when agterm is already running (its usual state); if it - isn't, launch agterm first, then run the command. The socket equivalent, and the way to place the session precisely, is - agtermctl session new --cwd <path>. +

+ global-hotkey takes exactly one + chord and binds it system-wide: it summons the quick terminal while any + application is frontmost, which no other binding here can do — the rest only fire while agterm has the keyboard. It + must carry a modifier, takes no alternatives and no leader sequence, and a second + global-hotkey line replaces the + first. macOS registers it by physical key position, so it keeps firing on a non-Latin layout. Because the system owns + it rather than agterm, it takes no part in the collision rules below. Note which side wins: the system-wide chord + takes the key even while agterm is in front, so binding one a menu item already uses means that menu shortcut stops + firing. It is unset unless you add the line: agterm ships no default because registering a chord takes it + from every other application on the Mac, and that is not a cost to impose on someone who may never summon + the panel from outside agterm. Spending it on the chord the in-app binding already uses is allowed and is + the way to get one shortcut that works everywhere — + global-hotkey ctrl+` + makes ⌃` summon the panel + from any application, at the price of that chord no longer reaching anything else.

-

- Each command targets a session or workspace by its UUID, a unique prefix of that UUID (git-style), or the keyword - active (the selected session / - current workspace). --target - defaults to active, so the - current one rarely needs naming. --target/--workspace take an id, a - prefix, or active — never a - name. For workspaces, - active is the one a new session - lands in: a workspace you just created in the foreground, otherwise the selected session's, otherwise the last one. - A newly created workspace stays the target until the selection changes — to a different session or to none — - or until you delete it or hide it behind the workspace filter. - workspace select moves the - target to the workspace you name, an empty one included. So New Session (and - session new) right after - creating one lands in the new workspace rather than the one you came from. Mutating commands normally print the - affected id; batch - session close and - session move accept repeated - --target options and print the - number of sessions actually changed. Add - --json for the raw response, - or --socket PATH to override - the socket. The exit code is zero on success, non-zero on error. +

+ One binding can offer several alternatives, joined by + | inside a single token with no + spaces around it — + map cmd+t|ctrl+a>t toggle_scratch + fires the action from either, and a command + takes alternatives the same way. For a built-in, the first single-chord alternative the menu can carry becomes the menu + shortcut; every + other alternative, on either verb, is delivered by a key monitor and so must carry a modifier on its first chord. This + is also how a built-in gets a leader sequence, because a menu item holds exactly one key equivalent: + map ctrl+a>s toggle_split binds + the sequence and leaves the action with no menu shortcut at all — its shipped ⌘D is gone rather than kept, and free for + another action to claim. A typo in one alternative rejects the whole line, so a mistake can't hide behind a line that + half worked; an alternative that merely breaks a rule or collides with an existing binding drops on its own and its + siblings keep firing. If a line ends up binding nothing at all, the action simply keeps the shortcut it shipped with. +

+

+ Chords are written in Latin and keep working on a non-Latin keyboard layout. A layout that cannot type ASCII — + Russian, Greek, Hebrew, Arabic, Thai — resolves every chord by the physical key position, so + cmd+o still fires on the key + marked O even though it types щ. + A layout that can type ASCII binds what it types, so an alternative Latin layout keeps its own letter positions: on + Dvorak, cmd+o follows the O you + actually type.

- Native picker + Bindable built-in actions

-

- agtermctl pick reads nonblank - lines or a JSON array of - {id,label,subtitle?} objects - from stdin and opens agterm's fuzzy picker. It blocks by default and prints the picked, custom, or cancelled result as - bare JSON. Add --no-block to - get the picker id immediately, then use - pick result or - pick cancel. The target tree's - top-level pickPending field - carries that id while the picker waits. -

-

- Typing matches item labels only; a subtitle is shown but never searched, so consequence text on one row cannot filter - out its safer neighbour. An empty query lists the items in the order they were supplied, so the caller's first item is - the one Return runs on open. - --query TEXT prefills the field - and filters immediately, which ranks by match score and therefore does not preserve that order. With - --allow-custom the item list may - be empty, which turns the picker into a plain text prompt: the custom row appears as soon as the query is nonblank, - whether prefilled by --query or - typed. An itemless call still reads stdin, so redirect it - (< /dev/null) or it blocks. -

- printf '%s\n' staging production | agtermctl pick --prompt - "Deploy where?"

- agtermctl pick --allow-custom --query "$name" --prompt - "Rename to" < /dev/null

- id=$(printf '%s\n' alpha beta | - agtermctl pick --no-block | jq -r '.id')
- agtermctl pick result "$id"
- agtermctl pick cancel "$id"
- agtermctl tree --json | jq -r - '.result.tree.pickPending // empty' + new_window   rename_window   delete_window
new_workspace   rename_workspace   delete_workspace
new_session   open_directory   rename_session   duplicate_session
close_session   reopen_recent   undo_close   clear_status
increase_font_size   decrease_font_size   reset_font_size
toggle_split   toggle_horizontal_split   toggle_scratch   toggle_search
toggle_sidebar   toggle_flag   toggle_flagged_view
focus_left_pane   focus_right_pane   focus_workspace   toggle_workspace_filter
toggle_workspace_collapse
previous_session   next_session   first_session   last_session
previous_workspace   next_workspace
previous_attention_session   next_attention_session
quick_terminal   session_palette   command_palette
custom_command_palette   show_attention
select_theme   toggle_fullscreen   toggle_terminal_zoom
dashboard
+

+ toggle_fullscreen is the one + action with no menu item of its own. macOS adds an "Enter Full Screen" item to the View menu whenever that menu is + drawn, and nothing suppresses it, so an item of agterm's own would sit beside it as a duplicate. The binding (⌃⌘F by + default) is handled directly instead, which is why the menu entry advertises the system's Globe+F rather than your + chord. Both work, and rebinding + toggle_fullscreen changes the + chord as usual — it just won't show up next to that menu item. +

- Sessions & workspaces + Command tokens

- # print the workspace/session tree with ids
agtermctl tree

# create a workspace, capture its id, open a session in it
ws=$(agtermctl workspace new work)
agtermctl session new --workspace - "$ws" --cwd ~/src/agterm

# run a command as the session's process (argv-style; wrap in sh -c for shell syntax)
agtermctl session new --command "ssh user@host"
agtermctl session new --command - "zsh -lc 'make test'" --wait -   # hold open after the command exits (press any key to close); needs --command
agtermctl session new - --name myhost --workspace-name - servers --create-workspace

# step / reorder / relocate
agtermctl session go --to next -    # next|prev|first|last
agtermctl session move --to up -    # reorder: up|down|top|bottom
agtermctl session move - "$ws"     # relocate to a workspace
agtermctl session new --after active -   # create right after the current session (--before to precede)
agtermctl session new --cwd ~/src/agterm --no-select -   # create in the background without switching to it
agtermctl session duplicate --target 9f3c -   # a fresh shell in that session's workspace and cwd, right after it
agtermctl session move --after - 9f3c   # place after an anchor (its workspace is used; relocates cross-workspace)
agtermctl session move "$ws" --target 9f3c --target abcd -   # move a batch as one ordered block
agtermctl session close --target 9f3c --target abcd -   # one grace-period undo for the batch
agtermctl workspace go --to next -    # step to the next workspace and select its first session
agtermctl workspace move --to top -    # reorder a workspace
agtermctl workspace new work --collapsed -    # create a workspace closed in the sidebar (fill with session new --no-select)
agtermctl workspace collapse --target 9f3c -  # collapse one workspace; workspace expand re-opens it
agtermctl workspace focus on -    # mark this workspace alone and apply the filter (on|off|toggle|add) -
agtermctl workspace focus add --target a1b2 -   # mark another one without narrowing the tree yet
agtermctl workspace filter on -      # apply the marked set; filter off suspends it without losing the set -
agtermctl session reveal --target 9f3c -  # reveal the focused pane's cwd in Finder + {AGT_SESSION_ID}   {AGT_SESSION_NAME}   {AGT_SESSION_PWD}
{AGT_WORKSPACE_ID}   {AGT_WORKSPACE_NAME}
{AGT_WINDOW_ID}   {AGT_WINDOW_NAME}
{AGT_PANE}   {AGT_SELECTION}   {AGT_SOCKET}
- -

- Typing, selection & text -

+

+ Tokens expand at fire time (also exported as + $AGT_* env vars on the spawned + process). A token is substituted raw into the shell line, so for content you + don't control — + {AGT_SELECTION}, and also + {AGT_SESSION_NAME}/{AGT_SESSION_PWD} + (a remote host can set these via OSC) — prefer the matching quoted env var, e.g. + "$AGT_SELECTION". +

- # inject text (every newline is a real Return; use $'…\n' or --stdin)
agtermctl session type --target 9f3c $'make test\n'
echo - 'make test' | agtermctl session type --stdin

# route to a pane: left (default) | right (split) | scratch (even when hidden)
agtermctl session type --pane right $'ls\n'

# type into a background session without changing focus
agtermctl session type --target "$id" - $'echo hi\n'

# read a pane's selection (does not touch the clipboard) or its output
sel=$(agtermctl session copy --target 9f3c)
agtermctl session text - --pane scratch --target 9f3c

# paste the system clipboard (⌘V), or select the whole buffer (⌘A) then copy it
agtermctl session paste --target 9f3c
agtermctl session select-all --target 9f3c + Detached, no TTY — a + custom command runs as a detached + /bin/sh -c with no controlling terminal, so + it suits fire-and-forget launches (GUI apps, scripts) — not interactive TUIs. Run a TUI like + lazygit in an overlay + (agtermctl session overlay open) or a scratch + terminal, which have a real TTY. A non-zero exit posts a notification banner.
- -

- Splits, scratch, quick & search -

+ GUI PATH — a custom + command resolves its binaries against the app's GUI + PATH: the launchd default plus the bundled + agtermctl, + /usr/local/bin and + /opt/homebrew/bin. A bare + agtermctl or Homebrew binary works; + anything else your shell profile adds does not, and fails with exit 127. Give it an absolute path + or wrap the line in + zsh -lc '…' — + zsh -ilc '…' when that + PATH comes from + ~/.zshrc, which + -lc does not read. The program an + overlay or scratch terminal runs gets the app's own unwidened + PATH and always needs one of those. +
+

+ Open the file with File ▸ Edit Keymap… (or the ⌃⇧P palette): it opens in a + 95% overlay running $VISUAL/$EDITOR + (falling back to vi) and + reloads on quit. Apply edits made elsewhere with + File ▸ Reload Keymap or + agtermctl keymap reload. A + malformed line never discards the rest — it surfaces in the diagnostics list in Settings ▸ Key Mapping while the good + lines still apply. +

+

+ To check what is actually bound, + agtermctl keymap list prints every + built-in with the binds it resolved to (the menu shortcut first, then any alternatives, joined with + |), the custom commands, + each diagnostic in full, and the key equivalents the menu + bar is really carrying. If a binding will not fire, compare the last two: an action whose chord no menu item holds is + usually a menu problem, not a keymap one. Only the menu shortcut can appear there — an alternative never does, and + neither do + undo_close (⌘Z) and + toggle_fullscreen (⌃⌘F), which a + key monitor delivers rather than a menu item. +

+

+ v1 limitations +

+ +
+ + +
+

+ agtermctl reference +

+
+

+ Looking for the full list? Every command, with its arguments, return values, and errors, lives on the + Command reference → +

+
+

+ agterm can be driven from a script over a local unix-domain socket through the companion CLI, + agtermctl. This is for + fire-and-forget scripting that manages workspaces and sessions, injects text, invokes control actions, and polls a + control-event feed for status and lifecycle changes. There is no terminal-output streaming. +

+

+ To open a terminal at a directory without the CLI, + open -a agterm <path> — or + right-click a folder in Finder and choose Open With ▸ agterm. agterm adds a + session in that directory to the last-active window. This works when agterm is already running (its usual state); if it + isn't, launch agterm first, then run the command. The socket equivalent, and the way to place the session precisely, is + agtermctl session new --cwd <path>. +

+

+ Each command targets a session or workspace by its UUID, a unique prefix of that UUID (git-style), or the keyword + active (the selected session / + current workspace). --target + defaults to active, so the + current one rarely needs naming. --target/--workspace take an id, a + prefix, or active — never a + name. For workspaces, + active is the one a new session + lands in: a workspace you just created in the foreground, otherwise the selected session's, otherwise the last one. + A newly created workspace stays the target until the selection changes — to a different session or to none — + or until you delete it or hide it behind the workspace filter. + workspace select moves the + target to the workspace you name, an empty one included. So New Session (and + session new) right after + creating one lands in the new workspace rather than the one you came from. Mutating commands normally print the + affected id; batch + session close and + session move accept repeated + --target options and print the + number of sessions actually changed. Add + --json for the raw response, + or --socket PATH to override + the socket. The exit code is zero on success, non-zero on error. +

+ +

+ Native picker +

+

+ agtermctl pick reads nonblank + lines or a JSON array of + {id,label,subtitle?} objects + from stdin and opens agterm's fuzzy picker. It blocks by default and prints the picked, custom, or cancelled result as + bare JSON. Add --no-block to + get the picker id immediately, then use + pick result or + pick cancel. The target tree's + top-level pickPending field + carries that id while the picker waits. +

+

+ Typing matches item labels only; a subtitle is shown but never searched, so consequence text on one row cannot filter + out its safer neighbour. An empty query lists the items in the order they were supplied, so the caller's first item is + the one Return runs on open. + --query TEXT prefills the field + and filters immediately, which ranks by match score and therefore does not preserve that order. With + --allow-custom the item list may + be empty, which turns the picker into a plain text prompt: the custom row appears as soon as the query is nonblank, + whether prefilled by --query or + typed. An itemless call still reads stdin, so redirect it + (< /dev/null) or it blocks. +

+
- agtermctl session split toggle --axis horizontal
agtermctl session split close -      # vertical=left/right; horizontal=top/bottom
agtermctl session resize --split-ratio 0.7 -  # primary fraction; grow role/position aliases also work
agtermctl session scratch toggle -     # on|off|toggle
agtermctl session flag on -        # on|off|toggle|clear
agtermctl session seen --target 9f3c -  # clear the unseen badge, focus-free
agtermctl - sidebar mode flagged    # tree|flagged|toggle
agtermctl quick - toggle        # show|hide|toggle
agtermctl quick type 'ls -la' -  # or --stdin; quick text reads it back
agtermctl surface zoom        # fill the window with the active surface (show|hide|toggle; --target surface:<id>:right)
agtermctl dashboard "$a" "$b" "$c" --auto-size  # view-only grid; a split session is two cells, capped at 9 panes (--mru; --font-size N | --auto-size; --close)
agtermctl font inc           # main pane font size
agtermctl font dec --pane right  # just the split pane (left|right|scratch)

# open the search bar, print the "N of M" counter
agtermctl - session search "error"
agtermctl session search --next -     # --prev | --close + printf '%s\n' staging production | agtermctl pick --prompt + "Deploy where?"

+ agtermctl pick --allow-custom --query "$name" --prompt + "Rename to" < /dev/null

+ id=$(printf '%s\n' alpha beta | + agtermctl pick --no-block | jq -r '.id')
+ agtermctl pick result "$id"
+ agtermctl pick cancel "$id"
+ agtermctl tree --json | jq -r + '.result.tree.pickPending // empty'

- Overlays + Sessions & workspaces

- # full overlay on a session (hides the shell beneath)
agtermctl session - overlay open "revdiff HEAD~3" --target 9f3c

# print the workspace/session tree with ids
agtermctl tree

# floating framed panel at 70% of the pane, optionally tinted
agtermctl session overlay open "htop" --size-percent 70
agtermctl - session overlay open "revdiff HEAD~3" --size-percent 80 --background-color - "#2a1a3a"

# create a workspace, capture its id, open a session in it
ws=$(agtermctl workspace new work)
agtermctl session new --workspace + "$ws" --cwd ~/src/agterm

# run a command as the session's process (argv-style; wrap in sh -c for shell syntax)
agtermctl session new --command "ssh user@host"
agtermctl session new --command + "zsh -lc 'make test'" --wait +   # hold open after the command exits (press any key to close); needs --command
agtermctl session new + --name myhost --workspace-name + servers --create-workspace

# step / reorder / relocate
agtermctl session go --to next +    # next|prev|first|last
agtermctl session move --to up +    # reorder: up|down|top|bottom
agtermctl session move + "$ws"     # relocate to a workspace
agtermctl session new --after active +   # create right after the current session (--before to precede)
agtermctl session new --cwd ~/src/agterm --no-select +   # create in the background without switching to it
agtermctl session duplicate --target 9f3c +   # a fresh shell in that session's workspace and cwd, right after it
agtermctl session move --after + 9f3c   # place after an anchor (its workspace is used; relocates cross-workspace)
agtermctl session move "$ws" --target 9f3c --target abcd +   # move a batch as one ordered block
agtermctl session close --target 9f3c --target abcd +   # one grace-period undo for the batch
agtermctl workspace go --to next +    # step to the next workspace and select its first session
agtermctl workspace move --to top +    # reorder a workspace
agtermctl workspace new work --collapsed +    # create a workspace closed in the sidebar (fill with session new --no-select)
agtermctl workspace collapse --target 9f3c +  # collapse one workspace; workspace expand re-opens it
agtermctl workspace focus on +    # mark this workspace alone and apply the filter (on|off|toggle|add) +
agtermctl workspace focus add --target a1b2 +   # mark another one without narrowing the tree yet
agtermctl workspace filter on +      # apply the marked set; filter off suspends it without losing the set +
agtermctl session reveal --target 9f3c +  # reveal the focused pane's cwd in Finder +
+ +

+ Typing, selection & text +

+
+ # inject text (every newline is a real Return; use $'…\n' or --stdin)
agtermctl session type --target 9f3c $'make test\n'
echo + 'make test' | agtermctl session type --stdin

# route to a pane: left (default) | right (split) | scratch (even when hidden)
agtermctl session type --pane right $'ls\n'

# type into a background session without changing focus
agtermctl session type --target "$id" + $'echo hi\n'

# read a pane's selection (does not touch the clipboard) or its output
sel=$(agtermctl session copy --target 9f3c)
agtermctl session text + --pane scratch --target 9f3c

# paste the system clipboard (⌘V), or select the whole buffer (⌘A) then copy it
agtermctl session paste --target 9f3c
agtermctl session select-all --target 9f3c +
+ +

+ Splits, scratch, quick & search +

+
+ agtermctl session split toggle --axis horizontal
agtermctl session split close +      # vertical=left/right; horizontal=top/bottom
agtermctl session resize --split-ratio 0.7 +  # primary fraction; grow role/position aliases also work
agtermctl session scratch toggle +     # on|off|toggle
agtermctl session flag on +        # on|off|toggle|clear
agtermctl session seen --target 9f3c +  # clear the unseen badge, focus-free
agtermctl + sidebar mode flagged    # tree|flagged|toggle
agtermctl quick + toggle        # show|hide|toggle
agtermctl quick type 'ls -la' +  # or --stdin; quick text reads it back
agtermctl surface zoom        # fill the window with the active surface (show|hide|toggle; --target surface:<id>:right)
agtermctl dashboard "$a" "$b" "$c" --auto-size  # view-only grid; a split session is two cells, capped at 9 panes (--mru; --font-size N | --auto-size; --close)
agtermctl font inc           # main pane font size
agtermctl font dec --pane right  # just the split pane (left|right|scratch)

# open the search bar, print the "N of M" counter
agtermctl + session search "error"
agtermctl session search --next +     # --prev | --close +
+ +

+ Overlays +

+
+ # full overlay on a session (hides the shell beneath)
agtermctl session + overlay open "revdiff HEAD~3" --target 9f3c

# floating framed panel at 70% of the pane, optionally tinted
agtermctl session overlay open "htop" --size-percent 70
agtermctl + session overlay open "revdiff HEAD~3" --size-percent 80 --background-color + "#2a1a3a"

# switch the user to the target as the overlay opens
agtermctl session overlay open "revdiff HEAD~3" --size-percent 80 --target 9f3c --follow

--size-percent overrides the width only, bounded to at most 80% of the pane, so a message never covers the session it is about. - --text-color sets the text's - own color and - --background-color the panel's - backing, so the two halves are independent. - An update replaces the whole message, so - --detail, - --spinner and - --text-color have to be repeated - to survive it. The spinner takes a style — - --spinner-style bar|braille|circle|blocks|dot|none, - which turns it on by itself, with - dot blinking rather than - animating — and an update may switch style in place; - --background-color is the - exception, read once when the panel is created and kept — in the panel and in the read-back — across every - update, which is why an update does not take it at all. -

-

- It shares the overlay slot, so a second - hud replaces the first, - session overlay open replaces a - HUD, and ⌘W or - session overlay close takes one - down; a HUD over a running program is refused instead, because a message is replaceable and a program is - not. - session overlay result over a - HUD errors, and - session overlay resize accepts a - percent but refuses --full, - which would cover the session the message is about. - agtermctl tree --json reports - the panel as the session node's - hud object, with - overlay reading false beside it; - there is no event, so a script that needs the state polls the tree. -

- -

- Windows -

-
- agtermctl window list     # id name [open] [active]
w=$(agtermctl window new work)
agtermctl window new proj-b - --minimized  # create one, parked in the Dock
agtermctl window select - "$w"  # raise it (opening if closed)
agtermctl window rename "$w" personal -
agtermctl window minimize "$w" on -  # park it in the Dock (off restores)
agtermctl window close - "$w"   # close its window (bundle kept)
agtermctl window delete "$w" -  # delete (last window can't be deleted)

# --window targets a specific window's tree on session/workspace/tree/font
agtermctl tree --window "$w"
agtermctl session new --window - "$w" --cwd ~/src/agterm -
- -

- Keymap, config & notify -

-
- agtermctl keymap reload -  # re-read keymap.conf, returns the diagnostic count
agtermctl config - reload  # re-read the ghostty config, returns the diagnostic count
agtermctl notify --title Build --body - "tests passed" -
- -
- In-session env — a shell - inside agterm gets AGTERM_ENABLED, - AGTERM_WINDOW_ID, - AGTERM_WORKSPACE_ID, - AGTERM_SESSION_ID, - AGTERM_SOCKET (the live socket path), and - AGTERM_PANE (which pane the shell runs in — - left (main), - right (split), or - scratch; unset in an overlay), and - AGTERM_PANE_ID (a stable per-surface token the - agent-status hook forwards as session status --pane-id, - so a promoted-then-re-split pane still tags correctly), so a - script can drive its own window without hard-coding ids. -
- -

- The cookbook collects - complete agtermctl - workflows, each in its own directory with a README and, where it needs one, its scripts: switching the sidebar to a - single project, closing a project's workspaces and bringing them back later, picking a path with - fzf and typing it into the - shell, and giving each tab its own Claude Code or Codex conversation across a restart. They are written to be copied - into your own setup and edited, not only read; - cookbook/CONTRIBUTING.md - has the rules for adding one. -

-

- Recipes come from other people as well as the maintainer. Every one is reviewed before it is accepted, but they are - shell scripts you run on your own machine against your own sessions, and several close sessions or delete - workspaces, so read a recipe before you run it. -

-
- - -
-

- Customizing keys -

-

- agterm reads a user-editable, kitty-flavored keymap file at - ~/.config/agterm/keymap.conf. It rebinds built-in menu shortcuts and defines custom shell commands bound to keys (and listed in the action - palette). The file is optional — a commented starter is written on first launch, and the directory can be changed in - Settings ▸ Key Mapping. Three verbs; blank lines and - # comments are ignored. -

-
- # rebind a built-in to a chord (mods joined by +)
map - cmd+shift+l   toggle_split
map - ctrl+shift+k  command_palette

# or to several alternatives, joined by | with no spaces around it
map - cmd+t|ctrl+a>t  toggle_scratch

# define custom commands ("name" shows in the palette; chord optional)
command - "Open in Zed"  cmd+shift+e  open -a Zed {AGT_SESSION_PWD}
command - "Lazygit"      ctrl+a>g     agtermctl session overlay open 'zsh -lc lazygit' - --socket {AGT_SOCKET}
command - "Deploy"               ./deploy.sh

# one system-wide chord that summons the quick terminal from any application
global-hotkey ctrl+opt+space -
-

- A chord is modifier words (ctrl, cmd, - opt, - shift) joined by - + and a base key (a single - character or tab/space/return/delete/left/right/up/down). A Shift-typed symbol is written - shift+<base> - (e.g. shift+/ for - ?, - shift+= for - +) — the base key, not the - shifted symbol. Custom commands may also use a leader sequence - (ctrl+a>g), and their chord - must include a modifier — a bare key can't shadow a plain terminal key. -

-

- global-hotkey takes exactly one - chord and binds it system-wide: it summons the quick terminal while any - application is frontmost, which no other binding here can do — the rest only fire while agterm has the keyboard. It - must carry a modifier, takes no alternatives and no leader sequence, and a second - global-hotkey line replaces the - first. macOS registers it by physical key position, so it keeps firing on a non-Latin layout. Because the system owns - it rather than agterm, it takes no part in the collision rules below. Note which side wins: the system-wide chord - takes the key even while agterm is in front, so binding one a menu item already uses means that menu shortcut stops - firing. It is unset unless you add the line: agterm ships no default because registering a chord takes it - from every other application on the Mac, and that is not a cost to impose on someone who may never summon - the panel from outside agterm. Spending it on the chord the in-app binding already uses is allowed and is - the way to get one shortcut that works everywhere — - global-hotkey ctrl+` - makes ⌃` summon the panel - from any application, at the price of that chord no longer reaching anything else. -

-

- One binding can offer several alternatives, joined by - | inside a single token with no - spaces around it — - map cmd+t|ctrl+a>t toggle_scratch - fires the action from either, and a command - takes alternatives the same way. For a built-in, the first single-chord alternative the menu can carry becomes the menu - shortcut; every - other alternative, on either verb, is delivered by a key monitor and so must carry a modifier on its first chord. This - is also how a built-in gets a leader sequence, because a menu item holds exactly one key equivalent: - map ctrl+a>s toggle_split binds - the sequence and leaves the action with no menu shortcut at all — its shipped ⌘D is gone rather than kept, and free for - another action to claim. A typo in one alternative rejects the whole line, so a mistake can't hide behind a line that - half worked; an alternative that merely breaks a rule or collides with an existing binding drops on its own and its - siblings keep firing. If a line ends up binding nothing at all, the action simply keeps the shortcut it shipped with. + --text-color sets the text's + own color and + --background-color the panel's + backing, so the two halves are independent. + An update replaces the whole message, so + --detail, + --spinner and + --text-color have to be repeated + to survive it. The spinner takes a style — + --spinner-style bar|braille|circle|blocks|dot|none, + which turns it on by itself, with + dot blinking rather than + animating — and an update may switch style in place; + --background-color is the + exception, read once when the panel is created and kept — in the panel and in the read-back — across every + update, which is why an update does not take it at all.

- Chords are written in Latin and keep working on a non-Latin keyboard layout. A layout that cannot type ASCII — - Russian, Greek, Hebrew, Arabic, Thai — resolves every chord by the physical key position, so - cmd+o still fires on the key - marked O even though it types щ. - A layout that can type ASCII binds what it types, so an alternative Latin layout keeps its own letter positions: on - Dvorak, cmd+o follows the O you - actually type. + It shares the overlay slot, so a second + hud replaces the first, + session overlay open replaces a + HUD, and ⌘W or + session overlay close takes one + down; a HUD over a running program is refused instead, because a message is replaceable and a program is + not. + session overlay result over a + HUD errors, and + session overlay resize accepts a + percent but refuses --full, + which would cover the session the message is about. + agtermctl tree --json reports + the panel as the session node's + hud object, with + overlay reading false beside it; + there is no event, so a script that needs the state polls the tree.

- Bindable built-in actions + Windows

- new_window   rename_window   delete_window
new_workspace   rename_workspace   delete_workspace
new_session   open_directory   rename_session   duplicate_session
close_session   reopen_recent   undo_close   clear_status
increase_font_size   decrease_font_size   reset_font_size
toggle_split   toggle_horizontal_split   toggle_scratch   toggle_search
toggle_sidebar   toggle_flag   toggle_flagged_view
focus_left_pane   focus_right_pane   focus_workspace   toggle_workspace_filter
toggle_workspace_collapse
previous_session   next_session   first_session   last_session
previous_workspace   next_workspace
previous_attention_session   next_attention_session
quick_terminal   session_palette   command_palette
custom_command_palette   show_attention
select_theme   toggle_fullscreen   toggle_terminal_zoom
dashboard + agtermctl window list     # id name [open] [active]
w=$(agtermctl window new work)
agtermctl window new proj-b + --minimized  # create one, parked in the Dock
agtermctl window select + "$w"  # raise it (opening if closed)
agtermctl window rename "$w" personal +
agtermctl window minimize "$w" on +  # park it in the Dock (off restores)
agtermctl window close + "$w"   # close its window (bundle kept)
agtermctl window delete "$w" +  # delete (last window can't be deleted)

# --window targets a specific window's tree on session/workspace/tree/font
agtermctl tree --window "$w"
agtermctl session new --window + "$w" --cwd ~/src/agterm
-

- toggle_fullscreen is the one - action with no menu item of its own. macOS adds an "Enter Full Screen" item to the View menu whenever that menu is - drawn, and nothing suppresses it, so an item of agterm's own would sit beside it as a duplicate. The binding (⌃⌘F by - default) is handled directly instead, which is why the menu entry advertises the system's Globe+F rather than your - chord. Both work, and rebinding - toggle_fullscreen changes the - chord as usual — it just won't show up next to that menu item. -

- Command tokens + Keymap, config & notify

- {AGT_SESSION_ID}   {AGT_SESSION_NAME}   {AGT_SESSION_PWD}
{AGT_WORKSPACE_ID}   {AGT_WORKSPACE_NAME}
{AGT_WINDOW_ID}   {AGT_WINDOW_NAME}
{AGT_PANE}   {AGT_SELECTION}   {AGT_SOCKET} -
-

- Tokens expand at fire time (also exported as - $AGT_* env vars on the spawned - process). A token is substituted raw into the shell line, so for content you - don't control — - {AGT_SELECTION}, and also - {AGT_SESSION_NAME}/{AGT_SESSION_PWD} - (a remote host can set these via OSC) — prefer the matching quoted env var, e.g. - "$AGT_SELECTION". -

-
- Detached, no TTY — a - custom command runs as a detached - /bin/sh -c with no controlling terminal, so - it suits fire-and-forget launches (GUI apps, scripts) — not interactive TUIs. Run a TUI like - lazygit in an overlay - (agtermctl session overlay open) or a scratch - terminal, which have a real TTY. A non-zero exit posts a notification banner. + agtermctl keymap reload +  # re-read keymap.conf, returns the diagnostic count
agtermctl config + reload  # re-read the ghostty config, returns the diagnostic count
agtermctl notify --title Build --body + "tests passed"
+
- GUI PATH — a custom - command resolves its binaries against the app's GUI - PATH: the launchd default plus the bundled - agtermctl, - /usr/local/bin and - /opt/homebrew/bin. A bare - agtermctl or Homebrew binary works; - anything else your shell profile adds does not, and fails with exit 127. Give it an absolute path - or wrap the line in - zsh -lc '…' — - zsh -ilc '…' when that - PATH comes from - ~/.zshrc, which - -lc does not read. The program an - overlay or scratch terminal runs gets the app's own unwidened - PATH and always needs one of those. + In-session env — a shell + inside agterm gets AGTERM_ENABLED, + AGTERM_WINDOW_ID, + AGTERM_WORKSPACE_ID, + AGTERM_SESSION_ID, + AGTERM_SOCKET (the live socket path), and + AGTERM_PANE (which pane the shell runs in — + left (main), + right (split), or + scratch; unset in an overlay), and + AGTERM_PANE_ID (a stable per-surface token the + agent-status hook forwards as session status --pane-id, + so a promoted-then-re-split pane still tags correctly), so a + script can drive its own window without hard-coding ids.
-

- Open the file with File ▸ Edit Keymap… (or the ⌃⇧P palette): it opens in a - 95% overlay running $VISUAL/$EDITOR + The cookbook collects + complete agtermctl + workflows, each in its own directory with a README and, where it needs one, its scripts: switching the sidebar to a + single project, closing a project's workspaces and bringing them back later, picking a path with + fzf and typing it into the + shell, and giving each tab its own Claude Code or Codex conversation across a restart. They are written to be copied + into your own setup and edited, not only read; + cookbook/CONTRIBUTING.md - (falling back to vi) and - reloads on quit. Apply edits made elsewhere with - File ▸ Reload Keymap or - agtermctl keymap reload. A - malformed line never discards the rest — it surfaces in the diagnostics list in Settings ▸ Key Mapping while the good - lines still apply. + has the rules for adding one.

-

- To check what is actually bound, - agtermctl keymap list prints every - built-in with the binds it resolved to (the menu shortcut first, then any alternatives, joined with - |), the custom commands, - each diagnostic in full, and the key equivalents the menu - bar is really carrying. If a binding will not fire, compare the last two: an action whose chord no menu item holds is - usually a menu problem, not a keymap one. Only the menu shortcut can appear there — an alternative never does, and - neither do - undo_close (⌘Z) and - toggle_fullscreen (⌃⌘F), which a - key monitor delivers rather than a menu item. +

+ Recipes come from other people as well as the maintainer. Every one is reviewed before it is accepted, but they are + shell scripts you run on your own machine against your own sessions, and several close sessions or delete + workspaces, so read a recipe before you run it.

-

- v1 limitations -

-
diff --git a/site/index.html b/site/index.html index e76c5459..f9bfd6c3 100644 --- a/site/index.html +++ b/site/index.html @@ -290,7 +290,7 @@ /> A file manager in a floating overlay over the active session
overlay -  Run a program in a floating panel over the active session. +  The yazi file manager in a floating panel over the active session, from one + command line in + keymap.conf. + Extend agterm