CLI-first code intelligence for AI agents. patch gives agents a small, explicit command set for reading code, finding symbols, searching text, tracing callers, listing files, mapping a codebase, and checking file-level dependencies.
The product goal is simple: make code navigation transparent, predictable, and cheap enough that an agent can recover from a bad query without spiraling into tool thrash.
Generic shell tools force agents to compose too many steps:
- list files
- guess which file matters
- read too much
- grep again
- re-read a narrower slice
patch turns those loops into explicit commands with stable output contracts. The CLI is the product. There is no query-classification shorthand, no hidden mode switch, and no host/editor install flow to understand before using it.
patch uses explicit subcommands only:
patch read <path>
patch symbol find <query>
patch symbol callers <query>
patch search text <query>
patch search regex <pattern>
patch files <pattern>
patch deps <path>
patch mapEvery command supports:
- dense text output by default
--jsonfor a stable machine-readable envelope--budgetto cap response size
Scope-aware commands also accept --scope <dir>.
When you pass --scope <dir>, patch reads at most one .patchignore file from the active scope root itself. patch does not look in parent directories, and it does not merge multiple ignore files.
Traversal commands honor that scope-root .patchignore: files, symbol find, symbol callers, search text, search regex, deps, and map.
Supported launch syntax is unchanged: point --scope at the directory whose root contains the .patchignore file you want honored.
cargo run -- files "*.rs" --scope tests/fixtures/patchignoreIn that example, only tests/fixtures/patchignore/.patchignore is read.
.gitignore is not read.
- read still works for ignored paths because it reads the explicit path you asked for.
- deps accepts an ignored target path but filters traversal-derived results such as discovered dependents.
- Root-relative rules stay anchored to the active scope root, so nested paths are not treated as parent-root matches.
cargo build
cargo run -- symbol find main --scope src
cargo run -- files "*.rs" --scope src
cargo run -- deps src/main.rs
cargo run -- map --scope srcRead a file in full, by line range, by markdown heading, or by JSON selectors.
cargo run -- read README.md --lines 7:17
cargo run -- read README.md --heading "## Command families"
cargo run -- read tests/fixtures/json/users.json --key users.0.accounts
cargo run -- read tests/fixtures/json/root-array.json --index 0:1
cargo run -- read tests/fixtures/json/users.json --key users.0.accounts --index 0:1Use read when you already know the path and need exact content. Markdown --lines remains valid for arbitrary chunk reads; when the first selected line is itself a recognized heading, patch may also suggest a --heading follow-up to read the full section. JSON files always render as TOON text, including --full and selector reads. --key and --index are JSON-only selectors: --key <PATH> drills into a subtree using dot-separated object keys and numeric array segments, and --index START:END slices arrays with zero-based, end-exclusive bounds.
Find symbol definitions and usages with explicit kind filtering.
cargo run -- symbol find main --scope src
cargo run -- symbol find render --scope src/output --kind definitionUse symbol find when the target is code structure, not just matching text.
Find call sites plus second-hop impact.
cargo run -- symbol callers render --scope src/outputUse symbol callers before changing a symbol that may affect downstream code.
Search literal text in comments, strings, docs, and code.
cargo run -- search text "symbol callers" --scope srcUse search text for exact phrases, docs, TODOs, or log strings.
Search with an explicit regex command instead of slash-delimited magic.
cargo run -- search regex "symbol\\s+callers" --scope srcUse search regex when the match pattern is genuinely regular-expression based.
Find files by glob.
cargo run -- files "*.rs" --scope src
cargo run -- files "*.rs" --scope tests/fixtures/patchignoreUse files to narrow the surface area before reading or searching.
Inspect what a file imports and what imports it.
cargo run -- deps src/main.rsUse deps before moving, renaming, or heavily restructuring a file.
If the target path is explicit, deps still accepts it even when that path matches .patchignore, but any traversal-derived results continue to honor the scope-root ignore rules.
Generate a compact structural map of a codebase.
cargo run -- map --scope srcUse map once when entering an unfamiliar repo, then switch to targeted commands.
patch is designed for agent recovery, not just happy-path demos.
Text output is optimized for direct consumption in an agent loop:
- summary header first
MetaEvidenceNextDiagnostics
Empty Next and Diagnostics sections render as (none).
Text errors use the same V2 section layout and render on stderr with a non-zero exit status.
Diagnostics are ordered by severity:
- errors
- warnings
- hints
That ordering is stable across commands so agents can read the useful evidence before deciding whether a recovery hint matters.
JSON output uses a shared envelope across all commands:
{
"command": "symbol.find",
"schema_version": 2,
"ok": true,
"data": {
"meta": {}
},
"next": [],
"diagnostics": []
}Command-specific payloads live under data. data.meta is always present in V2, and top-level next is always present even when empty. Diagnostics contain real warnings, hints, or errors rather than placeholder success entries. See docs/cli-contract.md for the exact contract.
# symbol.find
## Meta
- definitions: 1
- noise: medium
- query: main
- scope: /abs/path/to/src
- stability: medium
- usages: 1
## Evidence
symbol find "main" in /abs/path/to/src — 2 matches
- main.rs:5-15 [definition]
fn main() {
## Next
(none)
## Diagnostics
(none)
cargo run -- files "*.definitely-nope" --scope src --json{
"command": "files",
"schema_version": 2,
"ok": true,
"data": {
"meta": {
"files": 0,
"noise": "low",
"pattern": "*.definitely-nope",
"scope": "/abs/path/to/src",
"stability": "high"
},
"files": [],
"pattern": "*.definitely-nope",
"scope": "/abs/path/to/src"
},
"next": [
{
"kind": "suggestion",
"message": "Try a broader or available file pattern for /abs/path/to/src",
"command": "patch files \"*.rs\" --scope /abs/path/to/src",
"confidence": "high"
}
],
"diagnostics": [
{
"level": "hint",
"code": "no_file_matches",
"message": "no file matches found for \"*.definitely-nope\""
}
]
}cargo run -- search regex "(" --scope src# search.regex
## Meta
(none)
## Evidence
(none)
## Next
(none)
## Diagnostics
- error: invalid query "(": regex parse error: … [code: invalid_query]
cargo run -- read README.md --lines 7:17# read
## Meta
- file_kind: markdown
- heading_aligned: true
- noise: low
- path: README.md
- selector_display: 7:17
- selector_kind: lines
- stability: high
## Evidence
# README.md (11 lines, ~103 tokens) [section]
7 │ ## Why patch exists
8 │
9 │ Generic shell tools force agents to compose too many steps:
## Next
- Read the full markdown section starting at line 7 with --heading (command: patch read "README.md" --heading "## Why patch exists"; confidence: high)
## Diagnostics
(none)
patch does not silently reinterpret user intent.
- Wrong selector? Return an error diagnostic.
- No matches? Return a sparse recovery hint plus a high-confidence
Nextsuggestion when one is available. - Probably meant a different command? Return a high-confidence suggestion only.
The CLI prefers explicit nudges over clever fallback behavior because predictable failures are easier for agents to recover from than magical behavior that changes across releases.
Current output limits are intentionally strict:
- at most 2 warnings
- at most 1 hint
- invalid command inputs aim to produce exactly 1 error diagnostic
For an unfamiliar codebase:
map --scope srcfiles "*.rs" --scope srcsymbol find <target> --scope srcsymbol callers <target> --scope srcbefore signature changesread <path>only after you know the exact file or section you need
For a likely text match rather than a symbol:
search textsearch regexonly if the literal search is too broad
For change planning:
deps <path>symbol callers <symbol>
When a scoped traversal misses an expected path, check the scope root for .patchignore before assuming the file is unavailable.
cargo install --path .The repository ships a CLI-only installer that targets a user-local bin directory.
./install.sh --dry-run
./install.shThe installer does not mutate editor settings, host configs, or external tool manifests.
cargo build --release
cargo test
cargo clippy -- -D warnings
cargo fmt --checkpatch aims to keep these surfaces stable:
- explicit subcommand names
- shared JSON envelope
- diagnostics schema
- text section ordering
What is intentionally not supported:
- legacy query-shorthand mode
- removed install hosts or editor-integration flows
- undocumented aliases or fuzzy flag spellings
If you change command names, JSON shape, or diagnostic behavior, update:
README.mddocs/cli-contract.md- relevant integration tests in
tests/
MIT