Skip to content
claudewheel.project_hooks
Edit
On this page

Load project .claude/settings*.json hooks, compute a SHA-256 fingerprint for change detection, and produce a listing for approval.

#claudewheel.project_hooks

#claudewheel.project_hooks

Read and fingerprint a target project's Claude Code hooks.

A project can contribute its own Claude Code hooks via .claude/settings.json and .claude/settings.local.json (each file's top-level hooks section). Those hooks run arbitrary commands, so claudewheel must show them for explicit approval before a launch trusts them -- and re-prompt whenever they change.

This module is the reader half: it loads both config files, extracts their hooks sections, computes a canonical fingerprint over the combined content (so a launch can detect first-sighting and change), and produces a flattened, human-readable listing for the approval page. Malformed JSON is a hard error carrying the offending filename (it aborts the launch -- never a silent skip).

#MalformedProjectHooksError

A project settings file could not be parsed as JSON.

Carries the bare :attr:filename (e.g. settings.local.json) so callers can name the broken file in an actionable abort message.

#ProjectHooks

The combined hooks a project contributes, keyed by source filename.

sources maps each contributing file's basename (e.g. settings.json) to that file's hooks section. Files that are absent, that lack a hooks key, or whose hooks is empty do NOT appear -- so has_hooks is a clean "does this project contribute anything" signal.

#has_hooks

python
def has_hooks(self) -> bool

True when at least one settings file contributes a hooks section.

#fingerprint

python
def fingerprint(self) -> str

Stable sha256 hex over the canonical JSON of the combined hooks.

Canonical serialization (sorted keys, compact separators) makes the fingerprint independent of key ordering and whitespace: identical hooks content always yields the same fingerprint, and any change -- including moving a hook between the two settings files -- yields a different one.

#listing_lines

python
def listing_lines(self) -> list[str]

Flatten the hooks into human-readable lines for the approval page.

One line per command: the event name, the matcher (when present), and the command string. The Claude Code hooks schema is tolerated loosely -- missing or extra fields never raise; only what exists is shown. An entry with a matcher but no commands still contributes a line so the reviewer sees every matcher. Ordering is deterministic (sorted by filename, then event) so the listing is stable across launches.

#_entry_lines

python
def _entry_lines(event: str, entry: Any) -> list[str]

Render one hooks entry ({matcher?, hooks: [...]}) to display lines.

#_load_hooks_section

python
def _load_hooks_section(path: Path) -> Any

Return the hooks section of path, or None if absent/empty.

An absent file or a file without a (non-empty) hooks key contributes nothing. Unparseable JSON raises :class:MalformedProjectHooksError naming the file -- this is a hard error, never a silent skip.

#read_project_hooks

python
def read_project_hooks(directory: str) -> ProjectHooks

Read the combined Claude Code hooks a project under directory declares.

Reads <directory>/.claude/settings.json and settings.local.json, extracting each file's hooks section. Absent files and absent/empty hooks keys are simply skipped. Malformed JSON in either file raises :class:MalformedProjectHooksError carrying the offending filename.

#target_directory

python
def target_directory(selections: dict[str, str | None]) -> str

Resolve the launch target directory from selections (mirrors launch).

Uses the directory selection (~ expanded) when set, else the current working directory -- the same rule the launch config resolver applies.

More tools from this site

  • claudestream Drive Claude Code from Python: run it as a subprocess and read its output as typed events, with async and sync sessions, sandbox policies, and tools you define in Python
  • dirstat Fast, single-binary directory statistics CLI: every file under a tree grouped by format, with counts, sizes, and lines of code, as a colored terminal table or as JSON
  • fastware A batteries-included ASGI framework: msgspec JSON, a managed Granian server, dependency injection, SSE, WebSockets, auth, and a test client
  • go-toml-edit Zero-dep TOML editing library for Go with comment preservation
  • howmuchleft The fastest Claude Code statusline: context window, 5-hour, and weekly limit usage as three customizable gradient bars, rendering in about 6 ms
  • orxtra
  • pgdesign
  • predraw Declarative rendering pipeline: describe a scene in JSON and get SVG, PNG and WebP out, with light and dark style tokens, reusable components and text converted to path outlines
  • reposummary Turn a git repository's history into a Markdown journal: pick a time window or revision range and get a readable digest of what changed, optionally narrated by an LLM
  • rlsbl Release orchestration and project scaffolding CLI that bumps versions, validates a structured JSONL changelog, tags only the commit CI verified, and publishes to npm, PyPI, Go and more
  • safegit git wrapper CLI that gives each commit its own temporary index and retries ref updates on conflict, so concurrent agents share one repository
  • saferm Command-line replacement for rm that archives every deletion with a mandatory reason and the context it ran in, so deleted files can be listed, inspected and restored
  • selfdoc Static Site Generator that builds a project's documentation site directly from its source code, so the docs can never drift from the code they describe, with SEO/AEO, first-class blog, search, and cross-project linking built in
  • strictcli
  • stricttest An always-on test-isolation floor: a pytest plugin and a Go env-hygiene module that make a test suite structurally unable to reach real credentials, the real HOME, the network, or the development repository.
  • wesktop A Python framework that turns an ASGI web app into a desktop application, serving it from a local Granian server and displaying it in a native OS window via pywebview
Search