cd /news/developer-tools/show-hn-trigger-tree-see-which-proje… · home topics developer-tools article
[ARTICLE · art-66679] src=github.com ↗ pub= topic=developer-tools verified=true sentiment=· neutral

Show HN: Trigger-tree; see which project docs your AI coding agent reads

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.

read15 min views3 publishedJul 21, 2026
Show HN: Trigger-tree; see which project docs your AI coding agent reads
Image: source

See which docs your AI actually discovers.

AI 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.

✓ heat & cold maps of your documentation · ✓ live pulse dashboard · ✓ evidence-backed router fixes

** Website** ·

Privacy policy·

Changelog

Why measure documentation?·Quick start·CommandsHow it works·The improvement loop·The dashboardStructuring your docs for discoveryConfiguration·Privacy & dataPlatform support·FAQ·Development

In AI-assisted development, documentation is no longer just for humans — it is the steering wheel. Your CLAUDE.md, conventions, and architecture docs tell the assistant how your team builds software: which patterns to copy, which guardrails to respect, which decisions were already made. When the assistant reads the right doc, it works your way. When it doesn't, it guesses.

And here is the uncomfortable part: a rule that is never read protects nothing. Teams invest heavily in writing docs, then assume they work — but an unread guardrail fails silently. You only notice when the AI "ignores" a convention that, in truth, it simply never found.

trigger-tree closes that loop. It measures which docs are actually consulted per task, surfaces the ones that never are (and why — unrouted? unreferenced? obsolete?), and proves whether your fixes worked. Documentation stops being a hopeful artifact and becomes monitored infrastructure — with a health grade to track sprint over sprint.

This measurement doesn't exist anywhere else. Anthropic's own best-practices guide warns that a bloated CLAUDE.md causes instructions to be ignored — so high-performing teams review context ruthlessly. But nothing tells them whether those changes were right. Agent observability platforms (Langfuse, Arize, W&B Weave) measure tokens and traces; none measure which project docs were read per task. And just as Fallow made unused-code review evidence-based; trigger-tree gives you evidence to review, protect, reroute, or rescue docs nobody dared judge.

You are Your question trigger-tree answers with
Senior developer "Why maintain docs nobody reads?" Read counts and router gaps per file
Tech lead "Was our CLAUDE.md pruning correct?" Trend: hunting ratio before/after each /tt note
Product owner "We track token cost — where's doc utility?" One A–F documentation health grade

Use this path for /tt

slash commands, Claude hooks, and the optional Claude statusline:

/plugin marketplace add Hedde/trigger_tree
/plugin install trigger-tree@trigger-tree
/tt setup          # wires the project; local 200-character prompt previews by default
/tt doctor         # proves this repo is wired and receiving telemetry

Work normally for a few sessions — the hooks log silently — then:

/tt status         # snapshot: current heat, lifetime reads, untouched paths
/tt insights       # full heat/cold map report + HTML
/tt tips           # Claude-specific memory and instruction maintenance tips

Use this path for Codex lifecycle hooks and natural-language trigger-tree workflows:

codex plugin marketplace add Hedde/trigger_tree
codex plugin add trigger-tree@trigger-tree

Start a new Codex thread, review and trust the bundled hooks with /hooks

, then ask “Show trigger-tree status” or “Open the trigger-tree live dashboard.” Telemetry is collected automatically from official Codex lifecycle hooks; no wrapper is required.

Claude Code OpenAI Codex
Install /plugin marketplace add
codex plugin marketplace add
Invoke /tt status , /tt watch , etc.
Ask for the desired trigger-tree workflow
Hooks Claude plugin hooks Codex lifecycle hooks; trust via /hooks
Statusline Optional trigger-tree counter Codex’s built-in /statusline is separate
Visible after GitHub install Claude marketplace/plugin list Configured marketplace and Installed plugins

GitHub 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.

Claude Code exposes one command with nine subcommands. In Codex, ask for the matching outcome in natural language; the bundled trigger-tree

skill runs the same local core.

Command Does
/tt status
Snapshot: current heat, lifetime reads, untouched paths
/tt watch
Live ASCII pulse dashboard (tmux split or a new terminal window)
/tt watch demo
Dashboard with synthetic events — see it without waiting
/tt insights
Heat/cold map analysis: untouched paths, hunting, trend, task clusters + HTML report
/tt suggestions
Concise scope + max 5 evidence-backed fixes; full stats stay off stdout
/tt tips
Client-aware instruction maintenance: Claude memory/rules or Codex AGENTS.md
/tt note <text>
Annotate the timeline ("sharpened UX router") — visible in the trend
/tt doctor
Verify hooks, privacy, statusline, and live telemetry with actionable fixes
`/tt setup [truncate hash
Wire the project and choose recognizable local previews or privacy-first markers

Tips are intentionally client-specific. Claude advice follows Anthropic's guidance to audit auto memory, keep CLAUDE.md concise, and remove conflicting instructions. Codex advice follows OpenAI's guidance to maintain AGENTS.md and a reproducible development environment.

trigger-tree registers three lightweight hooks — full transparency:

Hook Event Records
SessionStart new session session marker
UserPromptSubmit your prompt first 200 characters after setup; choose hash or off for stronger privacy
SessionStart + PostToolUse `Read Glob
doc reads, native searches, skill names, explicit rg /grep /find targets, and expanded Bash reader paths

Hooks log shell-side to$PROJECT/.trigger-tree/history.jsonl

— 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 intt-stats.py

; the model only interprets, never computes.Discovery stays model-driven— your CLAUDE.md remains the router. trigger-treemeasuresit; it never injects context or overrides routing. Bash searches count only whenrg

,grep

, orfind

explicitly 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.

Subagent reads are attributed (Explore

, Plan

, …). Auto-loaded context—including the recursive CLAUDE.md

@import

graph—is invisible to Read telemetry by design and classified as always loaded; invoked skills are measured.

Files with zero reads are review candidates, never removal recommendations. Protected context (always-loaded files, safety paths, critical tags/globs, and docs with many in-links) is called out as likely-keep. Then the loop closes:

/tt insights

shows thefolder heat & cold map and flagsrouter gaps: untouched files that no other doc even links to./tt suggestions

turns that into concrete edits ("add X to docs/README.md under Y — untouched and unreferenced, 0 reads in 3 weeks").- You apply, then /tt note "sharpened UX router"

. - The trend(hunting ratio per week) shows whether the change actually worked — measured, not guessed.

/tt watch

opens a live view in a second terminal. Every read flashes white and ripples up through its parent folders, then fades back to its heat color:

 ⠹ trigger-tree  myproject · live doc-discovery

 docs/design/  · 1 unread
   ├─ principles.md                       ███·· h 3.2 · 12×
   ├─ ui-patterns.md                      █████ h 6.8 · 17×
   └─ accessibility.md     ·   0
 docs/database/  · 🔍 2 searches · 1 unread
   └─ migrations.md                       ██··· h 1.4 · 4×

 33 reads · 2 scans (hunting) · 1 skill uses · 3 sessions
   ● docs/design/ui-patterns.md · 2s ago
   🔍 docs/database [Explore] · 31s ago

Heat and read count are deliberately different signals. Reads are the lifetime evidence and never decrease. Heat is current attention: each timestamped read has weight 0.5^(age_days / 30)

, so its contribution halves every 30 days (1 today, 0.5 after 30 days, 0.125 after 90 days, and effectively zero after a year). A new read reheats the file immediately. /tt insights

also shows exact 7-, 30-, and 90-day read windows, the last-read date, and lifetime reads. Folder heat is the sum of its file heat. Cold therefore means inactive now, never obsolete or safe to remove; untouched and protected-context classifications remain separate safeguards.

Folder labels keep two signals separate: 🔍 N searches

proves the folder was explicitly searched, while N unread

counts files without a Read event. Searching a folder never pretends its files were consulted; reading one lowers only unread

. The same counters are scoped to the selected prompt when browsing with ←/→. The live tree refreshes its inventory every five seconds: deleted files and folders disappear from the current overview while their evidence remains available in historical prompt browsing and aggregate trends. Recently active folders temporarily move above quiet folders so live work stays inside a small viewport. They settle back into alphabetical order after eight seconds; files within folders and prompt-history views remain alphabetically stable. The live view shows at most ten folders with proven activity and collapses untouched folders/files into one quiet summary. /tt insights

remains the complete cold-path inventory; nothing is removed from the underlying telemetry.

The live rows use horizontal five-cell heat bars and place their heat/lifetime column against the available right edge, so wider panes expose more of long filenames instead of leaving unused space. Sorting is explicit: f

restores recent-focus, h

shows hottest first, c

shows coldest first (including untouched files), and n

toggles A–Z and Z–A. Always-loaded CLAUDE.md

, AGENTS.md

, rules, and skills are labeled injected

instead of being misrepresented as cold. The current mode is always printed in the footer. Press s

to change prompt privacy inside the dashboard; changes apply to future prompts and are written atomically to the gitignored project config. The controls occupy their own persistent legend row (with a compact form for narrow panes), separate from prompt navigation and the live heartbeat, so the keys remain discoverable instead of disappearing at the right edge.

Browse per prompt: press ← to move to older prompts and → to move to newer ones — the tree filters to exactly what was aggregated for that input (its reads, scans and skill uses, with its prompt label in the header). The timeline never wraps or changes mode at its ends; a

returns to the live overview.

/tt setup

defaults to TT_LOG_PROMPTS='truncate'

, so history shows a recognizable preview of at most 200 characters. The data remains local and gitignored, but it is still prompt text on disk. Choose /tt setup hash

for stable fingerprints without text, or /tt setup off

for marker-only history. Only future prompts are affected; previously hashed prompt text cannot and should not be reconstructed.

--demo

for instant synthetic events, --replay

to re-run your real history, q

or Ctrl+C to quit. The watcher uses a full-screen terminal buffer with wrapping disabled, so refreshes do not accumulate as scrollback; your normal terminal state is restored when it exits.

How should a docs tree look so an assistant actually finds things? Claude Code has native mechanisms, and there is one popular community convention — trigger-tree measures whichever you use. The facts, per the official memory docs:

Mechanism Loads Status
Root CLAUDE.md — keep it under 200 lines, pointers over inlining
at launch ✅ official
Nested CLAUDE.md per subdirectory
on demand, when files there are read ✅ official
.claude/rules/*.md with paths: glob frontmatter
on demand, on matching files ✅ official
@imports (@docs/foo.md , max 4 hops deep)
at launch — they always cost context ✅ official
Per-folder index.md /README.md routers + _template.md
when the model follows your router instructions community pattern

Practical guidance, as encoded in /tt suggestions

:

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— anindex.md

(or a nestedCLAUDE.md

) that says what lives there and when to read what. trigger-tree flags folders without one ("no index file") and/tt suggestions

proposes 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 asalways loadedinstead of guessing. Router files read via tools (index.md

)are measurable. If you want provable discovery, route through index files and keep injected files thin.Prefix templates with(_

_template.md

). 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.

Per-project settings live in .trigger-tree/config.sh

(created by /tt setup

):

Variable Default Meaning
TT_WATCH_REGEX
docs/agents/skills/agent-briefs + CLAUDE/AGENTS.md which reads count as documentation
TT_SCAN_REGEX
doc folders which Glob/Grep and explicit Bash-search targets count as hunting
TT_ALWAYS_LOADED_REGEX
CLAUDE/AGENTS.md, .claude/skills auto-loaded files, augmented by recursive @imports and excluded from review candidates
TT_CRITICAL_GLOB
empty comma-separated globs protected as rare-but-critical review items
TT_LOG_PROMPTS
truncate after setup; hash without config
truncate (200 local chars) · hash (sha1 only) · off (marker only)
TT_ROTATE_BYTES
5 MB rotate history.jsonl to a timestamped archive beyond this size
TT_EXPERIMENTAL_OUTCOMES
off
on enables a local, correlational committed-vs-abandoned session view

The 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.

Team auto-install — in your project's .claude/settings.json

:

{
  "extraKnownMarketplaces": {
    "trigger-tree": { "source": { "source": "github", "repo": "Hedde/trigger_tree" } }
  },
  "enabledPlugins": { "trigger-tree@trigger-tree": true }
}

Codex support is native. Its adapter normalizes official lifecycle events, unified terminal cmd

payloads, native reads, and filesystem MCP reads into the same history schema used by Claude Code. Starting Codex from a repository subdirectory still writes to the repository-root dataset.

Git hooks, editors, and other tools can feed that same telemetry through a stable entry point:

python3 <plugin>/scripts/tt-log.py ingest '{"t":"read","path":"docs/design/index.md"}'

Missing ts

/session

are stamped automatically; invalid events are dropped silently.

  • No network calls of any kind— python3 standard library only; audit every line. - ✅ Nothing leaves your machine— data lives in$PROJECT/.trigger-tree/

(gitignored). - ✅ Paths and metadata only— filecontentsare never read or stored. - ✅ Prompt privacy is explicit— setup explains its local preview default;hash

andoff

remain one-command alternatives. - ✅ You own deletion— remove.trigger-tree/

and all history is gone.

Full policy: PRIVACY.md · Security reports: SECURITY.md

Platform Telemetry & analysis /tt watch window
macOS iTerm2 split (same window), tmux split, or Terminal.app
Linux tmux split, gnome-terminal, konsole, xterm
Windows ✅ (Git Bash) Windows Terminal (wt.exe ) or start

CI runs the full test suite on all three platforms. Requirements: python3

(or python

) on PATH — nothing else.

Honesty over marketing — know what the measurement can and cannot see:

Injected context is invisible. Root/nested CLAUDE.md,.claude/rules

and@imports

enter the context without a Read call; trigger-tree lists them asalways loadedrather 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 arereview 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 throughingest

(see Codex adapter and external tools).

Where is my data? $PROJECT/.trigger-tree/history.jsonl

, per project, on your machine, gitignored.

Does this slow the agent down? No tokens are ever spent; the hook adds a few milliseconds of shell time per relevant tool call.

Why does the statusline say "0 docs consulted" at session start? CLAUDE.md is injected into the system prompt, not read via tools — the router is loaded, but discovery hasn't happened yet. That's also why always-loaded files are excluded from cold-path analysis.

A file shows as untouched but I know it matters. Untouched is a signal, not a verdict — check /tt insights

: if it's a router gap (no doc links to it), the fix is a link, not deletion.

The /tt watch split flashes and disappears instantly. Your session is running a stale cached plugin version from before v0.3.3. Run

/reload-plugins

in that session (or start a fresh one). Since v0.3.7 the confirmation line prints the running version — if it doesn't match the latest release, reload. A

realcrash keeps the pane open with the error since v0.3.3.

How do I know this repository is wired correctly? Run /tt doctor

. It checks the plugin hooks, local-data gitignore, statusline registration, and whether this exact repository has received valid telemetry. /tt watch

always binds its split to the repository that invoked it and tails new hook events in real time.

Can I change prompt logging? Yes: /tt setup truncate

stores recognizable 200-character previews locally, /tt setup hash

stores only a short SHA-1 fingerprint, and /tt setup off

stores marker-only events. Existing history is not rewritten.

Always use a virtual environment (never your system python):

python3 -m venv .venv
.venv/bin/pip install -r requirements-test.txt -r requirements-dev.txt
.venv/bin/black --check scripts tests .github/scripts
.venv/bin/ruff check scripts tests .github/scripts
.venv/bin/python -m coverage run -m pytest tests -q
.venv/bin/python -m coverage report --fail-under=100
shellcheck scripts/tt-open.sh scripts/tt-shell-capture.sh
claude plugin validate .
python3 scripts/tt-watch.py --demo

CI: Black + Ruff, pytest with a 100% coverage gate on Ubuntu/macOS/Windows, Python 3.10–3.13 compatibility, shellcheck, actionlint + zizmor workflow auditing, plugin validation, and an isolated marketplace-install smoke test. Release tags must agree with the manifest and changelog. See CONTRIBUTING.md.

MIT — © Hedde van der Heide

── more in #developer-tools 4 stories · sorted by recency
── more on @hedde 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/show-hn-trigger-tree…] indexed:0 read:15min 2026-07-21 ·