Zian's Blog

What Runs Automatically in Your Claude Code Setup?

“Hook X-ray: See what runs automatically. A scan line passes over a stack of configuration rows, highlighting two.

Claude Code customization piles up across user settings, project settings, plugins, skills, subagents, mods, launchers and managed policy. That makes a plain question surprisingly hard to answer: what runs automatically in my setup? Where did it come from, which plugin owns it, is its configuration broken or surprising, and what should I review first?

I built Hook X-ray to help: a free, open-source, local tool for reviewing Claude Code hooks and mods. Its tagline is “See what runs automatically,” and this post covers where that promise stops. If you use plugins, write them, or maintain a shared team config, it’s for you.

Hooks and Mods, Briefly

A hook is configuration that makes Claude Code run something at a point in its lifecycle: around a tool call, at session start, when you submit a prompt. Hooks come from user, project and local settings, managed policy, plugins, and skill and subagent frontmatter. Skill hooks register once the skill is invoked; subagent hooks run only while that subagent does.

A mod is a plugin that runs JavaScript inside Claude Code. It sees every prompt and tool call and can change them.

The official docs are specific. Command hooks, local MCP servers, plugin monitors and LSP servers run outside the Bash sandbox with your full access, and native Windows has no sandbox. Mods aren’t sandboxed either. Permission rules apply to Claude’s tool calls, not to a mod’s own file and process calls: with Read(.env) denied, a mod can still read that file with $.fs.read.

Whether a mod can approve a call that a deny rule refuses depends on the setup. The permissions page says the call stays refused with managed settings or a Team or Enterprise login, and that without either, a mod can approve it.

None of this means your setup is unsafe, and the problem isn’t only malicious code. Ordinary mistakes cost you too: duplicate handlers, confusing matchers, per-prompt context, and a 600-second default timeout nobody chose.

Why I Built It

The project began as an evaluation of three small tools for Claude Code users: a skill collision checker, a hook inventory and review CLI, and a plugin update diff tool. I picked the hook tool first and kept it narrow: inventory, ownership and config lint, not a broad security scanner.

Testing on real configurations exposed a second problem: a raw inventory is another wall of output. Repeated notes, long lists of missing files, and unclear ownership buried what deserved attention. So the direction became: show what matters first, explain who owns it, and make the output useful.

What a Review Turned Up

These are observations from my development runs; yours will differ.

Be careful with that last one. The inventory showed a mod with a hook on conversation-related events and a network-capable API call. That is a reason to read its source, not evidence that conversation content is sent anywhere. I haven’t confirmed it either way. Hook X-ray has no rule that combines those signals today; that analysis is planned, not shipped.

In another workspace, ~/.claude/settings.json was empty while a launcher supplied a Stop hook through another source. That helped motivate the live companion below.

How the Review Works

The workflow: inventory, owner attribution, configuration findings, then your judgment. The most actionable items come first:

Each finding carries a confidence: certain only for facts from configuration, likely for anything inferred from a hook’s command or script.

This excerpt is illustrative: a throwaway demo configuration built to trigger a few rules, not a real machine.

Never runs or runs twice
  ⚠ h1   SessionStart command · user · 600s default
       echo hi
       `if: "Bash(git *)"` on SessionStart: this hook never runs
       fix: Remove `if`, or move the hook to a tool event.

  ⚠ h5   PostToolUse [*] command · plugin:lint-helper · 10s
       ./fmt.sh
       same handler as h3 (user): runs twice
       fix: Remove one of the two copies.
       Plugin-owned: report it to the author; disable the plugin if the
       behavior is unwanted.

Per the hooks reference, if only works on tool events, and a plugin’s copy of a handler runs separately from a settings copy.

Hook X-ray never reads a mod’s JavaScript; it reports what claude plugin validate --json lists: hooks, API calls and environment access. Without claude on your PATH, a mod shows as present, validation unavailable.

Quick Start: One Command

Node 20 or newer is required.

npx hook-xray                     # this project, user, managed settings, plugins
npx hook-xray --all-projects      # every project listed in ~/.claude.json
npx hook-xray --sources           # sources found, missing or broken, with a short summary
npx hook-xray --event PreToolUse  # one event
npx hook-xray --json --redact     # a report to review before sharing

--json keeps every hook and finding. --redact hides tokens, header and env values, hosts, prompts and home paths, but skim before sharing. Scratch workspaces are labeled, and unreadable files are told apart from invalid JSON.

Disk Review and the Live Companion

By default it reads files on disk: user, project, local and managed settings, plugin hooks, skill and subagent frontmatter, and any file you pass with --settings. Some sources aren’t readable files: launcher-supplied settings, and managed policy delivered through MDM, the Windows registry or server-managed settings. For those, the repository includes a companion mod, mods/hook-xray-live. It reads session settings with $.settings.read and lints them with the rules that depend only on configuration. The CLI can load its snapshot and apply all its rules.

The companion isn’t in the npm package, so clone the repository to use it. Because it is itself a mod, it needs Claude Code 2.1.287 or later:

git clone https://github.com/nazrul-kabir/hook-xray
claude --plugin-dir ./hook-xray/mods/hook-xray-live
# in the session:
#   /hook-xray                  hooks from --settings/SDK and managed policy
#   /hook-xray save live.json   write a snapshot
npx hook-xray --live live.json

I’m not claiming it sees more than the built-in /hooks browser. What it adds is workflow: reusable lint findings, an exportable snapshot, and scriptable output. /hooks is a good look at one session; Hook X-ray reviews across projects, with exact plugin ownership, dormant skill and subagent hooks, and machine-readable reports.

Cost, Requirements and Limits

It is not antivirus, and a clean report doesn’t mean a plugin is safe. It enforces nothing, and can’t see runtime-registered hooks or plugins loaded only with --plugin-dir. Broader risk analysis (network and package-execution signals, settings changes, per-handler explanations) is planned, not in the current release, 0.1.1.

An Early Milestone

Per the npm API, hook-xray had 543 downloads across the first three days of download activity (November 6 to 8, 2026), spanning two releases. That mixes automation, repeats and updates: a launch milestone, not a measure of users.

Final Thoughts: Try It and Tell Me

Run npx hook-xray on a setup you know, then tell me what it found, or missed. Open a GitHub issue, ideally with a redacted --json report. The package is on npm.

Exit mobile version