5 ファイル変更 +45 -11

この更新の概要

フックの終了コード0の意味が、ツールの実行を許可するのではなく「異議なし」を報告し通常の権限フローに従う挙動に変更されました。管理設定としてstrictPluginOnlyCustomizationが追加され、スキルやMCPサーバーなどをプラグインまたは管理ソースのみに制限するカスタマイズロックダウン機能が利用可能です。JSON出力による詳細なフック制御にinitialUserMessageやwatchPathsなどの新フィールドが追加され、非対話モードでの動作やファイル監視の制御が強化されています。

admin-setup +1 -0

カスタマイズをプラグインや管理設定のみに制限するstrictPluginOnlyCustomization設定の概要が追加されました。

@@ -74,6 +74,7 @@ Managed settings can lock down tools, sandbox execution, restrict MCP servers an
| [Managed policy CLAUDE.md](/en/memory#deploy-organization-wide-claude-md) | Org-wide instructions loaded in every session, cannot be excluded | File at the managed policy path |
| [MCP server control](/en/mcp#managed-mcp-configuration) | Restrict which MCP servers users can add or connect to | `allowedMcpServers`, `deniedMcpServers`, `allowManagedMcpServersOnly` |
| [Plugin marketplace control](/en/plugin-marketplaces#managed-marketplace-restrictions) | Restrict which marketplace sources users can add and install from | `strictKnownMarketplaces`, `blockedMarketplaces` |
| [Customization lockdown](/en/settings#strictpluginonlycustomization) | Block skills, agents, hooks, and MCP servers from user and project sources, so they can only come from plugins or managed settings | `strictPluginOnlyCustomization` |
| [Hook restrictions](/en/settings#hook-configuration) | Only managed hooks load; restrict HTTP hook URLs | `allowManagedHooksOnly`, `allowedHttpHookUrls` |
| [Disable agent view](/en/agent-view#how-background-sessions-are-hosted) | Turn off `claude agents`, `--bg`, `/background`, and the on-demand supervisor | `disableAgentView` |
| [Version floor](/en/settings) | Prevent auto-update from installing below an org-wide minimum | `minimumVersion` |
hooks-guide +3 -3

フックの終了コード0が明示的な承認ではなく、通常の権限フローに委ねるサイレントな状態であることを明確化し、JSONによる制御の詳細が追記されました。

@@ -512,18 +512,18 @@ if echo "$COMMAND" | grep -q "drop table"; then
exit 2 # exit 2 = block the action
fi
exit 0 # exit 0 = let it proceed
exit 0 # exit 0 = no decision; the normal permission flow applies
```
The exit code determines what happens next:
- **Exit 0**: the action proceeds. For `UserPromptSubmit`, `UserPromptExpansion`, and `SessionStart` hooks, anything you write to stdout is added to Claude's context.
- **Exit 0**: the hook reports no objection and the action proceeds normally. For a `PreToolUse` hook this doesn't approve the tool call: the normal [permission flow](/en/permissions) still applies. For `UserPromptSubmit`, `UserPromptExpansion`, and `SessionStart` hooks, anything you write to stdout is added to Claude's context.
- **Exit 2**: the action is blocked. Write a reason to stderr, and Claude receives it as feedback so it can adjust. Some events cannot be blocked: for `SessionStart`, `Setup`, `Notification`, and others, exit 2 shows stderr to the user and execution continues. See [exit code 2 behavior per event](/en/hooks#exit-code-2-behavior-per-event) for the full list.
- **Any other exit code**: the action proceeds. The transcript shows a `<hook name> hook error` notice followed by the first line of stderr; the full stderr goes to the [debug log](/en/hooks#debug-hooks).
#### Structured JSON output
Exit codes give you two options: allow or block. For more control, exit 0 and print a JSON object to stdout instead.
Exit codes only let you block or stay silent. For more control, exit 0 and print a JSON object to stdout instead.
Use exit 2 to block with a stderr message, or exit 0 with JSON for structured control. Don't mix them: Claude Code ignores JSON when you exit 2.
hooks +14 -8

フックにおける新しい決定制御フィールドやセッション開始時のコンテキスト追加方法、および終了コードとJSON出力の挙動の違いについて詳細な説明が追加されました。

@@ -89,7 +89,7 @@ if echo "$COMMAND" | grep -q 'rm -rf'; then
}
}'
else
exit 0 # allow the command
exit 0 # no decision; normal permission flow applies
fi
```
@@ -117,7 +117,7 @@ The script inspects the full command and finds `rm -rf`, so it prints a decision
}
```
If the command had been a safer `rm` variant like `rm file.txt`, the script would hit `exit 0` instead, which tells Claude Code to allow the tool call with no further action.
If the command had been a safer `rm` variant like `rm file.txt`, the script would hit `exit 0` instead. Exit code 0 with no output means the hook has no decision to report, so the tool call continues through the normal [permission flow](/en/permissions). The hook can deny the call, but staying silent doesn't approve it.
Claude Code reads the JSON decision, blocks the tool call, and shows Claude the reason.
@@ -585,7 +585,7 @@ if [[ "$command" == rm* ]]; then
exit 2 # Blocking error: tool call is prevented
fi
exit 0 # Success: tool call proceeds
exit 0 # No decision: the normal permission flow applies
```
For most hook events, only exit code 2 blocks the action. Claude Code treats exit code 1 as a non-blocking error and proceeds with the action, even though 1 is the conventional Unix failure code. If your hook is meant to enforce a policy, use `exit 2`. The exception is `WorktreeCreate`, where any non-zero exit code aborts worktree creation.
@@ -640,7 +640,7 @@ Unlike command hooks, HTTP hooks cannot signal a blocking error through status c
### JSON output
Exit codes let you allow or block, but JSON output gives you finer-grained control. Instead of exiting with code 2 to block, exit 0 and print a JSON object to stdout. Claude Code reads specific fields from that JSON to control behavior, including [decision control](#decision-control) for blocking, allowing, or escalating to the user.
Exit codes only let you block or stay silent, but JSON output gives you finer-grained control. Instead of exiting with code 2 to block, exit 0 and print a JSON object to stdout. Claude Code reads specific fields from that JSON to control behavior, including [decision control](#decision-control) for blocking, allowing, or escalating to the user.
You must choose one approach per hook, not both: either use exit codes alone for signaling, or exit 0 and print JSON for structured control. Claude Code only processes JSON on exit 0. If you exit 2, any JSON is ignored.
@@ -658,7 +658,7 @@ The JSON object supports three kinds of fields:
| :- | :- | :- |
| `continue` | `true` | If `false`, Claude stops processing entirely after the hook runs. Takes precedence over any event-specific decision fields |
| `stopReason` | none | Message shown to the user when `continue` is `false`. Not shown to Claude |
| `suppressOutput` | `false` | If `true`, omits stdout from the debug log |
| `suppressOutput` | `false` | If `true`, hides the hook's stdout from the transcript. Stdout still appears in the debug log |
| `systemMessage` | none | Warning message shown to the user |
| `terminalSequence` | none | A terminal escape sequence for Claude Code to emit on your behalf, such as a desktop notification, window title, or bell. Restricted to OSC `0`/`1`/`2`/`9`/`99`/`777` and BEL. If the value contains anything outside the allowlist, the field is ignored. Use this instead of writing to `/dev/tty`, which is unavailable to hooks |
@@ -749,6 +749,7 @@ Not every event supports blocking or controlling behavior through JSON. The even
| WorktreeCreate | path return | Command hook prints path on stdout; HTTP hook returns `hookSpecificOutput.worktreePath`. Hook failure or missing path fails creation |
| Elicitation | `hookSpecificOutput` | `action` (accept/decline/cancel), `content` (form field values for accept) |
| ElicitationResult | `hookSpecificOutput` | `action` (accept/decline/cancel), `content` (form field values override) |
| SessionStart, Setup, SubagentStart | Context only | `hookSpecificOutput.additionalContext` adds context for Claude. SessionStart also accepts [`initialUserMessage` and `watchPaths`](#sessionstart-decision-control). No blocking or decision control |
| WorktreeRemove, Notification, SessionEnd, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, FileChanged | None | No decision control. Used for side effects like logging or cleanup |
Here are examples of each pattern in action:
@@ -833,6 +834,8 @@ Any text your hook script prints to stdout is added as context for Claude. In ad
| Field | Description |
| :- | :- |
| `additionalContext` | String added to Claude's context at the start of the conversation, before the first prompt. See [Add context for Claude](#add-context-for-claude) for how the text is delivered and what to put in it |
| `initialUserMessage` | String used as the first user message of the session. Applies in [non-interactive mode](/en/headless) (`-p`), where it becomes the first turn even if no prompt is provided. If a prompt is provided, it follows as the next turn. Unlike `additionalContext`, which attaches to an existing turn, this creates the turn |
| `watchPaths` | Array of absolute paths to watch for [FileChanged](#filechanged) events during this session |
```json
{
@@ -1011,6 +1014,7 @@ To block a prompt, return a JSON object with `decision` set to `"block"`:
| `reason` | Shown to the user when `decision` is `"block"`. Not added to context |
| `additionalContext` | String added to Claude's context alongside the submitted prompt. See [Add context for Claude](#add-context-for-claude) |
| `sessionTitle` | Sets the session title. Use to name sessions automatically based on the prompt content |
| `suppressOriginalPrompt` | If `true` when `decision` is `"block"`, omits the original prompt text from the block message shown to the user |
```json
{
@@ -1353,7 +1357,7 @@ The `updatedPermissions` output field and the [`permission_suggestions` input fi
| `addRules` | `rules`, `behavior`, `destination` | Adds permission rules. `rules` is an array of `{toolName, ruleContent?}` objects. Omit `ruleContent` to match the whole tool. `behavior` is `"allow"`, `"deny"`, or `"ask"` |
| `replaceRules` | `rules`, `behavior`, `destination` | Replaces all rules of the given `behavior` at the `destination` with the provided `rules` |
| `removeRules` | `rules`, `behavior`, `destination` | Removes matching rules of the given `behavior` |
| `setMode` | `mode`, `destination` | Changes the permission mode. Valid modes are `default`, `acceptEdits`, `dontAsk`, `bypassPermissions`, and `plan` |
| `setMode` | `mode`, `destination` | Changes the permission mode. Valid modes are `default`, `auto`, `acceptEdits`, `dontAsk`, `bypassPermissions`, and `plan` |
| `addDirectories` | `directories`, `destination` | Adds working directories. `directories` is an array of path strings |
| `removeDirectories` | `directories`, `destination` | Removes working directories |
@@ -2429,6 +2433,7 @@ In addition to command, HTTP, and MCP tool hooks, Claude Code supports prompt-ba
Events that support all five hook types (`command`, `http`, `mcp_tool`, `prompt`, and `agent`):
- `PermissionDenied`
- `PermissionRequest`
- `PostToolBatch`
- `PostToolUse`
@@ -2438,6 +2443,7 @@ Events that support all five hook types (`command`, `http`, `mcp_tool`, `prompt`
- `SubagentStop`
- `TaskCompleted`
- `TaskCreated`
- `TeammateIdle`
- `UserPromptExpansion`
- `UserPromptSubmit`
@@ -2450,13 +2456,11 @@ Events that support `command`, `http`, and `mcp_tool` hooks but not `prompt` or
- `FileChanged`
- `InstructionsLoaded`
- `Notification`
- `PermissionDenied`
- `PostCompact`
- `PreCompact`
- `SessionEnd`
- `StopFailure`
- `SubagentStart`
- `TeammateIdle`
- `WorktreeCreate`
- `WorktreeRemove`
@@ -2524,7 +2528,9 @@ What happens on `ok: false` depends on the event:
- `PostToolUse`: by default the turn ends and the reason appears in the chat as a warning line. Set `continueOnBlock: true` to feed the reason back to Claude and continue the turn instead
- `PostToolBatch`, `UserPromptSubmit`, and `UserPromptExpansion`: the turn ends and the reason appears as a warning line. These events end the turn on `decision: "block"` regardless of `continue`
- `PostToolUseFailure`, `TaskCreated`, and `TaskCompleted`: the reason is returned to Claude as a tool error, similar to `PreToolUse`
- `TeammateIdle`: by default the teammate stops and the reason appears as a warning line. Set `continueOnBlock: true` to feed the reason back to the teammate and keep it working instead
- `PermissionRequest`: `ok: false` has no effect. To deny an approval from a hook, use a [command hook](#command-hook-fields) returning `hookSpecificOutput.decision.behavior: "deny"`
- `PermissionDenied`: `ok: false` has no effect because the denial already happened. The only output this event reads is `hookSpecificOutput.retry`, which prompt and agent hooks cannot set — they run on this event, but their output is discarded. Use a [command hook](#command-hook-fields) to return `retry`
If you need finer control on any event, use a [command hook](#command-hook-fields) with the per-event fields described in [Decision control](#decision-control).
permissions +1 -0

管理設定による機能制限の項目に、特定のリソースソースをブロックするためのstrictPluginOnlyCustomization設定が追加されました。

@@ -322,6 +322,7 @@ The following settings are only read from managed settings. Placing them in user
| `sandbox.filesystem.allowManagedReadPathsOnly` | When `true`, only `filesystem.allowRead` paths from managed settings are respected. `denyRead` still merges from all sources |
| `sandbox.network.allowManagedDomainsOnly` | When `true`, only `allowedDomains` and `WebFetch(domain:...)` allow rules from managed settings are respected. Non-allowed domains are blocked automatically without prompting the user. Denied domains still merge from all sources |
| `strictKnownMarketplaces` | Controls which plugin marketplace sources users can add and install plugins from. See [managed marketplace restrictions](/en/plugin-marketplaces#managed-marketplace-restrictions) |
| `strictPluginOnlyCustomization` | Block skills, agents, hooks, and MCP servers from user and project sources, so they can only come from plugins or managed settings. `true` locks all four surfaces; an array such as `["skills", "hooks"]` locks only the named ones. See [`strictPluginOnlyCustomization`](/en/settings#strictpluginonlycustomization) |
| `wslInheritsWindowsSettings` | When `true` in the Windows HKLM registry key or `C:\Program Files\ClaudeCode\managed-settings.json`, WSL reads managed settings from the Windows policy chain in addition to `/etc/claude-code`. See [Settings files](/en/settings#settings-files) |
`disableBypassPermissionsMode` is typically placed in managed settings to enforce organizational policy, but it works from any scope. A user can set it in their own settings to lock themselves out of bypass mode.
settings +26 -0

strictPluginOnlyCustomization設定の具体的な動作、対象となる4つのサーフェス、およびバージョンごとの互換性に関する詳細な仕様が定義されました。

@@ -242,6 +242,7 @@ A few keys are read once at session start and apply on the next restart instead:
| `sshConfigs` | SSH connections to show in the [Desktop](/en/desktop#pre-configure-ssh-connections-for-your-team) environment dropdown. Each entry requires `id`, `name`, and `sshHost`; `sshPort`, `sshIdentityFile`, and `startDirectory` are optional. When set in managed settings, connections are read-only for users. Read from managed and user settings only | `[{"id": "dev-vm", "name": "Dev VM", "sshHost": "user@dev.example.com"}]` |
| `statusLine` | Configure a custom status line to display context. See [`statusLine` documentation](/en/statusline) | `{"type": "command", "command": "~/.claude/statusline.sh"}` |
| `strictKnownMarketplaces` | (Managed settings only) Allowlist of plugin marketplace sources. Undefined = no restrictions, empty array = lockdown. Enforced on marketplace add and on plugin install, update, refresh, and auto-update, so a marketplace added before the policy was set cannot be used to fetch plugins. See [Managed marketplace restrictions](/en/plugin-marketplaces#managed-marketplace-restrictions) | `[{ "source": "github", "repo": "acme-corp/plugins" }]` |
| `strictPluginOnlyCustomization` | (Managed settings only) Block skills, agents, hooks, and MCP servers from user and project sources, so they can only come from plugins or managed settings. `true` locks all four surfaces; an array locks only the named ones. See [`strictPluginOnlyCustomization`](#strictpluginonlycustomization) | `["skills", "hooks"]` |
| `syntaxHighlightingDisabled` | Disable syntax highlighting in diffs, code blocks, and file previews | `true` |
| `teammateMode` | How [agent team](/en/agent-teams) teammates display: `auto` (picks split panes in tmux or iTerm2, in-process otherwise), `in-process`, or `tmux`. `--teammate-mode` overrides this for one session. See [choose a display mode](/en/agent-teams#choose-a-display-mode) | `"in-process"` |
| `terminalProgressBarEnabled` | Show the terminal progress bar in supported terminals: ConEmu, Ghostty 1.2.0+, and iTerm2 3.6.6+. Default: `true`. Appears in `/config` as **Terminal progress bar** | `false` |
@@ -948,6 +949,31 @@ With only `strictKnownMarketplaces` set, users can still add the allowed marketp
See [Managed marketplace restrictions](/en/plugin-marketplaces#managed-marketplace-restrictions) for user-facing documentation.
#### `strictPluginOnlyCustomization`
**Managed settings only**: blocks skills, agents, hooks, and MCP servers from user and project sources, so they can only come from plugins or managed settings. Combine it with `strictKnownMarketplaces` to control the full customization supply chain: the marketplace allowlist controls which plugins users can install, and this setting blocks everything that doesn't come from a plugin or from managed settings.
`strictPluginOnlyCustomization` requires Claude Code v2.1.82 or later. Earlier versions ignore the key and keep loading user and project customizations, so the lockdown isn't enforced until clients update.
The value is either `true` to lock all four surfaces, or an array naming the surfaces to lock:
```json
{
"strictPluginOnlyCustomization": ["skills", "hooks"]
}
```
For each locked surface, Claude Code skips user-level and project-level sources and loads only plugin-provided and managed sources:
| Surface | Blocked when locked | Still loads |
| :- | :- | :- |
| `skills` | `~/.claude/skills/`, `.claude/skills/` | Plugin skills, bundled skills, skills in the managed policy directory |
| `agents` | `~/.claude/agents/`, `.claude/agents/` | Plugin agents, built-in agents, agents in the managed policy directory |
| `hooks` | Hooks in user, project, and local `settings.json` | Plugin hooks, hooks in managed settings |
| `mcp` | Servers in `~/.claude.json` and `.mcp.json` | Plugin MCP servers, [`managed-mcp.json`](/en/mcp#managed-mcp-configuration) servers |
Surface names that a Claude Code version doesn't recognize are ignored rather than failing the settings file, so you can add new surface names before all clients have updated.
### Managing plugins
Use the `/plugin` command to manage plugins interactively: