readignore

module
v0.7.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 25, 2026 License: MIT

README

readignore

readignore

English | 中文

.gitignore for AI coding agents — declare files your AI agent must not read.

CI Go Report Card Go Version

🌐 Website: readignore.vercel.app

TL;DR — npm i -g readignore && readignore init && readignore install --all

AI coding agents (Claude Code, Cursor, Codex, opencode, kilo code, …) can read any file in your repo at runtime — including secrets like .env, *.pem, id_rsa, credentials.json. Existing defenses have gaps:

  • .gitignore only stops git from committing; the agent still reads the file.
  • Claude Code's permissions.deny: Read(.env) only blocks the Read tool — agents bypass it with Grep, Glob, or Bash (grep . .env works).

readignore closes that gap with one .readignore (gitignore syntax) that gets adapted into each agent's native defense mechanism — at the strongest level that agent actually supports.


How it works

You write one declarative .readignore. readignore translates it into the strongest available mechanism for each target agent — and honestly labels the enforcement strength of each, because the agents are not equivalent.


Capability matrix

readignore adapts .readignore into each agent's strongest real mechanism. Strength tiers are honest, not marketing:

Agent Strength Mechanism Status
Claude Code hard PreToolUse hook — blocks the tool call before it runs (Read, Grep, Glob, Bash). Programmatic interception at runtime. ✅ shipped
codex CLI hard .codex/hooks.json Claude-style PreToolUse hook (bash, calls readignore hook-check). Same runtime-deny mechanism as Claude Code; requires hook trust on first run. See codex note below. ✅ shipped
pi hard .pi/extensions/readignore.ts TypeScript extension that overrides the built-in read tool — calls readignore match and returns Access denied for matched paths before the file is read. Auto-loaded at startup. ✅ shipped
opencode config permission.read deny/allow globs in opencode.json; enforced when opencode loads config. ✅ shipped
Cursor soft .cursor/rules natural-language advisory (model may comply). 🗺 roadmap
kilo code config kilo.json permission.read deny/allow globs; enforced when kilocode loads config. ✅ shipped

⚠️ Known boundary (Claude Code): the Read tool can bypass PreToolUse hooks / permissions.deny in some configs (anthropics/claude-code#37540). readignore's hook still materially raises the bar; for full coverage pair it with filesystem permissions and/or a secret manager (zero-disk secrets).

What "strength" means
  • hard — code runs before the tool executes (or before its output reaches the model) and can deny the call. Three flavors today, all genuinely enforced at runtime:
    • Claude Code / codex — a PreToolUse hook script returns permissionDecision: "deny" and the tool never runs.
    • pi — a TypeScript extension registered with the same name as the built-in read tool overrides it; matched paths return Access denied and the file is never read.
  • config — readignore writes a native deny config (e.g. opencode's permission.read). Enforcement depends on the agent faithfully loading it. opencode's programmatic permission.ask hook is currently a no-op at runtime (opencode #7006), so we cannot reach hard there yet.
  • soft — a natural-language rule the model is asked to honor. No enforcement. Future adapters for Cursor-style tools land here.

readignore does not claim cross-agent equivalence. It adapts to whatever each agent can actually enforce.


Quickstart

# 1. Install (npm — no Go needed; other options in Installation below)
npm i -g readignore

# 2. In your repo:
cd your-repo
readignore init            # generates .readignore with common secret patterns

# 3. Edit .readignore to match your project, then install for your agent(s):
readignore install claude-code          # single agent
readignore install codex                # codex CLI (Claude-style PreToolUse hook)
readignore install pi                   # pi (.pi/extensions/ TS override, auto-loaded)
# or install for every agent detected in this repo:
readignore install --all

init refuses to overwrite an existing .readignore unless you pass --force.


Commands

# Generate a .readignore template (with .env, *.pem, id_rsa, .aws/, … patterns)
readignore init [--force]

# List registered adapters, their strength, and detection status in this repo
readignore adapters

# Dry-run: parse .readignore and print what an adapter would generate (stdout)
readignore generate claude-code
readignore generate codex
readignore generate pi
readignore generate opencode
readignore generate kilocode

# Write an adapter's output to disk
readignore install claude-code          # one adapter
readignore install --all                # all adapters detected here
readignore install claude-code --force  # overwrite existing files

# Refresh an adapter's files to the current readignore version (= install --force)
readignore update                       # all adapters detected (default = --all)
readignore update claude-code           # one adapter

# Remove an adapter's generated files (inverse of install)
readignore uninstall claude-code            # one adapter
readignore uninstall --all                  # all adapters detected here
readignore uninstall claude-code --dry-run  # preview only, don't delete

# Validate .readignore syntax and report each adapter's install status
readignore check

# Check if a path is denied by .readignore (exit 0=allow, 1=deny)
# The pi extension calls this at runtime; bash hooks use hook-check — you can use match directly for debugging
readignore match .env

If a target file already exists, install skips it (and tells you to merge manually) unless you pass --force. This avoids clobbering your existing .claude/settings.json or opencode.json.


.readignore syntax

100% gitignore-compatible. Zero learning curve. Segment into [read] (unreadable) / [edit] (read-only) / [delete] (no delete) sections — bare patterns default to [read] (backward compatible). Bare patterns also block write-class Bash commands that read the source (cp .env/mv .env/sed -i/> redirect read the source → matched against [read], leak guard); rm/delete needs an explicit [delete] section. [delete] is best-effort (only literal rm/rmdir intercepted; rm $F / find -delete bypass static analysis — combine with chattr +i for true undeletable):

# readignore — files this repo's AI agent must not read

# Secrets & keys
.env
.env.*
!.env.example            # ! un-ignores (negation): allow the template through
*.pem
*.key

# SSH / cloud credentials
**/id_rsa
.aws/
.gcp/

# Sensitive directories
secrets/
credentials.json

# Trailing / anchors to directories only
build/

Supported: *, **, ?, [abc] character classes, ! negation (last-match-wins, just like gitignore), trailing / for directory anchoring, # comments. See the gitignore spec.


What gets generated

Claude Code (readignore install claude-code)

Two files under .claude/:

.claude/hooks/readignore.sh   (0755)  # forwards PreToolUse JSON to `readignore hook-check`
.claude/settings.json                 # registers the hook on PreToolUse

The hook fires on Read | Grep | Glob | Bash, forwarding the PreToolUse JSON to readignore hook-check (the go-git authority for gitignore matching), which parses tool_input in Go and denies before execution when the path is matched (emits a permissionDecision: "deny" JSON). Claude Code's settings watcher picks up the change live — no restart needed.

Editing .readignore takes effect immediately — the hook re-reads cwd/.readignore on every call via readignore hook-check, so you never need to re-run install after editing rules. Same edit-and-go experience as .gitignore.

opencode (readignore install opencode)

A single opencode.json with permission.read deny/allow globs:

{
  "$schema": "https://opencode.ai/config.json",
  "permission": {
    "read": {
      ".env": "deny",
      ".env.*": "deny",
      ".env.example": "allow"
    }
  }
}

opencode reads this at startup.

Negation caveat (opencode only): opencode's glob engine has no gitignore order/negation semantics. readignore approximates ! negation via "more specific allow glob beats broader deny glob" — correct for common cases (*.env deny + !a.env allow → a.env allowed), but complex negation chains may diverge from gitignore. If you depend on intricate negation, prefer the Claude Code adapter (full gitignore semantics). See opencode adapter docs.

codex CLI (readignore install codex)

Two files under .codex/, mirroring the Claude Code layout:

.codex/hooks/readignore.sh   (0755)  # forwards PreToolUse JSON to `readignore hook-check`
.codex/hooks.json                    # registers the hook (Claude-style PreToolUse)

codex's hook protocol is Claude-style (PreToolUse + permissionDecision: "deny"), so the same bash hook runs — it calls readignore hook-check (go-git authority) and denies before the tool executes. Like Claude Code, editing .readignore takes effect immediately (no re-install needed).

Hook trust: codex gates project-level hooks behind a trust prompt. The first time a project hook runs you'll be asked to confirm trust; pass --dangerously-bypass-hook-trust to skip the check (e.g. in CI).

codex platform note — Bash-only hook trigger

Unlike Claude Code, codex has no standalone Read / Grep / Glob tools. The codex agent reads files exclusively via shell commands (cat .env, head -n 50 file, grep pattern file). Consequently the PreToolUse hook fires natively only on tool_name="Bash" (with the target path appearing inside tool_input.command), and readignore's matcher extracts the path from that command string.

The matcher: "Read|Grep|Glob|Bash" in hooks.json is deliberate, not a bug:

  • it keeps the codex adapter symmetric with the Claude Code adapter (same shared bash hook, both calling readignore hook-check);
  • it covers users who install MCP tools exposing Read/Grep/Glob into codex — those MCP tools would also be intercepted.

But for codex's native tool set, only the Bash arm ever fires. A native "read this file" call is always a Bash command, which the hook does intercept.

pi (readignore install pi)

A single TypeScript extension:

.pi/extensions/readignore.ts   (0644)  # overrides pi's built-in `read` tool

pi auto-loads .pi/extensions/*.ts at startup. The extension registers a tool named read — the same name as pi's built-in — which overrides it: it calls readignore match <path> (go-git authority) and matched paths return Access denied before the file is ever read; everything else delegates to a normal read. No pi types are imported, so the file type-checks in isolation. As with the hook adapters, editing .readignore takes effect immediately.

kilo code (readignore install kilocode)

A single kilo.json with permission.read deny/allow globs:

{
  "permission": {
    "read": {
      ".env": "deny",
      ".env.*": "deny",
      ".env.example": "allow"
    }
  }
}

kilo code (open-source, OpenCode fork) reads this on startup. Its permission system evaluates allow/deny/ask rules on every tool call — read, edit, glob, grep, bash.

glob limitation: kilo code's wildcard engine is simpler than gitignore — no ** directory traversal, no ! negation, no anchoring. readignore strips **/ prefixes (basename match) and approximates ! via "more specific allow beats broader deny". Known bugs exist where deny rules are occasionally bypassed (#8293, #11637), so this is honestly labeled config, not hard.


Installation

npm (recommended — no Go needed):

npm i -g readignore      # or: npx readignore

The npm wrapper's postinstall downloads the right Go binary for your platform from GitHub Releases.

go install (if you have Go 1.25+):

go install github.com/0xByteBard404/readignore/cmd/readignore@latest

Binary download: grab the archive for your platform from Releases, extract, and put readignore on your PATH.

curl | sh one-liner (Linux / macOS, no Go or npm needed):

curl -fsSL https://raw.githubusercontent.com/0xByteBard404/readignore/main/install.sh | sh

The script detects your OS/arch, fetches the matching binary + checksums.txt from the latest release, verifies the SHA256, and installs readignore to /usr/local/bin (falling back to ~/.local/bin, with a PATH hint if needed). Windows users: use npm, Scoop, or the .zip from Releases.

Homebrew:

brew tap 0xByteBard404/tap
brew install readignore

(Requires the 0xByteBard404/homebrew-tap repo.)

Coming soon: Scoop (Windows).


Update checks

readignore checks https://github.com/0xByteBard404/readignore/releases/latest for a newer version at most once every 24 hours when you run a non-hot-path command, and prints a green bilingual notice to stderr if yours is outdated. The check is skipped for match / hook-check / update and whenever stderr is not a TTY (pipes, CI).

This is a "phone-home": the request reaches GitHub from your IP. To opt out entirely, set:

READIGNORE_NO_UPDATE_CHECK=1

The check writes <cache-dir>/readignore/version-check.json (latest version + last-checked timestamp). It never blocks or fails your command — network errors are silently ignored.


Why not just .gitignore or permissions.deny?

Approach Fails to block
.gitignore Agent reads file at runtime (gitignore only stops commits).
Claude Code permissions.deny: Read(.env) Grep, Glob, Bash (grep . .env) bypass it.
Per-agent manual config Duplicated effort across 5+ agents; drifts out of sync.

readignore is one declaration, adapted per agent, at each agent's strongest enforcement point.


Project status

v0.3.0 — three hard adapters (Claude Code, codex CLI, pi) + one config adapter (opencode). All hooks route through the readignore binary's match engine (bash hooks via readignore hook-check, the pi extension via readignore match), so editing .readignore takes effect immediately — no re-install needed. Install via npm, curl | sh, or Homebrew. Cursor and kilo code adapters are on the roadmap.

See CHANGELOG.md for the version history.


Contributing

Contributions welcome — especially new adapters (Cursor rules, kilo code). Each adapter implements a small Adapter interface and self-registers in init().

See CONTRIBUTING.md and CODE_OF_CONDUCT.md. Please open an issue first to discuss adapter design before building.


License

MIT © 2026 0xByteBard404

Directories

Path Synopsis
cmd
readignore command
Package main 是 readignore CLI 入口(阶段5:委托给 internal/cli 调度命令)。
Package main 是 readignore CLI 入口(阶段5:委托给 internal/cli 调度命令)。
internal
adapter
Package adapter 定义「工具适配器」抽象层。
Package adapter 定义「工具适配器」抽象层。
adapter/claudecode
Package claudecode 实现 Claude Code 适配器:把 .readignore 翻译成 Claude Code 的 PreToolUse hook,实现「执行前可编程硬拦」—— 五个目标工具里唯一能在工具真正 执行前用脚本判定并阻断的,故本包是 readignore 的参考实现。
Package claudecode 实现 Claude Code 适配器:把 .readignore 翻译成 Claude Code 的 PreToolUse hook,实现「执行前可编程硬拦」—— 五个目标工具里唯一能在工具真正 执行前用脚本判定并阻断的,故本包是 readignore 的参考实现。
adapter/codex
Package codex 实现 OpenAI Codex CLI 适配器:把 .readignore 翻译成 codex 的 Claude-style PreToolUse hook,实现「执行前可编程硬拦」。
Package codex 实现 OpenAI Codex CLI 适配器:把 .readignore 翻译成 codex 的 Claude-style PreToolUse hook,实现「执行前可编程硬拦」。
adapter/kilocode
Package kilocode 实现 kilocode 适配器:把 .readignore 翻译成 kilo.json 的 permission 配置,在 kilocode 加载时按 glob deny 文件读取/改写。
Package kilocode 实现 kilocode 适配器:把 .readignore 翻译成 kilo.json 的 permission 配置,在 kilocode 加载时按 glob deny 文件读取/改写。
adapter/opencode
Package opencode 实现 opencode 适配器:把 .readignore 翻译成 opencode 的 permission 配置,在 opencode 加载时按 glob deny 文件读取/改写。
Package opencode 实现 opencode 适配器:把 .readignore 翻译成 opencode 的 permission 配置,在 opencode 加载时按 glob deny 文件读取/改写。
adapter/pi
Package pi 实现 pi(Earendil-works coding-agent,earendil-works/pi)适配器:把 .readignore 翻译成 pi 的 TypeScript extension,通过 override 内置 `read` 工具实现 「执行前可编程硬拦」。
Package pi 实现 pi(Earendil-works coding-agent,earendil-works/pi)适配器:把 .readignore 翻译成 pi 的 TypeScript extension,通过 override 内置 `read` 工具实现 「执行前可编程硬拦」。
adapter/shared/hookengine
Package hookengine 提供 Claude-style PreToolUse hook 的公共生成引擎。
Package hookengine 提供 Claude-style PreToolUse hook 的公共生成引擎。
cli
Package cli 实现 readignore 的命令行界面(基于 spf13/cobra)。
Package cli 实现 readignore 的命令行界面(基于 spf13/cobra)。
readignore
Package readignore 解析 .readignore(gitignore 语法)并判断路径命中。
Package readignore 解析 .readignore(gitignore 语法)并判断路径命中。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL