Skip to content
claudewheel.patch_profiles
Edit
On this page

Wizard hook-merge helper plus a thin delegate to the unified reconcile core.

#claudewheel.patch_profiles

#claudewheel.patch_profiles

Wizard hook-merge helper plus a thin delegate to the unified reconcile core.

The old additive, user-extras-preserving profile sync (sync_profile_settings / sync_shared_settings / run_patch_profiles) has been REPLACED by the unified reconcile core in reconcile.py, which prunes each target's guardrail sections to EXACTLY canonical. run_patch_profiles here is now a thin wrapper around that core -- the claudewheel patch-profiles command and claudewheel reconcile-permissions command do the same thing.

merge_hooks (and its _script_basename helper) remain because the wizard uses them to assemble a NEW profile's hooks from a canonical base plus any clone-source hooks at creation time. That is profile construction, distinct from the reconcile core's pruning of existing profiles.

#_script_basename

python
def _script_basename(command: str) -> str

Return the trailing script name of a hook command path (or "").

#_list_at

python
def _list_at(container: dict[str, Any], key: str) -> list[Any]

Return container[key] as a list; raise when it is not one.

The array counterpart of the reconcile core's _dict_at, and it follows the same rule: a MISSING key is created empty and returned (absence is ordinary bootstrap), an existing list is returned as is, and a present-but-non-list value raises :class:MalformedSettingsError naming the key and the type found. setdefault alone would hand the malformed value straight to .append and crash with a bare AttributeError naming neither the key nor the file. container is left exactly as it was: a value nobody can interpret is never silently repaired.

#merge_hooks

python
def merge_hooks(existing: dict[str, Any], canonical: dict[str, Any]) -> list[str]

Merge canonical hooks into existing (mutated in place).

Canonical entries are matched to existing ones by their "matcher" field. Individual canonical hooks are matched to existing ones by script basename:

  • a canonical hook whose basename is absent is APPENDED;
  • a canonical hook whose basename is present but whose command points at a

DIFFERENT (stale) absolute path is REPATHED in place to the canonical command -- this is how a workspace relocation is healed, so a profile whose hook commands reference an old scripts directory is brought to the current one without duplicating the entry.

Only claudewheel-managed wirings (those in canonical) are ever touched; user-custom, non-canonical hooks are matched by neither basename nor matcher and so are preserved exactly. Returns human-readable descriptions of every hook added or repathed.

A malformed existing value raises :class:MalformedSettingsError naming the key and the type found: an event whose value is not an array ("PreToolUse": null), or a matched entry whose "hooks" is not one. Hand-edited settings are reported, never crashed on and never silently skipped past.

Used by the wizard to assemble a new profile's hooks. Existing profiles are reconciled to exact canonical by the reconcile core, not by this merge.

#run_patch_profiles

python
def run_patch_profiles(ws: 'Workspace', dry_run: bool=False) -> int

Reconcile every managed profile and shared-settings.json to exact canonical.

Thin delegate to the unified reconcile core. This PRUNES each target's guardrail sections (hooks, disallowedTools, permissions deny/ask and the canonical settings keys) to exactly canonical -- the old additive, user-extras-preserving behavior is gone.

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