{"slug": "show-hn-trigger-tree-see-which-project-docs-your-ai-coding-agent-reads", "title": "Show HN: Trigger-tree; see which project docs your AI coding agent reads", "summary": "Trigger-tree, a new open-source tool by developer Hedde, measures which project documentation files AI coding assistants like Claude and Codex actually read, providing heat maps, cold maps, and a live dashboard to identify unread or ignored docs. The tool runs 100% locally with zero model tokens and no cloud or analytics vendors, closing a gap in agent observability by tracking doc discovery per task rather than just tokens or traces. Hedde argues that unread guardrails fail silently, and trigger-tree gives teams evidence to review, reroute, or prune documentation based on actual usage data.", "body_md": "See which docs your AI actually discovers.\n\nAI coding assistants read your project documentation to decide how to work. trigger-tree shows which docs they discover and use — and which ones they never find. 100% local. Zero model tokens. No cloud, no analytics vendors.\n\n✓ heat & cold maps of your documentation · ✓ live pulse dashboard · ✓ evidence-backed router fixes\n\n** Website** ·\n\n[Privacy policy](/Hedde/trigger_tree/blob/main/PRIVACY.md)·\n\n[Changelog](/Hedde/trigger_tree/blob/main/CHANGELOG.md)\n\n[Why measure documentation?](#why-measure-documentation)·[Quick start](#quick-start)·[Commands](#commands)[How it works](#how-it-works)·[The improvement loop](#the-improvement-loop)·[The dashboard](#the-dashboard)[Structuring your docs for discovery](#structuring-your-docs-for-discovery)[Configuration](#configuration)·[Privacy & data](#privacy--data)[Platform support](#platform-support)·[FAQ](#faq)·[Development](#development)\n\nIn AI-assisted development, documentation is no longer just for humans — it is the\n**steering wheel**. Your CLAUDE.md, conventions, and architecture docs tell the\nassistant how *your* team builds software: which patterns to copy, which guardrails\nto respect, which decisions were already made. When the assistant reads the right\ndoc, it works your way. When it doesn't, it guesses.\n\nAnd here is the uncomfortable part: **a rule that is never read protects nothing.**\nTeams invest heavily in writing docs, then assume they work — but an unread\nguardrail fails silently. You only notice when the AI \"ignores\" a convention that,\nin truth, it simply never found.\n\ntrigger-tree closes that loop. It measures which docs are actually consulted per\ntask, surfaces the ones that never are (and *why* — unrouted? unreferenced? obsolete?),\nand proves whether your fixes worked. Documentation stops being a hopeful artifact\nand becomes monitored infrastructure — with a health grade to track sprint over sprint.\n\n**This measurement doesn't exist anywhere else.** Anthropic's own\n[best-practices guide](https://code.claude.com/docs/en/best-practices) warns that a\nbloated CLAUDE.md causes instructions to be ignored — so high-performing teams review\ncontext ruthlessly. But nothing tells them whether those changes were *right*. Agent\nobservability platforms (Langfuse, Arize, W&B Weave) measure tokens and traces;\nnone measure which project docs were read per task. And just as\n[Fallow](https://github.com/fallow-rs/fallow) made unused-code review evidence-based;\ntrigger-tree gives you evidence to review, protect, reroute, or rescue docs nobody\ndared judge.\n\n| You are | Your question | trigger-tree answers with |\n|---|---|---|\n| Senior developer | \"Why maintain docs nobody reads?\" | Read counts and router gaps per file |\n| Tech lead | \"Was our CLAUDE.md pruning correct?\" | Trend: hunting ratio before/after each `/tt note` |\n| Product owner | \"We track token cost — where's doc utility?\" | One A–F documentation health grade |\n\nUse this path for `/tt`\n\nslash commands, Claude hooks, and the optional Claude\nstatusline:\n\n```\n/plugin marketplace add Hedde/trigger_tree\n/plugin install trigger-tree@trigger-tree\n/tt setup          # wires the project; local 200-character prompt previews by default\n/tt doctor         # proves this repo is wired and receiving telemetry\n```\n\nWork normally for a few sessions — the hooks log silently — then:\n\n```\n/tt status         # snapshot: current heat, lifetime reads, untouched paths\n/tt insights       # full heat/cold map report + HTML\n/tt tips           # Claude-specific memory and instruction maintenance tips\n```\n\nUse this path for Codex lifecycle hooks and natural-language trigger-tree workflows:\n\n```\ncodex plugin marketplace add Hedde/trigger_tree\ncodex plugin add trigger-tree@trigger-tree\n```\n\nStart a new Codex thread, review and trust the bundled hooks with `/hooks`\n\n, then ask\n“Show trigger-tree status” or “Open the trigger-tree live dashboard.” Telemetry is\ncollected automatically from official Codex lifecycle hooks; no wrapper is required.\n\n| Claude Code | OpenAI Codex | |\n|---|---|---|\n| Install | `/plugin marketplace add` |\n`codex plugin marketplace add` |\n| Invoke | `/tt status` , `/tt watch` , etc. |\nAsk for the desired trigger-tree workflow |\n| Hooks | Claude plugin hooks | Codex lifecycle hooks; trust via `/hooks` |\n| Statusline | Optional trigger-tree counter | Codex’s built-in `/statusline` is separate |\n| Visible after GitHub install | Claude marketplace/plugin list | Configured marketplace and Installed plugins |\n\nGitHub install versus OpenAI Curated:the commands above install trigger-tree directly; they do not add it to OpenAI’s public directory. An OpenAI Curated listing requires a separate skills-only submission through the[OpenAI plugin portal], followed by review, approval, and an explicit publish step.\n\nClaude Code exposes one command with nine subcommands. In Codex, ask for the matching\noutcome in natural language; the bundled `trigger-tree`\n\nskill runs the same local core.\n\n| Command | Does |\n|---|---|\n`/tt status` |\nSnapshot: current heat, lifetime reads, untouched paths |\n`/tt watch` |\nLive ASCII pulse dashboard (tmux split or a new terminal window) |\n`/tt watch demo` |\nDashboard with synthetic events — see it without waiting |\n`/tt insights` |\nHeat/cold map analysis: untouched paths, hunting, trend, task clusters + HTML report |\n`/tt suggestions` |\nConcise scope + max 5 evidence-backed fixes; full stats stay off stdout |\n`/tt tips` |\nClient-aware instruction maintenance: Claude memory/rules or Codex AGENTS.md |\n`/tt note <text>` |\nAnnotate the timeline (\"sharpened UX router\") — visible in the trend |\n`/tt doctor` |\nVerify hooks, privacy, statusline, and live telemetry with actionable fixes |\n`/tt setup [truncate|hash|off]` |\nWire the project and choose recognizable local previews or privacy-first markers |\n\nTips are intentionally client-specific. Claude advice follows Anthropic's guidance to\n[audit auto memory, keep CLAUDE.md concise, and remove conflicting instructions](https://code.claude.com/docs/en/memory).\nCodex advice follows OpenAI's guidance to\n[maintain AGENTS.md and a reproducible development environment](https://openai.com/business/guides-and-resources/how-openai-uses-codex/).\n\ntrigger-tree registers three lightweight hooks — full transparency:\n\n| Hook | Event | Records |\n|---|---|---|\n| SessionStart | new session | session marker |\n| UserPromptSubmit | your prompt | first 200 characters after setup; choose `hash` or `off` for stronger privacy |\n| SessionStart + PostToolUse | `Read|Glob|Grep` , `Skill` , and Bash |\ndoc reads, native searches, skill names, explicit `rg` /`grep` /`find` targets, and expanded Bash reader paths |\n\n**Hooks log shell-side** to`$PROJECT/.trigger-tree/history.jsonl`\n\n— zero model tokens, a few milliseconds per tool call, and a logging failure can never break your session (loggers always exit 0).**The aggregator is deterministic**— all counting happens in`tt-stats.py`\n\n; the model only interprets, never computes.**Discovery stays model-driven**— your CLAUDE.md remains the router. trigger-tree*measures*it; it never injects context or overrides routing. Bash searches count only when`rg`\n\n,`grep`\n\n, or`find`\n\nexplicitly targets an existing documentation path; search output is never treated as a read. In Bash sessions, lightweight reader wrappers observe expanded file arguments after variables, substitutions, loops, and globs resolve. They preserve command behavior and record only matching paths—not commands, patterns, output, or contents. Other shells use the conservative literal-path fallback.\n\nSubagent reads are attributed (`Explore`\n\n, `Plan`\n\n, …). Auto-loaded context—including\nthe recursive `CLAUDE.md`\n\n`@import`\n\ngraph—is invisible to Read telemetry by design\nand classified as **always loaded**; invoked skills *are* measured.\n\nFiles with zero reads are **review candidates**, never removal recommendations.\nProtected context (always-loaded files, safety paths, critical tags/globs, and docs\nwith many in-links) is called out as likely-keep. Then the loop closes:\n\n`/tt insights`\n\nshows the**folder heat & cold map** and flags**router gaps**: untouched files that no other doc even links to.`/tt suggestions`\n\nturns that into concrete edits (\"add X to docs/README.md under Y — untouched and unreferenced, 0 reads in 3 weeks\").- You apply, then\n`/tt note \"sharpened UX router\"`\n\n. - The\n**trend**(hunting ratio per week) shows whether the change actually worked — measured, not guessed.\n\n`/tt watch`\n\nopens a live view in a second terminal. Every read flashes white and\nripples up through its parent folders, then fades back to its heat color:\n\n```\n ⠹ trigger-tree  myproject · live doc-discovery\n\n docs/design/  · 1 unread\n   ├─ principles.md                       ███·· h 3.2 · 12×\n   ├─ ui-patterns.md                      █████ h 6.8 · 17×\n   └─ accessibility.md     ·   0\n docs/database/  · 🔍 2 searches · 1 unread\n   └─ migrations.md                       ██··· h 1.4 · 4×\n\n 33 reads · 2 scans (hunting) · 1 skill uses · 3 sessions\n   ● docs/design/ui-patterns.md · 2s ago\n   🔍 docs/database [Explore] · 31s ago\n```\n\nHeat and read count are deliberately different signals. **Reads** are the lifetime\nevidence and never decrease. **Heat** is current attention: each timestamped read has\nweight `0.5^(age_days / 30)`\n\n, so its contribution halves every 30 days (1 today,\n0.5 after 30 days, 0.125 after 90 days, and effectively zero after a year). A new\nread reheats the file immediately. `/tt insights`\n\nalso shows exact 7-, 30-, and\n90-day read windows, the last-read date, and lifetime reads. Folder heat is the sum\nof its file heat. Cold therefore means **inactive now**, never obsolete or safe to\nremove; untouched and protected-context classifications remain separate safeguards.\n\nFolder labels keep two signals separate: `🔍 N searches`\n\nproves the folder was\nexplicitly searched, while `N unread`\n\ncounts files without a Read event. Searching\na folder never pretends its files were consulted; reading one lowers only `unread`\n\n.\nThe same counters are scoped to the selected prompt when browsing with ←/→.\nThe live tree refreshes its inventory every five seconds: deleted files and folders\ndisappear from the current overview while their evidence remains available in\nhistorical prompt browsing and aggregate trends.\nRecently active folders temporarily move above quiet folders so live work stays\ninside a small viewport. They settle back into alphabetical order after eight\nseconds; files within folders and prompt-history views remain alphabetically stable.\nThe live view shows at most ten folders with proven activity and collapses untouched\nfolders/files into one quiet summary. `/tt insights`\n\nremains the complete cold-path\ninventory; nothing is removed from the underlying telemetry.\n\nThe live rows use horizontal five-cell heat bars and place their heat/lifetime\ncolumn against the available right edge, so wider panes expose more of long filenames\ninstead of leaving unused space. Sorting is explicit: `f`\n\nrestores recent-focus,\n`h`\n\nshows hottest first, `c`\n\nshows coldest first (including untouched files), and\n`n`\n\ntoggles A–Z and Z–A. Always-loaded `CLAUDE.md`\n\n, `AGENTS.md`\n\n, rules, and skills\nare labeled `injected`\n\ninstead of being misrepresented as cold. The current mode is\nalways printed in the footer. Press `s`\n\nto change prompt privacy inside the dashboard;\nchanges apply to future prompts and are written atomically to the gitignored project config.\nThe controls occupy their own persistent legend row (with a compact form for narrow\npanes), separate from prompt navigation and the live heartbeat, so the keys remain\ndiscoverable instead of disappearing at the right edge.\n\n**Browse per prompt**: press ← to move to older prompts and → to move to newer\nones — the tree filters to exactly what was aggregated for that input (its reads,\nscans and skill uses, with its prompt label in the header). The timeline never wraps or\nchanges mode at its ends; `a`\n\nreturns to the live overview.\n\n`/tt setup`\n\ndefaults to `TT_LOG_PROMPTS='truncate'`\n\n, so history shows a recognizable\npreview of at most 200 characters. The data remains local and gitignored, but it is\nstill prompt text on disk. Choose `/tt setup hash`\n\nfor stable fingerprints without\ntext, or `/tt setup off`\n\nfor marker-only history. Only future prompts are affected;\npreviously hashed prompt text cannot and should not be reconstructed.\n\n`--demo`\n\nfor instant synthetic events, `--replay`\n\nto re-run your real history,\n`q`\n\nor Ctrl+C to quit. The watcher uses a full-screen terminal buffer with wrapping\ndisabled, so refreshes do not accumulate as scrollback; your normal terminal state\nis restored when it exits.\n\nHow should a docs tree look so an assistant actually finds things? Claude Code has\nnative mechanisms, and there is one popular community convention — trigger-tree\nmeasures whichever you use. The facts, per the\n[official memory docs](https://code.claude.com/docs/en/memory.md):\n\n| Mechanism | Loads | Status |\n|---|---|---|\nRoot `CLAUDE.md` — keep it under 200 lines, pointers over inlining |\nat launch | ✅ official |\nNested `CLAUDE.md` per subdirectory |\non demand, when files there are read | ✅ official |\n`.claude/rules/*.md` with `paths:` glob frontmatter |\non demand, on matching files | ✅ official |\n`@imports` (`@docs/foo.md` , max 4 hops deep) |\nat launch — they always cost context | ✅ official |\nPer-folder `index.md` /`README.md` routers + `_template.md` |\nwhen the model follows your router instructions | community pattern |\n\nPractical guidance, as encoded in `/tt suggestions`\n\n:\n\n**Root CLAUDE.md is a router, not a manual.** Short, with a task→docs map (\"UI work → docs/design/, start at index.md\").**Give every folder one entry point**— an`index.md`\n\n(or a nested`CLAUDE.md`\n\n) that says what lives there and when to read what. trigger-tree flags folders without one (\"no index file\") and`/tt suggestions`\n\nproposes adding it.**Know the measurement trade-off.** Injected context (root and nested CLAUDE.md, rules, imports) is invisible to read-telemetry — trigger-tree honestly lists it as*always loaded*instead of guessing. Router files read via tools (`index.md`\n\n)**are** measurable. If you want provable discovery, route through index files and keep injected files thin.**Prefix templates with**(`_`\n\n`_template.md`\n\n). Claude attaches no special meaning to the underscore — but trigger-tree recognizes the convention and files them as intentional archive instead of nagging you about \"dead\" templates.\n\nPer-project settings live in `.trigger-tree/config.sh`\n\n(created by `/tt setup`\n\n):\n\n| Variable | Default | Meaning |\n|---|---|---|\n`TT_WATCH_REGEX` |\ndocs/agents/skills/agent-briefs + CLAUDE/AGENTS.md | which reads count as documentation |\n`TT_SCAN_REGEX` |\ndoc folders | which Glob/Grep and explicit Bash-search targets count as hunting |\n`TT_ALWAYS_LOADED_REGEX` |\nCLAUDE/AGENTS.md, .claude/skills | auto-loaded files, augmented by recursive `@imports` and excluded from review candidates |\n`TT_CRITICAL_GLOB` |\nempty | comma-separated globs protected as rare-but-critical review items |\n`TT_LOG_PROMPTS` |\n`truncate` after setup; `hash` without config |\n`truncate` (200 local chars) · `hash` (sha1 only) · `off` (marker only) |\n`TT_ROTATE_BYTES` |\n5 MB | rotate history.jsonl to a timestamped archive beyond this size |\n`TT_EXPERIMENTAL_OUTCOMES` |\n`off` |\n`on` enables a local, correlational committed-vs-abandoned session view |\n\nThe experimental outcome view records whether the repository HEAD changed during a session and the latest locally observed test-command result. It compares documents read in committed versus abandoned sessions. This is correlation only: it does not claim that reading a document caused an outcome.\n\nTeam auto-install — in your project's `.claude/settings.json`\n\n:\n\n```\n{\n  \"extraKnownMarketplaces\": {\n    \"trigger-tree\": { \"source\": { \"source\": \"github\", \"repo\": \"Hedde/trigger_tree\" } }\n  },\n  \"enabledPlugins\": { \"trigger-tree@trigger-tree\": true }\n}\n```\n\nCodex support is native. Its adapter normalizes official lifecycle events, unified\nterminal `cmd`\n\npayloads, native reads, and filesystem MCP reads into the same history\nschema used by Claude Code. Starting Codex from a repository subdirectory still writes\nto the repository-root dataset.\n\nGit hooks, editors, and other tools can feed that same telemetry through a stable entry point:\n\n```\npython3 <plugin>/scripts/tt-log.py ingest '{\"t\":\"read\",\"path\":\"docs/design/index.md\"}'\n```\n\nMissing `ts`\n\n/`session`\n\nare stamped automatically; invalid events are dropped silently.\n\n- ✅\n**No network calls of any kind**— python3 standard library only; audit every line. - ✅\n**Nothing leaves your machine**— data lives in`$PROJECT/.trigger-tree/`\n\n(gitignored). - ✅\n**Paths and metadata only**— file*contents*are never read or stored. - ✅\n**Prompt privacy is explicit**— setup explains its local preview default;`hash`\n\nand`off`\n\nremain one-command alternatives. - ✅\n**You own deletion**— remove`.trigger-tree/`\n\nand all history is gone.\n\nFull policy: [PRIVACY.md](/Hedde/trigger_tree/blob/main/PRIVACY.md) · Security reports: [SECURITY.md](/Hedde/trigger_tree/blob/main/SECURITY.md)\n\n| Platform | Telemetry & analysis | `/tt watch` window |\n|---|---|---|\n| macOS | ✅ | iTerm2 split (same window), tmux split, or Terminal.app |\n| Linux | ✅ | tmux split, gnome-terminal, konsole, xterm |\n| Windows | ✅ (Git Bash) | Windows Terminal (`wt.exe` ) or `start` |\n\nCI runs the full test suite on all three platforms. Requirements: `python3`\n\n(or\n`python`\n\n) on PATH — nothing else.\n\nHonesty over marketing — know what the measurement can and cannot see:\n\n**Injected context is invisible.** Root/nested CLAUDE.md,`.claude/rules`\n\nand`@imports`\n\nenter the context without a Read call; trigger-tree lists them as*always loaded*rather than guessing. Only tool-driven reads are measurable.**Read ≠ understood.** A read count proves discovery, not that the content was good or followed. Pair the telemetry with your own judgment.**Signals, not verdicts.** Untouched files are*review candidates*, never removal recommendations. Always-loaded, widely referenced, safety-path, configured-critical, and critical-tagged files are protected; low reads can mean rare-but-critical.**Tool hooks have surface boundaries.** Claude Code and Codex are supported natively. Hosted tools that bypass local lifecycle hooks remain invisible; other local tools can participate through`ingest`\n\n(see Codex adapter and external tools).\n\n**Where is my data?** `$PROJECT/.trigger-tree/history.jsonl`\n\n, per project, on your\nmachine, gitignored.\n\n**Does this slow the agent down?** No tokens are ever spent; the hook adds a few\nmilliseconds of shell time per relevant tool call.\n\n**Why does the statusline say \"0 docs consulted\" at session start?** CLAUDE.md is\ninjected into the system prompt, not read via tools — the router is loaded, but\n*discovery* hasn't happened yet. That's also why always-loaded files are excluded\nfrom cold-path analysis.\n\n**A file shows as untouched but I know it matters.** Untouched is a signal, not a\nverdict — check `/tt insights`\n\n: if it's a *router gap* (no doc links to it), the fix\nis a link, not deletion.\n\n**The /tt watch split flashes and disappears instantly.** Your session is running\na stale cached plugin version from before v0.3.3. Run\n\n`/reload-plugins`\n\nin that\nsession (or start a fresh one). Since v0.3.7 the confirmation line prints the\nrunning version — if it doesn't match the [latest release](https://github.com/Hedde/trigger_tree/releases), reload. A\n\n*real*crash keeps the pane open with the error since v0.3.3.\n\n**How do I know this repository is wired correctly?** Run `/tt doctor`\n\n. It checks\nthe plugin hooks, local-data gitignore, statusline registration, and whether this\nexact repository has received valid telemetry. `/tt watch`\n\nalways binds its split\nto the repository that invoked it and tails new hook events in real time.\n\n**Can I change prompt logging?** Yes: `/tt setup truncate`\n\nstores recognizable\n200-character previews locally, `/tt setup hash`\n\nstores only a short SHA-1 fingerprint,\nand `/tt setup off`\n\nstores marker-only events. Existing history is not rewritten.\n\nAlways use a virtual environment (never your system python):\n\n```\npython3 -m venv .venv\n.venv/bin/pip install -r requirements-test.txt -r requirements-dev.txt\n.venv/bin/black --check scripts tests .github/scripts\n.venv/bin/ruff check scripts tests .github/scripts\n.venv/bin/python -m coverage run -m pytest tests -q\n.venv/bin/python -m coverage report --fail-under=100\nshellcheck scripts/tt-open.sh scripts/tt-shell-capture.sh\nclaude plugin validate .\npython3 scripts/tt-watch.py --demo\n```\n\nCI: Black + Ruff, pytest with a 100% coverage gate on Ubuntu/macOS/Windows, Python\n3.10–3.13 compatibility, shellcheck, actionlint + zizmor workflow auditing, plugin\nvalidation, and an isolated marketplace-install smoke test. Release tags must agree\nwith the manifest and changelog. See [CONTRIBUTING.md](/Hedde/trigger_tree/blob/main/CONTRIBUTING.md).\n\n[MIT](/Hedde/trigger_tree/blob/main/LICENSE) — © Hedde van der Heide", "url": "https://wpnews.pro/news/show-hn-trigger-tree-see-which-project-docs-your-ai-coding-agent-reads", "canonical_source": "https://github.com/Hedde/trigger_tree", "published_at": "2026-07-21 09:34:34+00:00", "updated_at": "2026-07-21 09:52:46.966692+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-tools"], "entities": ["Hedde", "trigger-tree", "Claude", "Codex", "Anthropic", "Langfuse", "Arize", "W&B Weave"], "alternates": {"html": "https://wpnews.pro/news/show-hn-trigger-tree-see-which-project-docs-your-ai-coding-agent-reads", "markdown": "https://wpnews.pro/news/show-hn-trigger-tree-see-which-project-docs-your-ai-coding-agent-reads.md", "text": "https://wpnews.pro/news/show-hn-trigger-tree-see-which-project-docs-your-ai-coding-agent-reads.txt", "jsonld": "https://wpnews.pro/news/show-hn-trigger-tree-see-which-project-docs-your-ai-coding-agent-reads.jsonld"}}