{
  "markdown": "# Catenary\n\n[![CI](https://github.com/TwoWells/Catenary/actions/workflows/ci.yml/badge.svg)](https://github.com/TwoWells/Catenary/actions/workflows/ci.yml)\n[![CD](https://github.com/TwoWells/Catenary/actions/workflows/cd.yml/badge.svg)](https://github.com/TwoWells/Catenary/actions/workflows/cd.yml)\n\n<img width=\"1280\" height=\"640\" alt=\"github_catenary_hero_image\" src=\"https://github.com/user-attachments/assets/1f797daa-94bc-4ffd-bc85-f445da88d1e4\" />\n\n[![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg?style=flat-square)](https://github.com/TwoWells/Catenary/blob/main/LICENSE)\n[![Language: Rust](https://img.shields.io/badge/Language-Rust-orange.svg?style=flat-square&logo=rust&logoColor=white)](https://www.rust-lang.org/)\n[![Protocol: MCP](https://img.shields.io/badge/Protocol-MCP-8A2BE2.svg?style=flat-square)](https://modelcontextprotocol.io/)\n[![GitHub Discussions](https://img.shields.io/github/discussions/TwoWells/Catenary?style=flat-square&color=008080&logo=github)](https://github.com/TwoWells/Catenary/discussions)\n\nCatenary hands an AI coding agent a small, opinionated set of\ncode-intelligent commands — and a hook that keeps it on them. Reach for\n`grep` and you're redirected to `catenary grep`; reach for `ls` or `find`\nand you get `catenary glob`. Every command the agent can run is backed by a\nlanguage server, so it navigates code by meaning instead of brute-forcing\ntext. The generic path isn't blocked for safety — it's off the menu, so\nthe code-intelligent one is the only path left.\n\n## Why Catenary\n\nExposing language-server tools to an agent isn't novel anymore — most\ncoding CLIs do it, and Catenary did it early. But *having* a tool and\n*using* it are different things. Give an agent `grep`, `find`, raw file\nreads, **and** LSP navigation, and it reaches for whatever's nearest —\nusually brute-force text scanning that burns context and misses structure.\n\nCatenary takes the choice away. It exposes one curated, code-intelligent\nsurface and enforces it:\n\n- A `PreToolUse` hook runs an **allowlist** over every shell command the\n  agent issues.\n- Denied commands aren't dead ends — each denial **names the\n  code-intelligent command to run instead** (`grep` → `catenary grep`,\n  `ls`/`find` → `catenary glob`).\n- Edits flow through the host's tracked Edit/Write tools, so LSP\n  diagnostics come back automatically when the agent runs `catenary\n  diagnostics`.\n\nThe result is a workflow the agent follows by construction, not by\nprompt-engineering.\n\n**This is enforcement of a workflow, not a security sandbox.** The hook is\na cooperative contract — a Makefile can still run anything, and Catenary\ndoesn't isolate the filesystem or environment. Its job is narrower and\nmore useful: keep the agent's reads on code-intelligent search and its\nwrites on a tracked path, so navigation is structural and diagnostics are\nfree.\n\n### Less context, more signal\n\nA grep-and-read loop pulls whole files into the agent's context window and\nre-processes them every turn. `catenary grep` answers with the symbol, its\nsignature, and where it's used — tens of tokens instead of thousands.\nDiagnostics arrive through `catenary diagnostics` stdout, so the agent\nnever re-reads a file just to check whether its edit compiled.\n\n## The surface\n\n**Search** — always available, no setup beyond installation:\n\n| Command | What it does |\n|---------|--------------|\n| `catenary grep <pattern>` | Symbol, reference, and text search — LSP-enriched within tracked roots |\n| `catenary glob <path>` | File outlines, directory listings, glob matches |\n\n**The edit → diagnostics loop** — run in the host's shell tool:\n\n```bash\n# Edit files with the host's native Edit/Write tools. Editing starts\n# automatically on the first change to a server-covered file — there is\n# no start step.\ncatenary diagnostics      # print LSP diagnostics for every file you\n                          # touched, then clear the set\n```\n\n`catenary diagnostics` is the *end* of an edit batch: it opens the\nmodified files on their servers, waits for each to settle, and prints the\nerrors and warnings — like a linter, it's silent on success. For sweeps\ntoo broad for per-file edits, reach for native `sed -i` — the hook\nresolves its write-set and folds the changed files into the same\ndiagnostics batch.\n\n**Workspace roots** — manage which directories are indexed:\n\n```bash\ncatenary roots add <path>   # index a directory\ncatenary roots rm <path>\ncatenary roots ls\n```\n\n## How it fits together\n\nOne daemon per host manages a shared pool of language servers. Multiple\nagents connect over a Unix socket and share those servers — a single\n`rust-analyzer` serves every session on the same project. Catenary reaches\nthe agent through four decoupled surfaces; none depends on the others.\n\n```\nagents ──▶  Catenary daemon  ──▶  shared LSP server pool\n                                  rust-analyzer · pyright · gopls · …\n\nreached through four decoupled surfaces:\n\n  CLI     grep · glob · diagnostics         — via the host's shell tool\n  Hooks   allowlist enforcement             — one PreToolUse hook\n  MCP     heartbeat + workspace roots       — no query tools\n  TUI     live observability                — protocol & trace traffic\n```\n\n- **CLI** — the code-intelligent commands above, invoked through the\n  host's shell tool. Stateless: each command connects to the daemon,\n  delegates to the right language servers, and prints to stdout.\n- **Hooks** — the `PreToolUse` allowlist that enforces the workflow and\n  tracks edited files for the diagnostics batch.\n- **MCP** — a heartbeat only: the protocol handshake, the workspace-roots\n  channel, and user-facing notifications. It advertises **no** query tools.\n- **TUI** — real-time observability across every session and language\n  server.\n\n## Quick Start\n\n### 1. Install\n\n**Homebrew (macOS and Linux):**\n\n```bash\nbrew install twowells/tap/catenary\n```\n\n> Switching from a `cargo install` (the previously recommended path)?\n> Run `cargo uninstall catenary-cli` first — or `cargo uninstall\n> catenary-mcp` if you installed before 2.1.0, when the crate carried\n> that name. `~/.cargo/bin` usually precedes brew's bin dir on `PATH`,\n> so the stale binary keeps answering otherwise.\n\n**Prebuilt binary (Linux x86_64 / macOS arm64):**\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/TwoWells/Catenary/main/install.sh | sh\n```\n\n**From source (any platform with a Rust toolchain):**\n\n```bash\ncargo install catenary-cli\n```\n\nThe `catenary` binary must be on your `PATH` before configuring any\nclient. Plugins and extensions provide hooks and the MCP declaration but\n**do not include the binary** — this step is required.\n\n> **Upgrading from 1.x?** Every breaking change in 2.0 is to user\n> configuration — read the\n> [migration guide](https://twowells.github.io/Catenary/stable/migrating-to-2.0.html)\n> before upgrading, and run `catenary doctor` after: it flags each stale\n> config form with the exact rename.\n\n### 2. Configure language servers\n\nAdd your language servers to `~/.config/catenary/config.toml`:\n\n```toml\n# The [lsp.server.*] section key IS the binary Catenary spawns; bind a\n# language to it under [lsp.language.*]. Add `path = \"/abs/path\"` only to\n# relocate a binary that is not on PATH.\n[lsp.server.rust-analyzer]\n\n[lsp.server.pyright-langserver]\nargs = [\"--stdio\"]\n\n[lsp.language.rust]\nservers = [\"rust-analyzer\"]\n\n[lsp.language.python]\nservers = [\"pyright-langserver\"]\n```\n\nCatenary can install the servers itself. Opt in with:\n\n```toml\n[servers]\nauto_install = true\n```\n\nAny configured server that has passed Catenary's conformance gate is then\ninstalled at its vetted, pinned version into a Catenary-owned directory —\nin the background, at session start, with no per-server install step.\nServers already on `PATH` are left alone. The opt-in is honored from your\nuser config only; a project `.catenary.toml` can never switch it on.\n(`catenary install` is unrelated: it installs host plugins, not language\nservers.)\n\n### 3. Connect your agent\n\n**Claude Code**\n```bash\nclaude plugin marketplace add TwoWells/Catenary\nclaude plugin install catenary@catenary\n```\n\n**Antigravity CLI** — copy `plugins/catenary-antigravity/` to\n`.agents/plugins/catenary/` in your workspace.\n\n### 4. Verify\n\n```bash\ncatenary doctor\n```\n\n`doctor` reports each configured server's status (`ready`, `command not\nfound`, `spawn failed`, `initialize failed`) and whether the host's hooks\nare installed and current. Managed installs count as installed, and a\nsystem-installed server whose version drifts from the vetted pin draws an\nadvisory finding naming both versions. Pass a server name (`catenary\ndoctor rust-analyzer`) for verbose single-server diagnostics.\n\n## Observability\n\nRun `catenary` in a terminal to launch the TUI dashboard — a live view of\nevery session, every language server, and the protocol traffic between\nthem. Catenary keeps a `state.json` snapshot of live state and streams\nfull protocol and trace detail to a sharded JSONL telemetry firehose; the\nTUI reads the snapshot, and `catenary query` reads the firehose. (There is\nno SQLite database — a legacy one is drained on startup.)\n\n| Command | Description |\n|---------|-------------|\n| `catenary` | Launch the TUI dashboard |\n| `catenary query` | Query the telemetry firehose (by session, server, tool, time, …) |\n| `catenary doctor` | Verify language servers and hook installation |\n| `catenary version` | Show the CLI and running-daemon versions |\n| `catenary stop` | Stop the running daemon |\n\n## Documentation\n\nFull documentation at **[twowells.github.io/Catenary](https://twowells.github.io/Catenary/)**\n\n- **[Installation](https://twowells.github.io/Catenary/stable/installation.html)** — setup for Claude Code and Antigravity CLI\n- **[Configuration](https://twowells.github.io/Catenary/stable/configuration.html)** — language servers, routing, command allowlist\n- **[CLI & Dashboard](https://twowells.github.io/Catenary/stable/cli.html)** — the command surface and TUI dashboard\n\n## License\n\n**AGPL-3.0-or-later** — see [LICENSE](LICENSE) for details.\n\n**Commercial licensing** available for proprietary use — see\n[LICENSE-COMMERCIAL](LICENSE-COMMERCIAL). Contact `contact@twowells.dev`.\n",
  "bytes": 10201,
  "sha": "50928d18fb274962b62f13166ba22f4e881924e3de3adac30a457eae0ce10019",
  "repo_slug": "twowells/catenary",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/plg_twowells_catenary_8c90556b/readme"
}