hyperhive/docs/tools/bash.md

5.8 KiB

Bash execution tools

Background shell execution via hive-bash-mcp. Tools land as mcp__bash__<tool> (the MCP server name is bash, not hyperhive). Available on every agent unconditionally — nix/agent-modules/mcp.nix always injects bash into hyperhive.extraMcpServers (with allowedTools = ["*"]), so mcp__bash__* is in --allowedTools for every claude invocation regardless of tool groups.

Tools

run(cmd, timeout_secs?, wait_seconds?, name?)

Submit a shell command for background execution (runs via bash). Stdout and stderr stream to harness/bash-tasks/<id>.{out,err}. When the task completes (or times out, or the process errors), it surfaces as a todo in the agent's loose-ends (via get_loose_ends), carrying the exit code and a Read(<path>) pointer to the captured output. Handle it on a future turn — unless wait_seconds already delivered the terminal result inline, in which case no todo is created (see status below).

  • timeout_secs — kill the task after N seconds and mark it timed_out. Omit for no timeout (runs until natural exit).
  • wait_seconds — inline poll before returning (capped at 30). When the task finishes within the window the full status is returned immediately and no todo is created; when the window expires the task keeps running and the normal task started: id=<id> response is returned. Defaults to 3 — pass wait_seconds: 0 to disable inline waiting and always get the immediate response.
  • name — optional caller-chosen task id. When set it replaces the auto-generated hex id, so it surfaces in status(<name>) lookups and the loose-ends list — a memorable label instead of an opaque id. A name is reusable once its previous task has finished; submitting a name whose task is still pending/running is rejected. Allowed characters: ASCII letters, digits, ., _, - (max 64). Omit for the auto-generated id.

Exposed as mcp__bash__run.

status(id, wait_seconds?)

Poll the status of a task submitted with run. Returns:

  • statuspending / running / done / timed_out / interrupted / killed
  • exit_code — set when done
  • run duration
  • last 4 KiB of stdout and stderr (full output in the .out / .err files)

wait_seconds — optional inline poll (capped at 30): when the task finishes within the window the full status is returned immediately. Useful to avoid a separate round-trip when the task is expected to finish soon.

Any status call (waited or not) that observes a terminal task clears that task's completion todo — you already have the result in this response, so no redundant loose-end follows. Narrow best-effort race: a status/run inline wait that resolves in the same instant the task actually finishes can still occasionally get both.

Tasks marked interrupted had their process killed by a harness restart; a best-effort todo is still surfaced so the agent is not silently blocked.

Exposed as mcp__bash__status.

kill(id, force?)

Stop a running or pending task by its ID (from run). Fire-and-forget: sends the signal and returns without waiting — the completion surfaces in the agent's loose-ends; handle it on a future turn.

  • force: false (default) — SIGINT to the task's process group (graceful; lets the process clean up). The whole process group is signalled, so children spawned by the shell (cargo, nix, etc.) are also stopped.
  • force: true — SIGKILL.

If a SIGINT'd task doesn't exit, call kill again with force: true. A still-pending task is cancelled before it starts. The task ends as killed and surfaces in the loose-ends like any completion.

Exposed as mcp__bash__kill.

Namespace note

run and status live in the bash MCP server, not hyperhive. So the tool names in claude are mcp__bash__run and mcp__bash__status. The Bash built-in tool is blocked — all shell execution goes through this structured path so tasks get task-id tracking and structured output.

Architecture

The bash tooling follows the same daemon + stdio-bridge pattern as the matrix MCP:

  • hive-bash-daemon — long-running process (one per agent container, systemd service in nix/agent-modules/mcp.nix). Owns subprocess management, output file writing, and todo delivery on the harness's in-agent socket. Listens on /run/hive-bash/socket inside the container.

  • hive-bash-mcp — stdio bridge spawned by claude per turn (declared in hyperhive.extraMcpServers.bash). Connects to the daemon socket and forwards run / status tool calls. Has no subprocess management logic of its own.

This split keeps claude's turn-local MCP bridge lightweight while the daemon tracks long-running tasks that outlive a single turn.

Completion as a todo (loose-ends v2)

When a bash task changes state, hive-bash-daemon upserts a single keyed todo (key = task id) on the harness's in-agent socket (HIVE_AGENT_SOCKET) — "running" at start, then the completion summary when it finishes. The summary change signals the harness turn loop directly (in-process, no broker round-trip), so the agent is driven a turn to handle it via get_loose_ends, then clears the todo with mark_todo_done. Same mechanism the matrix daemon uses for unread rooms. An inline wait_seconds / status observation that already delivered the result instead clears the keyed todo, so no redundant loose-end follows.

Relationship to the execution tool group

ToolGroup::Execution exists and appears in AGENT_DEFAULT, but its tools() returns ["run", "status"] which the harness expands to mcp__hyperhive__run / mcp__hyperhive__status — tools that don't exist in the hyperhive MCP server (dead entries). Removing execution from an agent's groups has no effect on bash availability. Bash is registered separately via the extraMcpServers path described above.