COMPLEX.md
Tell your coding agent what it could break, before it edits.
One file at the root of your repository, computed from its history. Read by the agents you already use.
npx complex-md- 1 to 12 fixes
- 13 to 25
- 26 to 38
- 39 to 51
- Area: lines of code
--- complex_md: "0.3" generated: 2026-07-30 commit: b93d611c tool: complex-md/bench window_commits: 2000 files_analyzed: 50 profile: files_total: 395 files_in_scope: 50 loc_in_scope: 11311 kinds: "test 231, docs 52, source 50, ci 24, example 19, generated 9, other 8, asset 1, manifest 1" languages: "js 34, ts 16" dependency_edges: 383 commits_total: 4371 commits_analyzed: 1859 commits_skipped: 141 half_life_commits: 500 window_from: 2021-04-08 window_to: 2026-07-30 velocity_30d: 30.9 authors_total: 596 concentration_50: 5 hotspot_cut: 7 pair_min: 10 confidence: "structure+history" hotspots: - path: fastify.js kind: source loc: 1019 churn: 222 churn_w: 66.53 fixes: 51 authors: 66 owner_share: 0.45 fan_in: 7 tests: 63 score: 13821 - path: lib/errors.js kind: source loc: 548 churn: 47 churn_w: 15.81 fixes: 19 authors: 33 owner_share: 0.11 fan_in: 17 tests: 10 score: 10837 - path: lib/symbols.js kind: source loc: 73 churn: 28 churn_w: 8.12 fixes: 11 authors: 15 owner_share: 0.14 fan_in: 21 tests: 29 score: 5959 - path: lib/reply.js kind: source loc: 1076 churn: 93 churn_w: 29.32 fixes: 38 authors: 46 owner_share: 0.19 fan_in: 3 tests: 14 score: 5441 - path: lib/request.js kind: source loc: 397 churn: 50 churn_w: 19.96 fixes: 22 authors: 27 owner_share: 0.16 fan_in: 3 tests: 15 score: 3963 - path: lib/hooks.js kind: source loc: 433 churn: 18 churn_w: 4.44 fixes: 8 authors: 15 owner_share: 0.17 fan_in: 7 tests: 3 score: 3454 - path: lib/warnings.js kind: source loc: 75 churn: 37 churn_w: 13.66 fixes: 11 authors: 21 owner_share: 0.16 fan_in: 5 tests: 2 score: 3258 load_bearing: co_change: - files: [fastify.js, lib/route.js] count: 40 coupling: 45 - files: [docs/Reference/Errors.md, lib/errors.js] count: 26 coupling: 60 - files: [lib/reply.js, lib/request.js] count: 20 coupling: 40 - files: [fastify.js, lib/server.js] count: 18 coupling: 39 - files: [docs/Reference/Routes.md, docs/Reference/Server.md] count: 17 coupling: 37 - files: [fastify.js, lib/errors.js] count: 17 coupling: 36 - files: [lib/errors.js, types/errors.d.ts] count: 16 coupling: 94 - files: [docs/Reference/Warnings.md, lib/warnings.js] count: 16 coupling: 70 - files: [docs/Reference/Errors.md, types/errors.d.ts] count: 15 coupling: 88 - files: [types/hooks.d.ts, types/instance.d.ts] count: 15 coupling: 58 seams: blind_spots: - "9 generated or lock files excluded" - "1 credential shaped path left out of every list so a committed map never names them" ---
Before it edits this file, the agent reads
fastify.js
51 fixes222 commits7 importers63 tests
Before editing this file, run the covering tests for the area you touched and confirm any new symbol slot is declared in lib/symbols.js and copied by lib/plugin-override.js, or encapsulated child instances will silently lose it.
Moves with lib/route.js 45%, lib/server.js 39%
lib/errors.js
19 fixes47 commits17 importers10 tests
Before editing this file, open types/errors.d.ts and docs/Reference/Errors.md in the same change and run test/internals/errors.test.js.
Moves with types/errors.d.ts 94%, docs/Reference/Errors.md 60%
lib/symbols.js
11 fixes28 commits21 importers29 tests
Before editing this file, add keys rather than renaming or removing them, and grep the repo for the old name to find every reader.
lib/reply.js
38 fixes93 commits3 importers14 tests
Before editing this file, run test/diagnostics-channel/*.test.js and check that the onSend / preSerialization hook ordering and the sent / hijacked invariants still hold.
Moves with lib/request.js 40%
lib/request.js
22 fixes50 commits3 importers15 tests
Before editing this file, keep the plain and trust-proxy builders in step and run the request tests alongside lib/reply.js's, since the two move together.
Moves with lib/reply.js 40%
lib/hooks.js
8 fixes18 commits7 importers3 tests
The map gives this file no paragraph of its own.
lib/warnings.js
11 fixes37 commits5 importers2 tests
The map gives this file no paragraph of its own.
Moves with docs/Reference/Warnings.md 70%
A map from the fastify benchmark runs: 1,859 commits read, 50 files ranked. Select a tile.
fastify/fastify at commit b93d611c, generated 2026-07-30 by complex-md/bench, spec 0.3.
Agents follow it.
With COMPLEX.md14 of 15 risky edits
Without it3 of 17 risky edits
On two repositories with no shared tasks, agents ran the tests the map names on 14 of 15 risky edits, against 3 of 17 without it (11 of 11 discordant pairs, p about 0.001).
Claude Opus 5 in Claude Code, on fastify and undici. Every run, transcript and limit is in the results.
A search that finds nothing looks like nothing to find.
Put one NUL byte in a source file. Search the folder with ripgrep, and the file is not in the output: no line, no warning, exit 1.
Claude Code, Codex CLI and Cursor's agent CLI each ship a copy of ripgrep, and each copy prints nothing for that file. To the agent, the code is not there.
COMPLEX.md reads each source file up to 1 MB in full. It names these
files under blind_spots, with a search that reaches them.
$ rg -n /api/widgets (no output) exit 1 $ git grep -n -I --text /api/widgets routes.js:2: "/api/widgets": listWidgets,
blind_spots:
- "1 source file contains a NUL byte, so ripgrep, which Claude Code's Grep tool runs, skips it with no match and no warning, and GNU grep prints a binary file notice and no line: cli/src/resolve.js. Search with git grep -I --text, and fix the byte: it is almost always a separator that should have been written as an escape."
complex-md's own map carried this line at release 0.10.0. Run the repro.
How it works
1.Run it once
In any git repository, run npx complex-md. It reads
the commit history and the dependency graph, and writes COMPLEX.md at the
root. No Node.js? Hand your agent the skill, which does
the same analysis with git alone.
2.It wires itself in
One marked line near the top of AGENTS.md sends your agent to the
map before it edits a listed file. --profile full adds a brief
per hot file where each agent looks. Hooks come only with
--hooks.
3.Your agent reads before it edits
Each hot file gets one instruction: what to open with it, and
which tests to run. Run it again after big changes;
npx complex-md verify tells you when the map has drifted.
Works with the agents you already use.
Claude Code and Cursor get rules, and hooks on request. GitHub Copilot, OpenHands, Windsurf and Cline get a rule per hot file. Every other agent here reads COMPLEX.md through AGENTS.md or its own instructions file.
- Claude Code
- Cursor
- Codex
- Gemini CLI
- GitHub Copilot
- OpenHands
- Windsurf
- Cline
- Roo Code
- OpenClaw
- Hermes
- VS Code
- opencode
- goose
- Amp
- Jules
- Zed
- Warp
- Devin
- Factory
- Kilo Code
- Junie
- Augment
Open models welcome.
The numbers come from git, computed on your machine with no model. The prose comes from the model you choose, from the agent you already run, or from a template.
- DeepSeek, Kimi, Qwen, GLM, MiniMax
DEEPSEEK_API_KEY, MOONSHOT_API_KEY, ... - Ollama or LM Studio, on your machine
--provider ollama - Hermes, Kimi, Qwen and opencode CLIs
--via hermes - No key at all
template prose, your agent rewrites it
FAQ
How is this different from AGENTS.md?
AGENTS.md says how to work in the project. COMPLEX.md says where the risk is. One line in AGENTS.md sends the agent to it.
Do I need an API key?
No. Without a key, the file gets template prose built from the numbers, and your agent can rewrite it from .complex-md/prompt.md. With a key, one model call writes the prose.
What does it send, and where?
Without a key, nothing: the CLI computes the map with git and Node.js and sends no telemetry. With a key, it makes one call to the provider you chose, with the computed signals and excerpts of up to ten hotspot files, about 60,000 characters of code at most. With --via, the same request goes to the agent CLI you named.
Who writes the prose?
The numbers always come from git. The prose comes from one model call when a key is set, from your own agent CLI with --via, or else from a template built from the numbers. A template writes every sentence on a hosted page.
What does the score mean?
A ranking, not a prediction. Higher means an edit has more ways to go wrong. The spec shows the formula.
Will it get in my agent's way?
A default run writes COMPLEX.md, one marked line near the top of AGENTS.md, a marked block in .gitattributes and .complex-md/manifest.json. It writes no hooks unless you pass --hooks, and --no-wire writes only the file. --profile full adds at most 10 briefs per agent, in .claude/rules/complex-md/, .cursor/rules/complex-md/, .github/instructions/complex-md/, .agents/skills/, .agents/rules/, .devin/rules/, .windsurf/rules/, .kiro/steering/ and .clinerules/. npx complex-md unwire removes what wire wrote and names any file you edited since.
How does a maintainer remove a hosted page?
In one of three ways: commit COMPLEX.md to the repository, set hosted: false in its front matter, or send the removal form. Each takes effect within 24 hours.
Does it work outside GitHub?
The CLI works on any git repository, wherever it is hosted. Hosted pages and the lookup cover public GitHub repositories only, for now.
Can I write one by hand?
Yes. It is plain markdown with a YAML header. The walkthrough takes seven git commands.
npx says "No versions available". Why?
Your npm config sets min-release-age, which refuses packages published this week. Run npm_config_min_release_age=0 npx -y complex-md once.