settings.json Reference
settings.json is the root Claude Code configuration for this repository. It defines what Claude Code is allowed to do, which hooks run, which plugins are enabled, and which environment and UI defaults are applied.
Note: This file is written to work with companion files under
~/.claude/. Every hook command points at~/.claude/scripts/..., and the status line points at~/.claude/statusline.sh. If you want to use this configuration unchanged, make sure those files exist at those paths.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"includeCoAuthoredBy": false,
The schema line gives editors JSON Schema validation and autocomplete. includeCoAuthoredBy: false disables automatic Co-authored-by trailers.
What This File Controls
- Tool permissions and Bash allowlists
- Session startup checks
- Pre-tool safety hooks
- Prompt submission behavior
- Desktop notifications
- Enabled plugins and extra marketplaces
- Status-line rendering
- Environment flags and feedback settings
Permissions
The first safety layer is permissions.allow. It is intentionally narrow: direct file access is limited to /tmp/claude/**, and only a small set of Bash command patterns are pre-approved.
"permissions": {
"allow": [
"Read(/tmp/claude/**)",
"Edit(/tmp/claude/**)",
"Write(/tmp/claude/**)",
"Bash(mkdir -p /tmp/claude*)",
"Bash(claude:*)",
"Bash(sed -n:*)",
"Bash(grep:*)",
"Bash(mcpl:*)",
"Bash(git -C:*)",
"Bash(prek:*)",
"Bash(~/.claude/scripts/session-start-check.sh:*)",
"Bash(~/.claude/scripts/my-notifier.sh:*)",
"Bash(uv run ~/.claude/scripts/rule-injector.py:*)",
"Bash(uv run ~/.claude/scripts/rule-enforcer.py:*)",
"Bash(uv run ~/.claude/scripts/git-protection.py:*)",
"Grep",
"Bash(myk-claude-tools:*)"
]
},
In practice, that gives you these categories of access:
- Temporary workspace access:
Read,Edit, andWriteonly under/tmp/claude/** - Approved shell entry points:
claude,sed -n,grep,mcpl,git -C,prek, andmyk-claude-tools - Hook entry points: the startup checker, notifier, and Python hook scripts
- Native search access: the built-in
Greptool
A few details are worth calling out:
gitaccess is narrow here: the allowlist includesBash(git -C:*), not a blanketBash(git:*)pre-commitis not allowlisted directly; this config expectsprek- The repo-specific
myk-claude-toolscommand is a real CLI entry point defined inpyproject.toml
[project.scripts]
myk-claude-tools = "myk_claude_tools.cli:main"
The Duplicate Allowlist
This repository keeps a second allowlist in allowedTools. The checked-in file treats it as a mirror of the same approved tool set, and the file includes an explicit reminder to keep script entries synchronized.
"_scriptsNote": "Script entries must be duplicated in both permissions.allow and allowedTools arrays. When adding new scripts, update BOTH locations.",
"allowedTools": [
"Edit(/tmp/claude/**)",
"Write(/tmp/claude/**)",
"Read(/tmp/claude/**)",
"Bash(mkdir -p /tmp/claude*)",
"Bash(claude:*)",
"Bash(sed -n:*)",
"Bash(grep:*)",
"Bash(mcpl:*)",
"Bash(git -C:*)",
"Bash(prek:*)",
"Grep",
"Bash(~/.claude/scripts/session-start-check.sh:*)",
"Bash(~/.claude/scripts/my-notifier.sh:*)",
"Bash(uv run ~/.claude/scripts/rule-injector.py:*)",
"Bash(uv run ~/.claude/scripts/rule-enforcer.py:*)",
"Bash(uv run ~/.claude/scripts/git-protection.py:*)",
"Bash(myk-claude-tools:*)"
],
Warning: If you add, remove, or rename a script-based command, update both
permissions.allowandallowedTools. Updating only one of them is a common way to break this configuration.
A practical way to think about this is: there is one logical allowlist, but this file stores it twice.
Hooks
The hooks block is where most of the behavior lives. This configuration uses four hook phases.
| Hook event | Matcher | Runs | Purpose |
|---|---|---|---|
SessionStart |
"" |
~/.claude/scripts/session-start-check.sh |
Checks tool and plugin prerequisites |
UserPromptSubmit |
"" |
uv run ~/.claude/scripts/rule-injector.py |
Injects a short reminder into prompt context |
PreToolUse |
TodoWrite|Bash |
uv run ~/.claude/scripts/rule-enforcer.py |
Denies raw python, pip, and pre-commit usage |
PreToolUse |
Bash |
uv run ~/.claude/scripts/git-protection.py |
Blocks unsafe git commit and git push usage |
PreToolUse |
Bash |
inline prompt hook |
Blocks or asks about dangerous OS-level commands |
Notification |
"" |
~/.claude/scripts/my-notifier.sh |
Sends desktop notifications |
Note: Every Python hook is run through
uv run, souvis a hard requirement for this setup.
SessionStart
The startup hook is a non-blocking health check. It looks for required tools, optional helpers, and a set of official plugins.
# CRITICAL: uv - Required for Python hooks
if ! command -v uv &>/dev/null; then
missing_critical+=("[CRITICAL] uv - Required for running Python hooks
Install: https://docs.astral.sh/uv/")
fi
# OPTIONAL: gh - Only check if this is a GitHub repository
if git remote -v 2>/dev/null | grep -q "github.com"; then
if ! command -v gh &>/dev/null; then
missing_optional+=("[OPTIONAL] gh - Required for GitHub operations (PRs, issues, releases)
Install: https://cli.github.com/")
fi
fi
# OPTIONAL: jq - Required for AI review handlers
if ! command -v jq &>/dev/null; then
missing_optional+=("[OPTIONAL] jq - Required for AI review handlers (JSON processing)
Install: https://stedolan.github.io/jq/download/")
fi
# OPTIONAL: gawk - Required for AI review handlers
if ! command -v gawk &>/dev/null; then
missing_optional+=("[OPTIONAL] gawk - Required for AI review handlers (text processing)
The same script also checks for review-critical plugins and a wider set of optional official plugins:
# CRITICAL: Review plugins - Required for mandatory code review loop
critical_marketplace_plugins=(
pr-review-toolkit
superpowers
feature-dev
)
# OPTIONAL: Marketplace plugins - Check @claude-plugins-official plugins
optional_marketplace_plugins=(
claude-code-setup
claude-md-management
code-review
code-simplifier
coderabbit
commit-commands
frontend-design
github
gopls-lsp
jdtls-lsp
lua-lsp
playground
pyright-lsp
security-guidance
)
If anything is missing, the script prints a structured MISSING_TOOLS_REPORT and tells the assistant to offer installation help. It never blocks the session.
# Output report only if something is missing
if [[ ${#missing_critical[@]} -gt 0 || ${#missing_optional[@]} -gt 0 ]]; then
echo "MISSING_TOOLS_REPORT:"
echo ""
echo "[AI INSTRUCTION - YOU MUST FOLLOW THIS]"
echo "Some tools required by this configuration are missing."
echo ""
echo "Criticality levels:"
echo "- CRITICAL: Configuration will NOT work without these. Must install."
echo "- OPTIONAL: Enhances functionality. Nice to have."
# ...
fi
# Always exit 0 (non-blocking)
exit 0
A few practical takeaways:
uvis mandatory because the Python hooks use itghis only checked when the repo remote points at GitHubprekis only checked when.pre-commit-config.yamlexists, and this repository does include that filemcpl,jq, andgawkare optional, but they support real parts of this setup
Tip: Install
uvfirst. If you want the review workflow to work cleanly, installpr-review-toolkit,superpowers, andfeature-devnext.
UserPromptSubmit
The prompt-submit hook injects a fixed reminder string into the prompt context.
rule_reminder = (
"[SYSTEM RULES] You are a MANAGER. NEVER do work directly. ALWAYS delegate:\n"
"- Edit/Write → language specialists (python-expert, go-expert, etc.)\n"
"- ALL Bash commands → bash-expert or appropriate specialist\n"
"- Git commands → git-expert\n"
"- MCP tools → manager agents\n"
"- Multi-file exploration → Explore agent\n"
"HOOKS WILL BLOCK VIOLATIONS."
)
output = {"hookSpecificOutput": {"hookEventName": "UserPromptSubmit", "additionalContext": rule_reminder}}
That means the current checked-in implementation reinforces behavior with a static reminder rather than dynamically editing the user's prompt.
PreToolUse: rule-enforcer.py
The first PreToolUse hook is a Bash gate. It blocks direct python, python3, pip, pip3, and pre-commit commands. It explicitly allows uv and uvx, and the overall workflow prefers prek over raw pre-commit.
def is_forbidden_python_command(command: str) -> bool:
"""Check if command uses python/pip directly instead of uv."""
cmd = command.strip().lower()
# Allow uv/uvx commands
if cmd.startswith(("uv ", "uvx ")):
return False
# Block direct python/pip
forbidden = ("python ", "python3 ", "pip ", "pip3 ")
return cmd.startswith(forbidden)
def is_forbidden_precommit_command(command: str) -> bool:
"""Check if command uses pre-commit directly instead of prek."""
cmd = command.strip().lower()
# Block direct pre-commit commands
return cmd.startswith("pre-commit ")
The tests are a good example of the intended contract:
@pytest.mark.parametrize(
"command",
[
"python script.py",
"python3 -m pytest",
"pip install requests",
"pip3 freeze",
],
)
def test_deny_forbidden_bash_command(self, command: str) -> None:
# ...
@pytest.mark.parametrize(
"command",
[
"pre-commit run",
"pre-commit run --all-files",
"pre-commit install",
"pre-commit autoupdate",
],
)
def test_deny_forbidden_precommit_command(self, command: str) -> None:
# ...
@pytest.mark.parametrize(
"command",
[
"uv run script.py",
"uvx ruff check .",
"git status",
"ls -la",
],
)
def test_allow_permitted_bash_command(self, command: str) -> None:
One subtle detail: the hook matcher is TodoWrite|Bash, but the script itself only inspects Bash commands.
Tip: If you are adapting this config, prefer
uv run,uvx, andprekin your own commands. That matches both the hook behavior and the startup checks.
PreToolUse: git-protection.py
The second PreToolUse hook protects Git history. It blocks commits and pushes on protected branches, already-merged branches, and branches whose PRs are already merged.
The script also fails closed when PR-state lookup errors occur:
pr_merged, pr_info = get_pr_merge_status(current_branch)
if pr_merged is None:
# Error checking PR status - fail closed
return True, format_pr_merge_error("get_pr_merge_status()", pr_info)
The tests show the intended behavior clearly:
def test_detached_head(self, mock_is_repo: Any, mock_branch: Any) -> None:
"""Detached HEAD should block commit."""
mock_is_repo.return_value = True
mock_branch.return_value = None
should_block, reason = git_protection.should_block_commit('git commit -m "test"')
assert should_block is True
assert "detached HEAD" in reason
def test_on_main_branch(self, mock_is_repo: Any, mock_branch: Any, mock_pr_status: Any, mock_main: Any) -> None:
"""On main branch should block commit."""
mock_is_repo.return_value = True
mock_branch.return_value = "main"
mock_pr_status.return_value = (False, None)
mock_main.return_value = "main"
should_block, reason = git_protection.should_block_commit('git commit -m "test"')
assert should_block is True
assert "'main'" in reason
assert "protected" in reason.lower()
There is one important exception: --amend is allowed when the branch is ahead of its remote.
def is_amend_with_unpushed_commits(command: str) -> bool:
"""Check if this is an amend on unpushed commits (which should be allowed)."""
return "--amend" in command and is_branch_ahead_of_remote()
Warning: On GitHub repositories, this hook uses
ghto check whether the current branch already has a merged PR. If that lookup fails, the hook blocks the operation. If you rely on GitHub workflows, make sureghis installed and authenticated.
PreToolUse: destructive command gate
The third PreToolUse hook is an inline prompt-based safety check. It is not a shell script. It analyzes Bash commands and does three things:
- Blocks obviously catastrophic OS-destructive commands
- Asks for confirmation on commands that look risky but not clearly catastrophic
- Approves normal commands
The configured timeout is 10 seconds.
This is important because the config also enables skipDangerousModePermissionPrompt, so Bash safety here is handled by the allowlists and PreToolUse hooks rather than relying only on the default dangerous-mode flow.
Notification
The notification hook sends the Claude message text through notify-send. It explicitly requires both jq and notify-send.
# Check for required commands
for cmd in jq notify-send; do
if ! command -v "$cmd" &>/dev/null; then
echo "Error: Required command '$cmd' not found" >&2
exit 1
fi
done
# Read JSON input from stdin
input_json=$(cat)
# Parse JSON and extract message, capturing any jq errors
if ! notification_message=$(echo "$input_json" | jq -r '.message' 2>&1); then
echo "Error: Failed to parse JSON - $notification_message" >&2
exit 1
fi
# Send the notification and propagate any failures
if ! notify-send --icon="" --wait "Claude: $notification_message"; then
echo "Error: notify-send failed" >&2
exit 1
fi
Note:
session-start-check.shchecks forjq, but it does not check fornotify-send. If your machine does not havenotify-send, you will need to replace or remove theNotificationhook.
Enabled Plugins
The file enables official plugins, repo-owned plugins, and two third-party marketplace plugins.
"enabledPlugins": {
"pyright-lsp@claude-plugins-official": true,
"jdtls-lsp@claude-plugins-official": true,
"lua-lsp@claude-plugins-official": true,
"github@claude-plugins-official": true,
"myk-review@myk-org": true,
"myk-github@myk-org": true,
"code-simplifier@claude-plugins-official": true,
"playground@claude-plugins-official": true,
"frontend-design@claude-plugins-official": true,
"code-review@claude-plugins-official": true,
"superpowers@claude-plugins-official": true,
"feature-dev@claude-plugins-official": true,
"commit-commands@claude-plugins-official": true,
"security-guidance@claude-plugins-official": true,
"claude-md-management@claude-plugins-official": true,
"pr-review-toolkit@claude-plugins-official": true,
"claude-code-setup@claude-plugins-official": true,
"gopls-lsp@claude-plugins-official": true,
"coderabbit@claude-plugins-official": true,
"cli-anything@cli-anything": true,
"worktrunk@worktrunk": true,
"myk-acpx@myk-org": true
},
"extraKnownMarketplaces": {
"cli-anything": {
"source": { "source": "github", "repo": "HKUDS/CLI-Anything" }
},
"worktrunk": {
"source": { "source": "github", "repo": "max-sixty/worktrunk" }
}
},
A useful way to read this list is by category:
- Language servers:
pyright-lsp,jdtls-lsp,lua-lsp,gopls-lsp - Review and workflow helpers:
github,code-review,pr-review-toolkit,superpowers,feature-dev,coderabbit,security-guidance,code-simplifier,frontend-design,commit-commands,claude-md-management,claude-code-setup,playground - Repo marketplace plugins:
myk-review,myk-github,myk-acpx - Third-party marketplaces:
cli-anything,worktrunk
The repo-owned marketplace is defined in the checked-in manifest at .claude-plugin/marketplace.json:
{
"name": "myk-org",
"owner": {
"name": "myk-org"
},
"plugins": [
{
"name": "myk-github",
"source": "./plugins/myk-github",
"description": "GitHub operations - PR reviews, releases, review handling, CodeRabbit rate limits",
"version": "1.7.2"
},
{
"name": "myk-review",
"source": "./plugins/myk-review",
"description": "Local code review and review database operations",
"version": "1.7.2"
},
{
"name": "myk-acpx",
"source": "./plugins/myk-acpx",
"description": "Multi-agent prompt execution via acpx (Agent Client Protocol)",
"version": "1.7.2"
}
]
}
Note: The startup health check only validates a subset of official plugins. It does not currently verify installation of the
myk-*,cli-anything, orworktrunkplugins, even though they are enabled here.
Status Line
This configuration replaces the default status line with a shell script and disables extra padding.
"statusLine": {
"type": "command",
"command": "bash ~/.claude/statusline.sh",
"padding": 0
},
The script builds the line from workspace and runtime context:
model_name=$(echo "$input" | jq -r '.model.display_name')
current_dir=$(echo "$input" | jq -r '.workspace.current_dir')
# Get context usage percentage (pre-calculated by Claude Code v2.1.6+)
context_pct=$(echo "$input" | jq -r '.context_window.used_percentage // empty')
# Get current directory basename
dir_name=$(basename "$current_dir")
# Add SSH info if connected via SSH
if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ]; then
status_parts+=("$(whoami)@$(hostname -s)")
fi
# Add git branch if in a git repository
if git rev-parse --git-dir >/dev/null 2>&1; then
branch=$(git branch --show-current 2>/dev/null || echo "detached")
status_parts+=("$branch")
fi
# Add virtual environment if active
if [ -n "$VIRTUAL_ENV" ]; then
status_parts+=("($(basename "$VIRTUAL_ENV"))")
fi
# Add model and context usage
status_parts+=("$model_name")
if [ -n "$context_pct" ]; then
status_parts+=("(${context_pct}%)")
fi
In order, the rendered line includes:
- Current directory name
- SSH user and host, when connected over SSH
- Current Git branch, when inside a Git repo
- Active virtual environment name, when present
- Model display name
- Context-window usage percentage, when available
- Added/removed line counts, when nonzero
Tip:
statusline.shdepends onjqandgit. If the status line looks broken, check those first.
Environment And UX Flags
At the end of the file, settings.json sets runtime flags, environment variables, and survey behavior.
"alwaysThinkingEnabled": true,
"skipDangerousModePermissionPrompt": true,
"autoDreamEnabled": true,
"_scriptsNote": "Script entries must be duplicated in both permissions.allow and allowedTools arrays. When adding new scripts, update BOTH locations.",
"allowedTools": [
# ...
],
"env": {
"DISABLE_TELEMETRY": "1",
"DISABLE_ERROR_REPORTING": "1",
"CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY": "1"
},
"feedbackSurveyRate": 0,
"feedbackSurveyState": {
"lastShownTime": 1754082998309
}
Here is what those settings mean in practice:
alwaysThinkingEnabled: trueturns on Claude Code's thinking mode by defaultautoDreamEnabled: trueenables Claude Code's auto-dream behavior by defaultskipDangerousModePermissionPrompt: trueremoves the standard dangerous-mode confirmation prompt, but this config still protects Bash usage with allowlists andPreToolUsehooksDISABLE_TELEMETRYandDISABLE_ERROR_REPORTINGopt out of telemetry and automatic error reportingCLAUDE_CODE_DISABLE_FEEDBACK_SURVEYplusfeedbackSurveyRate: 0disables feedback survey promptsfeedbackSurveyState.lastShownTimeis local runtime state, not a setting most users need to edit
Note: If you are trimming this file for your own use, keep the safety model in mind. Disabling the dangerous-mode prompt makes the Bash allowlist and hooks even more important.
Keeping Everything In Sync
When you change this file, use this checklist:
- Keep
scripts/installed under~/.claude/scripts/andstatusline.shat~/.claude/statusline.sh. - If you add or rename a hook script, update the hook
commandand mirror itsBash(...)entry in bothpermissions.allowandallowedTools. - Keep
uvinstalled, because all Python hooks depend on it. - Prefer
uv run,uvx, andprekover rawpython,pip, andpre-commit. - If you want desktop notifications, make sure
jqandnotify-sendare available. - If you rely on GitHub PR checks, make sure
ghis installed and authenticated.
Verifying Changes
This repository validates the hook behavior with unit tests, and tox.toml runs those tests through uv:
[env.unittests]
description = "Run pytest tests"
deps = ["uv"]
commands = [["uv", "run", "--group", "tests", "pytest", "tests"]]
The repo also includes .pre-commit-config.yaml, which is why session-start-check.sh treats prek as relevant here.
Note: There are no checked-in GitHub Actions or Jenkins pipeline files in this repository. The visible validation path in-repo is local:
toxplus pre-commit.