Skip to content

maikbasel/repograph

Repository files navigation

repograph

CI crates.io License: MIT

A CLI tool for registering, grouping, and exposing local git repositories as structured context for AI agents and humans.

Getting StartedInstallCommandsJSON outputShell integrationExit codes

Getting Started

Three steps from zero to "my AI agent knows about my repos".

1. Install

cargo install repograph if you have a Rust toolchain (1.85+). Without one, use Homebrew, the Linux/macOS shell installer, the Windows PowerShell installer, or a prebuilt binary. Copy-paste commands for every method live under Install.

2. Run interactive setup

repograph init

This walks you through:

  • Pick your agent toolchain(s): claude-code, agents-md, cursor, aider, windsurf, copilot. Multi-select; detection-preselected.
  • Pick a projects root: the directory where your git clones live (e.g. ~/code). Persisted to config; asked once.
  • Bulk-register repos: every unregistered git repo under the projects root is offered as a checkbox. Add extra paths outside the root in the same pass.
  • Assign repos to workspaces: group related repos under a name (e.g. acme) for filtered listing and context aggregation.

repograph init also drops a per-agent instruction file (a "skill" for Claude Code, AGENTS.md for agents-md, .cursor/rules/repograph.mdc for Cursor, …) so the agent learns when to call repograph on its own. The full path matrix is under Per-agent artifact installation.

Non-interactive variant for dotfiles / CI: repograph init --no-prompt --agents claude-code,cursor --scope user.

3. Use it

repograph list                                  # see what's registered
repograph status                                # branch + working-tree state across all repos
repograph status --workspace acme               # scoped to one workspace
repograph context --workspace acme              # JSON payload of every CLAUDE.md / AGENTS.md / .cursorrules / … in the workspace
eval "$(repograph switch repograph)"            # cd to a registered repo by name (see Shell integration below for the rg-cd wrapper)
repograph doctor                                # read-only health check: missing paths, dangling members, missing agent docs

After init, run repograph doctor as a smoke test. If the agent artifact landed correctly you'll see a row like AgentDocPresent ok <repo> / claude-code for every repo × agent combination. Anything other than ok/warn is a problem; see Exit codes and the Doctor section for the full check catalog.

Install

Four ways in, by what you already have:

You have Use Gets you
brew (macOS, Linux) Homebrew Auto-upgrades with brew upgrade
Neither Rust nor brew Shell / PowerShell installer Prebuilt binary, no toolchain
A Rust toolchain (1.85+) crates.io Builds from source
An air-gapped or scripted setup Prebuilt tarball Manual placement + verification

Homebrew (macOS, Linux)

brew install maikbasel/tap/repograph

Shell installer (Linux, macOS)

Fetches the prebuilt binary for your platform into ~/.cargo/bin (or $CARGO_HOME/bin). No Rust toolchain needed.

curl -LsSf https://github.com/maikbasel/repograph/releases/latest/download/repograph-installer.sh | sh

PowerShell installer (Windows)

irm https://github.com/maikbasel/repograph/releases/latest/download/repograph-installer.ps1 | iex

From crates.io

Compiles from source, so you need Rust 1.85 or newer.

cargo install repograph

Pre-built binaries

Tarballs for x86_64-linux-gnu, aarch64-linux-gnu, x86_64-apple-darwin, aarch64-apple-darwin, and x86_64-pc-windows-msvc hang off every GitHub Release, each with a SHA-256 checksum and a detached GPG signature (.asc). Verify a download against the maintainer's key before running it; steps are in SECURITY.md.

Commands

Command Purpose
repograph add <path> Register a local git repository (validated via git2). Stores the canonical absolute path.
repograph completions <shell> Emit a static completion script for bash, zsh, fish, powershell, or elvish on stdout. Generated against the live Cli so the script never drifts from the actual command surface.
repograph context [<repos>…] [--workspace <name>] [--json] Aggregate per-repo agent docs (CLAUDE.md, AGENTS.md, .cursor/rules/*.md, .cursorrules, CONVENTIONS.md, .windsurfrules, .github/copilot-instructions.md) into one payload. JSON when piped or --json; Markdown when stdout is a TTY (paste-ready into a chat). Per-repo / per-file failures surface as inline warnings, not aborts.
repograph doctor [--json] Read-only health check over the config and every registered repo: missing paths, dangling workspace members, missing agent docs, malformed config. Coloured comfy-table summary on TTY; schema_version: 1 JSON envelope when piped or --json. Zero-network.
repograph init Interactive setup: pick the agent toolchain(s) you use; bulk-register repos found under your projects root (multiselect) and assign them to a workspace in one pass; add more at custom paths. Re-running shows a settings panel for editing the selection or resetting everything. Non-interactive: --no-prompt --agents <list>.
repograph list [--json] [--workspace <name>] List the registered repositories. --workspace restricts output to repos in the named workspace. Renders a table on a TTY, JSON envelope when piped or --json is set.
repograph remove <name> Remove a registered repository by name. Workspace memberships are preserved as dangling references; surface them via workspace show.
repograph status [<names>…] [--workspace <name>] [--json] [--fetch] Report branch, upstream, ahead/behind, and working-tree state across one, many, or all registered repos. Read-only; zero-network unless --fetch is set.
repograph switch <name> Emit cd <path> for the named registered repo on stdout, shell-eval-safe (single-quoted when the path contains whitespace or shell metacharacters). Pair with the rg-cd shell function below.
repograph workspace create <name> [--description <text>] Create an empty workspace. Names must match ^[a-z0-9][a-z0-9-]{0,62}$ and may not be default/all/none.
repograph workspace rm <name> Delete a workspace. Registered repos are not touched.
repograph workspace ls [--json] List the registered workspaces with name, description, and member count.
repograph workspace show <name> [--json] Show one workspace's resolved live members and dangling references.
repograph workspace add <workspace> <repo>… Attach one or more registered repos to a workspace. Idempotent on duplicates; atomic on missing repos.
repograph workspace remove <workspace> <repo>… Detach repos from a workspace. Does not deregister the repos themselves.

add accepts --name, --description, and --stack <a,b,c> (comma-separated tags). When --name is omitted, the path's basename is used.

A global --config-dir <PATH> flag overrides the default config directory. The resolution precedence is --config-dir > REPOGRAPH_CONFIG_DIR env var > the platform default ($XDG_CONFIG_HOME/repograph, ~/Library/Application Support/repograph, %APPDATA%\repograph).

Sample config

# ~/.config/repograph/config.toml

[repo.api]
path = "/home/user/code/api"
description = "Core HTTP API service"
stack = ["rust"]

[repo.web]
path = "/home/user/code/web"
stack = ["typescript"]

[workspace.acme]
description = "Acme rebuild"
members = [
    "api",
    "web",
]

[agents]
selected = [
    "claude-code",
    "agents-md",
]

[settings]
projects_root = "/home/user/code"

[agents] is written by repograph init (or auto-prompted by future agent-consuming commands on first run). Presence of the section signals "init has run"; an empty selected = [] is a valid configured state (user opted out of agent docs).

[settings] projects_root records where the user keeps their git projects, asked once during repograph init and reused by every subsequent repo-registration. The env var REPOGRAPH_PROJECT_ROOT overrides this at runtime (useful for CI / sandbox testing / dotfile parity). An empty env value falls through to the config value. Change the stored path anytime via repograph init → "Change project root".

First run

Interactive (TTY):

$ repograph init

A cliclack-driven flow:

  1. Agent multiselect: detection-preselected, deselectable.
  2. Projects root: pick from detected candidates (only those containing ≥1 git repo are surfaced), enter a custom path, or skip. Persisted to [settings] projects_root; asked only once.
  3. Bulk repo registration: multiselect of unregistered git repos under your projects root (no preselection, opt-in per repo), then an optional Register a repo at a custom path? loop for anything outside that root. The free-form path input has filesystem autocomplete (Tab) and ~ expansion. Basename conflicts during bulk add prompt once for an alternative name and otherwise log + skip so the rest of the batch proceeds.
  4. Per-repo workspace routing: when N>0 repos were registered, an outer Add these N repos to workspaces? confirm gates the step. On yes, you first walk an optional create-new loop (gated by a Create new workspaces first? confirm when workspaces already exist; entered directly otherwise) to seed the target pool. Then, for each registered repo in turn, a Workspaces for '<repo>' multiselect lets you pick that specific repo's workspaces: zero, one, or many. Different repos can land in different workspaces; empty picks leave a repo unassigned. The config is saved once at the end. The success log uses singular wording when exactly one repo lands in exactly one workspace; otherwise a multi-line workspace assignments: block lists each assigned repo with its chosen workspaces.
  5. Summary: final agents / repos / workspaces counts plus next-step hints.

Re-running on an existing config shows a settings panel with actions including "Update agent selection", "Change project root", "Register another repo" (re-enters the bulk flow), "Manage workspaces", "Reset everything", "Cancel".

Inside "Manage workspaces", Create asks for a name and then offers an immediate multiselect to populate the new workspace with any registered repos (default yes; skipped when no repos are registered). Add members also uses a multiselect, filtered to the repos that are NOT already in the chosen workspace, so a single trip through the menu can add many repos at once. Empty submissions are valid no-ops; if every registered repo is already a member (or the registry is empty), a WARN log explains the no-op and the menu returns without writing.

Non-interactive (CI, dotfiles, non-TTY):

$ repograph init --no-prompt --agents claude-code,cursor --scope user

--no-prompt requires --agents. The empty string --agents "" is valid and writes selected = [].

--scope <user|project>

Where to install per-agent artifacts (see "Per-agent artifact installation" below). Defaults to user when omitted in an interactive run. Required under --no-prompt when any selected agent has a meaningful scope choice (today: claude-code, windsurf); project-only agents (agents-md, aider, cursor) fall through to the project path regardless of this flag.

--force

Overwrite existing artifacts even outside the managed delimiter block. Without this flag, repograph rewrites only the delimited region of pre-existing files (preserving user-authored content above and below); with it, the file is replaced fresh. Use this to re-assert the canonical body after local edits drift, or to remove user content that has accumulated above the managed block.

Per-agent artifact installation

repograph init writes a native instruction file for each selected agent so the agent's runtime picks it up automatically and learns when to invoke repograph. The path matrix is fixed by each agent's convention:

Agent ID User-scope path Project-scope path
claude-code ~/.claude/skills/repograph/SKILL.md <cwd>/.claude/skills/repograph/SKILL.md
agents-md (project-only) <cwd>/AGENTS.md
cursor (project-only) <cwd>/.cursor/rules/repograph.mdc
aider (project-only) <cwd>/CONVENTIONS.md
windsurf ~/.codeium/windsurf/memories/repograph.md <cwd>/.windsurfrules
copilot (deferred, no v1 writer) (deferred, no v1 writer)

Files that may already contain user-authored prose (AGENTS.md, CONVENTIONS.md, .windsurfrules) are managed by a delimiter pair (<!-- repograph:begin --> … <!-- repograph:end -->); only the delimited region is repograph-managed and only it is rewritten on re-runs. Content above and below the delimiters is byte-preserved. Pass --force to replace the whole file with the bare delimited block. Per-agent install outcomes (Written / Unchanged / Skipped / Failed) are logged to stderr. A failure for one agent does not abort the others; the agent-selection persistence already succeeded.

Selecting copilot is valid but writes no file in v1; the agent's instruction format varies across surfaces (repo-level, editor-level, Copilot Workspace) and no single converged path covers them yet.

Agent registry

repograph init writes the user's selection of agent toolchains to [agents].selected. Each ID maps to a known set of rule-file patterns inside a repository. repograph context inlines the matching files into its payload (one per selected agent, scoped to the in-scope repos).

Agent ID File patterns
claude-code CLAUDE.md
agents-md AGENTS.md
cursor .cursor/rules/*.md, .cursorrules
aider CONVENTIONS.md
windsurf .windsurfrules
copilot .github/copilot-instructions.md

JSON output shapes

repograph list --json emits a repos-keyed envelope:

{ "repos": [ { "name": "...", "path": "...", "description": "...", "stack": [...] } ] }

Empty registry: { "repos": [] }.

repograph workspace ls --json emits a workspaces-keyed envelope:

{ "workspaces": [ { "name": "acme", "description": "Acme rebuild", "members": ["api", "ui"] } ] }

repograph workspace show <name> --json emits a single workspace with members resolved against the registry and a dangling array of names whose repos have been deregistered:

{
  "name": "acme",
  "description": "Acme rebuild",
  "members": [ { "name": "ui", "path": "/home/user/code/ui", "description": null, "stack": [] } ],
  "dangling": ["api"]
}

dangling is always present (even when empty), so agent consumers can detect drift without a key-existence check. A dangling member also produces a WARN line on stderr.

repograph status --json emits a repos-keyed envelope of richer per-repo status entries. The error field is always present (null on healthy rows) so consumers can branch on repo.error != null without a key-existence check:

{
  "repos": [
    {
      "name": "api",
      "path": "/home/user/code/api",
      "branch": "main",
      "upstream": "origin/main",
      "ahead": 0,
      "behind": 0,
      "dirty": false,
      "staged": 0,
      "unstaged": 0,
      "untracked": 0,
      "state": "clean",
      "error": null
    },
    {
      "name": "ui",
      "path": "/home/user/code/ui",
      "branch": "feat/x",
      "upstream": "origin/feat/x",
      "ahead": 2,
      "behind": 0,
      "dirty": true,
      "staged": 1,
      "unstaged": 1,
      "untracked": 0,
      "state": "dirty",
      "error": null
    },
    {
      "name": "ghost",
      "path": "/home/user/code/ghost",
      "branch": null,
      "upstream": null,
      "ahead": 0,
      "behind": 0,
      "dirty": false,
      "staged": 0,
      "unstaged": 0,
      "untracked": 0,
      "state": "missing",
      "error": "no such file or directory"
    }
  ]
}

state is one of clean, dirty, detached, unborn, bare, missing. A missing or broken repo in a batch run does not abort the command (exit 0); the failing row carries a populated error field and a WARN line lands on stderr. Asking for a single broken repo by name (repograph status <name>) exits 3 instead; that's a request, not a batch.

repograph status --workspace acme --json restricts the scope to live members of a workspace (dangling members skipped, parity with list --workspace).

repograph status --fetch is opt-in and runs git fetch against each repo's upstream remote before computing ahead/behind. Without it, no network calls happen. A fetch failure on any one repo populates that repo's error field and isolates the failure; the rest of the batch still completes.

Agent context

repograph context produces the payload an AI agent consumes. Three scope modes, mutually exclusive at the CLI layer:

repograph context                          # every registered repo
repograph context --workspace team-alpha   # members of one workspace
repograph context api ui lib               # explicitly named repos

On first use without [agents] configured, an interactive TTY prompts through the same multiselect as repograph init; non-TTY first use exits 2 and names repograph init.

Output mode:

  • TTY default: Markdown to stdout (paste-ready into Claude / Cursor / ChatGPT). One ## <repo> section per repo, one ### <agent> subsection, one fenced code block per matched file. Fences fall back to ~~~ if the file content contains a backtick fence (so an inlined CLAUDE.md with code samples renders correctly).
  • --json / non-TTY: single-line JSON object on stdout, versioned by schema_version. Each repo block carries name, canonical path, current branch (null for detached / unborn / bare / missing), one agent_docs entry per selected agent (each with sorted files), and an inline warnings array. Top-level warnings carries global issues.
{
  "schema_version": 1,
  "generated_at": "2026-05-24T14:23:11Z",
  "agents": ["claude-code", "cursor"],
  "scope": { "kind": "workspace", "name": "team-alpha" },
  "repos": [
    {
      "name": "api",
      "path": "/home/user/code/api",
      "branch": "main",
      "agent_docs": [
        {
          "agent": "claude-code",
          "files": [ { "path": "CLAUDE.md", "bytes": 1234, "content": "# api\n" } ]
        },
        {
          "agent": "cursor",
          "files": [
            { "path": ".cursor/rules/style.md", "bytes": 567, "content": "" },
            { "path": ".cursorrules", "bytes": 89, "content": "" }
          ]
        }
      ],
      "warnings": []
    }
  ],
  "warnings": []
}

Behavior contract:

  • No truncation. File contents are inlined verbatim. Total bytes is logged on stderr; downstream tooling owns the context-window budget.
  • Per-file errors are inline, not fatal. Unreadable, non-UTF-8, or missing files become warning entries on the enclosing repo's warnings array; the rest of the payload still ships and exit is 0.
  • Bounded filesystem walk. Flat patterns (CLAUDE.md) are existence-checked; glob patterns (.cursor/rules/*.md) are matched against the entries of their known parent directory only, with no recursion into node_modules or anywhere else.
  • Stable ordering. Top-level repos is sorted by name. Each agent_docs array preserves the order of [agents].selected. Each agent's files is sorted by path.
  • Exit codes. 0 success (including success-with-warnings); 2 for clap usage errors or non-TTY without [agents]; 3 for unknown workspace / repo name. 5 is not produced.

Shell integration

repograph switch <name> writes exactly cd <path> (and nothing else) to stdout. Wrap it in a one-line shell function so a single command teleports between registered repos:

# bash / zsh: add to ~/.bashrc or ~/.zshrc
rg-cd() { eval "$(repograph switch "$1")"; }
# fish: add to ~/.config/fish/config.fish
function rg-cd
    eval (repograph switch $argv[1])
end

Then rg-cd api jumps to the registered repo api. Unknown names exit 3 with a did you mean: … hint on stderr when there's a near-miss.

switch does not validate that the path still resolves; that's repograph doctor's job, and the user's shell surfaces a missing-dir cd error on its own. Run repograph doctor when something looks off.

One-time install of completions per shell (regenerate after upgrading repograph):

# bash (per-user)
repograph completions bash > ~/.local/share/bash-completion/completions/repograph
# zsh (assumes the first fpath entry is user-writable)
repograph completions zsh > "${fpath[1]}/_repograph"
# fish
repograph completions fish > ~/.config/fish/completions/repograph.fish
# powershell (per-session; append to $PROFILE for persistence)
repograph completions powershell | Out-String | Invoke-Expression
# elvish (then `use repograph` in rc.elv)
repograph completions elvish > ~/.config/elvish/lib/repograph.elv

Completions are generated against the live Cli struct, so they match the subcommands and flags the binary exposes.

Doctor

repograph doctor runs a read-only catalog of checks against the on-disk config and every registered repo. Findings are coloured rows in a comfy-table when stdout is a TTY; a schema_version: 1 JSON envelope when piped or --json.

Check What it verifies Severity on fail
ConfigPresent Config file exists at the resolved config dir. error
ConfigParse Config file parses as TOML. Only run when ConfigPresent passed. error
AgentsConfigured [agents] section is present in the config. warn
ProjectsRootExists [settings].projects_root, if set, points at an existing directory. warn
RepoPathExists Per repo: the registered path exists on disk. error
RepoIsGitRepo Per repo: the path opens as a git repository (gated by RepoPathExists). error
WorkspaceMembersResolve Per workspace member: the member name resolves to a registered repo. warn
AgentDocPresent Per repo × per selected agent: at least one file matches the pattern set. warn

Every check that passes emits an ok finding too, so you can audit which checks ran against which targets without consulting the catalog separately.

{
  "schema_version": 1,
  "generated_at": "2026-05-24T14:23:11Z",
  "checks": [
    {
      "check": "RepoPathExists",
      "severity": "error",
      "target": "api",
      "message": "path does not exist: /home/user/code/api"
    },
    {
      "check": "AgentDocPresent",
      "severity": "warn",
      "target": "ui / claude-code",
      "message": "no files matched claude-code patterns (CLAUDE.md)"
    }
  ],
  "summary": { "ok": 12, "warn": 1, "error": 1, "total": 14 }
}

Stdout is single-line for clean piping into jq. The checks array is sorted by (severity DESC, check ASC, target ASC): most pressing first, stable order across runs.

Exit codes:

  • 0: every finding is ok or warn (warnings do not gate; safe to wire into a precmd shell hook).
  • 1: at least one error finding. Also returned when the config file is missing; the report is still emitted so you can see what failed.
  • 4: the config file exists but cannot be read (permission denied). No report is emitted; the standard error path takes over.

doctor is read-only and zero-network: no config writes, no git fetch.

Filtering by workspace

repograph list --workspace acme --json

Restricts the registry listing to live members of acme. Dangling members are skipped (see workspace show for the audit view). A non-existent workspace name exits 3.

Exit codes

Code Meaning
0 Success
1 General failure (malformed config, runtime usage error)
2 CLI argument error (clap usage); also: repograph init in non-TTY without --no-prompt --agents; --no-prompt with a scope-bearing agent (claude-code, windsurf) and no --scope; agents-not-configured in non-TTY for agent-consuming commands
3 Resource not found (path is not a git repo, name not registered)
4 Permission denied (cannot read or write the config file)
5 Conflict (name or path already registered)

Workspace layout

crates/
├── repograph-core/   # domain library (no clap, no terminal output)
└── repograph/        # CLI binary, depends on repograph-core

The library is published separately (cargo add repograph-core) so future tools (alternate front-ends, editor plugins, or batch utilities) can share the same domain logic without going through the CLI. Agent integration ships as native per-agent instruction files written by repograph init (see "Per-agent artifact installation" above), not as a separate MCP binary.

Development

cargo check --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --all --check
cargo test --workspace --all-features
cargo deny check

Release pipeline

Releases are automated. Do not manually tag, bump versions, or edit the changelog.

  1. Push conventional commits to master.
  2. release-plz opens or updates a release PR with CHANGELOG.md updates and Cargo.toml version bumps. repograph and repograph-core move in lock-step via a shared version_group.
  3. Merge the release PR → release-plz publishes both crates to crates.io (core first, then bin) and pushes per-package tags (repograph-v*, repograph-core-v*).
  4. The dist-generated release.yml workflow fires on the repograph-v* tag and:
    • Builds binaries for all five target platforms
    • Uploads tarballs, the source tarball, and SHA-256 checksums to the GitHub Release
    • Publishes shell + PowerShell installers
    • Updates the maikbasel/homebrew-tap formula

The security.yml workflow runs rustsec/audit-check daily at 00:00 UTC and on demand via workflow_dispatch. The sign.yml workflow fires after Release completes and attaches GPG-detached signatures (.asc) for every binary, source tarball, and installer script in the GitHub Release. See SECURITY.md for verification instructions.

License

Licensed under the MIT License.

About

A CLI tool for registering, grouping, and exposing local git repositories as structured context for AI agents and humans.

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages