Show HN: Chameleon – shows Claude a real file from your repo before it edits A developer released Chameleon, an open-source tool that injects real code conventions from a repository into AI prompts to improve code quality. The tool shows the model a canonical file, team idioms, and counterexamples before edits, then reviews diffs for correctness and duplication. It addresses the maintainability regression caused by AI-generated code that ignores existing patterns. "Code that blends in." Your AI writes code that works, and it reads wrong. Chameleon shows the model your repo's own conventions at the moment it types, then reviews the turn's diff before you do. Real capture: every line above is the actual output of the PreToolUse hook run against a real TypeScript repo the archetype, the witness imports, the taught idiom, and the axios counterexample are verbatim, not a mockup . A hook injects this on the first touch of each kind of file an "archetype" . The block is: a real conforming file from your repo to imitate the canonical witness, capped at 16,000 chars your team's idioms for that kind of file a counterexample : a real off-pattern line from your own code, labeled "do NOT write it this way" appears once you teach a competing import the signatures of the collaborator files it is about to call, ranked by call proximity the inbound callers of the file being edited: "change this signature, update these call sites in the same turn" archetype facts : the base class contract this cohort implements, and the exports that already exist so it reuses instead of re-inventing Later edits to a seen archetype get a one-line pointer instead, so your context window survives. Zero commands. The hooks do it. Task: "add a fetch to the orders page." Without chameleon: python import axios from "axios"; // team standardized on @/lib/http months ago function formatMoney cents: number { // fmtMoney already exists in @/lib/format return "$" + cents / 100 .toFixed 2 ; } const res = await axios.get /api/orders/${id} ; Same task with the witness and the "already defined in this archetype" facts in context: js import { http } from "@/lib/http"; import { fmtMoney } from "@/lib/format"; const res = await http.get /api/orders/${id} ; The first version compiles, passes tests, and dies in review, where you have been catching it by hand every single time. Duplication up and reuse down is AI's biggest measured maintainability regression. Chameleon attacks it twice: at write time show the wrapper and the existing helper first and at turn end a semantic duplication catch grounded in your real call graph: "this already exists, called from 7 sites" . chameleon turn-end review CROSS-FILE deterministic : AuthModule is no longer exported from src/auth/auth.module.ts, but src/app.module.ts:3 still imports it re-verified live on disk . CORRECTNESS diff-only judge : src/jobs/sync.ts:27 this turn's refactor dropped the await on queue.flush ; the process can exit before the flush completes. The cross-file line is a real capture the golden-ts-nestjs fixture with AuthModule 's export removed : it is deterministic, from a prebuilt import index plus a live disk probe, for TypeScript, Python, and Ruby. The correctness line is representative, because the judge is a separate claude -p spawn whose prose varies per run: it reads only the turn's diff and hunts what static analysis misses inverted conditions, dropped awaits, missing guards , escalating to a stronger model on high-risk routes. An unaddressed HIGH finding resurfaces exactly once on a later turn, then stops nagging. Advisory by default; it never eats your turn. get callers src/auth/auth.service.ts create total: 1 deterministic, from the committed calls index src/auth/auth.controller.ts:18 grade=typed property DI edge Real output from the bundled golden-ts-nestjs fixture, so you can reproduce it exactly. Every returned site is a call the bootstrap actually recorded, graded by how it was resolved same file , import , constant receiver , typed property for a dependency-injection edge, module attribute for Python . get blast radius walks the same index to the transitive callers a change reaches. The same profile answers the rest of the comprehension surface fully offline: search codebase functions and classes by name, ranked, with signature and caller count , get callees , query symbol importers , describe codebase , and explain edit , a post-incident replay of exactly what chameleon knew and did the last time a file was edited. No embeddings service, no network. We ship a Rails + TypeScript codebase at Empire Flippers https://empireflippers.com/ , and we review every AI PR that touches it. The failure mode was always the same: the code worked, and it read wrong. axios where the team standardized on our HTTP wrapper, a hand-rolled date formatter next to the one we already had, a new service that skipped the base class every other service extends. We wrote CLAUDE.md rules for all of it, and they rotted in a week, because prose about code goes stale the moment the code moves and nobody's job is rewriting the style guide after every refactor. Then we noticed when the model DID conform: exactly when a real file from our repo happened to be sitting in its context. It needs to see one of our files at the moment it writes, not read prose about them. So we built the thing that shows it one, automatically, derived from the repo itself. That was 176 releases ago. We still run it daily on the code that pays our salaries. You have tried this: a conventions section in CLAUDE.md , a .cursorrules , an AGENTS.md . Three structural problems: You write them. Nobody enumerates every convention their repo follows. The list is partial on day one. They rot. Someone refactors the HTTP wrapper; the rule file still names the old one; the model follows the stale rule with full confidence. Rot is invisible until it bites. Prose loses. "Please use our wrapper" competes with everything else in context. A concrete file to imitate is how in-context learning actually works. Chameleon inverts all three. Conventions are derived by parsing the code itself: the official TypeScript Compiler API for TS/JS, Prism for Ruby, bundled libcst for Python. The profile derives from the production branch tip through a clean git worktree, never your dirty checkout, so a half-finished experiment cannot poison the team's norms. It refreshes automatically on drift. The rules cannot rot, because they are recomputed from the thing they describe. And chameleon writes the rule file for you, in the one channel models actually obey: .chameleon/conventions.md is the derived conventions rendered for Claude's memory channel, kept fresh by every refresh and teach. Wire it once — a one-line .claude/rules/chameleon-conventions.md covers the whole team without touching your CLAUDE.md or use CLAUDE.local.md to keep it personal . Measured, not asserted: the identical rules were followed 100% of the time through this channel versus 40% as hook-injected advice, across TypeScript, Ruby, and Python, on two models tests/effectiveness/results-published/multiconv-ab-2026-07-11.md . | The failure | The mechanism | When it fires | |---|---|---| axios instead of @/lib/http | The canonical witness already imports the wrapper; teach the rule once and every later edit also gets the counterexample quoted from your own repo | Before the edit | The second formatDate | Turn-end semantic duplication catch, grounded in the real call graph; search codebase finds the original first | Turn end | | Rename breaks 7 callers | Inbound-callers block before the edit; contract-break and removed-export checks against a prebuilt import index at turn end; get blast radius for the transitive picture | Before and after | Hardcoded sk live key | Deterministic write-time block AWS AKIA , GitHub ghp , Anthropic sk-ant- , Stripe sk live , Slack, Google, PEM keys, more , even on brand-new files; same for error-severity eval / exec | At write, always | | Hallucinated dependency | A don't-invent-dependencies protocol in context; phantom-import probes against the live disk; a deterministic dependency diff scan in PR review. About 1 in 5 AI-recommended packages do not exist, and attackers pre-register the recurring fake names | Write time and PR review | | The three-hour review loop | A turn-end correctness judge reads the diff in the same minute it was written; /chameleon-pr-review runs the mechanical review rounds before a human opens the PR | Turn end and pre-review | Six hooks across six lifecycle events, 19 MCP tools 16 comprehension/conformance + 3 operator dispatchers , 15 skills. You will call almost none of it directly; it works behind two moments, before the model writes and after the turn ends. | When | What runs | |---|---| | Session start | One-time convention summary + team principles for the repo | | Before each edit | Archetype resolve + the injection above; deterministic deny on hard-kind secrets and error-severity eval/exec, even on brand-new files | | After each edit x2 | Record the observation, then verify against the archetype's calibrated rules | | On your prompt | Intent capture: checkable assertion tokens secret-scanned digests, never your raw prose that sharpen the turn-end judge | | Turn end | Correctness judge, duplication vs call graph, cross-file existence, stale-test and co-change advisories Django model without a migration, Rails controller without a route, NestJS controller not registered in a module, Redux slice never added to the store , test-integrity facts, finding ledger, once-per-session idiom self-review | | On demand | 14 slash commands + the comprehension and review tool surface | Enforcement earns the right to block. A convention rule blocks only after it is calibrated: measured near-zero false positives against your repo's own committed files. Rules your team overrides get auto-demoted back to advisory. Modes are off / shadow / enforce ; shadow logs what would have blocked, and /chameleon-status --shadow shows you the evidence before you turn enforcement on. Inline escape hatch: // chameleon-ignore