using-grove · git:20260923.1a5058d · 2026-09-23 · sha256 33e38bbe068c12ed
using-grove git:20260923.1a5058dA
Immutable. This exact content is served forever at /api/v1/blob/33e38bbe068c12ed.
--- name: using-grove description: Use when orchestrating Grove workspaces from outside, including creating or supervising host or container agents, steering a fleet, using Grove CLI or MCP tools, or having agents write to each other through the mailbox. Also use when splitting work across workspaces, reviewing a diff from several angles, managing ticket links, or checking who needs attention. For a worker reporting from inside a workspace use working-in-grove. For setup use configuring-grove. --- # Using Grove [Grove](https://github.com/bearlike/Grove) runs a fleet of coding agents that live outside your context rather than sub-agents inside it. Each workspace is a whole separate agent process with its own runtime, its own context window and its own git worktree, addressable from outside. You create, steer, watch, pause, resume, respawn and kill them. Reach for Grove when you want parallel workspaces that outlive a single turn. For quick in-session parallelism across a few scoped sub-tasks, plain sub-agents are lighter and fit better. Grove earns its keep on work too big, too parallel or too cross cutting for one agent in one context. - **Decompose and parallelize.** Split by component and spin up one workspace per slice. Each gets a clean worktree, so they never touch each other's files. - **Triage at scale.** Forty Sentry issues or a whole backlog, one workspace per issue or per cluster. Each agent investigates independently and you review results rather than noise. - **Work one ticket from several angles at once.** Implementation, tests and docs run in parallel in separate environments instead of one after another. - **Independent review.** Point separate agents, on different models and different harnesses, at the same diff for uncorrelated second opinions. ## The model - **One workspace is one isolated runtime for one agent.** A dedicated git worktree, or the repo root for a lightweight case, plus a tmux session running that agent's own CLI, on the host or inside a container. - **Model and harness agnostic by design.** Every workspace picks its own agent and model. The point is giving each slice the harness that suits it, not stretching one agent across everything. - **You own the fleet and each member owns its own task.** Decide the split, spawn one workspace per slice, then poll and steer and collect. A map step and a reduce step, with real isolation so one agent's long tool call never blocks another. ## What Grove reports back Three independent axes describe a workspace and you need all three. Conflating them is the mistake the design exists to prevent. | Axis | Answers | Reported by | |---|---|---| | Workspace status | Is the runtime there. `active`, `idle`, `paused`, `offline`, `orphaned`, `provisioning`, `error` | Grove observes it | | Agent activity | Is the agent moving right now. `working`, `waiting`, `blocked`, `error`, `idle` | Grove infers it from transcript, hooks and pane | | Task phase | How far through the task the agent believes it is | The agent declares it, and nothing derives it | Phase is the axis you could not get before. An agent reads `working` both while it is still reading the ticket and while it is pushing the branch, and those two call for opposite responses from you. Only the agent can tell them apart, which is why this axis is pushed rather than observed. The six phases run `scope`, `plan`, `build`, `verify`, `deliver`, `handoff`. Four things about phase are easy to get wrong. - **A missing phase is not `scope`.** A workspace that never reported carries no phase at all. That is a fact about the agent, where `scope` is a fact about the task, so render them differently. - **Backwards is a correct report.** An agent that discovers in `verify` that its design was wrong should say `plan` again. Grove never enforces forward motion, and an honest reversal is worth more than a phase that only climbs. - **There is still no `error` phase — that lives on the activity axis.** An agent can flag `blocked: true` beside a phase instead, meaning it has no way to finish that piece of work; it still names the phase it actually reached. This is not the activity axis's own `blocked`, which means waiting on a human right now and clears the moment they answer. - **Grove renders no staleness verdict.** Three hours in `build` is a long task, not a stale report. Whether the agent is alive is what the other two axes answer. The timestamp is the file's own mtime, so hold your own policy if you want one. Phase does not survive `pause`. It lives in the worktree that pause removes, so a resumed workspace reports afresh and the durable record stays the commit log. ## Watching the fleet `grove fleet` is the read built for supervising many workspaces at once. It is host wide rather than repo scoped, takes no arguments and emits JSON. One call carries every workspace with its state, its reported phase, todo counts, git ahead and behind, recent commits, and per session activity including which agents are waiting on you. ```bash grove fleet | jq '.projects[].workspaces[] | select(.needs_attention)' ``` Prefer it over per workspace phase and peek calls the moment you supervise more than one agent. `grove_get_fleet_status` is the same payload over MCP. It has no pagination, so the response grows with the fleet. For a single workspace, `grove show` gives git, agent, transcript and live pane state at a glance. ## Tying a workspace to the work item A workspace carries references to the issues and pull requests it is working. This is what makes a fleet addressable by work item rather than by workspace id. ```bash grove tickets attach 42 grove tickets attach https://example.com/acme/widgets/pull/17 grove tickets list grove tickets detach 42 ``` You never name a provider. Grove infers both the provider and whether the ref is an issue or a pull request from the shape of what you pass. Four input forms work, a full URL, `#42`, `42`, and `owner/repo#42`. Linear keys such as `ENG-123` parse for free. Pass `--workspace` to name one, or let Grove infer it from the directory you are standing in. Attach and detach are both idempotent. Five things worth knowing before you document or automate this. - **A pull request is not a separate kind of attachment.** It is one ref list with a kind discriminator, so there is no separate verb to go looking for. Re-attaching a ref Grove recorded as an issue corrects the kind in place rather than duplicating the row. - **Grove never detects your pull request from the branch.** Branch names yield issue refs only. A pull request enters the list solely by explicit attach. - **Attach stores a link and fetches nothing at that moment.** Title, status and assignee are fetched on demand and never persisted, which is what keeps attach fast and safe offline. - **Attaching subscribes the workspace to the ticket.** While the workspace runs, Grove mails its agent when a person changes the title, state, draft flag or description, or adds a comment. Each ticket is read at most once a minute, and Grove's own status comment never triggers a message. Detaching, pausing or killing stops it. You never register or cancel these yourself. - **A merged pull request reads `merged` and never `closed`.** The issues endpoint calls a merged PR closed, which is true and useless, so Grove reads the pulls namespace and normalizes. This is how you learn work landed. - **Attaching a ticket is a private local link.** It does not make an issue reference safe to put in pull request text. That ban exists because PR text mirrors to public remotes where a bare number resolves against a different tracker, and attaching changes nothing about the mirror. - **Attaching is also what makes the status mirror appear.** When issue-ops is enabled, Grove keeps ONE live comment per attached ticket carrying the workspace's state, its reported phase and its todo checklist. Nothing is published for a workspace with no refs, so an attach is the difference between a fleet a human reads from the tracker and one they can only see in Grove. - **Every one of those comments is authored as whatever account the configured token belongs to.** Point it at a bot. Forges set authorship at creation and never change it on edit, so a comment first written under a personal token stays attributed to that person permanently. - **Attach succeeding tells you nothing about whether the tracker is reachable.** It performs no network call, and publish failures are swallowed so an outage cannot break the activity poll — so a misconfigured provider presents as silence rather than an error. If comments never appear, suspect the credential before the code. ### Keeping the tracker current The mirror is only as good as what the agent reports, and the agent is the only one who can report a phase. Two consequences for you as the orchestrator: - **Tell a workspace to report, and to keep its todo list honest**, in the prompt that starts it. An agent that never writes a phase publishes a comment with no progress in it, and Grove will not invent one — a missing phase renders as nothing rather than as `scope`. This is the single most common reason a status comment looks empty. - **Attach every ticket the workspace owns, up front.** Attaching is also what seeds an entry of its own for that ticket, at `scope` — so a ticket linked late starts with no history and no phase until the agent's next report. Each attached ticket now carries its own phase, seeded the moment it is attached and updated independently of the workspace's own — an agent in verify one issue while another sits untouched reports exactly that, rather than one shared answer for both. `blocked: true` sits beside a phase, on the workspace's own claim or on any one ticket's, and means the agent has no way to finish that piece of work — distinct from the activity axis's own `blocked`, which means waiting on a human right now and clears the moment they answer. You can correct a report from outside with `grove phase <ref> <phase> [--ticket <ticket-ref>] [--blocked]` or `grove_set_workspace_phase` — for when an agent has stopped reporting or has plainly mis-stated where it is. Use it to fix the record, not to drive it: a phase you set is your claim about someone else's work, and the next thing the agent writes overwrites it. Before you create a second workspace for a ticket, ask whether one already exists. Over MCP, `grove_list_workspaces` narrows by `ticket_provider` plus `ticket_id` and answers exactly that, which is the difference between steering the workspace already on the job and starting a duplicate. ### Handing work over by assignment The tracker's assignee field can be the fleet's inbound queue. With issue-ops pickup enabled, the daemon polls each configured tracker for open issues assigned to Grove's account and starts a workspace on each one, under a ceiling on how many pickup-started workspaces may be live at once. `grove tickets handover <ref>` does the same thing on the spot rather than waiting for the poll, `grove tickets owned` lists what the fleet is holding host wide, and `grove tickets handback <ref>` unassigns without touching the workspace. Assigning on the tracker and handing over from the CLI take the same path, so a ticket handed over is never picked up twice. Handover needs a provider with an owner and a repo configured, which is Gitea and GitHub; Linear refuses, because two repos' issue #7 would otherwise share one marker. Gitea, GitHub and Linear are supported and all three stay off until a repo opts in. Each wants `enabled`, its scope, a base URL, and the NAME of an env var holding the token rather than a token literal. With nothing configured, an attach by bare ref fails loudly and names the providers that are enabled. ## CLI Run from inside any git repo Grove knows about. `grove config add-project` registers a repo you are not currently standing in. | Command | What it does | |---|---| | `grove create <title> [--agent <name>] [--model <id>] [--prompt <text>] [--runtime host\|container] [--branch/--base/...]` | Spin up a workspace and kick it off with a first task | | `grove ls` | This repo's workspaces, as JSON | | `grove show [<ref>]` | One workspace's git, agent, transcript and live pane state | | `grove fleet` | Every workspace on the host, as JSON | | `grove message <ref> <text>` | Steer a running agent | | `grove phase [<ref>] [<phase>] [--ticket <ticket-ref>] [--blocked] [--note <text>]` | Read a phase, or set one — the workspace's own or one ticket's — from outside | | `grove tickets attach\|list\|detach <ref> [--workspace <id>]` | Issue and pull request links | | `grove tickets handover <ref>`, `grove tickets owned`, `grove tickets handback <ref>` | Hand an issue to the fleet by assigning it, list what the fleet holds, give one back | | `grove pause\|resume\|respawn\|kill <ref>` | Lifecycle | | `grove attach <ref>` | Drop into the workspace's live tmux session yourself | | `grove shell <ref>` | An interactive shell inside a container workspace, persistent across visits | | `grove code <ref>` | Attach VS Code to a container workspace | | `grove agent list\|add\|kill\|peek\|message\|attach` | Several agents sharing one container workspace | | `grove sessions list\|show\|dump\|remap` | Recorded agent transcripts across the project | | `grove doctor` | Check the host dependencies each runtime needs | | `grove config`, `grove init devcontainer`, `grove skills install`, `grove mcp install` | Setup and onboarding | `<ref>` is a workspace id or a unique id prefix. `grove <command> --help` carries every flag. `grove shell`, `grove code` and the whole `grove agent` group require a containerized workspace and error cleanly on a host one. A fresh container workspace reads `provisioning` while its image builds, which can be minutes. That is the one status whose remedy is to wait, and it ends on its own — `respawn` is the one action that destroys the build in flight. `--runtime` is fixed at create time and never editable afterwards. If a container was wanted and the runtime was unavailable, the workspace falls back to host and names the reason, which `grove show` and `grove ls` report. Fix the runtime, then `grove respawn` to promote it. A container workspace with no project `.devcontainer/` runs Grove's default image, and `grove init devcontainer` graduates it to a committed config. ## MCP The same fleet, over MCP. What your server actually registers is the census; each tool's own schema documents its inputs, so read those rather than guessing from here. | Group | Tools | |---|---| | Discovery | `grove_list_projects`, `grove_list_workspaces`, `grove_get_workspace`, `grove_list_agents`, `grove_list_sessions` | | Watching | `grove_get_fleet_status`, `grove_peek_workspace`, `grove_get_workspace_phase`, `grove_get_workspace_todo`, `grove_attach_instruction` | | Waiting | `grove_register_watch`, `grove_list_watches`, `grove_cancel_watch` | | Lifecycle | `grove_create_workspace`, `grove_pause_workspace`, `grove_resume_workspace`, `grove_respawn_workspace`, `grove_kill_workspace` | | Workspace metadata | `grove_update_workspace` | | Steering | `grove_send_workspace_message`, `grove_set_workspace_phase` | | Links and sessions | `grove_attach_ticket`, `grove_detach_ticket`, `grove_remap_workspace_session` | `grove_list_projects` takes no arguments and returns the repo roots every other tool wants, so start there. - `grove_kill_workspace` requires `delete_branch` explicitly and rejects the call without it. Destructive tools never guess. - `grove_send_workspace_message` that times out means delivery is unknown rather than failed. Peek before you resend, or the agent gets the message twice. - `grove_set_workspace_phase` is for correcting an agent's report from outside, with `ticket` and `blocked` parameters mirroring the CLI's `--ticket` and `--blocked`. The agent's own channel is a file, described below. - A server started with `--read-only` withholds every mutating tool by not registering it, because a tool an agent can see is a tool it will try to call. - `grove_peek_workspace` caps its pane snapshot, and a trailing ellipsis is the signal that it trimmed. ## Container workers report through the mounted worktree A container agent should write its phase file because that path works across runtimes. Ordinary container workspaces may not have the Grove CLI or a general Grove MCP registration, so handle lifecycle and ticket links from outside. Mailbox workers are the deliberate exception. Grove supplies their private coordinator connection when the container meets the native prerequisites. That grants the session's control channel. It does not expose the general fleet control surface. Attach the pull request the agent opens from outside when the agent cannot reach the relevant CLI or MCP tool. You do not have to restate the reporting contract in your prompt. By default Grove hands every new workspace a one-paragraph brief on its first turn pointing it at the companion `working-in-grove` skill, and `--no-brief` turns that off per workspace. How it arrives differs, and the difference bites in a container: a host `claude_code` agent gets it from Grove's status hook whether or not you gave it a task, while an agent with no such hook gets it prepended to the initial prompt — so a container workspace created with no `--prompt` is never briefed at all. ## Native sessions and peer mail A Claude Code or Codex workspace is a Grove-owned native session by default: Grove runs the provider's own protocol worker (`claude -p` stream-json or the Codex app-server) and holds its control channel, so interrupt, a model switch and peer messaging are real controls. The `claude-terminal` / `codex-terminal` entries (or `native: false` on your own entry) run the interactive UI instead. Peer mail is never a way to send text to an arbitrary interactive TUI or to retrofit a socket into an existing session. A native session is not resumable and does not attach to an existing conversation. ## Agents writing to each other Any live agent in the fleet can write to any other, addressed like email: `--from`, `--to`, a subject and a body. Native and interactive terminal sessions are both ordinary recipients, because delivery reuses whatever channel Grove already steers that workspace through. Nothing is enrolled and no peer holds a credential of its own. ```bash grove mailbox contacts grove mailbox send --from <id> --to <id> --subject "Ready for review" --body "PR #42 is open." ``` A contact reports whether it is `live`; a paused or offline workspace is listed and refused with `not_live` rather than silently swallowed. The MCP tools are `grove_list_mailbox_contacts` and `grove_send_mailbox_message`. A receipt reports Grove's own observation and nothing more: `delivered` means the text reached that session, never that a model read or obeyed it. Do not retry an `unknown` send or type a fallback into a pane. A reply is an ordinary send with the addresses swapped, so a conversation survives a daemon restart. The agent-facing workflow belongs in `working-in-grove`; configuration belongs in `configuring-grove`. Run `grove mailbox --help` and read the installed MCP schema before supplying flags. ## The other Grove skills - **`working-in-grove`** — the same fleet seen from the inside, including a mailbox worker's peer-message workflow. Point a workspace agent at it rather than restating the phase or mailbox contract in every prompt. - **`configuring-grove`** — the config cascade, agents, init scripts, containers, native-session switch, and ticket providers. Reach for it when a workspace will not start the way you meant, or when the status mirror is silent. - **`reinstalling-grove`** — when an update did not take: stale daemon, stale webapp build, old UI after a pull. ## Progressive help Use `grove <command> --help` for installed CLI syntax and each MCP tool schema for its inputs. `grove skills list --details` and `grove_get_skill(details=True)` list richer skill metadata. Retain the existing skill names. A mailbox message never grants a default native tool permission. ## Configuration Setting Grove up is a separate concern from using the fleet. If a `configuring-grove` skill is available here, use it for that. `grove skills install` and `grove mcp install`, or `grove config init --with-onboarding`, is how this skill and Grove's MCP server arrived in the first place. For anything this skill does not cover, `grove --help` or a repo aware research tool pointed at `bearlike/Grove` will get you unstuck faster than guessing.