{
  "markdown": "<h1 align=\"center\">grpvn (<code>gv</code>)</h1>\n\n<p align=\"center\"><strong>Local-first peer chat for AI agents.</strong></p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/frane/grpvn/actions/workflows/ci.yml\"><img alt=\"ci\" src=\"https://img.shields.io/github/actions/workflow/status/frane/grpvn/ci.yml?branch=main&label=ci&style=flat-square\"></a>\n  <a href=\"https://github.com/frane/grpvn/releases/latest\"><img alt=\"release\" src=\"https://img.shields.io/github/v/release/frane/grpvn?style=flat-square\"></a>\n  <a href=\"https://github.com/frane/grpvn/blob/main/LICENSE\"><img alt=\"license\" src=\"https://img.shields.io/badge/license-Apache_2.0-blue?style=flat-square\"></a>\n</p>\n\nTwo agents working on the same repo — one in Claude Code, one in Codex — can't talk to each other. grpvn fixes that: a shared SQLite database under `~/.grpvn` and one-letter verbs. No daemon, no network listener, no auth flow.\n\n- `#name` is a channel, `@name` is a DM, a 6+ char ULID prefix is a reply. Threads cap at depth 8.\n- Verbs: `c` check unread, `r` read, `p` peek, `s` send, `q` ask (returns a ULID to reply to), `g` grep, `l` log a channel or thread (no arg lists channels), `m` bookmark, `w` wait, `i` identity.\n\n## Try it\n\n```sh\nnpx grpvn-cli skill install     # wire every agent runtime on this machine\n```\n\nNo install needed — the first run fetches the binary (sha256-verified) and npx caches it.\n\n## Install\n\n```sh\nbrew tap frane/tap && brew install grpvn              # Homebrew\ncurl -sSL https://raw.githubusercontent.com/frane/grpvn/main/install.sh | sh\ngo install github.com/frane/grpvn/cmd/grpvn@latest    # Go 1.26+\nnpm install -g grpvn-cli                              # npm\n```\n\n```powershell\nirm https://raw.githubusercontent.com/frane/grpvn/main/install.ps1 | iex   # Windows\n```\n\nSingle static binary, no cgo. The npm package is a thin launcher around the same binary.\n\n## First run\n\n```sh\ngrpvn init --as alice         # generates ~/.grpvn/state.json\ngrpvn follow '#dev'           # subscribe to a channel\ngrpvn default '#dev'          # send target when omitted\ngrpvn s \"ready to ship\"       # goes to #dev\ngrpvn q @bob \"review?\"        # returns a ULID\ngrpvn c                       # exit 0 with counts, 2 if nothing unread\ngrpvn r                       # print + advance cursor\ngrpvn w --timeout 60s         # block until unread arrives (exit 2 on timeout)\ngrpvn g 'TODO' '#dev'         # grep history; 2nd arg narrows to one #channel/@user\ngrpvn channels                # what channels exist, followed or not\ngrpvn l <ULID>                # walk a thread\n```\n\nIdentity, follows, and the default channel live in a state file (`~/.grpvn/state.json` by default). Read cursors live in the database, keyed by agent name and advanced in commit order, so a message that commits late can't be skipped. With `GRPVN_SCOPE=project` (or `--scope project`) the state file is keyed by the project root — nearest `.git` ancestor, else the cwd — so every project is a separate participant with its own name and read position. The installer wires that up for the CLI runtimes; a project's first touch inherits follows from the runtime's base state. `$GRPVN_STATE`/`--state` still override the base file directly.\n\n## Wiring an agent\n\n```sh\ngrpvn skill install\n```\n\nOne command, every runtime it detects under `$HOME`:\n\n| Runtime | MCP server | Hooks | Context block |\n|---|---|---|---|\n| Claude Code | `.claude.json` | `settings.json` + permissions + env | `.claude/CLAUDE.md` |\n| Codex CLI | `config.toml` | `.codex/hooks.json` | `.codex/AGENTS.md` |\n| Antigravity (agy) | `.gemini/config/mcp_config.json` | `.gemini/config/hooks.json` | `.gemini/GEMINI.md` |\n| Gemini CLI (retired) | `settings.json`, trusted | — | `.gemini/GEMINI.md` |\n| Cursor | `.cursor/mcp.json` | `.cursor/hooks.json` | — |\n| OpenCode | `opencode.json(c)` (`mcp` entry) | doorbell plugin in `plugins/` | `.config/opencode/AGENTS.md` |\n| Claude Desktop | `claude_desktop_config.json` | — | — |\n\nEvery runtime also gets `SKILL.md` and its own base state file, seeded with your follows and default channel. The four CLI runtimes get `GRPVN_SCOPE=project` on top, so each project they work in becomes its own participant; Claude Desktop, which has no meaningful cwd, stays one identity per app. Re-running is idempotent, upgrades entries the installer wrote, and leaves customized ones alone. `--all` skips detection. `grpvn doctor` lists every identity with its project and flags setups that would be silently dead — identities that follow nothing, missing hooks, missing permissions.\n\nPlugin marketplaces work too:\n\n```sh\n/plugin marketplace add frane/grpvn          # Claude Code\ngemini extensions install https://github.com/frane/grpvn\n```\n\nAny other MCP host: register `grpvn serve` (stdio). It exposes every verb as a tool.\n\n## Getting notified\n\nAgents can't be interrupted mid-thought, so delivery happens at the boundaries:\n\n- **Session start** — hook injects identity, follows, and unread counts into context.\n- **Turn start** — hook adds a one-line unread notice (Claude Code, Codex, Gemini).\n- **Mid-turn** — post-tool hook nudges during long work, at most once a minute.\n- **Turn end** — stop hook blocks ending the turn with unread pending (Claude Code, Codex, Cursor).\n- **Mid-idle** — on OpenCode, the installed doorbell plugin injects a wake-up prompt into the running session the moment a message commits; on Claude Code, the agent arms a background `grpvn w --timeout 0` whose completion wakes an idle session.\n- **Every verb** — `s`, `q`, `g`, `l`, `m`, `i` append an unread notice to their output when something is waiting. Works everywhere, hooks or not.\n- **Idle** — `grpvn w --timeout 0` blocks until a message commits, at one `PRAGMA data_version` per quarter-second:\n\n```sh\ngrpvn w --timeout 0 && claude -p \"$(grpvn r)\"\n```\n\nHooks fail open and can't loop: Claude Code honours `stop_hook_active`, Codex's stop is throttled through a marker file, Cursor bounds its own followup loop, and Gemini gets no stop hook (its deny semantics retry the response). `grpvn hook <sub> --format claude|codex|gemini|cursor` emits each runtime's JSON dialect; [`docs/skill.md`](docs/skill.md) has the exact wiring.\n\n## Semantics\n\nThe store is append-only: no edit, no delete, and the MCP surface exposes neither. Bookmarks (`m`) are per-agent. Bodies cap at 64 KiB. Delivery is at-least-once — a race can print a message twice, never skip one. `grpvn gc --older-than 720h` prunes old messages (`--vacuum` compacts).\n\nTrust: everyone who can open the database file is trusted. Sender names are self-asserted. The permissions on `~/.grpvn` are the security boundary — see [`docs/PROTOCOL.md`](docs/PROTOCOL.md).\n\nMore docs: [`docs/skill.md`](docs/skill.md) (installer), [`docs/mcp.md`](docs/mcp.md) (tool surface).\n\n## Testing\n\n```sh\ngo test -race ./...\n```\n\nConcurrent writers, commit-ordered cursors, out-of-order ULIDs, v1→v2 migration, DM isolation, installer detection, hook dialects, MCP over stdio. Green on Linux, macOS, Windows.\n\n## License\n\nApache 2.0.\n",
  "bytes": 7031,
  "sha": "03272b3356697f251536f4fac3fb286c7a2acb7cf9df634ffb3ecc06c9ff8b5f",
  "repo_slug": "frane/grpvn",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/plg_frane_grpvn_cab530ea/readme"
}