- name
- kelpi-agentic
- description
- Use the Kelpi terminal multiplexer and its CLI to orchestrate multi-agent development workflows. Enables spawning named child panes, starting Claude agents in them, farming out parallel work, coordinating results via markdown files and direct pane messaging, AND driving / observing a web app from an agent via the `kelpi web` pane (open URL, capture page text or screenshot, drain console buffer, arm element picker to paste structured payloads into an agent pane). Trigger when: the user asks to "spawn agents", "fan out work", "create worker panes", "orchestrate panes", "use kelpi to coordinate", "multi-agent", "farm out tasks", or any variation of parallelizing work across Kelpi panes. Also trigger when an agent needs to "drive the browser", "watch the page console", "capture a screenshot of the app", "pick an element from the page", or coordinate with the web pane in any way.
# Kelpi Agentic Development Skill
Orchestrate multi-agent development workflows using the Kelpi terminal
multiplexer. Spawn named child panes, start Claude agents in them, distribute
work, and collect results.
## Prerequisites
Pane-relative commands use the calling Kelpi pane. Check whether one is available with:
```bash
kelpi pane id
```
Exit 0 with the pane UUID on stdout means you're in Kelpi; exit 1 with empty output means
there is no calling pane. Commands that take explicit workspace/pane targets can still work
from an external terminal, and offline plugin authoring needs no daemon. `pane id` itself is
purely local (no socket, no shell `$` expansion), so it allowlists cleanly as
`Bash(kelpi pane id)`.
## Required up-front questions
Before spawning any panes, confirm two choices with the user. Skip a
question only if the answer is unambiguous from the invoking prompt
(e.g. "spawn headless workers with --dangerously-skip-permissions").
Otherwise ask via `AskUserQuestion` — do not assume defaults.
1. **Execution mode** — headless or interactive?
- **Headless** (`claude -p "<prompt>"`): non-interactive, runs to
completion, exits when done. Best for fan-out: workers write
result files, coordinator polls. Default for most automation.
- **Interactive** (`claude` then `pane send` the prompt): Claude
stays open in the pane for follow-ups. Use when the user wants to
supervise, iterate, or intervene mid-task.
2. **Permission mode** — which `--permission-mode` flag?
- `default` — prompt on each tool use (safest; requires the user to
babysit each worker)
- `acceptEdits` — auto-accept file edits, still prompt on Bash/other
- `plan` — planning only, no writes
- `auto` — auto-approves all tool calls with background safety
checks. Covers Bash/Edit/Read/web, unlike `acceptEdits` which is
file-writes only. Good middle ground for trusted fan-outs that
still want some guardrails
- `bypassPermissions` (aka `--dangerously-skip-permissions`) — full
autonomy, no prompts, no checks. Common for trusted fan-outs in
worktrees or sandboxed VMs
- `dontAsk` — auto-denies any tool not pre-approved via
`/permissions` or an allowlist. Strictest mode for unattended
orchestration with an explicit whitelist; non-whitelisted calls
silently fail
Ask both in a single `AskUserQuestion` call with two questions. Record
the answers and reuse them for every worker spawn in the session unless
the user changes them.
Once chosen, the worker-start command shape is:
```bash
# Headless
kelpi pane send --target worker-1 claude -p --permission-mode <mode> "<prompt>"
# Interactive
kelpi pane send --target worker-1 claude --permission-mode <mode>
sleep 2
kelpi pane send --target worker-1 "<prompt>"
```
## Kelpi CLI Reference
The `kelpi` CLI communicates with the daemon through the pane's injected `KELPI_SOCKET`
route, or the default Unix socket `/tmp/kelpi.sock`. When targeting a private development
instance from outside its panes, use its printed TCP route with `KELPI_REQUIRE_SOCKET=1`.
### Plugin commands
Kelpi plugins can add custom panes, replace workbench views and native pane renderers,
register commands/hooks, and provide services. Use `kelpi plugin --help` for the complete
command list. `init`, `validate` and `pack` operate locally without a daemon; `dev` and
installation execute trusted code on the selected daemon machine.
```bash
# Offline authoring; the default pane template also supports either sidebar.
kelpi plugin init ./my-plugin --id example.my-plugin --template pane
kelpi plugin validate ./my-plugin
kelpi plugin pack ./my-plugin --out ./my-plugin.kelpi-plugin
# Run against the intended daemon. The source/artifact must exist on that machine.
kelpi plugin install ./my-plugin.kelpi-plugin --trust
kelpi plugin open example.my-plugin example.my-plugin.home
kelpi plugin run example.my-plugin.summary
kelpi plugin dev ./my-plugin --trust
# Inspect or select retained builds; rollback preserves native panes and sessions.
kelpi plugin history example.my-plugin
kelpi plugin rollback example.my-plugin
kelpi plugin logs example.my-plugin
```
Packing creates a local artifact; it does not publish or install it. `dev` validates and
applies stable changed revisions, keeps watching after invalid edits or failed updates, and
leaves the selected revision installed when stopped. `reload` restarts installed bytes
without copying source edits. Native document, terminal and browser replacements attach to
existing native panes; use Settings → Plugins → Workbench views to select them.
For development beside installed Kelpi, follow the
[private-instance guide](https://github.com/benfriebe/kelpi/blob/main/docs/plugin-development.md).
The [plugin reference](https://github.com/benfriebe/kelpi/blob/main/docs/plugins.md) documents
trust, dependencies and recovery; the
[roadmap](https://github.com/benfriebe/kelpi/blob/main/docs/plugin-roadmap.md) records progress.
### Pane Commands
```bash
# Split a pane (creates a new pane alongside). Splits YOUR pane by
# default, or the pane named by --target. Works from outside Kelpi
# via --target (UUID = global, label needs scope) or --workspace.
# The new pane opens in the BACKGROUND (focus stays where the user has
# it); --focus moves focus to it. Request/response: prints the new pane
# id, exits non-zero on failure.
kelpi pane split [--direction horizontal|vertical] [--path /dir] [--name <label>] [--target <name-or-uuid>] [--workspace <name-or-uuid>] [--focus] [--json]
# Create a new pane (alias for horizontal split). Works from outside Kelpi
# via --workspace (or --target's workspace). Background by default, like
# split; --focus moves focus to it. Request/response: prints the new pane
# id, exits non-zero on failure.
kelpi pane create [--path /dir] [--name <label>] [--target <name-or-uuid>] [--workspace <name-or-uuid>] [--focus] [--json]
# Close current pane
kelpi pane close
# Set a label on a pane (visible as pill badge in title bar). Without
# --target, renames the calling pane; with --target, renames any pane.
kelpi pane name [--target <name-or-uuid>] [--workspace <name-or-uuid>] <label>
# Resize a pane against its immediate split sibling. Set an exact share
# with --ratio, or nudge with --grow/--shrink (default step 0.05). The
# key use is keeping the coordinator prominent after a fan-out (below):
# it addresses the pane by name, so it does NOT depend on focus the way
# `layout select main-*` does. Effective share clamps to [0.1, 0.9].
kelpi pane resize [--target <name-or-uuid>] [--workspace <name-or-uuid>] (--ratio <0..1> | --grow [amt] | --shrink [amt])
# Send text to another pane (typed into its PTY + Enter)
# Label resolution is scoped to the sender's own workspace by default.
# Pass --workspace <name-or-id> to target another workspace
# or to disambiguate a label collision; UUID targets are always global.
# `--bare` writes the text without appending Enter — pair with
# `pane send-key` for compositional flows (autocomplete with `tab`,
# escape sequences, partial input, paste-safe structured content).
kelpi pane send [--bare] [--json] --target <label-or-uuid> [--workspace <name-or-uuid>] <command...>
# List panes (read-only inventory — use for reconciliation)
kelpi pane list [--workspace <name-or-id> | --current] [--json] [--no-header]
# Read another pane's terminal contents as text (viewport, or full
# scrollback with --scrollback). WITHOUT --target it captures the CALLING
# pane — always pass --target to read a worker. The target is flag-only:
# a bare `kelpi pane capture <uuid>` is rejected, not treated as --target.
kelpi pane capture [--target <name-or-uuid>] [--workspace <name-or-uuid>] [--lines N] [--scrollback]
# Move a pane so it docks against another pane (CLI form of GUI
# drag-drop). --below stacks the target under the anchor; --above /
# --left-of / --right-of are the other edges. Both panes resolve within
# the same workspace. Pairs with `pane resize` for full layout control.
# Background by default (the user's focus stays put); --focus focuses
# the moved pane.
kelpi pane move --target <name-or-uuid> (--above|--below|--left-of|--right-of) <anchor> [--workspace <name-or-uuid>] [--focus] [--json]
# Move the calling pane toward its neighbour (directional form).
# Also background by default; --focus focuses the calling pane.
kelpi pane move [--focus] <left|right|up|down>
# Move a pane to another workspace (creates it with --create). Background:
# the user's view stays put (unless you move the pane they are typing in);
# --focus makes the view follow the pane.
kelpi pane move-to-workspace --to-workspace <name-or-uuid> [--create] [--focus]
# List workspace groups and their ordered member workspaces
kelpi group list [--json] [--no-header]
# List every workspace (grouped + top-level) with its parent group
kelpi workspace list [--json] [--no-header]
# Print current pane's UUID (local; no socket). Exit 1 if not in Kelpi.
kelpi pane id
```
### New panes open in the background (never take the user's focus)
The user is often typing in another pane while you work. Every pane you
create (`pane split`, `pane create`, `web open`, `open`, `md`, `diff`,
`plugin open`, `web tab-new`) opens in the **background**: it appears in
the layout, but the focused pane and the keyboard stay exactly where the
user left them, and your own pane is not focused either. So spawning
workers never sends the user's keystrokes into the wrong pane. The same
goes for workspaces: `workspace create` and `pane move-to-workspace` leave
every window, and ACTIVE in `workspace list`, where they were (the sidebar
scrolls to and highlights the new row instead).
- **Do not pass `--focus`** unless the user asked to be taken to the new
pane or workspace ("open it and put me there"). `--focus` moves focus
and the keyboard (or every window) there, interrupting whatever the user
is typing.
- **Name the workspace in follow-ups.** A new workspace does not become
ACTIVE, so after `workspace create` pass `--workspace <id>` (or
`--target`) to `pane create` / `pane split`; a bare `pane create` from
outside that workspace lands in the user's workspace.
- **Place panes with `--target`**, not with focus. `pane split` splits
your own pane unless `--target` names another; nothing you do moves
focus, so there is no "split the focused pane" trick to rely on. Use
`kelpi pane move --target <new> --right-of <anchor>` (or `--below`,
...) to dock a pane somewhere else after creating it. `pane move` is
background too: it rearranges the layout without moving focus (pass
`--focus` only if the user asked to be taken to the moved pane).
- `kelpi web open` still splits the workspace's *focused* pane (often
the user's, not yours). If it lands somewhere awkward, move it with
`kelpi pane move`.
- Address panes by name or UUID (`--target worker-1`), never by
assuming which pane is focused.
### Keep the layout readable (grid + prominent coordinator)
`kelpi pane split` always bisects the target pane 50/50 with no
rebalancing. So if you loop the **same** split direction against the
same pane — `for w in ...; do kelpi pane split --direction horizontal;
done` — the 1st worker takes half, the 2nd a quarter, the 3rd an
eighth… after 4-5 workers the panes are unreadable slivers. Do NOT do
that. Instead, after spawning the workers, reflow with the built-in
tmux-style layouts:
```bash
# Balanced grid — panes stay roughly square, not thin columns.
kelpi layout select tiled
# Or: one large "main" pane + the rest tiled beside it.
kelpi layout select main-vertical # main on the left, workers stacked right
kelpi layout select main-horizontal # main on top, workers in a row below
```
**Caveat — which pane becomes "main".** `layout select main-*` makes
the **currently focused** pane the large "main" pane. Your splits no
longer move focus, so after the spawn loop the focused pane is whatever
the user last had focused: maybe the coordinator, maybe a pane in the
middle of their own work. Running `main-vertical` would enlarge that
pane, and there is no `kelpi pane focus` CLI to pick one (and you should
not steal the user's focus to fix a layout anyway).
The focus-independent lever is `kelpi pane resize`, which addresses the
pane **by name**. `--ratio` is the pane's *share* of its split, so use
a value **> 0.5** to make the coordinator the larger side:
```bash
kelpi layout select tiled # balanced grid first
kelpi pane resize --target coordinator --ratio 0.65 # coordinator > its sibling
```
**What resize does and doesn't do.** `pane resize` only rebalances the
coordinator's *immediate* split (its boundary with the one neighbour it
shares that split with) — it does **not** make the coordinator dominate
the whole grid the way a `main-*` layout's root split does. So:
- Want a readable grid where the coordinator is merely the larger cell?
`layout select tiled` + `pane resize --target coordinator --ratio 0.65`.
Focus-independent, safe to run straight from the coordinator script.
- Want one pane that dominates the entire workspace? `layout select
main-vertical`, but it enlarges whatever is *focused* (the user's
choice, not yours), so it's only reliable when you know the
coordinator is the focused pane at call time.
`pane resize` also lets a coordinator fine-tune any pane at any time
(`--grow` / `--shrink` to nudge, `--ratio` to set exactly) without
touching the GUI. The effective share clamps to `[0.1, 0.9]`.
### `kelpi doctor` — when CLI commands stop working
If `kelpi` commands suddenly start failing with `Error: kelpi …: cannot reach
Kelpi …` or `no response from Kelpi`, run `kelpi doctor` first.
It runs seven named checks and prints `[PASS|FAIL|WARN] <name>: <detail>`
plus a concrete repair line for any failure:
- `transport` — Unix socket path or TCP destination in use.
- `socket` / `resolve` — file exists on disk (Unix) or hostname resolves (TCP).
- `ping` — round-trip a `ping` command and parse the JSON reply.
- `process` — scans the process list (`ps`) for `Kelpi.app`. If `ping` failed
but the process is up, the app is wedged (restart it); if no process, Kelpi
isn't running.
- `version` — CLI version vs running-app version. Warn on drift (rebuild Kelpi).
- `hooks` — the user-level Claude Code hook config (`~/.claude/settings.json`
plus `settings.local.json`) still carries every kelpi hook, and the
`SessionStart` entry fires for resumed sessions. Warn on drift: a stale
matcher means `claude --continue` / `--resume` panes never bind their
session id, so scripts that gate on `agent_session_id != null` silently
skip them (issue #181). Repair by re-running the bundled
`install-hooks.sh`. Skipped when no `~/.claude` directory exists.
- `codex-hooks` — same idea for `~/.codex/hooks.json` (Codex CLI ≥ 0.142,
issue #101). Skipped when no `~/.codex` directory exists; WARN-only.
Doctor cannot see codex's hook *trust* state — if the wiring passes but
codex panes never change status, run `/hooks` inside codex and trust
the kelpi hooks.
Pass `--json` for a machine-readable report. Exits non-zero only when a
check FAILs; WARNs are reported but leave the exit code 0. Use this as
the first triage step before restarting the app.
Every CLI error now emits an `Error: …\nRepair: …` pair pointing at the
matching repair step. Fire-and-forget commands (`kelpi event …`, hooks)
print a `Warning: …` to stderr but still exit 0 so Claude Code Stop
hooks etc. don't break — set `KELPI_SILENT=1` to suppress entirely.
`kelpi event …` (the Claude Code hook entrypoint) suppresses the warning
by default to avoid stderr spam when Kelpi is closed; set
`KELPI_VERBOSE_HOOKS=1` to surface them again.
### `pane list` — reconcile with live state
`pane list` is Kelpi's read-only pane inventory. Use it whenever a coordinator
needs to know what panes actually exist right now — panes can be closed by
the user, crash, or be moved between workspaces. `pane send` exits non-zero
with a structured error on a missing/ambiguous target, but checking
`pane list` first lets a coordinator skip sends to dead workers and surface
a clearer message.
```bash
# Human-readable (default)
kelpi pane list
# JSON for scripts — stable shape, exit code encodes success
kelpi pane list --json
# Only panes in the current pane's workspace (requires KELPI_PANE_ID)
kelpi pane list --current
# Only panes in a named workspace
kelpi pane list --workspace kelpi
```
The default table's `ID` column prints the full pane UUID, so you can
copy it straight into `--target <uuid>` (the agent session id in
`SESSION` stays truncated — it's never a `--target`). Scripts should
still prefer `--json`.
Each JSON entry includes: `id`, `label`, `type` (`shell` / `markdown` /
`scratchpad` / `diff` / `csv` / `web`), `title`, `workspace_id`, `workspace_name`,
optional `group_id` and `group_name` (both absent for top-level workspaces),
View on GitHub