AI agents edit code blind. HashPilot gives them cryptographic certainty.
HashPilot is the safe, verifiable, observable file-editing primitive for coding agents. While other tools match text or rewrite whole files, HashPilot anchors every edit to a cryptographic digest — if the hash doesn't match, nothing changes. With AST-native editing, provenance trails, and a verify pipeline, it gives agents deterministic control over how code changes land on disk. It occupies the one position no competitor does: hash-anchored + AST-aware + provenance-tracked. See
docs/COMPETITIVE-ANALYSIS.md.
Landing page: https://bigknoxy.github.io/HashPilot/ Architecture: docs/ARCHITECTURE.md · MCP setup: docs/INTEGRATION-MCP.md · CLI reference: docs/CLI-QUICKREF.md · Roadmap & backlog: ROADMAP.md
Every edit is anchored by a SHA-256 hash — not a fragile line number or a fuzzy text match. If the hash matches, you're editing the right content. No guessing, no retries, no silent corruption.
HashPilot is a CLI (hashpilot) and editing protocol that replaces fuzzy text editing with precision operations:
- Hash-anchored replacement — target content by its cryptographic fingerprint
- AST-aware refactoring — rename symbols, replace function bodies, manage imports (TypeScript, JS, Python, Go, Rust)
- Stale-anchor detection — catch race conditions before they corrupt files
- Plan-and-execute intents — describe a multi-file change, HashPilot discovers call sites and executes every step
- Provenance tracking — every edit records who, what, when, and why (like
git blamefor agents)
It's a global, tool-agnostic core. Claude Code, OpenCode, Pi, Codex CLI, Cursor — any agent that edits files.
You're editing files fundamentally differently than a human. You don't have an IDE. You can't visually locate the right line. You don't keep a working memory of the file structure. Yet most tools expect you to edit the same way a human would:
| Problem | What Happens | Cost |
|---|---|---|
| Line counting | "Replace line 42" breaks when one import shifts every line |
+3 retries, corrupted file |
| Fuzzy find-replace | Guess the right text to match — or match the wrong occurrence | Silent corruption, wasted tokens |
| Race conditions | Two edits to the same file interleave | Invalid syntax, agent confusion |
| No verification | Edit, move on. No check that it compiles | Bug ships to production |
| HashPilot Feature | What It Does | For Whom |
|---|---|---|
| SHA-256 anchors | Target content by its fingerprint — not a line number | Agents — never edit the wrong content |
| 3-tier routing | AST → Hash → Diff. Auto-selects the best strategy per file+op | Agents — one command, right tool |
| Stale recovery | Detects out-of-date edits and re-applies transparently | Agents — survive race conditions |
| Intents (M5) | "Add a parameter to processData" → discovers call sites, plans, executes |
Agents — multi-file refactors in one shot |
| Verification bundling | Run formatter + linter + typechecker + tests in one command | Developers — know it works |
| Provenance | provenance query src/utils.ts shows edit history per line |
Teams — audit trail for AI changes |
| Telemetry | Structured JSONL with health reports and trend analysis | Operators — monitor agent edit reliability |
You use AI coding agents to ship faster. When your agent uses HashPilot:
- Edits work on the first try. No more "oops, that was the wrong line."
- Every change is auditable. Provenance tells you who changed what and why.
- You can trust AI refactors. Multi-file renames execute correctly across your whole codebase.
- It's invisible. The agent uses HashPilot automatically — you just see better results.
You edit source files every time a user asks for a change. With HashPilot:
- You know the hash is correct.
replace-hashtargets content by its SHA-256 fingerprint. No ambiguity. - You don't need to re-read the file. The hash from
read-manyis valid until the file changes. That's one less API round-trip. - AST edits are syntax-safe.
rename-symbol,replace-body,add-import— tree-sitter guarantees the edit is structurally valid.rename-symbolis file-scoped and binding-aware: it renames a symbol and its references within the target file, but refuses withAMBIGUOUS_SYMBOLwhen the same name binds more than one symbol in that file — a shadowed local, a foreignimport, or a duplicate top-level declaration — so a file-wide rename never clobbers an unintended binding. Disambiguate by scoping the rename or renaming each declaration separately. - Intents handle the blast radius. One
intentcommand handles definition + all call sites + verification. - Telemetry tells you when something's wrong. Stale-anchor rates, per-language failure rates, verify pass rates — all queryable.
You need reproducible, auditable AI workflows:
- Provenance = compliance. Every edit logged with actor, task ID, and reason.
- Config = consistency. Team-wide route policies in
.hashpilot.json. - Batch = scale. Same edit applied across 100+ files, parallel or serial.
- Verify = confidence. Auto-detect project tools, run checks, revert on failure.
curl -fsSL https://raw.githubusercontent.com/bigknoxy/HashPilot/main/scripts/install.sh | bashWhat it does: Downloads HashPilot, auto-installs Bun 1.2+ if not present, installs all dependencies, configures adapters for Claude Code, OpenCode, and Pi automatically.
📋 Copy-paste for your agent's install method
For any agent that runs shell commands:
curl -fsSL https://raw.githubusercontent.com/bigknoxy/HashPilot/main/scripts/install.sh | bashFor agents with a "run command" or "execute" tool:
{
"command": "curl -fsSL https://raw.githubusercontent.com/bigknoxy/HashPilot/main/scripts/install.sh | bash",
"description": "Install HashPilot structured editing CLI"
}Claude Code: The installer adds HashPilot commands to ~/.claude/CLAUDE.md automatically.
OpenCode: The installer adds the skill at ~/.config/opencode/skills/hashpilot/ and subagent at ~/.config/opencode/agent/hashpilot.md.
Pi: The installer adds the extension at ~/.pi/agent/extensions/hashpilot.ts with /hp slash command.
bun install
bun run install-cli # symlinks the launcher AND adds ~/.agentic-tools/bin to your shell rc
exec $SHELL -l # or: export PATH="$HOME/.agentic-tools/bin:$PATH"
hashpilot doctorinstall-cli writes a marked block (# >>> hashpilot path >>>) into the rc file
for your current $SHELL; hashpilot uninstall removes it. doctor's
bin-on-path check reports when the directory is missing from PATH, so a
launcher that exists but cannot be resolved is a visible failure rather than a
silent one.
hashpilot upgrade # upgrade to latest from main
hashpilot upgrade --dry-run # preview what would happenhashpilot uninstall # remove everything (prompts for confirmation)
hashpilot uninstall --keep-config # remove binaries, keep config + telemetry
hashpilot uninstall --dry-run # preview what would be removed
hashpilot uninstall --force # skip confirmation promptHashPilot is Bun-only today. The core uses Bun APIs and ships as TypeScript source, so there is no Node-compatible build yet.
| Runtime | Supported | Notes |
|---|---|---|
| Bun ≥ 1.2 | ✅ | The only supported runtime. Enforced by engines.bun. |
| Bun < 1.2 | ❌ | npm/bun warn at install time via engines. |
| Node.js (any version) | ❌ | hashpilot exits 127 with an install message pointing at https://bun.sh. |
The hashpilot binary is a small CommonJS shim (src/cli-node.cjs) that any Node can
parse. It hands off to Bun and forwards Bun's exit status unchanged, so a Node-only machine
gets one actionable line instead of a syntax-error stack trace.
# Verify it works — human-readable by default in a TTY, JSON when piped or in CI
hashpilot doctor # TTY: human-readable summary
hashpilot doctor --format json
# --json still works but is deprecated (emits a warning on stderr)
hashpilot doctor --json
# See your merged config
hashpilot config# 1. Read a file — get its content hash
hashpilot read-many src/main.ts
# 2. Edit by hash — target the exact content
HASH="abc123..." # from read-many output
hashpilot replace-hash src/main.ts "$HASH" " port: 8080" --range 5:5
# 3. Verify nothing broke
hashpilot verify-changes src/main.ts --auto-detectEditing the same region twice needs no second read: a successful replace-hash
returns newHash — the hash of the content it just wrote — plus newRange,
where that content now lives. Feed the pair back as the next call's oldHash
and --range. (fileHash is the whole file after the edit; it is reported for
information and is not an anchor.)
A dry run previews rather than dumps. --dry-run (MCP dryRun: true) returns a
unified diff of the changed hunks plus sourceOmitted: true — not the whole
post-edit file, which used to make previewing an edit cost more context than
making it. Pass --include-source (includeSource: true) when you really want
the full text back.
Telemetry stays a log, not an object store. Every record is capped at telemetry.maxRecordBytes (4 KB by default); a captured diff larger than that is written to a content-addressed store under ~/.agentic-tools/logs/payloads/ and referenced from the record by hash, which took one real edit's record from 27,526 bytes to 185. Reads rehydrate the diff, so provenance query and telemetry health see exactly what they saw before.
Every check is opt-in, so --auto-detect (or an explicit --formatter /
--linter / --typecheck / --test-runner) is what makes the command verify
anything. A run with no check reports overall: "skipped" and exits 4 rather
than the vacuous "pass" it used to return over an empty check set.
┌─────────────┐
│ Your Edit │
└──────┬──────┘
│
▼
┌──────────────────────┐
│ 1. AST Route │ ◄── tree-sitter syntax-aware edits
│ (TS/TSX/JS/Python/ │ rename-symbol, replace-body,
│ Go/Rust) │ add-import, remove-import,
│ │ insert-before/after
└──────────┬───────────┘
│ unsupported
▼
┌──────────────────────┐
│ 2. Hash Route │ ◄── SHA-256 anchored replacement
│ (any file) │ replace-hash with stale-anchor
│ │ detection + auto-recovery
└──────────┬───────────┘
│ no hash provided
▼
┌──────────────────────┐
│ 3. Diff Route │ ◄── LCS-based search-and-replace
│ (fallback) │ with duplicate detection and
│ │ fuzzy matching
└──────────────────────┘
The router auto-selects. A single route-edit command tries AST first, falls back to Hash, then Diff. Every route records telemetry and provenance.
┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ read- │ │ replace- │ │ verify- │ │ done. │
│ many │───▶│ hash │───▶│ changes │───▶│ │
│ │ │ │ │ │ │ │
│ hash: │ │ content │ │ lint │ │ audited, │
│ abc123 │ │ matched │ │ typecheck│ │ verified │
└─────────┘ │ by hash │ │ tests │ └──────────┘
└──────────┘ │ revert? │
└──────────┘
Read → Edit → Verify. Every step outputs structured JSON for agent consumption.
HashPilot ships a benchmark harness so its safety claims are checkable rather
than asserted. bun run bench replays every case in bench/ through
the real routing entry point and classifies the result.
Baseline — harness v1, HashPilot v4.3.2, 23 cases:
| Metric | Value |
|---|---|
| Correct | 15 |
| Correct refusals (ambiguous / stale / unparseable input, refused untouched) | 6 |
| False refusals | 0 |
| Silent corruption — reported success, wrong bytes on disk | 2 (8.7%) |
| Correctness rate (excludes correct refusals) | 88.2% |
Both silent-corruption cases are grouped-import handling on the AST tier
(#102,
#103) and are committed as
red regression guards. The hash and diff tiers are at 100% across their cases,
including stale-anchor refusal, anchor relocation, ambiguous-content refusal, and
unified-diff reserved tokens (---, +++, @@) appearing as ordinary file
content.
Silent corruption is the number that matters. An apply-success rate counts an edit that "succeeded" while quietly deleting three imports as a win; this harness counts it as corruption. A refusal costs a retry — a confident wrong answer gets committed.
Methodology, case format, and the honest scope limits are in
bench/README.md. Results are committed as JSON
(bench/results/latest.json) so trends are
diffable, and CI fails on any case that regresses from green.
| Command | What It Does |
|---|---|
read-many <files...> |
Batch read files with SHA-256 content hashes |
read-hash <file> <line> |
Read a specific line with context hash |
grep-many <pattern> <paths...> |
Regex search across files. Both forms work: hashpilot grep-many "safeWrite" src/ or hashpilot grep-many --pattern "safeWrite" --path src/ (--path repeatable); supplying both is a usage error. Any parse error — unknown flag, missing argument, unknown subcommand — returns the JSON usage envelope with INVALID_ARGUMENT and exit 1 rather than a bare Commander line on stderr |
symbol-lookup-many <paths...> --names n1,n2 |
Find symbol definitions by name |
| Command | What It Does |
|---|---|
upgrade [--dry-run] [--channel <branch>] [--target <dir>] [--keep-telemetry] [--force] |
Upgrade HashPilot from GitHub to latest version |
| Command | What It Does |
|---|---|
replace-hash <file> <hash> <content> |
Replace content identified by SHA-256 hash (auto-recovers on stale anchor) |
| Command | What It Does |
|---|---|
changesets [--limit N] |
List undoable changeSets, newest first |
undo <changeSetId> |
Restore every file in a changeSet to its pre-edit contents |
undo --last |
Undo the most recent changeSet |
Every write goes to a sibling temp file, is fsynced, and is renamed over the
target, so an interrupted write can never leave a truncated source file — a reader
sees either the whole old file or the whole new one, and the target's permissions
are preserved. Before the write, the file's original bytes are stored in a
content-addressed snapshot store under ~/.agentic-tools/snapshots/, keyed by the
changeSet the invocation belongs to. undo refuses any file that changed after the
edit was applied unless --force is passed, and --dry-run reports without writing.
Retention defaults to 200 changeSets / 7 days, configurable under snapshots in
.hashpilot.json.
| Command | What It Does |
|---|---|
ast capabilities |
Show supported languages, operations, and limitations |
ast find-symbols <file> |
List all symbols (functions, classes, variables), with 1-indexed startLine/endLine/startColumn/endColumn (matching the hash tier's range) alongside the raw 0-indexed tree-sitter startRow/endRow/startCol/endCol. Returns { symbols, truncated }; the walk is bounded at depth 200, and truncated: true (plus a SEARCH_TRUNCATED warning) means symbols nested deeper were not visited |
ast rename-symbol <file> <old> <new> |
Rename a symbol and all its references within one file, binding-aware — refuses with AMBIGUOUS_SYMBOL (exit 2) when the name binds more than one symbol (a shadowed local, a foreign import, or a duplicate declaration) |
ast replace-body <file> <symbol> <body> |
Replace a function/method body. The body is statements only — no surrounding braces, no leading indentation; the command owns both and re-indents each line to the symbol. Passing either back is silent: the file still parses (#108) |
ast add-import <file> <spec> |
Add an import with grouped-import merging. The spec is parsed as source, so the module path is quoted: '{ Foo } from "./bar"'; unquoted is a PARSE_ERROR (#109) |
ast remove-import <file> <spec> |
Remove an import statement |
ast insert-before <file> <symbol> <content> |
Insert content before a named symbol. Only declarations anchor an insertion — a name that is only a parameter or import specifier is refused, and an ambiguous name lists every candidate. Content is indented to match the anchor |
ast insert-after <file> <symbol> <content> |
Insert content after a named symbol (same anchor rules and indentation as insert-before) |
AST edits are guarded at both ends. A file that does not already parse is refused
(PARSE_ERROR, exit 2, with the line and column of the break) rather than edited
against a tree tree-sitter had to error-recover; and every edit is reparsed before
anything reaches disk, so an edit that would corrupt a file that parsed cleanly is
discarded instead of written. The same post-edit check applies to hash and diff
edits whenever a parser exists for the language. --allow-parse-errors waives the
pre-check for deliberately editing a broken file; the post-check always stands.
There is no file-size ceiling. Through v3.0.0 every AST operation failed on any source over 32KB — the binding's string-marshalling limit — which silently demoted large files to the diff route.
| Command | What It Does |
|---|---|
diff generate <file> <old> <new> |
Generate a unified diff |
diff apply <file> --patch <patch> |
Apply a patch with fuzzy matching (--fuzzy 0 = strict: exact offset and content). An ambiguous window — context matching more than once within --fuzzy lines — is refused with every candidate line named, and each applied hunk reports expectedAt / appliedAt / offset so a hunk that slid is visible |
| Command | What It Does |
|---|---|
route-edit <file> <operation> |
Auto-routed edit through AST → Hash → Diff |
batch <operation> <files...> |
Same edit on many files in parallel or serial |
intent <json> |
Declarative multi-file edit — plan, discover references, execute |
route <file> <operation> |
Preview which route would be chosen |
intent never invents source text. If part of the intent cannot be computed —
add-parameter with no param.default leaves nothing to pass at the call
sites — it lists the gap under plan.unresolved (file, operation, reason,
resolution) and refuses the whole plan with UNSUPPORTED_OPERATION rather
than applying it halfway. Give the parameter a default, or pass --yes to
apply only the steps it could compute.
| Command | What It Does |
|---|---|
verify-changes <files...> |
Run formatter + linter + typechecker + tests with auto-detection and revert-on-failure |
| Command | What It Does |
|---|---|
telemetry summary |
Operation counts and timing |
telemetry health [-w <days>] [--trend] |
Health report with per-language stats and threshold warnings |
telemetry sessions |
List session summaries |
provenance query <file> [line] |
Edit history for a file (like git blame for agent edits) |
provenance changeset <id> |
All edits in a changeSet |
All commands accept
--actor,--task-id, and--reasonfor provenance tracking. Every command outputs structured JSON.
Every command writes the same JSON shape to stdout, so an adapter has one parse path:
{
"apiVersion": "1",
"ok": true,
"command": "read-many",
"data": [{ "path": "src/api.ts", "hash": "a1b2c3d4e5f6", "content": "...", "lines": 42 }],
"error": null,
"warnings": []
}data— the per-command payload (what used to sit at the top level).error—nullwhenok, else{ code, message, recovery? }. Branch oncode, never onmessage.warnings— non-fatal notices:ROUTE_FALLBACK(the edit was downgraded to a less safe route),ANCHOR_RELOCATED(the anchor moved and the edit landed elsewhere),TELEMETRY_LOG_CORRUPT,VERIFY_NO_CHECKS(verify-changesran no check at all — see below).okis derived from the exit code below, so the two never disagree.
Schema: schema/hashpilot-envelope.schema.json.
Raw modes for piping: diff generate --raw, telemetry export --ndjson.
Breaking in v3.0.0 (from v2.x, which returned a different shape per command) — see
docs/ADAPTER-CONTRACT.md for migration.
Every command exits with a stable code so agents and CI can branch on the result without parsing text.
| Code | Meaning | What to do |
|---|---|---|
0 |
Success | Continue |
1 |
Usage error — bad arguments, denied path, unsupported operation | Fix the invocation |
2 |
Edit failed | Try another route or report |
3 |
Stale anchor / precondition failed | Retryable: re-read and retry with the fresh hash |
4 |
Verification failed (format/lint/test) | Inspect the verify output |
5 |
I/O error | Check the path and permissions |
70 |
Internal error | File a bug |
Batch commands return the worst code across all items.
By default HashPilot only writes inside the project root (the nearest ancestor
containing .git). Anything else fails with PATH_DENIED and exit code 1.
hashpilot --allowed-root /srv/generated ast rename-symbol ... # widen for one run
hashpilot --allow-outside-root ... # disable containment{ "allowedRoots": ["/srv/generated"] }Some locations are never writable, and neither allowedRoots nor
--allow-outside-root re-enables them: ~/.ssh, ~/.aws, ~/.gnupg, /etc,
shell startup files (~/.zshrc, ~/.bashrc, ~/.profile), and HashPilot's own
telemetry log. Symlinks are resolved before the check, so a link inside the
project that points outside it is still refused.
HashPilot writes a local JSONL event log to ~/.agentic-tools/logs/. Nothing is
ever sent off the machine.
Turning it off — highest priority first:
hashpilot --no-telemetry ast rename-symbol ... # one invocation
export HASHPILOT_TELEMETRY=0 # whole shell (also: false, off, no){ "telemetry": { "enabled": false } }What is in the log. Operation name, route, file path, language, success,
elapsed time, and any --actor / --task-id / --reason you pass. Source code
is not recorded by default: the log holds content hashes, not content.
Diff capture is opt-in. Setting provenance.captureDiffs records a unified
diff of each edit, which puts real source lines on disk in plaintext:
{ "provenance": { "captureDiffs": true } }Even then, files that are secret by definition are never diffed — .env*,
*.pem, *.key, *.p12, *.pfx, id_rsa/id_ed25519, credentials,
.npmrc, .netrc, secrets.{yaml,json,toml}. Their hashes still record that
the file changed.
Redaction. Everything written to the log is scrubbed for credential shapes
first — AWS keys, OpenAI/Anthropic/GitHub/Slack/Google tokens, JWTs, private-key
blocks, Authorization headers, passwords in connection strings, and any
secret/token/password/api_key-named assignment. Matches are replaced with
[REDACTED]. The log directory is created 0700 and the log file 0600;
pre-existing logs from older versions are tightened on the next write.
Retention is enforced automatically. The log rotates at
telemetry.maxFileSize (10 MB) keeping telemetry.maxRotatedFiles (10), and
rotated logs past telemetry.retentionDays (30) are deleted without anyone
running hashpilot telemetry prune — the sweep is gated by a marker file to at
most once a day, so the cost on every other invocation is a single stat.
Orphaned diff payloads are collected in the same pass. telemetry health
reports diskBytes and hashpilot doctor reports the store size, both warning
past 100 MB.
HashPilot installs adapters for the three major coding agent platforms:
| Platform | What Gets Installed |
|---|---|
| Claude Code | HashPilot section injected into ~/.claude/CLAUDE.md teaching Claude to use hashpilot commands |
| OpenCode | Skill at ~/.config/opencode/skills/hashpilot/ + subagent at ~/.config/opencode/agent/hashpilot.md |
| Pi | Native extension at ~/.pi/agent/extensions/hashpilot.ts with 7 custom tools and /hp slash command |
HashPilot is also an MCP server, which is the recommended way to wire it into any MCP-capable host — Claude Code, Claude Desktop, Cursor, Zed, and others:
claude mcp add hashpilot -- hashpilot mcp --stdioEvery tier shows up as a typed tool with the same locking, provenance, and undo guarantees the CLI has, and multi-line content with quotes or backticks rides inside JSON instead of fighting the shell. Copy-paste configs per host: docs/INTEGRATION-MCP.md. The CLI remains fully supported and is the fallback for hosts without MCP, and for shell and CI use.
All adapters follow the Adapter Contract — a machine-readable JSON protocol any agent can consume.
┌──────────────────────────────────────────────────────────────┐
│ hashpilot CLI │
│ (Commander-based, Bun) │
├─────────┬──────────┬──────────┬──────────┬───────────────────┤
│ Read │ AST │ Hash │ Diff │ Verify + Batch │
│ Search │ Ops │ Ops │ Ops │ + Intent + Route │
├─────────┴──────────┴──────────┴──────────┴───────────────────┤
│ Router (auto-select) │
│ chooseRoute(): AST → Hash → Diff │
│ routeEdit(): execute + telemetry + provenance │
├──────────────────────────────────────────────────────────────┤
│ Cross-Cutting Layers │
│ • Telemetry (JSONL) • Provenance (agent git blame) │
│ • Config (env→CLI→project→global) • Error/exit codes │
└──────────────────────────────────────────────────────────────┘
Key Modules: cli.ts (entry), router.ts (dispatch), ast-edit.ts (tree-sitter), hash-edit.ts (SHA-256), diff-engine.ts (LCS), read.ts, grep.ts, intent.ts (M5), plan-executor.ts, verify.ts, provenance.ts, telemetry.ts, config.ts, batch-edit.ts, doctor.ts.
For deep design rationale, module internals, data flow, and all architecture decisions, see the design doc.
AST Language Support:
| Language | Extensions | All 7 Operations |
|---|---|---|
| TypeScript | .ts (not .d.ts) |
✓ |
| TSX | .tsx |
✓ |
| JavaScript | .js, .jsx, .mjs, .cjs |
✓ |
| Python | .py |
✓ |
| Go | .go |
✓ |
| Rust | .rs |
✓ |
Layered config. Highest priority wins:
HASHPILOT_ROUTE_POLICYenv var--config <path>CLI flag.hashpilot.jsonin project root~/.config/hashpilot/config.json- Built-in defaults
{
"routePolicy": {
"languageOverrides": { "python": "hash", "go": null },
"operationOverrides": { "add-import": "diff" },
"conflictResolution": "operation"
},
"telemetry": { "enabled": true },
"provenance": { "captureDiffs": false },
"allowedRoots": []
}In languageOverrides and operationOverrides, null unsets an entry inherited
from a lower-priority config — the example above forces Python to the hash tier while
opting out of whatever go rule the global config set.
See Telemetry and Privacy and Where HashPilot Will Write for what those last two do.
git clone https://github.com/bigknoxy/HashPilot.git
cd HashPilot
bun install
bun test # 424 tests
bun run build # Build CLI to dist/
bun test tests/hash-edit.test.ts # Single test file
bun test -t "test name pattern" # Filter by test name| Tool | Problem | HashPilot |
|---|---|---|
sed |
Line-number based, fragile | Hash-anchored, recovery on stale anchors |
grep + sed |
Wrong match on first occurrence | Cryptographic content identity |
awk |
Pattern-based, no AST awareness | Tree-sitter AST for syntax-safe edits |
| Manual edit | 3-5 retries per change | 1-2 operations, no re-reading |
HashPilot isn't competing with Unix tools — it's the infrastructure layer that lets AI agents use those tools correctly.
MIT — see LICENSE.
Active development. Core editing engine, AST operations, telemetry, and all three adapter integrations are production-ready. Intent-based editing (M5) and provenance tracking (M6) are available as preview features.
Docs policy: The landing page (README.md) and design doc are living documents. Every PR that touches src/ must update one or both. Every deploy is verified with browser automation. See the CI check docs-verify.
Agent quick reference: docs/CLI-QUICKREF.md is the one page an agent should read before invoking the CLI — every command, flag, output shape, exit code, and the gotchas that otherwise cost a guess-and-retry loop. Its command reference is generated from the CLI's own --help, and bun run lint:docs (run in CI and by bun test) fails if the doc drifts from the binary or if ROADMAP.md grows a duplicate or out-of-order row.
v1.3.1 — Release notes
HASHPILOT_TELEMETRY: set to0/false/off/noto disable telemetry logging. Overridden by--no-telemetry.HASHPILOT_ROUTE_POLICY: JSON route policy, highest-priority config layer.