Short version: nothing leaves your machine. No network, no telemetry, no opt-out needed because there's nothing to opt out of. Details below for enterprise reviewers.
The official plugin-directory package enables only the lightweight
SessionStart and UserPromptSubmit hooks. The broader local installer in
setup.sh can add extra guards (PreToolUse, PostToolUse, Stop, and
PreCompact) when a user explicitly opts into the full project setup.
| Component | Trigger | Where | What it reads | What it writes | Network? |
|---|---|---|---|---|---|
build_repo_graph.py |
Install + /refresh-graph; background on staleness |
Your machine, as your user | Source files in repo (ext allowlist) + git log --name-only --since=90.days |
.context-os/repo-graph.json in repo root |
No |
auto_context.py |
UserPromptSubmit hook, per prompt |
Your machine, as your user | Prompt + .context-os/repo-graph.json |
stdout (consumed by Claude Code) | No |
prewarm.py |
SessionStart hook |
Your machine | .context-os/repo-graph.json + git metadata |
stdout + background build_repo_graph.py on stale graph |
No |
dedup_guard.py |
PostToolUse |
Your machine | Recent Read/Glob/Grep tool-call log in session | .context-os/dedup-cache.json |
No |
loop_guard.py |
PostToolUse |
Your machine | Recent Edit tool-call log in session | .context-os/loop-cache.json |
No |
file_size_guard.py |
PreToolUse (on Read) |
Your machine | File size of target Read | stderr (warning) | No |
session_profile.py |
Stop |
Your machine | Session tool-call log | .context-os/session-reports/<ts>.md |
No |
Every path is local. Every hook is stdlib-only Python. No requests, no urllib.urlopen, no sockets, no external binaries beyond git (invoked with explicit -C <repo> and no shell).
Everything lives under .context-os/ in your repo root. Add it to .gitignore if you don't want to commit it (we don't auto-.gitignore because some teams intentionally commit the graph for shared tooling).
.context-os/
├── repo-graph.json # symbols, imports, hot-files — from your source
├── dedup-cache.json # recent (file, mtime) → hash for re-Read blocking
├── loop-cache.json # edit-count-per-file for iteration guard
├── session-reports/<ts>.md # per-session token-spend profile (local only)
└── handoff.md # optional; for cross-session continuity
Typical size: 50–200 KB for small repos, ~2 MB for a 5k-file repo. Entirely regenerable — you can rm -rf .context-os/ and the tooling rebuilds on next prompt.
Per file indexed:
- Relative path (e.g.
src/api/router.py) - Language (inferred from extension)
- Symbol names (top-level
def/class/fn/struct/const— identifier names only, not function bodies) - Line numbers for those symbols (so the hook can output
file:42) - Import paths (module names — e.g.
src.db.queries, not the imported values) - Line count (size signal)
Per repo:
- Hot files: list of file paths with change counts from
git log --name-only --since=90.days
Not stored: file contents, comments, string literals, variable values, commit messages, authorship, diffs, env vars, credentials, .env file contents, contents of any directory in EXCLUDE_DIRS (node_modules, target, dist, .venv, __pycache__, …).
On each prompt, auto_context.py emits to stdout a block like:
<context-os:autocontext>
Graph-matched candidates (structure only, no files read yet):
- `src/auth/login.py:42` · `validate_credentials` (def) · imports: src.utils.crypto, src.db.queries
- `src/utils/crypto.py:1` · `hash_password` (def)
- `src/api/router.py:12` · `APIRouter.add` (def) · hot (7 touches/90d)
</context-os:autocontext>
Claude Code prepends this to your prompt. Contents: up to 5 file paths, symbol names you already declared, and their import edges. Nothing from the function bodies, no stdout of any command, no environment data.
This is the only data the hook contributes to any Claude turn. You can inspect it yourself by running:
echo '{"prompt":"..."}' | python3 hooks/python/auto_context.py- Will not read a file that's not already scanned during graph build. The hook only consults the graph; it does not open any source file at query time.
- Will not traverse outside
$CWD.build_repo_graph.pyusesos.walk(root); all relative paths are rooted at the repo. - Will not escape
.claudeignore/EXCLUDE_DIRS. Standard deny-list plus prefix denies for fixture directories (autocontext_fixture*). - Will not exec code. Pure regex +
os.walkfor scanning; noimportlib, noeval, noexec, no AST-walker that runs__init__.py. - Will not shell out with user data. The only subprocess is
git logwith fixed args; the prompt never reaches a shell.
- Corrupt
.context-os/repo-graph.json→ hook logs nothing, exits 0, Claude proceeds as if no hook ran. Validated byrobustness.md(corrupt-graphcase). - Missing
.context-os/directory →build_repo_graph.pycreates it withos.makedirs(exist_ok=True); if that fails (read-only FS), the hook exits 0. - Prompt contains adversarial characters (null bytes, 100K chars, unicode, shell metacharacters, regex-bomb
a*) → all 9 adversarial cases inrobustness.mdexit 0 in <25ms. - stdin empty / not JSON → exit 0, no output. Caught in
robustness.md.
The hook cannot crash Claude Code. Every code path is wrapped in try/except at main() level with sys.exit(0) on any unhandled exception. Verified by the robustness suite.
Does the graph leak proprietary code? It contains names (symbols, paths, imports). If your threat model considers function names and file paths as sensitive (rare, but possible), you can:
- Disable the hook:
CONTEXT_OS_AUTOCONTEXT=0insettings.jsonenv. - Delete
.context-os/between sessions. - Add the directory to
.gitignore(default — your choice whether to commit).
Can the hook be used to exfiltrate data? No network stack is imported; attempting to add one would be a code-level change visible in the git history. The hook is ~400 lines; a security review takes under an hour.
Can I audit what was injected into Claude? Yes:
echo '{"prompt":"<the exact prompt you used>"}' \
| python3 hooks/python/auto_context.pyThe output is the full injected block, byte-for-byte.
Is the dedup-cache.json sensitive? It stores (file_path, mtime) → SHA256(file_contents_truncated_32KB). Hashes are salted by a per-install random secret; hashes leaking does not reveal content without a brute-force dictionary attack against your exact file. Delete the cache to rotate.
hooks/python/auto_context.py— 476 lines, one file, no dependencies outside stdlib. Review in ~30 minutes.hooks/python/build_repo_graph.py— 258 lines, one file, one subprocess (git log), no dependencies.python/evals/runners/robustness_test.py— 18 adversarial cases; CI-enforced.- No external telemetry or analytics anywhere in the codebase. Searchable:
grep -r 'requests\|urllib.request\|http\.client\|socket\|sentry' hooks/ crates/returns nothing.
If you find a security concern, email sridharsravan@icloud.com or open a GitHub issue. We'll respond within 24 hours and release a fix within 72.