grove

Command line

grove is a small command-line client for the app. It does two things: open files and folders in Grove from any shell — the way code opens VS Code — and drive Grove's MCP tools (notes, comments, workspaces, code navigation) from your terminal or a script.

It ships inside Grove.app and is installed onto your PATH on request. It's a thin, dependency-light binary — it talks to a running Grove over loopback and does no heavy work itself.

Same surface as the MCP, for agents that can't use it. The tool verbs are thin wrappers over the exact MCP tools an agent gets in-process — same server, same behavior — reached through the shell instead of the MCP protocol. grove note write is note_write; grove refs is find_references. That makes the CLI the fallback whenever MCP isn't available: an agent whose harness can't speak MCP still gets Grove's full tool surface through plain shell commands, and anything without a dedicated verb is one grove mcp call <tool> <json> away.

Install

The fastest way is the command palette: press Cmd+K and run Install 'grove' Command in PATH. You can also open Settings → General → grove CLI and click install — both do the same thing.

Grove copies the bundled binary into the first writable, already-on-PATH bin directory it finds — Homebrew's bin, then /usr/local/bin, ~/.local/bin, ~/bin — so grove usually works in a new shell immediately. If the only writable spot isn't on your PATH, Grove tells you which line to add.

Nothing touches your PATH until you click install. Re-running the action upgrades the binary in place, so reinstall after a Grove update to refresh the CLI.

grove --version      # confirm it's installed
grove status         # confirm it can reach a running Grove

Opening files

The default verb is open, so you rarely type it. A bare path — or no argument at all — opens in Grove:

grove                 # open the current directory
grove .               # same
grove src/app.ts      # open a file
grove notes/ todo.md  # open several at once

If Grove isn't running, grove open launches it and waits (~20s) for it to come up, then opens your paths. Pass --no-launch to fail instead of launching. Relative paths are resolved against your current directory.

A first argument that names a real path is treated as open; a known subcommand (below) or an unknown word falls through to normal parsing, so typos still get a clear "unrecognized subcommand" and --help / --version work as expected.

grove browse https://example.com   # open a URL in a Grove browser tab

grove browse opens a URL in Grove's embedded browser; scope it to a workspace with --workspace (see below).

How it talks to Grove

The CLI speaks to two loopback servers a running Grove exposes, discovered through small files under ~/.grove:

  • the CLI bridge — for grove open and grove browse;
  • the in-process MCP server — for every tool verb (grove note, grove comments, grove refs, …).

Both are bound to your machine and token-authenticated — the same trust model as your shell, nothing exposed to the network. Tool verbs need Grove to be running and fail fast with a clear message if it isn't. (grove uninstall is the one exception — it works with the app shut down, which is exactly when you need it.)

Global flags

These apply to any verb:

Flag Effect
--json Print the raw JSON result instead of the human-readable text — for scripting.
--workspace <ID> Scope the call to a specific workspace instead of the one Grove has focused.
--project <PATH> Derive the project from this directory instead of the current one (for the guard below).
--force Run even when the current project isn't the one Grove has focused.

The project guard

Grove's MCP server serves a single bound project (a git repo linked to your vault — see Vaults and projects). If you run a code-oriented verb from a different project, the results would be about the wrong repo, so the CLI refuses and tells you the mismatch:

grove: Grove is focused on a different project:
  bound:   /Users/you/Dev/api
  current: /Users/you/Dev/web
Open this project in Grove, cd into the bound one, or pass --force.

You pass the guard whenever your directory is inside (or equal to) the bound project — which includes Grove's own workspace worktrees. Verbs about the vault rather than about code inside it — grove project add, grove project list — skip the guard, since you naturally run them from outside the vault.

Status

grove status (aliased grove doctor) reports the loopback ports, the bound project, whether Grove is reachable, and the health of every project in the vault — flagging bindings whose repo has moved or lost its base branch, which otherwise look fine in the sidebar. It exits non-zero when Grove is unreachable, so it works in a script's readiness check.

grove status
Grove CLI status
  open bridge:  51873
  mcp server:   51874
  project:      /Users/you/Dev/api
  reachable:    yes
  projects:     2
    ✓ api   /Users/you/Dev/api  (base main)
    ✗ web   /Users/you/Dev/web  (base main)
      └ repo path no longer exists

Notes

Read and write your vault's notes from the shell. A written note appears live in Grove's sidebar. Content can be passed inline, or with - / omitted to read from stdin.

Command Purpose
grove note list [PATH] List notes, optionally under a vault-relative subfolder.
grove note read <NAME> Print a note.
grove note write <NAME> [CONTENT] Create or overwrite a note (-/stdin for the body).
grove note delete <NAME> Delete a note.
echo "# Scratch" | grove note write scratch.md -
grove note read scratch.md

Comments

Drive the comment review loop — the durable inbox agents and reviewers share.

Command Purpose
grove comments pending List unresolved comments (the inbox).
grove comments list List all comments, resolved included.
grove comments create <FILE> <LINE> [BODY] Comment on a file and line (-/stdin for the body).
grove comments reply <ID> [BODY] [--resolve] Reply to a thread; --resolve closes it in the same step.
grove comments resolve <ID> [--note TEXT] Mark a comment resolved.

Workspaces

Manage workspaces — the isolated worktrees Grove runs agents in.

Command Purpose
grove workspace list List workspaces.
grove workspace get Show a workspace (defaults to the --workspace scope).
grove workspace create <NAME> [--plan TEXT] [--parent FOLDER] New workspace and worktree off the base branch; --plan seeds its plan.md, --parent nests it under a store folder.
grove workspace tabs <TAB>… [--activate] Save the workspace's tab group — the tabs that reopen with it. --activate also switches to the workspace and opens them now.

For workspace tabs, an entry with a URL scheme (https://…) becomes a browser tab; anything else is treated as a file path (worktree-relative or absolute).

grove --workspace ws_abc123 workspace tabs src/app.ts https://localhost:3000 --activate

Chats

Talk to the agents running in Grove — the shell side of agent-to-agent messaging. Handy for scripting a fan-out, or for driving a fleet from a terminal you already have open.

Command Purpose
grove chat list List the vault's chats, flagging which ones are running.
grove chat send <CHAT_ID> [MESSAGE] [--no-submit] Send a message to a chat's agent (-/stdin for the body), opening and starting the chat if needed. --no-submit leaves it staged at the prompt for a human to review and send.
grove chat read <CHAT_ID> [--lines N] [--raw-wrap] Print what's on a running chat's screen. --lines caps the rows returned (default 200); --raw-wrap keeps the terminal's hard wrapping instead of rejoining wrapped lines.
grove chat list --json | jq -r '.chats[] | select(.live) | .chatId'
grove chat send cht_abc123 "rebase onto main and re-run the tests"
grove chat read cht_abc123 --lines 40

chat send returns once the message is in the agent's prompt — not once it has answered, and there's no reply channel back. chat read prints the screen as currently rendered rather than scrollback: it answers "what is it doing right now", not "what did it say ten minutes ago".

Projects

Bind git repos into your vault. Binding a repo is the prerequisite for creating workspaces against it.

Command Purpose
grove project list List the vault's projects, flagging broken bindings.
grove project add [REPO] [--name NAME] Bind a git repo (default: the current directory) into the vault.
cd ~/Dev/api
grove project add            # bind this repo into the focused vault

Code navigation

Query Grove's language intelligence at a source location, given as FILE:LINE or FILE:LINE:COL. Paths that themselves contain : are tolerated — the trailing numbers are taken as line and column.

Command Purpose
grove refs <FILE:LINE[:COL]> Find references to the symbol at that location.
grove def <FILE:LINE[:COL]> Go to its definition.
grove impl <FILE:LINE[:COL]> Go to implementations of an interface or trait.
grove hover <FILE:LINE[:COL]> Type signature and docs.
grove symbols <FILE> Outline of the file's named symbols.
grove def src/app.ts:42:10

Raw MCP access

Every Grove MCP tool is reachable even without a dedicated verb:

grove tools                                  # list the available tools
grove mcp call <tool> '<json-args>'          # call one directly
echo '{"name":"scratch.md"}' | grove mcp call note_read -

grove tools prints each tool with a one-line description (or full JSON with --json). For grove mcp call, the arguments are a JSON object, or - to read the JSON from stdin. See MCP integration for the full tool surface.

Shell completions

Generate a completion script for your shell and source it however your shell expects:

grove completions zsh  > ~/.grove-completions.zsh   # bash | zsh | fish | powershell | elvish

Scripting

The CLI is built to compose:

  • --json on any verb prints the raw tool result — pipe it to jq.
  • Exit codes are meaningful — tool errors and an unreachable Grove exit non-zero, so grove status && … gates on readiness.
  • Stdin — anywhere a body or JSON argument is expected, - (or omitting it) reads stdin, so you can pipe content in.
grove --json comments pending | jq '.[] | .file'

Uninstall

grove uninstall removes Grove from the machine while keeping every Markdown note — it deletes Grove's bookkeeping (.grove/, task worktrees, settings, language servers, caches, logs) and leaves your vault as a plain Markdown folder you can open in any editor. Grove must be quit first (there's also a Settings → Uninstall flow that hands off to this).

Flag Effect
--dry-run List what would be removed, then exit without touching anything.
-y, --yes Skip the typed confirmation.
--store <PATH> Clean a specific vault (default: the one containing the current directory).
--keep-worktrees Keep task worktrees and their git registrations.
--keep-app-data Keep settings, language servers, caches, and logs.
--keep-cli Leave the grove command on your PATH.
--discard-pending Remove worktrees without first committing their uncommitted changes — destroys pending work; off by default.
grove uninstall --dry-run     # see exactly what would go

Uninstalling writes a Markdown log of what was removed and kept to the vault (or ~/grove-uninstall-log.md). Grove.app itself stays in /Applications — drag it to the Trash to finish.