Agents running inside programa (Claude Code, Codex, OpenCode, or anything else that reads Anthropic-style SKILL.md files) can drive the app itself, not just the shell inside their own pane — inspect the window/workspace/pane/surface hierarchy, split panes and run commands without stealing the user's focus, read output from a sibling pane, spawn a helper agent, and coordinate with it.
The skill is SKILL.md at the repo root. This page is the longer version — background, the install flow, and the full command reference the skill points at.
SKILL.md gets dropped automatically by the existing integration installers, so a normal programa claude install-integration / programa codex install-hooks / programa opencode install-integration is enough — there's no separate skill-install step.
| Tool | Install command | Skill location |
|---|---|---|
| Claude Code | programa claude install-integration |
~/.claude/skills/programa/SKILL.md (or $CLAUDE_CONFIG_DIR/skills/programa/SKILL.md) |
| Codex | programa codex install-hooks (alias install-integration) |
~/.agents/skills/programa/SKILL.md |
| OpenCode | programa opencode install-integration |
~/.config/opencode/skills/programa/SKILL.md (or $OPENCODE_CONFIG_DIR/skills/programa/SKILL.md) |
~/.claude/skills and ~/.agents/skills are also both read by OpenCode's own global skill discovery, so running any one of the three installers above already covers OpenCode for most setups — the OpenCode-specific install just makes it explicit and respects OPENCODE_CONFIG_DIR.
Uninstalling (programa claude uninstall-integration, programa codex uninstall-hooks, programa opencode uninstall-integration) removes the matching copy the same way it already removes hooks/plugin files, and refuses to touch a file that isn't marked as programa-managed.
Every terminal surface programa creates already exports PROGRAMA_SURFACE_ID, PROGRAMA_WORKSPACE_ID, and PROGRAMA_SOCKET_PATH (see TerminalSurface.swift), and the programa CLI is already on PATH. An agent running in that pane has everything it needs to call the socket API — it just has no reason to know that unless something tells it. The skill is that something.
The first thing the skill does is check PROGRAMA_SURFACE_ID and PROGRAMA_SOCKET_PATH. If either is unset, it stops and reports that it isn't running inside programa — no socket call, no guessed path. This matters because the same SKILL.md gets installed globally (~/.claude/skills/...) and loaded by every session that tool runs, including ones in a plain Terminal.app window with no programa socket to talk to.
- Inspecting your surroundings —
programa tree(the hierarchy, with◀ active/◀ heremarkers), pluslist-workspaces/list-panes/list-pane-surfaces/identifyfor narrower queries. - Splitting and running without stealing focus —
new-split/new-pane/new-surfacecreate UI without moving the user's cursor;send/send-key/send-panel/send-key-panelrun commands in another pane the same way. Onlyfocus-pane,focus-window,focus-panel,select-workspace, and thenext/previous/last-windowtriad actually move focus — see the socket focus policy in the rootCLAUDE.md. - Reading a sibling pane —
read-screen(aliascapture-pane), with--scrollback/--linesfor history beyond the visible viewport. - Spawning and coordinating a helper agent — split,
senda command likeclaude ...into the new surface, then read its output and answer it the same way you'd talk to any other pane.set-statusandnotifyreport progress through the sidebar instead of a pane the user isn't looking at. - Waiting —
wait-surfacegives a server-owned blocking wait on a surface:--pattern <regex>resolves when new output matches,--exitwhen the process exits, both with--timeout. Waiting on agent state (idle/working/blocked) is implemented too — the underlyingsurface.waitsocket method takes anagent_statecondition (seedocs/v2-api-migration.md), andprompt-agentis built directly on it to send a prompt and wait for the agent to go idle again. The tmux-compatiblewait-for/wait-for -Snamed-signal rendezvous covers two cooperating processes. - Authoring screen-detection manifests for an unsupported agent — programa's agent-state detection reads a pane's own lifecycle hooks when available (Claude Code, Codex, OpenCode), and otherwise falls back to regex-matching visible screen text against a manifest. For any other agent,
programa agent-detection scaffold <agent-id>captures the current screen and writes a starter manifest to~/.config/programa/agent-detection/<agent-id>.json; fill in itspatternsarrays, thenprograma agent-detection test <agent-id>to check them against the live screen.programa agent-detection listshows every loaded manifest (bundled + your overrides). - Writing a recap — when the user asks for a summary of a change, write markdown to
.programa/recaps/<slug>.mdand open it withprograma recap open <slug>(programa recap listshows what's saved). The markdown panel renders mermaid diagrams, GitHub-style alerts, and:::comparebefore/after blocks, so the skill tells the agent to reach for those instead of plain prose when they fit.
There's no automated test for "does an agent actually behave correctly" — this is prompt content, not code. Manually verify by starting a fresh Claude Code (or Codex/OpenCode) session inside a programa pane with the skill installed and asking it to open a split and tail a dev server log; it should do so via the programa CLI, and the user's focus should stay on the pane they were already in.
The skill only covers the commands relevant to agent coordination. For the full CLI surface (SSH workspaces, the in-app browser, tmux-compat commands, hooks) run programa help, or see docs/v2-api-migration.md for the underlying socket API.