Follow Discord
Sweep 22 Oct 2026 · 04:32Z Build v2.1.308 527 read Stable v2.1.308 Latest v2.1.308 Next v2.1.308 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One capture · claude-code

One read of Claude Code CLIclaude-code-20261020T183702Z

212 pages moved out of 228 read.

Pages moved 212 significant first
Pages read 228 in this capture
Captured 18:37 UTC
Corpus hash f4a7dc09bf09 corpus-hash

What this read moved

151-175 of 212, page 7 of 9

This capture is too large to show at once. Changes 151-175 of 212 are below, significant first; the rest are on the following screens.

remote-control Changed · +171 / -182 lines

#### Auto-connect with `DISABLE_TELEMETRY` or `DO_NOT_TRACK` set ### Enable Trusted Devices for a Team or Enterprise organization ### "Remote Control is disabled by your organization's policy" ### "Remote Control was turned off by your organization's policy" ### "Couldn't verify your organization's policy for remote control" ### "Couldn't reconnect to your Remote Control session" ### "Previous session is unavailable — run /remote-control to start a new one"

from line 13
1313 
1414Unlike [cloud sessions](/docs/en/claude-code-on-the-web), which run on cloud infrastructure, Remote Control sessions run directly on your machine and interact with your local filesystem. The web and mobile interfaces are a window into that local session, so your computer has to stay on and the `claude` process has to keep running.
1515 
16<Note>
17 Remote Control is a Claude Code feature. For conversations in other Claude products, see the [Claude Help Center](https://support.claude.com).
18</Note>
16> [!NOTE]
17>
18> Remote Control is a Claude Code feature. For conversations in other Claude products, see the [Claude Help Center](https://support.claude.com).
1919 
2020## Requirements
2121 
from line 36
3636 
3737You can start a Remote Control session from the CLI, the [Claude Desktop app](/docs/en/desktop), or the VS Code extension. The CLI offers three invocation modes; the Desktop app and VS Code use the `/remote-control` command.
3838 
39<Tabs>
40 <Tab title="Server mode">
41 In your project directory, run:
39**Server mode**
4240 
43 ```bash theme={null}
44 claude remote-control
45 ```
41In your project directory, run:
4642 
47 Until you accept Remote Control's one-time confirmation, `claude remote-control` explains what it does and asks `Enable Remote Control? (y/n)` before starting the server. Answer `y` to accept and start the server. If you decline, Claude Code exits without starting the server and asks again the next time you run the command.
43```bash theme={null}
44claude remote-control
45```
4846 
49 The process stays running in your terminal in server mode, waiting for remote connections. It displays a session URL you can use to [connect from another device](#connect-from-another-device), and you can press spacebar to show a QR code for quick access from your phone. While a remote session is active, the terminal shows connection status and tool activity.
47Until you accept Remote Control's one-time confirmation, `claude remote-control` explains what it does and asks `Enable Remote Control? (y/n)` before starting the server. Answer `y` to accept and start the server. If you decline, Claude Code exits without starting the server and asks again the next time you run the command.
5048 
51 Pass any of these flags after `remote-control`:
49The process stays running in your terminal in server mode, waiting for remote connections. It displays a session URL you can use to [connect from another device](#connect-from-another-device), and you can press spacebar to show a QR code for quick access from your phone. While a remote session is active, the terminal shows connection status and tool activity.
5250 
53 | Flag | Description |
54 | - | - |
55 | `--name "My Project"` | Set a custom session title visible in the session list at claude.ai/code. |
56 | `--remote-control-session-name-prefix <prefix>` | Prefix for auto-generated session names when no explicit name is set. Defaults to your machine's hostname, producing names like `myhost-graceful-unicorn`. Set `CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX` for the same effect. |
57 | `-c`, `--continue` | Bring back the session that the last server in this directory started with, instead of creating a new one. See [Resume sessions after stopping the server](#resume-sessions-after-stopping-the-server). Can't be combined with `--session-id`, `--spawn`, `--capacity`, or `--create-session-in-dir`. Requires Claude Code v2.1.200 or later. |
58 | `--session-id <id>` | Bring back one session by its ID. See [Resume sessions after stopping the server](#resume-sessions-after-stopping-the-server). Can't be combined with `--continue`, `--spawn`, `--capacity`, or `--create-session-in-dir`. Requires Claude Code v2.1.200 or later. |
59 | `--spawn <mode>` | How the server creates sessions.<br />• `same-dir` (default): all sessions share the current working directory, so they can conflict if editing the same files.<br />• `worktree`: each on-demand session gets its own [git worktree](/docs/en/worktrees). Requires a git repository or a [`WorktreeCreate` hook](/docs/en/hooks#worktreecreate).<br />• `session`: single-session mode. Serves exactly one session and rejects additional connections. Set at startup only.<br />Press `w` at runtime to toggle between `same-dir` and `worktree`. |
60 | `--capacity <N>` | Maximum number of concurrent sessions. Default is 32. Cannot be used with `--spawn=session`. |
61 | `--[no-]create-session-in-dir` | Pre-create one session in the current directory when the server starts, so you have somewhere to type immediately. In `worktree` mode this session stays in the current directory while on-demand sessions get isolated worktrees. On by default. If you pass `--no-create-session-in-dir` to start with none, Claude Code archives the server's sessions when you stop it, so there's nothing to [resume](#resume-sessions-after-stopping-the-server). |
62 | `--permission-mode <mode>` | Set the starting [permission mode](/docs/en/permission-modes) for the server's sessions, such as `acceptEdits`. Accepts `manual` as an alias for `default`; an unrecognized mode stops the server at startup and lists the valid modes. |
63 | `--chrome` / `--no-chrome` | Turn [Chrome integration](/docs/en/chrome) on or off in the sessions the server creates, so Claude can use Chrome on your machine while you work from another device. Without either flag, the session the server pre-creates and any session you start yourself from claude.ai/code or the Claude app begin with Chrome off, even if you [enabled Chrome by default](/docs/en/chrome#enable-chrome-by-default). A session the server starts for one of your [project](/docs/en/claude-projects) threads follows that setting instead, except in `bypassPermissions` mode. Requires Claude Code v2.1.273 or later. |
64 | `-d`, `--debug[=<filter>]` | Turn on debug logging for the server, optionally filtered by category. Pass a filter only in the `=` form, such as `--debug=api,hooks`. Requires Claude Code v2.1.282 or later. |
65 | `--debug-file <path>` | Write debug logs to the given file. |
66 | `--verbose` | Show detailed connection and session logs. |
67 | `--sandbox`, `--no-sandbox` | Removed in v2.1.300. If you pass either flag, the server [exits with an error](/docs/en/errors#sandbox-is-not-supported-by-claude-remote-control) instead of starting. |
51Pass any of these flags after `remote-control`:
6852 
69 If you pass a global `claude` flag before `remote-control`, or a wrapper script adds one, Claude Code doesn't carry the flag over to the sessions the server creates. Claude Code lets the flag through only when dropping it is known not to change what those sessions can do, such as `--verbose` or `--model`. For any other flag, such as `--settings`, Claude Code [refuses to start](/docs/en/errors#not-carried-over-to-the-sessions-remote-control-starts) and names the flag to remove.
53| Flag | Description |
54| - | - |
55| `--name "My Project"` | Set a custom session title visible in the session list at claude.ai/code. |
56| `--remote-control-session-name-prefix <prefix>` | Prefix for auto-generated session names when no explicit name is set. Defaults to your machine's hostname, producing names like `myhost-graceful-unicorn`. Set `CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX` for the same effect. |
57| `-c`, `--continue` | Bring back the session that the last server in this directory started with, instead of creating a new one. See [Resume sessions after stopping the server](#resume-sessions-after-stopping-the-server). Can't be combined with `--session-id`, `--spawn`, `--capacity`, or `--create-session-in-dir`. Requires Claude Code v2.1.200 or later. |
58| `--session-id <id>` | Bring back one session by its ID. See [Resume sessions after stopping the server](#resume-sessions-after-stopping-the-server). Can't be combined with `--continue`, `--spawn`, `--capacity`, or `--create-session-in-dir`. Requires Claude Code v2.1.200 or later. |
59| `--spawn <mode>` | How the server creates sessions. • `same-dir` (default): all sessions share the current working directory, so they can conflict if editing the same files. • `worktree`: each on-demand session gets its own [git worktree](/docs/en/worktrees). Requires a git repository or a [`WorktreeCreate` hook](/docs/en/hooks#worktreecreate). • `session`: single-session mode. Serves exactly one session and rejects additional connections. Set at startup only. Press `w` at runtime to toggle between `same-dir` and `worktree`. |
60| `--capacity <N>` | Maximum number of concurrent sessions. Default is 32. Cannot be used with `--spawn=session`. |
61| `--[no-]create-session-in-dir` | Pre-create one session in the current directory when the server starts, so you have somewhere to type immediately. In `worktree` mode this session stays in the current directory while on-demand sessions get isolated worktrees. On by default. If you pass `--no-create-session-in-dir` to start with none, Claude Code archives the server's sessions when you stop it, so there's nothing to [resume](#resume-sessions-after-stopping-the-server). |
62| `--permission-mode <mode>` | Set the starting [permission mode](/docs/en/permission-modes) for the server's sessions, such as `acceptEdits`. Accepts `manual` as an alias for `default`; an unrecognized mode stops the server at startup and lists the valid modes. |
63| `--chrome` / `--no-chrome` | Turn [Chrome integration](/docs/en/chrome) on or off in the sessions the server creates, so Claude can use Chrome on your machine while you work from another device. Without either flag, the session the server pre-creates and any session you start yourself from claude.ai/code or the Claude app begin with Chrome off, even if you [enabled Chrome by default](/docs/en/chrome#enable-chrome-by-default). A session the server starts for one of your [project](/docs/en/claude-projects) threads follows that setting instead, except in `bypassPermissions` mode. Requires Claude Code v2.1.273 or later. |
64| `-d`, `--debug[=<filter>]` | Turn on debug logging for the server, optionally filtered by category. Pass a filter only in the `=` form, such as `--debug=api,hooks`. Requires Claude Code v2.1.282 or later. |
65| `--debug-file <path>` | Write debug logs to the given file. |
66| `--verbose` | Show detailed connection and session logs. |
67| `--sandbox`, `--no-sandbox` | Removed in v2.1.300. If you pass either flag, the server [exits with an error](/docs/en/errors#sandbox-is-not-supported-by-claude-remote-control) instead of starting. |
7068 
71 To sandbox the sessions the server starts, turn on [sandboxing](/docs/en/sandboxing) in a settings file.
69If you pass a global `claude` flag before `remote-control`, or a wrapper script adds one, Claude Code doesn't carry the flag over to the sessions the server creates. Claude Code lets the flag through only when dropping it is known not to change what those sessions can do, such as `--verbose` or `--model`. For any other flag, such as `--settings`, Claude Code [refuses to start](/docs/en/errors#not-carried-over-to-the-sessions-remote-control-starts) and names the flag to remove.
7270 
73 Claude Code checks Remote Control eligibility before printing help, so `claude remote-control --help` returns an error instead of this flag list when you aren't signed in with an eligible account.
74 </Tab>
71To sandbox the sessions the server starts, turn on [sandboxing](/docs/en/sandboxing) in a settings file.
7572 
76 <Tab title="Interactive session">
77 To start a normal interactive Claude Code session with Remote Control enabled, use the `--remote-control` flag (or `--rc`):
73To print the `remote-control` flags in your terminal, run `claude remote-control --help`. Before v2.1.302, without an eligible sign-in the command returned one of the errors under [Troubleshooting](#troubleshooting) instead.
7874 
79 ```bash theme={null}
80 claude --remote-control
81 ```
75**Interactive session**
8276 
83 Optionally pass a name for the session:
77To start a normal interactive Claude Code session with Remote Control enabled, use the `--remote-control` flag (or `--rc`):
8478 
85 ```bash theme={null}
86 claude --remote-control "My Project"
87 ```
79```bash theme={null}
80claude --remote-control
81```
8882 
89 This gives you a full interactive session in your terminal that you can also control from claude.ai or the Claude app. Unlike `claude remote-control` (server mode), you can type messages locally while the session is also available remotely.
90 </Tab>
83Optionally pass a name for the session:
9184 
92 <Tab title="From an existing session">
93 If you're already in a Claude Code session and want to continue it remotely, use the `/remote-control` (or `/rc`) command:
85```bash theme={null}
86claude --remote-control "My Project"
87```
9488 
95 ```text theme={null}
96 /remote-control
97 ```
89This gives you a full interactive session in your terminal that you can also control from claude.ai or the Claude app. Unlike `claude remote-control` (server mode), you can type messages locally while the session is also available remotely.
9890 
99 Pass a name as an argument to set a custom session title:
91**From an existing session**
10092 
101 ```text theme={null}
102 /remote-control My Project
103 ```
93If you're already in a Claude Code session and want to continue it remotely, use the `/remote-control` (or `/rc`) command:
10494 
105 This starts a Remote Control session that carries over your current conversation history.
95```text theme={null}
96/remote-control
97```
10698 
107 Until you accept Remote Control's one-time confirmation, a dialog appears before `/remote-control` connects. Select **Enable Remote Control** to accept and connect. If you select **Never mind** or press Esc, Claude Code doesn't connect and asks again the next time you run `/remote-control`.
99Pass a name as an argument to set a custom session title:
108100 
109 The `--verbose` flag is not available with this command.
110 </Tab>
101```text theme={null}
102/remote-control My Project
103```
111104 
112 <Tab title="VS Code">
113 In the [Claude Code VS Code extension](/docs/en/vs-code), type `/remote-control` or `/rc` in the prompt box.
105This starts a Remote Control session that carries over your current conversation history.
114106 
115 ```text theme={null}
116 /remote-control
117 ```
107Until you accept Remote Control's one-time confirmation, a dialog appears before `/remote-control` connects. Select **Enable Remote Control** to accept and connect. If you select **Never mind** or press Esc, Claude Code doesn't connect and asks again the next time you run `/remote-control`.
118108 
119 While Remote Control is on, Claude Code shows a **Remote Control** indicator in the prompt box footer. Once the session connects, click the indicator to go directly to the session, or find it in the session list at [claude.ai/code](https://claude.ai/code). Claude Code also posts the session URL in the conversation. To disconnect, run `/remote-control` again.
109The `--verbose` flag is not available with this command.
120110 
121 Unlike the CLI, the VS Code command does not accept a name argument or display a QR code. The session title is derived from your conversation history or first prompt.
122 </Tab>
111**VS Code**
123112 
124 <Tab title="Desktop app">
125 In a local session in the [Claude Desktop app's](/docs/en/desktop) Code tab, type `/remote-control` or `/rc` in the prompt box.
113In the [Claude Code VS Code extension](/docs/en/vs-code), type `/remote-control` or `/rc` in the prompt box.
126114 
127 ```text theme={null}
128 /remote-control
129 ```
115```text theme={null}
116/remote-control
117```
130118 
131 Once the session connects, find it in the session list at [claude.ai/code](https://claude.ai/code). To disconnect, run `/remote-control` again.
119While Remote Control is on, Claude Code shows a **Remote Control** indicator in the prompt box footer. Once the session connects, click the indicator to go directly to the session, or find it in the session list at [claude.ai/code](https://claude.ai/code). Claude Code also posts the session URL in the conversation. To disconnect, run `/remote-control` again.
132120 
133 To turn Remote Control on for every session by default instead, see [Enable Remote Control for all sessions](#enable-remote-control-for-all-sessions).
134 </Tab>
135</Tabs>
121Unlike the CLI, the VS Code command does not accept a name argument or display a QR code. The session title is derived from your conversation history or first prompt.
136122 
123**Desktop app**
124 
125In a local session in the [Claude Desktop app's](/docs/en/desktop) Code tab, type `/remote-control` or `/rc` in the prompt box.
126 
127```text theme={null}
128/remote-control
129```
130 
131Once the session connects, find it in the session list at [claude.ai/code](https://claude.ai/code). To disconnect, run `/remote-control` again.
132 
133To turn Remote Control on for every session by default instead, see [Enable Remote Control for all sessions](#enable-remote-control-for-all-sessions).
134 
137135### Check connection status
138136 
139137In an interactive session, while Remote Control is connected, the terminal shows an `/rc active` indicator that links to the session on claude.ai. The indicator is hidden when the terminal is too narrow to fit it. To see the session URL and a QR code for [connecting from another device](#connect-from-another-device), run `/remote-control` again to open the status panel. The panel also lets you disconnect Remote Control while your local session keeps running.
140138 
141<span id="session-ended-elsewhere" />If the connection fails in an interactive session, the indicator changes to show the failure, and Claude Code shows the reason in a notification and adds it to the conversation. Run `/remote-control` to reconnect, unless the reason says the session changed somewhere else:
139If the connection fails in an interactive session, the indicator changes to show the failure, and Claude Code shows the reason in a notification and adds it to the conversation. Run `/remote-control` to reconnect, unless the reason says the session changed somewhere else:
142140 
143141* **Another connection took over this session**: another device or Claude Code session has it now. Run `/remote-control` only if you want to take it back.
144142* **This session was ended or archived from another device or app**: run `/remote-control` only if you want the session back. Claude Code reopens an archived session.
from line 170
172170* **Compaction and `/clear`**: while Claude Code [compacts the conversation](/docs/en/context-window#what-survives-compaction), connected devices show the progress and then where the conversation was compacted. When you run `/clear`, the conversation resets on connected devices too.
173171* **Switching conversations with `/resume`**: the connected device doesn't receive the switched-to conversation's title or earlier history, but new messages in both directions go to and from whichever conversation is open in your terminal. To work on the original conversation from the device again, run `/resume` in your terminal and switch back to it.
174172* **Pulling a session with `/teleport`**: when you pull a [cloud session](/docs/en/claude-code-on-the-web#from-cloud-to-terminal) into your terminal with `/teleport`, the connected device doesn't receive the pulled conversation's earlier history. New messages in both directions go to and from the pulled conversation, which is now the one open in your terminal.
175* **Messages from your other sessions**: with [cross-session messaging](/docs/en/cross-session-messaging), the same connection carries messages between your own sessions on different machines and from your [cloud sessions](/docs/en/claude-code-on-the-web).
173* **Messages from your other sessions**: with [cross-session messaging](/docs/en/cross-session-messaging), the same connection carries messages between your own sessions on different machines.
176174* **Diff of your changes**: when the session's directory is in a git repository, a connected device's diff pane shows your changes. On a branch that has commits ahead of the repository's default branch, the pane shows the changes since the branch split from it, including your uncommitted edits. On the default branch itself, or on a branch that isn't ahead of it, the pane shows only your uncommitted changes.
177175* **Model**: when you pick a [model](/docs/en/model-config) from a connected device, Claude Code runs the session on that model. Requires Claude Code v2.1.238 or later. A model you pick from the device's model control applies to the current session only. When you send `/model <name>` from the device to an interactive session, Claude Code also sets your default for new sessions.
178176* **Effort level**: when you set the [effort level](/docs/en/model-config#adjust-effort-level) from a connected device, with `/effort` or the device's effort control, Claude Code applies it to the session on your machine. If you pinned a level with `CLAUDE_CODE_EFFORT_LEVEL`, the session keeps that level, and Claude Code refuses a different pick from the effort control. Picking a level from the effort control requires Claude Code v2.1.234 or later on your machine.
from line 182
184182 
185183* **`true`**: connect automatically when an interactive session starts.
186184* **`false`**: turn auto-connect off, though a `true` from [managed settings](/docs/en/managed-settings) outranks it, because Claude Code saves the choice to your user settings. A `false` in project or local settings (`.claude/settings.json`, `.claude/settings.local.json`) turns auto-connect off even over a managed `true`.
187* **`default`**: clear your choice and follow your organization's admin default if one is set, otherwise Claude Code's current default.
185* **`default`**: clear your choice and follow your organization's admin default if one is set, otherwise Claude Code's current default. If you set `DISABLE_TELEMETRY` or `DO_NOT_TRACK`, see [Auto-connect with `DISABLE_TELEMETRY` or `DO_NOT_TRACK` set](#auto-connect-with-disable-telemetry-or-do-not-track).
188186 
189187The VS Code extension and the Desktop app also have an auto-connect toggle:
190188 
from line 195
197195 
198196With this setting on, each interactive Claude Code process registers one remote session. If you run multiple instances, each one gets its own remote session. To run multiple concurrent sessions from a single process, use [server mode](#start-a-remote-control-session) instead.
199197 
198#### Auto-connect with `DISABLE_TELEMETRY` or `DO_NOT_TRACK` set
199 
200With only `DISABLE_TELEMETRY` or `DO_NOT_TRACK` set, your sessions don't auto-connect through your organization's admin default or Claude Code's own default. If something else makes Remote Control unavailable for you, such as `DISABLE_GROWTHBOOK` or an LLM gateway, start with [Requirements](#requirements) instead.
201 
202Turning auto-connect on with only `DISABLE_TELEMETRY` or `DO_NOT_TRACK` set requires Claude Code v2.1.283 or later. To auto-connect, check whether your organization requires [Trusted Devices](#trusted-devices):
203 
204* **Not required**: set **Enable Remote Control for all sessions** to `true` in `/config`.
205* **Required**: Remote Control isn't available while `DISABLE_TELEMETRY` or `DO_NOT_TRACK` is set. Unset whichever of the two variables you set. If your sessions still don't auto-connect, set **Enable Remote Control for all sessions** to `true` in `/config`.
206 
200207### Resume sessions after stopping the server
201208 
202209When you stop `claude remote-control` with Ctrl+C, the sessions it was serving stop responding from your phone or browser. As long as you weren't running another `claude remote-control` in the same directory and didn't start this one with `--no-create-session-in-dir`, Claude Code doesn't archive them. To bring them back, run one of these commands in the same directory:
from line 232
225232 
226233## Trusted Devices
227234 
228<Note>
229 Trusted Devices is currently in beta. Features and functionality may evolve as the experience is refined.
235> [!NOTE]
236>
237> Trusted Devices is currently in beta. Features and functionality may evolve as the experience is refined.
238>
239> Trusted Devices is available on Pro, Max, Team, and Enterprise plans and is off by default. On Team and Enterprise plans, an Owner turns it on for the organization. On Pro and Max plans, you turn on **Require trusted devices** yourself in your settings, on the Cowork or Account page.
230240 
231 Trusted Devices is available on Pro, Max, Team, and Enterprise plans and is off by default. On Team and Enterprise plans, an Owner turns it on for the organization. On Pro and Max plans, you turn on **Require trusted devices** yourself in your settings, on the Cowork or Account page.
232</Note>
233 
234241Trusted Devices requires each member of your organization, or you alone on a Pro or Max plan, to verify their device before they can view or steer Remote Control sessions from claude.ai, the Claude mobile apps, or Claude Desktop. It ties Remote Control access to a known device and a recent authentication, not just a signed-in account.
235242 
236243When the setting is on, interacting with a Remote Control session requires both of the following:
from line 249
242249 
243250The setting applies to Remote Control in Claude Code and to Computer access in [Cowork](https://claude.com/docs/cowork/overview). This page covers the Claude Code side. Regular Claude chat, Claude Code in the terminal, and API usage are unaffected.
244251 
245<h3 id="enable-trusted-devices-for-your-organization">
246 Enable Trusted Devices for a Team or Enterprise organization
247</h3>
252### Enable Trusted Devices for a Team or Enterprise organization
248253 
249254An Owner enables the setting from the claude.ai organization settings.
250255 
251<Steps>
252 <Step title="Go to the Capabilities page">
253 Go to [**Organization settings > Capabilities > Remote sessions**](https://claude.ai/admin-settings/capabilities). The **Require trusted devices** toggle appears in that section.
254 </Step>
256**1. Go to the Capabilities page**
255257 
256 <Step title="Turn on Require trusted devices">
257 The setting applies to every member of the organization and to Remote Control sessions started after you enable it. Sessions that were already running before the toggle was turned on are not retroactively protected and continue without the device requirement until they end. Per-team or per-project scoping is not available.
258 </Step>
258Go to [**Organization settings > Capabilities > Remote sessions**](https://claude.ai/admin-settings/capabilities). The **Require trusted devices** toggle appears in that section.
259259 
260 <Step title="Tell members what to expect">
261 The first time a member views or steers a new Remote Control session from a browser, phone, or desktop app after the setting is enabled, they are prompted to enroll that device. Letting them know ahead of time avoids confusion.
262 </Step>
263</Steps>
260**2. Turn on Require trusted devices**
264261 
262The setting applies to every member of the organization and to Remote Control sessions started after you enable it. Sessions that were already running before the toggle was turned on are not retroactively protected and continue without the device requirement until they end. Per-team or per-project scoping is not available.
263 
264**3. Tell members what to expect**
265 
266The first time a member views or steers a new Remote Control session from a browser, phone, or desktop app after the setting is enabled, they are prompted to enroll that device. Letting them know ahead of time avoids confusion.
267 
265268### What members see
266269 
267270Enrollment is a one-time step per device. After that, the only visible change is an occasional biometric prompt.
from line 308
305308 
306309To set up mobile push notifications:
307310 
308<Steps>
309 <Step title="Install the Claude mobile app">
310 Download the Claude app for [iOS](https://apps.apple.com/us/app/claude-by-anthropic/id6473753684) or [Android](https://play.google.com/store/apps/details?id=com.anthropic.claude).
311 </Step>
311**1. Install the Claude mobile app**
312312 
313 <Step title="Sign in with your Claude Code account">
314 Use the same account and organization you use for Claude Code in the terminal.
315 </Step>
313Download the Claude app for [iOS](https://apps.apple.com/us/app/claude-by-anthropic/id6473753684) or [Android](https://play.google.com/store/apps/details?id=com.anthropic.claude).
316314 
317 <Step title="Allow notifications">
318 Accept the notification permission prompt from the operating system.
319 </Step>
315**2. Sign in with your Claude Code account**
320316 
321 <Step title="Enable push in Claude Code">
322 In your terminal, run `/config` and enable **Push when Claude decides** for proactive notifications, **Push when actions required** for permission prompts and questions, or both.
323 </Step>
324</Steps>
317Use the same account and organization you use for Claude Code in the terminal.
325318 
319**3. Allow notifications**
320 
321Accept the notification permission prompt from the operating system.
322 
323**4. Enable push in Claude Code**
324 
325In your terminal, run `/config` and enable **Push when Claude decides** for proactive notifications, **Push when actions required** for permission prompts and questions, or both.
326 
326327If notifications don't arrive:
327328 
328329* If `/config` shows **No mobile registered**, open the Claude app on your phone so it can refresh its push token. The warning clears the next time Remote Control connects.
from line 360
359360 
360361When a claude.ai connector needs authentication in a session you drive over Remote Control, the `/mcp` panel isn't available from the mobile app or the web. Get the authorization link from a terminal on the machine where the session runs, then open the link on the device you're using. You can't run the command from the mobile app or the web. A line you send from there that starts with `!` goes to Claude as a message and doesn't run in [shell mode](/docs/en/interactive-mode#shell-mode-with-prefix).
361362 
362<Steps>
363 <Step title="Get the authorization link">
364 In a terminal on the machine where the session runs, such as over SSH, run `claude mcp login` with the connector's name in quotes. A connector's name starts with `claude.ai`, such as `claude.ai Slack` for the Slack connector. The following command gets the link for the Slack connector:
363**1. Get the authorization link**
365364 
366 ```bash theme={null}
367 claude mcp login "claude.ai Slack" --no-browser
368 ```
365In a terminal on the machine where the session runs, such as over SSH, run `claude mcp login` with the connector's name in quotes. A connector's name starts with `claude.ai`, such as `claude.ai Slack` for the Slack connector. The following command gets the link for the Slack connector:
369366 
370 The command prints a claude.ai link and exits. Open the link in your browser and finish authorizing on claude.ai. `--no-browser` keeps the command from opening a browser on that machine, which might not be the device you're using.
371 </Step>
367```bash theme={null}
368claude mcp login "claude.ai Slack" --no-browser
369```
372370 
373 <Step title="Use the connector in a session">
374 Start a new session, or reconnect the connector in the session that's already running:
371The command prints a claude.ai link and exits. Open the link in your browser and finish authorizing on claude.ai. `--no-browser` keeps the command from opening a browser on that machine, which might not be the device you're using.
375372 
376 * **New session**: a session you start after authorizing connects to the connector without any further step.
377 * **Running session**: run `/mcp reconnect` with the same name and no quotes, at the Claude Code prompt or from the mobile app or the web. To check that your session accepts the command from there, see the `/mcp` entry under [which commands work from mobile and web](#limitations).
373**2. Use the connector in a session**
378374 
379 The following command reconnects the Slack connector:
375Start a new session, or reconnect the connector in the session that's already running:
380376 
381 ```text theme={null}
382 /mcp reconnect claude.ai Slack
383 ```
377* **New session**: a session you start after authorizing connects to the connector without any further step.
378* **Running session**: run `/mcp reconnect` with the same name and no quotes, at the Claude Code prompt or from the mobile app or the web. To check that your session accepts the command from there, see the `/mcp` entry under [which commands work from mobile and web](#limitations).
384379 
385 In the terminal, Claude Code prints `Successfully reconnected to claude.ai Slack`. From the mobile app or the web, the reply is `Reconnected "claude.ai Slack".`
386 </Step>
387</Steps>
380The following command reconnects the Slack connector:
388381 
382```text theme={null}
383/mcp reconnect claude.ai Slack
384```
385 
386In the terminal, Claude Code prints `Successfully reconnected to claude.ai Slack`. From the mobile app or the web, the reply is `Reconnected "claude.ai Slack".`
387 
389388## Troubleshooting
390389 
391390### "Remote Control requires a claude.ai subscription"
from line 430
431430 
432431The message names what routed the session away from the Anthropic API, such as `CLAUDE_CODE_USE_BEDROCK` or a custom `ANTHROPIC_BASE_URL`. If you have an eligible claude.ai login, unset the named variable, remove it from the `env` key in [settings](/docs/en/settings) if you set it there, and restart the session.
433432 
434<h3 id="remote-control-is-disabled-by-your-organizations-policy">
435 "Remote Control is disabled by your organization's policy"
436</h3>
433### "Remote Control is disabled by your organization's policy"
437434 
438435A policy blocks Remote Control. Check these causes in order:
439436 
from line 441
444441 
445442Before v2.1.281, this message also appeared when Claude Code hadn't loaded your organization's policy on this machine, for example after starting offline. Later versions report that state as [`Couldn't verify your organization's policy for remote control`](#couldnt-verify-your-organizations-policy-for-remote-control) instead.
446443 
447<h3 id="remote-control-was-turned-off-by-your-organizations-policy">
448 "Remote Control was turned off by your organization's policy"
449</h3>
444### "Remote Control was turned off by your organization's policy"
450445 
451446Your organization's policy stopped allowing Remote Control while a session was connected, so Claude Code disconnected it. What happens to the session depends on how you started Remote Control:
452447 
from line 452
457452 
458453Remote Control doesn't reconnect on its own. To turn it back on after your organization allows it again, run `/remote-control` in the session or `claude remote-control` in your shell. Either command fails with [`Remote Control is disabled by your organization's policy`](#remote-control-is-disabled-by-your-organizations-policy) until Claude Code on this machine has fetched the changed policy. An open session fetches it about once an hour. To find out what is blocking Remote Control, match the full text the command prints against that entry.
459454 
460<h3 id="couldnt-verify-your-organizations-policy-for-remote-control">
461 "Couldn't verify your organization's policy for remote control"
462</h3>
455### "Couldn't verify your organization's policy for remote control"
463456 
464457Claude Code couldn't fetch your organization's policy and has no saved copy on this machine to use instead, so it keeps Remote Control off until it can confirm that your organization allows it. This usually happens when you start Claude Code offline or before a VPN connects, or when a proxy interferes with the request. On a slow connection it can also appear while the first request is still in flight.
465458 
from line 479
486479* Network or proxy issue: a firewall or proxy may be blocking the outbound HTTPS request. Remote Control requires access to the Anthropic API on port 443.
487480* Session creation failed: if you also see `Session creation failed — see debug log`, the failure happened earlier in setup. Check that your subscription is active.
488481 
489<h3 id="couldnt-reconnect-to-your-remote-control-session">
490 "Couldn't reconnect to your Remote Control session"
491</h3>
482### "Couldn't reconnect to your Remote Control session"
492483 
493484When you resume a conversation with `claude --resume` or `claude --continue`, Claude Code reconnects to the Remote Control session recorded in that conversation. This message means the reconnection failed for a reason that may be temporary, such as a network interruption or a server error, so Claude Code can't confirm whether the remote session still exists.
494485 
495486Run `/remote-control` to retry the connection, or start a new session with `claude --remote-control` to create a new Remote Control session. Your local session keeps running without Remote Control in the meantime.
496487 
497<h3 id="previous-session-is-unavailable">
498 "Previous session is unavailable — run /remote-control to start a new one"
499</h3>
488### "Previous session is unavailable — run /remote-control to start a new one"
500489 
501490Claude Code couldn't bring back the previous Remote Control session and stopped instead of starting a new one on its own. You can see this message after you resume a conversation with `claude --resume` or `claude --continue`, or after Claude Code [reconnects on its own following a disconnect](/docs/en/errors#remote-control-couldnt-refresh-your-login).
502491 

routines Changed · +126 / -150 lines

### `/schedule` returns "Unknown command" ### "Routines are disabled by your organization's policy"

from line 2
22 
33> Put Claude Code on autopilot. Create scheduled tasks, formerly routines, that run in the cloud on a schedule, on API calls, or on GitHub events.
44 
5<Note>
6 Scheduled tasks are in research preview. Behavior, limits, and the API surface may change.
7</Note>
5> [!NOTE]
6>
7> Scheduled tasks are in research preview. Behavior, limits, and the API surface may change.
88 
99A cloud scheduled task, formerly a routine, is a saved Claude Code configuration: a prompt, one or more repositories, and a set of [connectors](/docs/en/mcp), packaged once and run automatically. Scheduled tasks execute on Anthropic-managed cloud infrastructure, or on your organization's [self-hosted environment](/docs/en/self-hosted-environments) when routed there, so they keep working when your laptop is closed.
1010 
from line 23
2323 
2424Scheduled tasks are available on Pro, Max, Team, and Enterprise plans. Create and manage them at [claude.ai/code/routines](https://claude.ai/code/routines), or from the CLI with `/schedule`. The earlier name, routines, [still appears](#where-the-name-routines-still-appears) in the web address, the CLI, and the API.
2525 
26<Note>
27 Two other features schedule work on your machine instead of in the cloud: [Desktop scheduled tasks](/docs/en/desktop-scheduled-tasks) and [`/loop`](/docs/en/scheduled-tasks). To choose among all three, see [Compare scheduling options](/docs/en/scheduled-tasks#compare-scheduling-options).
28</Note>
26> [!NOTE]
27>
28> Two other features schedule work on your machine instead of in the cloud: [Desktop scheduled tasks](/docs/en/desktop-scheduled-tasks) and [`/loop`](/docs/en/scheduled-tasks). To choose among all three, see [Compare scheduling options](/docs/en/scheduled-tasks#compare-scheduling-options).
2929 
3030Team and Enterprise Owners can turn off scheduled tasks for all members with the **Scheduled tasks (Was Routines)** toggle at [**Organization settings > Capabilities > Remote sessions**](https://claude.ai/admin-settings/capabilities). When the toggle is off, existing scheduled tasks stop running and members can't create new ones.
3131 
from line 40
4040* **Docs drift**: a schedule trigger runs weekly. The scheduled task scans merged PRs since the last run, flags documentation that references changed APIs, and opens update PRs against the docs repository for an editor to review.
4141* **Library port**: a GitHub trigger runs on `pull_request.closed` filtered to merged PRs in one SDK repository. The scheduled task ports the change to a parallel SDK in another language and opens a matching PR, keeping the two libraries in step without a human re-implementing each change.
4242 
43<span id="create-a-routine" />
44 
4543## Create a scheduled task
4644 
4745Create a scheduled task from the web at [claude.ai/code/routines](https://claude.ai/code/routines), from the Desktop app, or from the CLI. All three surfaces write to the same cloud account, so a scheduled task you create in one shows up in the others immediately. In the Desktop app's **Code** tab, click **Scheduled** in the sidebar or in the sidebar's **More** menu, then **New task**, and continue with [Create from the web](#create-from-the-web).
from line 58
6058 
6159### Create from the web
6260 
63<Steps>
64 <Step title="Open the creation dialog">
65 Go to [claude.ai/code/routines](https://claude.ai/code/routines) and click **New task**. If a menu opens first, select **Set up manually**. The **New scheduled task** dialog opens. The menu's other option, **Create with Claude**, lets you describe the task in a chat and have Claude create it instead.
61**1. Open the creation dialog**
6662 
67 If **Repositories** and **Environment** are already showing when the dialog opens, see [how that dialog differs](#dialog-with-repositories-and-environment-in-the-main-view). Otherwise, this dialog also creates scheduled tasks for claude.ai chat. To make yours what the dialog calls a Claude Code task, select a repository or add a GitHub event or API trigger in the steps that follow. That kind of task runs Claude Code in a cloud session.
68 </Step>
63Go to [claude.ai/code/routines](https://claude.ai/code/routines) and click **New task**. If a menu opens first, select **Set up manually**. The **New scheduled task** dialog opens. The menu's other option, **Create with Claude**, lets you describe the task in a chat and have Claude create it instead.
6964 
70 <Step title="Name the task and write the prompt">
71 Give the scheduled task a descriptive name and write the prompt Claude runs each time. The prompt is the most important part: the task runs autonomously, so the prompt must be self-contained and explicit about what to do and what success looks like. Select a model in the prompt input's model selector. Claude uses it on every run.
65If **Repositories** and **Environment** are already showing when the dialog opens, see [how that dialog differs](#dialog-with-repositories-and-environment-in-the-main-view). Otherwise, this dialog also creates scheduled tasks for claude.ai chat. To make yours what the dialog calls a Claude Code task, select a repository or add a GitHub event or API trigger in the steps that follow. That kind of task runs Claude Code in a cloud session.
7266 
73 When a trigger fires, the session receives the saved prompt as its assignment and carries it out, rather than treating it as untrusted content that arrived mid-conversation. The trigger attests only that the prompt was stored ahead of time by an authorized session on your account. The fired prompt is therefore not live user input and can't act as approval or consent for actions during the run. Content the session fetches during the run keeps its normal handling.
74 </Step>
67**2. Name the task and write the prompt**
7568 
76 <Step title="Select a trigger">
77 In the **Trigger** row, choose how the task starts. To combine several triggers, click **+** at the end of the row.
69Give the scheduled task a descriptive name and write the prompt Claude runs each time. The prompt is the most important part: the task runs autonomously, so the prompt must be self-contained and explicit about what to do and what success looks like. Select a model in the prompt input's model selector. Claude uses it on every run.
7870 
79 <Tabs>
80 <Tab title="Schedule">
81 Pick a preset frequency for a recurring run. See [Add a schedule trigger](#add-a-schedule-trigger) for timezone handling, late starts, custom cron intervals, and one-off runs.
82 </Tab>
71When a trigger fires, the session receives the saved prompt as its assignment and carries it out, rather than treating it as untrusted content that arrived mid-conversation. The trigger attests only that the prompt was stored ahead of time by an authorized session on your account. The fired prompt is therefore not live user input and can't act as approval or consent for actions during the run. Content the session fetches during the run keeps its normal handling.
8372 
84 <Tab title="GitHub event">
85 Select the repository, the event to react to, and optional filters. See [Add a GitHub trigger](#add-a-github-trigger) for the full list of supported events and filter fields.
86 </Tab>
73**3. Select a trigger**
8774 
88 <Tab title="API">
89 Select **API** here. When you click **Create**, the **API token** dialog shows the token once, so copy it before you close the dialog. See [Add an API trigger](#add-an-api-trigger) to copy the URL or generate a new token.
90 </Tab>
91 </Tabs>
92 </Step>
75In the **Trigger** row, choose how the task starts. To combine several triggers, click **+** at the end of the row.
9376 
94 <Step title="Select repositories">
95 Expand **Advanced settings**. Under **Repository** on the **General** tab, select one or more GitHub repositories for Claude to work in.
77**Schedule**
9678 
97 Each repository is cloned at the start of a run. Claude creates `claude/`-prefixed branches for its changes.
98 </Step>
79Pick a preset frequency for a recurring run. See [Add a schedule trigger](#add-a-schedule-trigger) for timezone handling, late starts, custom cron intervals, and one-off runs.
9980 
100 <Step title="Select an environment">
101 After you select a repository, **Cloud environment** appears below it. Pick a [cloud environment](/docs/en/cloud-environments) for the task. Environments control what the cloud session has access to:
81**GitHub event**
10282 
103 * **Network access**: set the level of internet access available during each run
104 * **Environment variables**: provide values Claude can use during each run. They're [visible to anyone who uses the environment](/docs/en/cloud-environments#what-carries-over-from-your-setup), so in an Anthropic-hosted environment, store keys for the APIs Claude calls during a run as [network secrets](/docs/en/cloud-environments#add-network-secrets) instead. That section also lists the requests that never get a secret
105 * **Setup script**: install dependencies and tools the task needs. The result is [cached](/docs/en/cloud-environments#environment-caching), so the script doesn't re-run on every session
83Select the repository, the event to react to, and optional filters. See [Add a GitHub trigger](#add-a-github-trigger) for the full list of supported events and filter fields.
10684 
107 A **Default** environment is provided with **Trusted** network access, which allows only the [default allowlist](/docs/en/cloud-environments#default-allowed-domains) of package registries, cloud provider APIs, container registries, and common development domains through the session's network. Connectors you add to the task reach their services through Anthropic's servers, so they don't need allowlist changes.
85**API**
10886 
109 If your task needs to reach your own services directly, or a domain outside that list, edit the environment's [network access](/docs/en/cloud-environments#network-access) before running. To use a separate environment, [create one](/docs/en/cloud-environments#configure-your-environment) first.
110 </Step>
87Select **API** here. When you click **Create**, the **API token** dialog shows the token once, so copy it before you close the dialog. See [Add an API trigger](#add-an-api-trigger) to copy the URL or generate a new token.
11188 
112 <Step title="Review connectors">
113 Switch to the **Connectors** tab of **Advanced settings**. All of your connected [MCP connectors](/docs/en/mcp) are included by default. Remove any the task doesn't need: Claude can use every tool from an included connector, including writes, without asking for permission during a run.
114 </Step>
89**4. Select repositories**
11590 
116 <Step title="Create the task">
117 Click **Create**. The dialog closes and the new task's detail page opens. The task runs the next time one of its triggers matches. To start a run immediately, click **Run now** on the detail page.
91Expand **Advanced settings**. Under **Repository** on the **General** tab, select one or more GitHub repositories for Claude to work in.
11892 
119 Each run creates a new session alongside your other sessions, where you can see what Claude did, review changes, and create a pull request.
120 </Step>
121</Steps>
93Each repository is cloned at the start of a run. Claude creates `claude/`-prefixed branches for its changes.
12294 
95**5. Select an environment**
96 
97After you select a repository, **Cloud environment** appears below it. Pick a [cloud environment](/docs/en/cloud-environments) for the task. Environments control what the cloud session has access to:
98 
99* **Network access**: set the level of internet access available during each run
100* **Environment variables**: provide values Claude can use during each run. They're [visible to anyone who uses the environment](/docs/en/cloud-environments#what-carries-over-from-your-setup), so in an Anthropic-hosted environment, store keys for the APIs Claude calls during a run as [network secrets](/docs/en/cloud-environments#add-network-secrets) instead. That section also lists the requests that never get a secret
101* **Setup script**: install dependencies and tools the task needs. The result is [cached](/docs/en/cloud-environments#environment-caching), so the script doesn't re-run on every session
102 
103A **Default** environment is provided with **Trusted** network access, which allows only the [default allowlist](/docs/en/cloud-environments#default-allowed-domains) of package registries, cloud provider APIs, container registries, and common development domains through the session's network. Connectors you add to the task reach their services through Anthropic's servers, so they don't need allowlist changes.
104 
105If your task needs to reach your own services directly, or a domain outside that list, edit the environment's [network access](/docs/en/cloud-environments#network-access) before running. To use a separate environment, [create one](/docs/en/cloud-environments#configure-your-environment) first.
106 
107**6. Review connectors**
108 
109Switch to the **Connectors** tab of **Advanced settings**. All of your connected [MCP connectors](/docs/en/mcp) are included by default. Remove any the task doesn't need: Claude can use every tool from an included connector, including writes, without asking for permission during a run.
110 
111**7. Create the task**
112 
113Click **Create**. The dialog closes and the new task's detail page opens. The task runs the next time one of its triggers matches. To start a run immediately, click **Run now** on the detail page.
114 
115Each run creates a new session alongside your other sessions, where you can see what Claude did, review changes, and create a pull request.
116 
123117#### Dialog with Repositories and Environment in the main view
124118 
125119Follow the [steps to create from the web](#create-from-the-web), and fill in each field in the dialog's main view rather than under **Advanced settings**. This dialog creates only Claude Code tasks.
from line 177
183177 
184178Add an API trigger on the web, either while you [create the task](#create-from-the-web) or later with the steps below. You can't add an API trigger or manage its token from the CLI.
185179 
186<Steps>
187 <Step title="Open the task for editing">
188 Go to [claude.ai/code/routines](https://claude.ai/code/routines), click the scheduled task you want to trigger via API, then open the menu next to its name and select **Edit**. The **Edit scheduled task** dialog opens.
189 </Step>
180**1. Open the task for editing**
190181 
191 <Step title="Add an API trigger">
192 In the **Trigger** row, click **+** and select **API** from the menu that opens. A panel with the **Fire URL** and **Token** for this task appears.
193 </Step>
182Go to [claude.ai/code/routines](https://claude.ai/code/routines), click the scheduled task you want to trigger via API, then open the menu next to its name and select **Edit**. The **Edit scheduled task** dialog opens.
194183 
195 <Step title="Copy the URL and generate a token">
196 Copy the **Fire URL**. Click **Generate token**, then copy the token from the **Token generated** dialog immediately. The token is shown once and can't be retrieved later, so store it somewhere secure such as your alerting tool's secret store.
184**2. Add an API trigger**
197185 
198 The API trigger takes effect as soon as you generate the token. You don't need to click **Save changes**, so close the dialog when you're done.
199 </Step>
186In the **Trigger** row, click **+** and select **API** from the menu that opens. A panel with the **Fire URL** and **Token** for this task appears.
200187 
201 <Step title="Call the endpoint">
202 Send the token in the `Authorization: Bearer` header when you POST to the URL. [Trigger a scheduled task](#trigger-a-scheduled-task) shows a complete example.
203 </Step>
204</Steps>
188**3. Copy the URL and generate a token**
205189 
190Copy the **Fire URL**. Click **Generate token**, then copy the token from the **Token generated** dialog immediately. The token is shown once and can't be retrieved later, so store it somewhere secure such as your alerting tool's secret store.
191 
192The API trigger takes effect as soon as you generate the token. You don't need to click **Save changes**, so close the dialog when you're done.
193 
194**4. Call the endpoint**
195 
196Send the token in the `Authorization: Bearer` header when you POST to the URL. [Trigger a scheduled task](#trigger-a-scheduled-task) shows a complete example.
197 
206198Each scheduled task has its own token, scoped to triggering that task only. To turn off API access or rotate the token, [open the task's **Edit scheduled task** dialog](#edit-and-control-scheduled-tasks) again. Both controls are next to the token in the API trigger's panel and take effect immediately:
207199 
208200* **Turn off API access**: click **Delete token**, the trash icon next to **Regenerate**. This removes the API trigger
209201* **Rotate the token**: click **Regenerate**. The previous token stops working, and the **Token generated** dialog shows the new one once
210202 
211<span id="trigger-a-routine" />
212 
213203#### Trigger a scheduled task
214204 
215205Send a POST request to the `/fire` endpoint with the bearer token in the `Authorization` header. The request body accepts an optional `text` field for run-specific context such as an alert body or a failing log, passed to the task alongside its saved prompt. The value is freeform text and is not parsed: if you send JSON or another structured payload, the task receives it as a literal string.
from line 231
241231 
242232Open the session URL in a browser to watch the run in real time, review changes, or continue the conversation manually.
243233 
244<Warning>
245 The `/fire` endpoint ships under the `experimental-cc-routine-2026-04-01` beta header. Request and response shapes, rate limits, and token semantics may change while the feature is in research preview. Breaking changes ship behind new dated beta header versions, and the two most recent previous header versions continue to work so that callers have time to migrate.
246</Warning>
234> [!WARNING]
235>
236> The `/fire` endpoint ships under the `experimental-cc-routine-2026-04-01` beta header. Request and response shapes, rate limits, and token semantics may change while the feature is in research preview. Breaking changes ship behind new dated beta header versions, and the two most recent previous header versions continue to work so that callers have time to migrate.
247237 
248238#### API reference
249239 
from line 245
255245 
256246A GitHub trigger starts a new session automatically when a matching event occurs on a connected repository. Claude Code doesn't reuse sessions across events, so two PR updates produce two independent sessions.
257247 
258<Note>
259 GitHub webhook events are subject to per-task and per-account hourly caps. Events beyond the limit are dropped until the window resets.
260</Note>
248> [!NOTE]
249>
250> GitHub webhook events are subject to per-task and per-account hourly caps. Events beyond the limit are dropped until the window resets.
261251 
262252The Claude GitHub App must be installed on the repository you want to subscribe to, whichever surface you configure the trigger from.
263253 
from line 254
264254* Configure GitHub triggers from the web UI, which prompts you to install the app when it's missing. Follow the steps below to configure one on the web.
265255* From the CLI, install the app from the [GitHub App page](https://github.com/apps/claude) first, then ask Claude to attach a GitHub trigger to an existing scheduled task, for example `/schedule add a GitHub trigger to my nightly review for pull requests opened in acme/webapp`. The CLI path requires Claude Code v2.1.225 or later. When Claude adds the trigger, it replies with a link to the task the trigger fires.
266256 
267<Steps>
268 <Step title="Open the task for editing">
269 Go to [claude.ai/code/routines](https://claude.ai/code/routines), click the scheduled task, then open the menu next to its name and select **Edit**. The **Edit scheduled task** dialog opens.
270 </Step>
257**1. Open the task for editing**
271258 
272 <Step title="Add a GitHub event trigger">
273 In the **Trigger** row, click **+** and select **GitHub event** from the menu that opens.
259Go to [claude.ai/code/routines](https://claude.ai/code/routines), click the scheduled task, then open the menu next to its name and select **Edit**. The **Edit scheduled task** dialog opens.
274260 
275 <Note>
276 Running `/web-setup` in the CLI grants repository access for cloning, but it does not install the Claude GitHub App and does not enable webhook delivery.
277 </Note>
278 </Step>
261**2. Add a GitHub event trigger**
279262 
280 <Step title="Configure the trigger">
281 Change the repository if needed, choose an event from the [supported events](#supported-events) list, and optionally add filters. Then click **Save changes** at the bottom of the dialog to apply the trigger.
282 </Step>
283</Steps>
263In the **Trigger** row, click **+** and select **GitHub event** from the menu that opens.
284264 
265> [!NOTE]
266>
267> Running `/web-setup` in the CLI grants repository access for cloning, but it does not install the Claude GitHub App and does not enable webhook delivery.
268 
269**3. Configure the trigger**
270 
271Change the repository if needed, choose an event from the [supported events](#supported-events) list, and optionally add filters. Then click **Save changes** at the bottom of the dialog to apply the trigger.
272 
285273#### Supported events
286274 
287275GitHub triggers can subscribe to either of the following event categories. Within each category you can pick a specific action, such as `pull_request.opened`, or react to all actions in the category.
from line 304
316304* **Ready-for-review only**: is draft is `false`. Skips drafts so the task only runs when the PR is ready for review.
317305* **Label-gated backport**: labels include `needs-backport`. Triggers a port-to-another-branch task only when a maintainer tags the PR.
318306 
319<span id="manage-routines" />
320 
321307## Manage scheduled tasks
322308 
323309Click a scheduled task in the list to open its detail page. The detail page shows the task's repositories, connectors, prompt, schedule, API tokens, GitHub triggers, and a list of past runs.
from line 312
326312 
327313Click any run to open it as a full session. From there you can see what Claude did, review changes, create a pull request, or continue the conversation. Each run session works like any other session: use the dropdown menu next to the session title to rename, archive, or delete it.
328314 
329<Note>
330 A green status in the run list means the session started and exited without an infrastructure error. It does not mean the work your prompt describes succeeded. Open the run to read the transcript and confirm what Claude actually did. Blocked network requests, missing connector tools, and failures in the work itself all surface there rather than in the status indicator.
331</Note>
315> [!NOTE]
316>
317> A green status in the run list means the session started and exited without an infrastructure error. It does not mean the work your prompt describes succeeded. Open the run to read the transcript and confirm what Claude actually did. Blocked network requests, missing connector tools, and failures in the work itself all surface there rather than in the status indicator.
332318 
333<span id="edit-and-control-routines" />
334 
335319### Edit and control scheduled tasks
336320 
337321From the detail page you can:
from line 325
341325* Open the menu next to the task's name and select **Edit** to change the name, prompt, repositories, environment, connectors, or any of the task's triggers. The **Edit scheduled task** dialog opens with the same layout as the [creation dialog](#create-from-the-web). To add a trigger, click **+** in the **Trigger** row. Click **Save changes** to apply your edits. API token actions take effect immediately, without **Save changes**.
342326* Open the same menu and select **Delete** to delete the task.
343327 
344<span id="manage-routines-from-the-cli" />
345 
346328### Manage scheduled tasks from the CLI
347329 
348330Run `/schedule list` to see your scheduled tasks, `/schedule update` to change one, or `/schedule run` to start one immediately.
from line 335
353335 
354336Scheduled tasks need GitHub access to clone repositories. When you create a scheduled task from the CLI with `/schedule`, Claude checks whether your account has GitHub access for the repository you ran it from. If it doesn't, Claude adds a setup note naming how to grant it.
355337 
356<Note>
357 See [GitHub authentication options](/docs/en/claude-code-on-the-web#github-authentication-options) for the two ways to grant access. On Team and Enterprise plans, an [Owner](/docs/en/server-managed-settings#access-control) of your Claude organization has to turn on each method before you can use it; see [Connect GitHub](/docs/en/web-quickstart#connect-github).
358</Note>
338> [!NOTE]
339>
340> See [GitHub authentication options](/docs/en/claude-code-on-the-web#github-authentication-options) for the two ways to grant access. On Team and Enterprise plans, an [Owner](/docs/en/server-managed-settings#access-control) of your Claude organization has to turn on each method before you can use it; see [Connect GitHub](/docs/en/web-quickstart#connect-github).
359341 
360342If your GitHub connection is missing or expired when a run is due, the scheduled task skips runs until you reconnect, for up to 72 hours. Reconnect GitHub within that window and the task resumes on its own. After 72 hours without a connection, the task turns off, and you turn it back on after reconnecting GitHub.
361343 
from line 384
402384 
403385To allow additional domains on one of your own environments, follow these steps. An [organization-shared environment](/docs/en/cloud-environments#organization-shared-environments) opens read-only here, so an Owner changes its network access from the **Cloud environments** page in [admin settings](https://claude.ai/admin-settings) instead.
404386 
405<Steps>
406 <Step title="Open the task for editing">
407 Go to [claude.ai/code/routines](https://claude.ai/code/routines), click the scheduled task, then open the menu next to its name and select **Edit**. The **Edit scheduled task** dialog opens.
408 </Step>
387**1. Open the task for editing**
409388 
410 <Step title="Open the environment selector">
411 Go to **Advanced settings > General > Cloud environment**, expanding **Advanced settings** first if it's collapsed. Click the cloud icon showing your environment's name, such as **Default**.
412 </Step>
389Go to [claude.ai/code/routines](https://claude.ai/code/routines), click the scheduled task, then open the menu next to its name and select **Edit**. The **Edit scheduled task** dialog opens.
413390 
414 <Step title="Open the environment settings">
415 Hover over the environment in the list and click the settings icon that appears on the right.
416 </Step>
391**2. Open the environment selector**
417392 
418 <Step title="Change the network access level">
419 In the **Edit cloud environment** dialog, change **Network access** to **Custom** and enter your domains in **Allowed domains**. Check **Also include default list of common package managers** to keep the [default allowlist](/docs/en/cloud-environments#default-allowed-domains) alongside your custom domains. Select **Full** instead for unrestricted access.
420 </Step>
393Go to **Advanced settings > General > Cloud environment**, expanding **Advanced settings** first if it's collapsed. Click the cloud icon showing your environment's name, such as **Default**.
421394 
422 <Step title="Save">
423 Click **Save changes** in the **Edit cloud environment** dialog, then close the **Edit scheduled task** dialog behind it. The new policy applies from the next run.
424 </Step>
425</Steps>
395**3. Open the environment settings**
426396 
397Hover over the environment in the list and click the settings icon that appears on the right.
398 
399**4. Change the network access level**
400 
401In the **Edit cloud environment** dialog, change **Network access** to **Custom** and enter your domains in **Allowed domains**. Check **Also include default list of common package managers** to keep the [default allowlist](/docs/en/cloud-environments#default-allowed-domains) alongside your custom domains. Select **Full** instead for unrestricted access.
402 
403**5. Save**
404 
405Click **Save changes** in the **Edit cloud environment** dialog, then close the **Edit scheduled task** dialog behind it. The new policy applies from the next run.
406 
427407See [Network access](/docs/en/cloud-environments#network-access) for details on access levels and the default allowlist.
428408 
429409## Usage and limits
from line 437
457437 
458438## Troubleshooting
459439 
460<h3 id="schedule-returns-unknown-command">
461 `/schedule` returns "Unknown command"
462</h3>
440### `/schedule` returns "Unknown command"
463441 
464442The CLI hides `/schedule` when one of its requirements isn't met: the command menu shows `No commands match "/schedule"` while you type. Submitting it returns `Unknown command: /schedule`, except in the cases below that note a different answer.
465443 
from line 451
473451 
474452Unless your organization's policy disables scheduled tasks or cloud sessions, you can create and manage scheduled tasks at [claude.ai/code/routines](https://claude.ai/code/routines) regardless of how the CLI is configured.
475453 
476<h3 id="routines-are-disabled-by-your-organizations-policy">
477 "Routines are disabled by your organization's policy"
478</h3>
454### "Routines are disabled by your organization's policy"
479455 
480456An Owner in your Team or Enterprise organization has likely turned off the **Scheduled tasks (Was Routines)** toggle at [**Organization settings > Capabilities > Remote sessions**](https://claude.ai/admin-settings/capabilities). On Claude Code v2.1.227 or later, the same toggle also hides `/schedule` in the CLI. This is a server-side organization setting, so it cannot be overridden from your local configuration. Ask an Owner to turn scheduled tasks back on for your organization.
481457 

sandbox-environments Changed · +11 / -11 lines

from line 6
66 
77Claude Code can run in several kinds of isolated environments, ranging from a lightweight per-command sandbox to a fully separate virtual machine. This page compares them by what they isolate and what they require, helps you choose one for your threat model, and shows how to enforce that choice across an organization.
88 
9<Info>
10 For the broader security model, see [Security](/docs/en/security). For Agent SDK deployments, see [Secure deployment](/docs/en/agent-sdk/secure-deployment).
11</Info>
9> [!NOTE]
10>
11> For the broader security model, see [Security](/docs/en/security). For Agent SDK deployments, see [Secure deployment](/docs/en/agent-sdk/secure-deployment).
1212 
1313## Compare sandboxing approaches
1414 
from line 25
2525 
2626The [sandboxed Bash tool](/docs/en/sandboxing) is built into Claude Code and restricts Bash commands. Built-in file tools, MCP servers, and hooks still run directly on your host. Every other approach in the table puts the whole Claude Code process inside the isolation boundary, so file tools, MCP servers, and hooks are restricted too.
2727 
28<Warning>
29 Sandbox isolation reduces the impact of a breach, but it does not eliminate risk. Any approach that allows network egress can still leak data the agent can read, and any approach that mounts your project directory writable can still modify that code. Review the [security limitations](/docs/en/sandboxing#security-limitations) before relying on a sandbox as a hard control.
28> [!WARNING]
29>
30> Sandbox isolation reduces the impact of a breach, but it does not eliminate risk. Any approach that allows network egress can still leak data the agent can read, and any approach that mounts your project directory writable can still modify that code. Review the [security limitations](/docs/en/sandboxing#security-limitations) before relying on a sandbox as a hard control.
31>
32> Isolation also does not change what is sent to the model. Your prompts and the files Claude reads are transmitted to the Anthropic API or your configured provider with or without a sandbox. See [Data usage](/docs/en/data-usage) for what Claude Code sends and how to reduce it.
3033 
31 Isolation also does not change what is sent to the model. Your prompts and the files Claude reads are transmitted to the Anthropic API or your configured provider with or without a sandbox. See [Data usage](/docs/en/data-usage) for what Claude Code sends and how to reduce it.
32</Warning>
33 
3434## Choose an approach
3535 
3636Match your goal to a row below, then read the detail section that follows.
from line 60
6060 
6161## Sandboxed Bash tool
6262 
63<Note>
64 This option does not support native Windows. On Windows hosts, use WSL2 or one of the container or VM approaches below.
65</Note>
63> [!NOTE]
64>
65> This option does not support native Windows. On Windows hosts, use WSL2 or one of the container or VM approaches below.
6666 
6767The sandboxed Bash tool is built into Claude Code. It uses operating system primitives to restrict the filesystem and network access of Bash, PowerShell, and Monitor tool commands Claude runs.
6868 

sandboxing Changed · +106 / -112 lines

from line 8
88 
99The sandbox runs on macOS, Linux, and WSL2. On native Windows, Claude Code runs commands unsandboxed. To use the sandbox on a Windows machine, run Claude Code inside a WSL2 distribution.
1010 
11<Note>
12 This page covers the sandbox around shell commands on your own machine. Other pages cover related questions:
11> [!NOTE]
12>
13> This page covers the sandbox around shell commands on your own machine. Other pages cover related questions:
14>
15> * For how a cloud session is isolated, see [Security and isolation](/docs/en/claude-code-on-the-web#security-and-isolation)
16> * To compare other isolation approaches such as dev containers, custom containers, and virtual machines, see [Sandbox environments](/docs/en/sandbox-environments)
17> * To reduce permission prompts for tools other than Bash, see [permission modes](/docs/en/permission-modes)
1318 
14 * For how a cloud session is isolated, see [Security and isolation](/docs/en/claude-code-on-the-web#security-and-isolation)
15 * To compare other isolation approaches such as dev containers, custom containers, and virtual machines, see [Sandbox environments](/docs/en/sandbox-environments)
16 * To reduce permission prompts for tools other than Bash, see [permission modes](/docs/en/permission-modes)
17</Note>
18 
1919## What the sandbox restricts
2020 
2121While the sandbox is on, the shell commands Claude runs start inside its boundary, and so do the processes they start. The sandbox is off by default. To turn it on, run `/sandbox` in a session, as [Get started](#get-started) shows, or set [`sandbox.enabled`](/docs/en/settings-reference#sandbox-enabled) to `true` in a [settings file](/docs/en/settings) such as `~/.claude/settings.json`.
from line 53
5353* **macOS**: sandboxing uses the built-in Seatbelt framework, so you can go straight to the steps
5454* **Linux and WSL2**: the sandbox relies on `bubblewrap` and `socat`, covered in [Set up Linux and WSL2](#set-up-linux-and-wsl2). Even if you haven't installed them yet, you can start with `/sandbox`, because its panel shows whether anything is missing
5555 
56<Steps>
57 <Step title="Run /sandbox">
58 Start a Claude Code session and run the `/sandbox` command:
56**1. Run /sandbox**
5957 
60 ```text theme={null}
61 /sandbox
62 ```
58Start a Claude Code session and run the `/sandbox` command:
6359 
64 This opens the sandbox panel with three tabs, plus a Dependencies tab on Linux when the optional seccomp filter is missing:
60```text theme={null}
61/sandbox
62```
6563 
66 * **Mode**: choose how sandboxed commands are approved, covered in the next step
67 * **Overrides**: choose whether commands that fail under the sandbox can fall back to running unsandboxed. This is the [`allowUnsandboxedCommands`](/docs/en/settings-reference#sandbox-allowunsandboxedcommands) setting
68 * **Config**: view the resolved sandbox settings
64This opens the sandbox panel with three tabs, plus a Dependencies tab on Linux when the optional seccomp filter is missing:
6965 
70 If the panel shows only a Dependencies tab, a required package is missing. Install it as described in [Set up Linux and WSL2](#set-up-linux-and-wsl2), restart Claude Code, and run `/sandbox` again.
71 </Step>
66* **Mode**: choose how sandboxed commands are approved, covered in the next step
67* **Overrides**: choose whether commands that fail under the sandbox can fall back to running unsandboxed. This is the [`allowUnsandboxedCommands`](/docs/en/settings-reference#sandbox-allowunsandboxedcommands) setting
68* **Config**: view the resolved sandbox settings
7269 
73 <Step title="Choose a mode">
74 On the Mode tab, select auto-allow or regular permissions. Auto-allow runs sandboxed commands without prompting, and regular permissions keeps the regular permission prompts even when commands are sandboxed. See [Sandbox modes](#sandbox-modes) for which commands still prompt in auto-allow mode.
75 </Step>
70If the panel shows only a Dependencies tab, a required package is missing. Install it as described in [Set up Linux and WSL2](#set-up-linux-and-wsl2), restart Claude Code, and run `/sandbox` again.
7671 
77 <Step title="Run a Bash command">
78 Ask Claude to run a command, such as a build or a test suite. By default, commands inside the sandbox can write to the working directory, a [per-user temp directory](/docs/en/env-vars), and any [directories you've added](/docs/en/permissions#additional-directories-grant-file-access-not-configuration) with `--add-dir`, `/add-dir`, or `permissions.additionalDirectories`.
72**2. Choose a mode**
7973 
80 The first time a command needs a new network domain, Claude Code prompts for approval; in [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode), Claude instead names the hosts a command needs [on the command itself](#per-command-allowed-domains-in-auto-mode) for the classifier to review with it.
74On the Mode tab, select auto-allow or regular permissions. Auto-allow runs sandboxed commands without prompting, and regular permissions keeps the regular permission prompts even when commands are sandboxed. See [Sandbox modes](#sandbox-modes) for which commands still prompt in auto-allow mode.
8175 
82 To widen or narrow what the sandbox allows, see [Configure sandboxing](#configure-sandboxing).
76**3. Run a Bash command**
8377 
84 If sandboxed commands fail with `Operation not permitted` inside a container, see [Bubblewrap fails to start inside a container](#bubblewrap-fails-to-start-inside-a-container).
85 </Step>
86</Steps>
78Ask Claude to run a command, such as a build or a test suite. By default, commands inside the sandbox can write to the working directory, a [per-user temp directory](/docs/en/env-vars), and any [directories you've added](/docs/en/permissions#additional-directories-grant-file-access-not-configuration) with `--add-dir`, `/add-dir`, or `permissions.additionalDirectories`.
8779 
80The first time a command needs a new network domain, Claude Code prompts for approval; in [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode), Claude instead names the hosts a command needs [on the command itself](#per-command-allowed-domains-in-auto-mode) for the classifier to review with it.
81 
82To widen or narrow what the sandbox allows, see [Configure sandboxing](#configure-sandboxing).
83 
84If sandboxed commands fail with `Operation not permitted` inside a container, see [Bubblewrap fails to start inside a container](#bubblewrap-fails-to-start-inside-a-container).
85 
8886When you select a mode in the panel, Claude Code saves it to your project's local settings at `.claude/settings.local.json`, which apply to the current project. Claude Code adds that file to your global gitignore when it saves a setting there. To enable the sandbox across all of your projects, set [`sandbox.enabled`](/docs/en/settings-reference#sandbox-enabled) to `true` in your user settings at `~/.claude/settings.json`. To enforce sandboxing for every developer in an organization, use [managed settings](#enforce-sandboxing-with-managed-settings).
8987 
9088To change the sandbox for one session without writing to a settings file, start Claude Code with [`--settings`](/docs/en/settings#change-a-setting-for-one-session). For example, this command starts a sandboxed session in which Claude can't retry a blocked command outside the sandbox:
from line 91
9391claude --settings '{"sandbox": {"enabled": true, "allowUnsandboxedCommands": false}}'
9492```
9593 
96<Warning>
97 By default, if the sandbox can't start because a dependency is missing or the platform is unsupported, Claude Code runs commands without sandboxing. To make Claude Code exit at startup instead, set [`sandbox.failIfUnavailable`](/docs/en/settings-reference#sandbox-failifunavailable) to `true`. Managed deployments that require sandboxing as a security gate can use this setting.
98</Warning>
94> [!WARNING]
95>
96> By default, if the sandbox can't start because a dependency is missing or the platform is unsupported, Claude Code runs commands without sandboxing. To make Claude Code exit at startup instead, set [`sandbox.failIfUnavailable`](/docs/en/settings-reference#sandbox-failifunavailable) to `true`. Managed deployments that require sandboxing as a security gate can use this setting.
9997 
10098### Confirm commands run inside the sandbox
10199 
from line 115
117115 
118116Install them with your distribution's package manager:
119117 
120<Tabs>
121 <Tab title="Ubuntu/Debian">
122 ```bash theme={null}
123 sudo apt-get install bubblewrap socat
124 ```
125 </Tab>
118**Ubuntu/Debian**
126119 
127 <Tab title="Fedora">
128 ```bash theme={null}
129 sudo dnf install bubblewrap socat
130 ```
131 </Tab>
132</Tabs>
120```bash theme={null}
121sudo apt-get install bubblewrap socat
122```
133123 
124**Fedora**
125 
126```bash theme={null}
127sudo dnf install bubblewrap socat
128```
129 
134130When a dependency is missing, the Dependencies tab in `/sandbox` lists which of `ripgrep`, `bubblewrap`, `socat`, and the seccomp filter your platform lacks. If you don't see the tab after installing and restarting Claude Code, all dependencies are present.
135131 
136132Ripgrep is bundled with the native Claude Code binary. The seccomp filter is optional and adds Unix domain socket blocking. Install it with `npm install -g @anthropic-ai/sandbox-runtime` if it is missing.
from line 133
137133 
138134When a required dependency is missing, the Dependencies tab is the only tab shown until you install it. When only the optional seccomp filter is missing, the Dependencies tab appears alongside the other tabs. The dependency check runs at startup, so restart Claude Code after installing packages for `/sandbox` to detect them.
139135 
140<AccordionGroup>
141 <Accordion title="Ubuntu 24.04 and later: allow bubblewrap to create user namespaces">
142 On Ubuntu 24.04 and later, the default AppArmor policy prevents bubblewrap from creating the user namespaces it needs for isolation.
136> **Ubuntu 24.04 and later: allow bubblewrap to create user namespaces**
137>
138> On Ubuntu 24.04 and later, the default AppArmor policy prevents bubblewrap from creating the user namespaces it needs for isolation.
139>
140> To check whether your environment enforces this restriction, including inside WSL2, run `sysctl kernel.apparmor_restrict_unprivileged_userns`. If the command returns `0`, skip this step. If it prints a `No such file or directory` error, the key doesn't exist and you can skip this step. If it returns `1`, add an AppArmor profile that grants `bwrap` this capability:
141>
142> ```bash theme={null}
143> sudo tee /etc/apparmor.d/bwrap > /dev/null <<'EOF'
144> abi <abi/4.0>,
145> include <tunables/global>
146>
147> profile bwrap /usr/bin/bwrap flags=(unconfined) {
148> userns,
149> include if exists <local/bwrap>
150> }
151> EOF
152> ```
153>
154> The profile applies only to `bwrap` itself, not to the commands it runs inside the sandbox. Reload AppArmor to apply it:
155>
156> ```bash theme={null}
157> sudo systemctl reload apparmor
158> ```
143159 
144 To check whether your environment enforces this restriction, including inside WSL2, run `sysctl kernel.apparmor_restrict_unprivileged_userns`. If the command returns `0`, skip this step. If it prints a `No such file or directory` error, the key doesn't exist and you can skip this step. If it returns `1`, add an AppArmor profile that grants `bwrap` this capability:
160> **WSL2 notes**
161>
162> Check your WSL version with `wsl -l -v` from PowerShell. If you see `Sandboxing requires WSL2`, your distribution is running WSL1. Upgrade it to WSL2 or run Claude Code without sandboxing.
163>
164> On WSL2, WSL hands a launch of a Windows binary such as `cmd.exe`, `powershell.exe`, or anything under `/mnt/c/` to the Windows host over a Unix socket, so whether a sandboxed command can launch one follows the sandbox's [Unix-socket settings](/docs/en/settings-reference#sandbox-network-allowunixsockets): the optional seccomp filter has to be installed to block the socket in the first place. To allow these launches, set `allowAllUnixSockets`, which opens every Unix socket to sandboxed commands.
145165 
146 ```bash theme={null}
147 sudo tee /etc/apparmor.d/bwrap > /dev/null <<'EOF'
148 abi <abi/4.0>,
149 include <tunables/global>
150 
151 profile bwrap /usr/bin/bwrap flags=(unconfined) {
152 userns,
153 include if exists <local/bwrap>
154 }
155 EOF
156 ```
157 
158 The profile applies only to `bwrap` itself, not to the commands it runs inside the sandbox. Reload AppArmor to apply it:
159 
160 ```bash theme={null}
161 sudo systemctl reload apparmor
162 ```
163 </Accordion>
164 
165 <Accordion title="WSL2 notes">
166 Check your WSL version with `wsl -l -v` from PowerShell. If you see `Sandboxing requires WSL2`, your distribution is running WSL1. Upgrade it to WSL2 or run Claude Code without sandboxing.
167 
168 On WSL2, WSL hands a launch of a Windows binary such as `cmd.exe`, `powershell.exe`, or anything under `/mnt/c/` to the Windows host over a Unix socket, so whether a sandboxed command can launch one follows the sandbox's [Unix-socket settings](/docs/en/settings-reference#sandbox-network-allowunixsockets): the optional seccomp filter has to be installed to block the socket in the first place. To allow these launches, set `allowAllUnixSockets`, which opens every Unix socket to sandboxed commands.
169 </Accordion>
170</AccordionGroup>
171 
172166### Sandbox modes
173167 
174168Claude Code offers two sandbox modes. In both, the sandbox enforces the same filesystem and network restrictions; the difference is only in whether sandboxed commands are auto-approved or require explicit permission.
from line 191
197191 
198192An allow rule that matches a command also approves an [unsandboxed retry](#the-unsandboxed-retry-escape-hatch) of it, so the command can run outside the sandbox with no prompt.
199193 
200<Info>
201 Auto-allow mode works independently of your permission mode setting, with three exceptions: [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode), an auto mode command that carries [per-command allowed domains](#per-command-allowed-domains-in-auto-mode), and [server-side classifier review](/docs/en/permission-modes#how-the-classifier-evaluates-actions) of sandboxed commands in auto mode. Even if you're not in "accept edits" mode, sandboxed Bash commands run automatically when auto-allow is enabled. This means Bash commands that modify files within the sandbox boundaries execute without prompting, even in Manual mode, where the file edit tools would prompt.
194> [!NOTE]
195>
196> Auto-allow mode works independently of your permission mode setting, with three exceptions: [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode), an auto mode command that carries [per-command allowed domains](#per-command-allowed-domains-in-auto-mode), and [server-side classifier review](/docs/en/permission-modes#how-the-classifier-evaluates-actions) of sandboxed commands in auto mode. Even if you're not in "accept edits" mode, sandboxed Bash commands run automatically when auto-allow is enabled. This means Bash commands that modify files within the sandbox boundaries execute without prompting, even in Manual mode, where the file edit tools would prompt.
197>
198> In plan mode, auto-allow doesn't widen approvals; see [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode) for how Claude Code gates commands while you plan.
202199 
203 In plan mode, auto-allow doesn't widen approvals; see [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode) for how Claude Code gates commands while you plan.
204</Info>
205 
206200#### Regular permissions mode
207201 
208202All Bash commands go through the regular permission flow, even when sandboxed. This provides more control but requires more approvals.
from line 324
330324 
331325To confirm an entry matches, switch to Manual mode and ask Claude to run a matching command that changes something, such as `docker compose up -d`. The permission prompt is titled "Bash command (unsandboxed)".
332326 
333<Warning>
334 An excluded command runs with your full access. A broad entry such as `docker *` covers everything that tool can do. If you write a pattern that covers an interpreter, a script inside your working directory, or a tool that acts on a file there, as `docker compose` does with its compose file, Claude can write that file and then run it outside the sandbox. A narrower pattern leaves less that Claude can run outside the sandbox.
335</Warning>
327> [!WARNING]
328>
329> An excluded command runs with your full access. A broad entry such as `docker *` covers everything that tool can do. If you write a pattern that covers an interpreter, a script inside your working directory, or a tool that acts on a file there, as `docker compose` does with its compose file, Claude can write that file and then run it outside the sandbox. A narrower pattern leaves less that Claude can run outside the sandbox.
336330 
337331### Disable filesystem isolation
338332 
from line 350
356350 
357351`sandbox.filesystem.disabled` defaults to `false`. Requires Claude Code v2.1.216 or later.
358352 
359<Warning>
360 With filesystem isolation off and commands auto-allowed, a sandboxed command can write files that later commands run or read, such as shell startup files, executables on `$PATH`, or `~/.claude/settings.json`, and use them to widen its own access on the next run. Set `filesystem.disabled` to `true` only for workloads you trust not to escalate their own access. Locking network domains with [`allowManagedDomainsOnly`](#keep-developers-from-widening-the-policy) narrows the risk but doesn't remove it, since that lock applies only to commands running inside the sandbox.
361</Warning>
353> [!WARNING]
354>
355> With filesystem isolation off and commands auto-allowed, a sandboxed command can write files that later commands run or read, such as shell startup files, executables on `$PATH`, or `~/.claude/settings.json`, and use them to widen its own access on the next run. Set `filesystem.disabled` to `true` only for workloads you trust not to escalate their own access. Locking network domains with [`allowManagedDomainsOnly`](#keep-developers-from-widening-the-policy) narrows the risk but doesn't remove it, since that lock applies only to commands running inside the sandbox.
362356 
363357#### Which settings can disable it
364358 
from line 458
464458 
465459Masking replaces the whole value by default. For a value with structure, such as a `DATABASE_URL` connection string or a JWT, use the [`extract`, `decode`, `maskClaims`, and `onExtractNoMatch` fields](/docs/en/settings-reference#sandbox-credentials-envvars) so tools that parse the value keep working.
466460 
467<span id="ipv6-destinations-in-injecthosts" />For an IPv6 destination, spell the address differently in the two lists:
461For an IPv6 destination, spell the address differently in the two lists:
468462 
469463* **`network.allowedDomains`**: the bracketed form, such as `"[::1]"`
470464* **`injectHosts`**: the bare address in its canonical compressed form, such as `"::1"`
from line 508
514508 
515509Without `extract` or `decode`, Claude Code replaces the entire file with one sentinel, which suits a file holding a single bare secret. Use the [`extract`, `decode`, `maskClaims`, `onExtractNoMatch`, and `maskDuplicates` fields](/docs/en/settings-reference#sandbox-credentials-files) to control partial masking and what happens when the pattern matches nothing.
516510 
517<Warning>
518 When matching finds nothing to mask, the default `onExtractNoMatch` value, `warn`, skips the entry, so sandboxed commands can read the real file unmasked. On macOS, Claude Code applies `mask` entries as `deny` before the pattern runs whenever filesystem isolation is on, so the no-match outcomes take effect there only when [filesystem isolation is off](#disable-filesystem-isolation). The default suits credentials that may be legitimately absent. If the secret might be present but the pattern might miss it, use [`deny`](/docs/en/settings-reference#mask-fields-for-files).
519</Warning>
511> [!WARNING]
512>
513> When matching finds nothing to mask, the default `onExtractNoMatch` value, `warn`, skips the entry, so sandboxed commands can read the real file unmasked. On macOS, Claude Code applies `mask` entries as `deny` before the pattern runs whenever filesystem isolation is on, so the no-match outcomes take effect there only when [filesystem isolation is off](#disable-filesystem-isolation). The default suits credentials that may be legitimately absent. If the secret might be present but the pattern might miss it, use [`deny`](/docs/en/settings-reference#mask-fields-for-files).
520514 
521515`mask` applies to a single file, so list each credential file individually. Claude Code falls back to `deny` for a `mask` entry it can't mask safely: a directory path, a glob pattern, a file larger than 8 MiB, or a file that isn't UTF-8 text.
522516 
from line 568
574568 
575569In a `WebFetch(domain:...)` rule, the sandbox honors two wildcard forms: a leading `*.`, such as `*.example.com`, and a bare `*`. The bare `*` form requires Claude Code v2.1.186 or later. A wildcard in any other position, such as `WebFetch(domain:example.*)`, still matches fetches but has no effect on sandboxed commands.
576570 
577<Note>
578 The built-in proxy enforces the allowlist based on the requested hostname and, by default, does not terminate or inspect TLS traffic. The experimental [`network.tlsTerminate`](/docs/en/settings-reference#sandbox-network-tlsterminate) setting makes the built-in proxy terminate TLS itself, which [`mask` credential entries](#mask-credentials) require. See [Security limitations](#security-limitations) for the implications of the default, and [Custom proxy configuration](#custom-proxy-configuration) if your threat model requires TLS inspection.
579</Note>
571> [!NOTE]
572>
573> The built-in proxy enforces the allowlist based on the requested hostname and, by default, does not terminate or inspect TLS traffic. The experimental [`network.tlsTerminate`](/docs/en/settings-reference#sandbox-network-tlsterminate) setting makes the built-in proxy terminate TLS itself, which [`mask` credential entries](#mask-credentials) require. See [Security limitations](#security-limitations) for the implications of the default, and [Custom proxy configuration](#custom-proxy-configuration) if your threat model requires TLS inspection.
580574 
581575#### Hosts outside your allowed domains
582576 
from line 794
800794 
801795Claude Code ignores a port set anywhere else. Before v2.1.285, any settings file could set a port.
802796 
803<Warning>
804 Once either port applies, your proxy is responsible for filtering everything sent to it. Claude Code's own network controls, such as `allowedDomains`, `deniedDomains`, `strictAllowlist`, approval prompts, and the [local-address check](#hostnames-that-resolve-to-local-addresses), stop applying to that traffic. A sandboxed command can connect to either proxy, so if you set only one port, Claude Code's domain lists on the other proxy don't limit what the command reaches through yours.
805</Warning>
797> [!WARNING]
798>
799> Once either port applies, your proxy is responsible for filtering everything sent to it. Claude Code's own network controls, such as `allowedDomains`, `deniedDomains`, `strictAllowlist`, approval prompts, and the [local-address check](#hostnames-that-resolve-to-local-addresses), stop applying to that traffic. A sandboxed command can connect to either proxy, so if you set only one port, Claude Code's domain lists on the other proxy don't limit what the command reaches through yours.
806800 
807801## Troubleshooting
808802 
from line 982
988982 
989983* **Network filtering**: the sandbox restricts which domains processes can connect to. By default the built-in proxy does not terminate or inspect TLS on outbound traffic, so the contents of encrypted connections are not examined. The experimental [`network.tlsTerminate`](/docs/en/settings-reference#sandbox-network-tlsterminate) setting terminates TLS at the proxy for [`mask` credential substitution](#mask-credentials) but does not add content filtering. You are responsible for ensuring that only trusted domains are allowed in your policy.
990984 
991<Warning>
992 Allowing broad domains such as `github.com` can create paths for data exfiltration. Because the proxy makes its allow decision from the client-supplied hostname without inspecting TLS, code running inside the sandbox can potentially use [domain fronting](https://en.wikipedia.org/wiki/Domain_fronting) or similar techniques to reach hosts outside the allowlist. If your threat model requires stronger guarantees, configure a [custom proxy](#custom-proxy-configuration) that terminates TLS and inspects traffic, and install its CA certificate inside the sandbox. Stronger TLS-aware network isolation is an active area of development.
993</Warning>
985> [!WARNING]
986>
987> Allowing broad domains such as `github.com` can create paths for data exfiltration. Because the proxy makes its allow decision from the client-supplied hostname without inspecting TLS, code running inside the sandbox can potentially use [domain fronting](https://en.wikipedia.org/wiki/Domain_fronting) or similar techniques to reach hosts outside the allowlist. If your threat model requires stronger guarantees, configure a [custom proxy](#custom-proxy-configuration) that terminates TLS and inspects traffic, and install its CA certificate inside the sandbox. Stronger TLS-aware network isolation is an active area of development.
994988 
995989* **Privilege escalation via Unix sockets**: the `allowUnixSockets` configuration can inadvertently grant access to system services that could lead to sandbox bypasses. For example, allowing access to `/var/run/docker.sock` effectively grants access to the host system through the Docker socket. Consider carefully any Unix sockets that you allow through the sandbox.
996990* **Filesystem permission escalation**: overly broad filesystem write permissions can enable privilege escalation attacks. Allowing writes to directories containing executables in `$PATH`, system configuration directories, or user shell configuration files such as `.bashrc` or `.zshrc` can lead to code execution in different security contexts when other users or system processes access these files.
from line 1001
10071001* **A boundary around the whole process**: to put the processes in [What runs outside the sandbox](#what-runs-outside-the-sandbox) behind a boundary too, run Claude Code (local mode) inside the [sandbox runtime](/docs/en/sandbox-environments#sandbox-runtime) with a network allowlist limited to the hosts you approve, or in the [dev container](/docs/en/devcontainer) with its firewall script. For the background service and the sessions it hosts, see [Run Claude Code behind a corporate launcher](/docs/en/corporate-launcher).
10081002* **Mods**: a [mod](/docs/en/plugins/mods/overview) is a plugin that runs its own code inside Claude Code, and a process that a mod starts runs outside the sandbox. See [What a mod can reach](/docs/en/plugins/mods/overview#what-a-mod-can-reach).
10091003 
1010<Warning>
1011 Effective sandboxing requires both filesystem and network isolation. Without network isolation, a compromised agent could exfiltrate sensitive files like SSH keys. Without filesystem isolation, whether from a permissive policy or from [disabling the filesystem layer](#disable-filesystem-isolation), a compromised agent could backdoor system resources to gain network access. When you widen the defaults, check that an `allowWrite` path, a broad `allowedDomains` entry, or an `excludedCommands` exception does not undo a restriction on the other side.
1012</Warning>
1004> [!WARNING]
1005>
1006> Effective sandboxing requires both filesystem and network isolation. Without network isolation, a compromised agent could exfiltrate sensitive files like SSH keys. Without filesystem isolation, whether from a permissive policy or from [disabling the filesystem layer](#disable-filesystem-isolation), a compromised agent could backdoor system resources to gain network access. When you widen the defaults, check that an `allowWrite` path, a broad `allowedDomains` entry, or an `excludedCommands` exception does not undo a restriction on the other side.
10131007 
10141008## See also
10151009 

scheduled-tasks Changed · +12 / -20 lines

from line 4
44 
55Session-scoped scheduled tasks let Claude re-run a prompt automatically on an interval within a Claude Code session. Use them to poll a deployment, babysit a PR, check back on a long-running build, or remind yourself to do something later in the session.
66 
7<Note>
8 These cases are covered on other pages:
7> [!NOTE]
8>
9> These cases are covered on other pages:
10>
11> * **Reacting to events as they happen instead of polling**: see [Channels](/docs/en/channels). Your CI can push the failure into the session directly.
12> * **Keeping the session working turn after turn toward a condition rather than on an interval**: see [`/goal`](/docs/en/goal).
913 
10 * **Reacting to events as they happen instead of polling**: see [Channels](/docs/en/channels). Your CI can push the failure into the session directly.
11 * **Keeping the session working turn after turn toward a condition rather than on an interval**: see [`/goal`](/docs/en/goal).
12</Note>
13 
1414Tasks are session-scoped. When you resume with `--resume` or `--continue`, Claude Code restores tasks that haven't [expired](#seven-day-expiry), except those listed under [Limitations](#limitations). For scheduling that survives independently of any session, create a [cloud scheduled task](/docs/en/routines), set up a [Desktop scheduled task](/docs/en/desktop-scheduled-tasks), or use [GitHub Actions](/docs/en/github-actions).
1515 
1616## Compare scheduling options
from line 29
2929| Customizable schedule | Via `/schedule` in the CLI | Yes | Yes |
3030| Minimum interval | 1 hour | 1 minute | 1 minute |
3131 
32<Tip>
33 Use **cloud tasks** for work that should run reliably without your machine. Use **Desktop tasks** when you need access to local files and tools. Use **`/loop`** for quick polling during a session.
34</Tip>
32> [!TIP]
33>
34> Use **cloud tasks** for work that should run reliably without your machine. Use **Desktop tasks** when you need access to local files and tools. Use **`/loop`** for quick polling during a session.
3535 
3636## Run a prompt repeatedly with /loop
3737 
from line 76
7676 
7777A dynamically scheduled loop appears in your [scheduled task list](#manage-scheduled-tasks) like any other task, so you can list or cancel it the same way. The list shows its pending wakeup as a one-shot task, so a wakeup scheduled for `:00` or `:30` can [run a little early](#when-a-one-shot-task-runs-early). The [seven-day expiry](#seven-day-expiry) applies to the loop too.
7878 
79<span id="loop-provider-differences" />
79> [!NOTE]
80>
81> Dynamically chosen intervals and the [built-in maintenance prompt](#run-the-built-in-maintenance-prompt) work on every provider, and with [feature-flag fetching](/docs/en/env-vars#features-that-need-feature-flag-fetching) turned off. On Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, and Microsoft Foundry, or with fetching turned off, both require Claude Code v2.1.248 or later.
8082 
81<Note>
82 Dynamically chosen intervals and the [built-in maintenance prompt](#run-the-built-in-maintenance-prompt) work on every provider, and with [feature-flag fetching](/docs/en/env-vars#features-that-need-feature-flag-fetching) turned off. On Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, and Microsoft Foundry, or with fetching turned off, both require Claude Code v2.1.248 or later.
83</Note>
84 
8583### Run the built-in maintenance prompt
8684 
8785When you omit the prompt, Claude uses a built-in maintenance prompt instead of one you supply. On each iteration it works through the following, in order:
from line 140
142140 
143141Claude pins the fire time to a specific minute and hour using a cron expression and confirms when it will fire.
144142 
145<span id="manage-scheduled-tasks" />
146 
147143## Manage session-scoped scheduled tasks
148144 
149145Ask Claude in natural language to list or cancel tasks, or reference the underlying tools directly.
from line 162
166162 
167163Each scheduled task has an 8-character ID you can pass to `CronDelete`. A session can hold up to 50 scheduled tasks at once.
168164 
169<span id="how-scheduled-tasks-run" />
170 
171165## How session-scoped scheduled tasks run
172166 
173167The scheduler checks every second for due tasks and enqueues them at low priority. A scheduled prompt fires between your turns, not while Claude is mid-response. If Claude is busy when a task comes due, the prompt waits until the current turn ends.
from line 217
223217Day-of-week uses `0` or `7` for Sunday through `6` for Saturday. Extended syntax like `L`, `W`, `?`, and name aliases such as `MON` or `JAN` is not supported.
224218 
225219When both day-of-month and day-of-week are constrained, a date matches if either field matches. This follows standard vixie-cron semantics.
226 
227<span id="disable-scheduled-tasks" />
228220 
229221## Disable session-scoped scheduled tasks
230222 

security Changed · +8 / -8 lines

from line 62
6262* **Fail-closed matching**: In Manual mode, unmatched commands require approval by default
6363* **Secure credential storage**: API keys and tokens are stored in the macOS Keychain when available. On Linux they're stored in a file with mode `0600`, and on Windows in a file that inherits the access controls of your user profile directory. See [Credential Management](/docs/en/authentication#credential-management)
6464 
65<Warning>
66 **Windows WebDAV security risk**: When running Claude Code on Windows, we recommend against enabling WebDAV or allowing Claude Code to access paths such as `\\*` that may contain WebDAV subdirectories. [WebDAV has been deprecated by Microsoft](https://learn.microsoft.com/en-us/windows/whats-new/deprecated-features#:~:text=The%20Webclient%20\(WebDAV\)%20service%20is%20deprecated) due to security risks. Enabling WebDAV may allow Claude Code to trigger network requests to remote hosts, bypassing the permission system.
67</Warning>
65> [!WARNING]
66>
67> **Windows WebDAV security risk**: When running Claude Code on Windows, we recommend against enabling WebDAV or allowing Claude Code to access paths such as `\\*` that may contain WebDAV subdirectories. [WebDAV has been deprecated by Microsoft](https://learn.microsoft.com/en-us/windows/whats-new/deprecated-features#:~:text=The%20Webclient%20\(WebDAV\)%20service%20is%20deprecated) due to security risks. Enabling WebDAV may allow Claude Code to trigger network requests to remote hosts, bypassing the permission system.
6868 
6969**Best practices for working with untrusted content**:
7070 
from line 74
74744. Use virtual machines (VMs) to run scripts and make tool calls, especially when interacting with external web services
75755. Report suspicious behavior with `/feedback`
7676 
77<Warning>
78 While these protections significantly reduce risk, no system is completely
79 immune to all attacks. Always maintain good security practices when working
80 with any AI tool.
81</Warning>
77> [!WARNING]
78>
79> While these protections significantly reduce risk, no system is completely
80> immune to all attacks. Always maintain good security practices when working
81> with any AI tool.
8282 
8383## MCP security
8484 

security-guidance Changed · +0 / -6 lines

from line 62
6262 
6363You can extend each layer by [adding your own rules](#add-your-own-rules). Built-in checks cannot be removed individually, but you can [disable each layer](#disable-or-uninstall) independently.
6464 
65<span id="on-each-file-edit" />
66 
6765### Checks on each file edit
6866 
6967When Claude writes to a file, the plugin scans the new content for known risky patterns. This is a pattern match with no model call, so it adds no usage cost.
from line 77
7977 
8078You can [add your own patterns](#add-custom-per-edit-patterns) to this layer with a `security-patterns.yaml` file.
8179 
82<span id="at-the-end-of-each-turn" />
83 
8480### Checks at the end of each turn
8581 
8682A turn is one round of Claude responding: you send a message, Claude works and replies, and the turn ends. After each turn, the plugin computes a git diff of everything that changed in the working tree during the turn, including changes from Claude's edit tools, Bash commands, and subagents, and sends it to a separate Claude review focused on security. The review runs in the background, so Claude's reply is not delayed. If the review finds issues, Claude is re-prompted with the findings and addresses them as a follow-up.
from line 90
9490* Weak cryptography
9591 
9692You see both the finding and Claude's resolution directly in your session. The review covers up to 30 changed files per turn and fires at most three times in a row before yielding back to you.
97 
98<span id="on-each-commit-or-push-claude-makes" />
9993 
10094### Checks on each commit or push Claude makes
10195 

self-hosted-environments Changed · +4 / -10 lines

from line 2
22 
33> Run Claude Code cloud sessions on infrastructure you control: set up a self-hosted environment, deploy runners, and route sessions to your own compute.
44 
5<Note>
6 Self-hosted environments are in public beta on Team and Enterprise plans and are off by default. See [Availability and limitations](#availability-and-limitations) for the enablement path and what's excluded.
7</Note>
5> [!NOTE]
6>
7> Self-hosted environments are in public beta on Team and Enterprise plans and are off by default. See [Availability and limitations](#availability-and-limitations) for the enablement path and what's excluded.
88 
99A self-hosted environment executes Claude Code cloud sessions on infrastructure your organization operates. A [cloud session](/docs/en/claude-code-on-the-web) is any session that runs somewhere other than the developer's machine. Developers start them from claude.ai, the mobile and desktop apps, the terminal with [`claude --cloud`](/docs/en/claude-code-on-the-web#from-terminal-to-cloud), and [cloud scheduled tasks](/docs/en/routines). By default, cloud sessions execute on Anthropic's infrastructure.
1010 
from line 22
2222 
2323When a developer starts a cloud session, the session-start UI shows an environment picker listing Anthropic-hosted environments alongside any your organization has created. If they choose yours, Anthropic's control plane places the session on your environment's queue, where a runner claims it, clones the repository the developer chose, and starts a Claude Code process on your host to run it. The runner authenticates to your git host with credentials you configure; [Configure git](/docs/en/self-hosted-environments-deploy#configure-git) covers the options. Sessions reach your internal services from inside your network, and your git host the same way when it's internal; the traffic to Anthropic, queue polling, the session's event stream, and by default model inference, is outbound HTTPS to `api.anthropic.com`, with the short list of further hosts sessions can reach in [Network requirements](/docs/en/self-hosted-environments-deploy#network-requirements). Anthropic never connects into your network.
2424 
25<div style={{maxWidth: "640px", margin: "0 auto"}}>
26 <Frame>
27 <img src="https://mintcdn.com/claude-code/Y0sJ2uDoOVbOVZrQ/images/self-hosted-network-paths.svg?fit=max&auto=format&n=Y0sJ2uDoOVbOVZrQ&q=85&s=8056103fc1c5564c7f0ef219d260b99d" className="dark:hidden" alt="Architecture diagram of a self-hosted environment: your network boundary contains a runner, two Claude Code session processes inside it, and your git host, with api.anthropic.com outside holding queue, session stream, and inference. The runner polls the queue and reaches the git host, each session process opens its own stream, inference, and git connections, and every connection is outbound from your network, with none inbound." width="680" height="320" data-path="images/self-hosted-network-paths.svg" />
28 
29 <img src="https://mintcdn.com/claude-code/Y0sJ2uDoOVbOVZrQ/images/self-hosted-network-paths-dark.svg?fit=max&auto=format&n=Y0sJ2uDoOVbOVZrQ&q=85&s=fec6aef3b0740d80eaf6d6a7000a2233" className="hidden dark:block" alt="Architecture diagram of a self-hosted environment: your network boundary contains a runner, two Claude Code session processes inside it, and your git host, with api.anthropic.com outside holding queue, session stream, and inference. The runner polls the queue and reaches the git host, each session process opens its own stream, inference, and git connections, and every connection is outbound from your network, with none inbound." width="680" height="320" data-path="images/self-hosted-network-paths-dark.svg" />
30 </Frame>
31</div>
25![Architecture diagram of a self-hosted environment: your network boundary contains a runner, two Claude Code session processes inside it, and your git host, with api.anthropic.com outside holding queue, session stream, and inference. The runner polls the queue and reaches the git host, each session process opens its own stream, inference, and git connections, and every connection is outbound from your network, with none inbound.](https://mintcdn.com/claude-code/Y0sJ2uDoOVbOVZrQ/images/self-hosted-network-paths.svg?fit=max\&auto=format\&n=Y0sJ2uDoOVbOVZrQ\&q=85\&s=8056103fc1c5564c7f0ef219d260b99d)
3226 
3327The two Claude Code boxes in the diagram are session processes: one runner executing two sessions at once, up to its configured capacity. A runner serves one [owner](#key-concepts) at a time and locks to that owner when it claims its first session, so checked-out code never mixes between owners; [Runner lifecycle](#runner-lifecycle) covers the rule.
3428 

self-hosted-environments-configuration Changed · +72 / -78 lines

from line 2
22 
33> Customize self-hosted environment sessions with wrapper scripts for per-session credentials, lifecycle hooks, and on-demand runner spawning.
44 
5<Note>
6 Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner](/docs/en/cloud-environments#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page assumes a working runner; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy) for the fleet recipes.
7</Note>
5> [!NOTE]
6>
7> Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner](/docs/en/cloud-environments#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page assumes a working runner; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy) for the fleet recipes.
88 
99A [self-hosted environment](/docs/en/self-hosted-environments) runs Claude Code [cloud sessions](/docs/en/claude-code-on-the-web) on your own infrastructure, executed by a runner process you deploy. With no configuration, that runner clones the session's repository, spawns Claude Code, and cleans up. This page is for the platform engineer operating the runners: it covers the extension points for when those defaults don't fit, from per-session credential provisioning to replacing checkout entirely. Wrappers and hooks run as executable files on the runner host, which is Linux or macOS, and the examples on this page assume a POSIX shell.
1010 
from line 358
358358 
359359Sessions are routed to an environment, not to a runner, and a requeued or resumed session can run on a different runner. Configure every runner in the environment the same way. Before you start, read [what differs on these providers](#what-differs-from-sessions-on-the-anthropic-api).
360360 
361<Steps>
362 <Step title="Prepare the cloud account and your egress rules">
363 Set up model access, a narrowly scoped policy or role, and network access:
361**1. Prepare the cloud account and your egress rules**
364362 
365 * **Amazon Bedrock**: [submit use case details](/docs/en/amazon-bedrock#1-submit-use-case-details), then create the policy in [IAM configuration](/docs/en/amazon-bedrock#iam-configuration), limiting `bedrock:InvokeModel` and `bedrock:InvokeModelWithResponseStream` to the inference profiles your sessions use and the foundation models behind them
366 * **Agent Platform**: [enable the API](/docs/en/google-vertex-ai#1-enable-agent-platform-api) and [request model access](/docs/en/google-vertex-ai#2-request-model-access), then create the custom role that [IAM configuration](/docs/en/google-vertex-ai#iam-configuration) describes, with only `aiplatform.endpoints.predict`
367 * **Egress**: allow your provider's endpoints through your egress rules. See [Network requirements](/docs/en/self-hosted-environments-deploy#network-requirements). If sessions can't reach them, Claude Code can keep retrying for hours before the session shows an error.
368 </Step>
363Set up model access, a narrowly scoped policy or role, and network access:
369364 
370 <Step title="Give sessions narrowly scoped credentials">
371 Attach the policy or role from step 1 to an identity that can do nothing else. For the methods Claude Code accepts, see [Configure AWS credentials](/docs/en/amazon-bedrock#2-configure-aws-credentials) and [Configure GCP credentials](/docs/en/google-vertex-ai#3-configure-gcp-credentials).
365* **Amazon Bedrock**: [submit use case details](/docs/en/amazon-bedrock#1-submit-use-case-details), then create the policy in [IAM configuration](/docs/en/amazon-bedrock#iam-configuration), limiting `bedrock:InvokeModel` and `bedrock:InvokeModelWithResponseStream` to the inference profiles your sessions use and the foundation models behind them
366* **Agent Platform**: [enable the API](/docs/en/google-vertex-ai#1-enable-agent-platform-api) and [request model access](/docs/en/google-vertex-ai#2-request-model-access), then create the custom role that [IAM configuration](/docs/en/google-vertex-ai#iam-configuration) describes, with only `aiplatform.endpoints.predict`
367* **Egress**: allow your provider's endpoints through your egress rules. See [Network requirements](/docs/en/self-hosted-environments-deploy#network-requirements). If sessions can't reach them, Claude Code can keep retrying for hours before the session shows an error.
372368 
373 <Warning>
374 Anyone who can get code to run in a session, including through prompt injection, can use these credentials at your cost for as long as the credentials are valid. Claude Code runs inside the session, so the credential it calls the model with has to be readable there.
369**2. Give sessions narrowly scoped credentials**
375370 
376 The shell commands Claude runs, your [Claude Code hooks](/docs/en/hooks), and stdio MCP servers inherit the session's environment and run as the same user as Claude Code. As a result, they can read credential variables and credential files.
371Attach the policy or role from step 1 to an identity that can do nothing else. For the methods Claude Code accepts, see [Configure AWS credentials](/docs/en/amazon-bedrock#2-configure-aws-credentials) and [Configure GCP credentials](/docs/en/google-vertex-ai#3-configure-gcp-credentials).
377372 
378 When you [harden your deployment](/docs/en/self-hosted-environments-deploy#harden-your-deployment), you keep host credentials out of sessions, but you can't keep this credential out. Give the identity behind it nothing beyond the policy or role from step 1.
379 </Warning>
373> [!WARNING]
374>
375> Anyone who can get code to run in a session, including through prompt injection, can use these credentials at your cost for as long as the credentials are valid. Claude Code runs inside the session, so the credential it calls the model with has to be readable there.
376>
377> The shell commands Claude runs, your [Claude Code hooks](/docs/en/hooks), and stdio MCP servers inherit the session's environment and run as the same user as Claude Code. As a result, they can read credential variables and credential files.
378>
379> When you [harden your deployment](/docs/en/self-hosted-environments-deploy#harden-your-deployment), you keep host credentials out of sessions, but you can't keep this credential out. Give the identity behind it nothing beyond the policy or role from step 1.
380380 
381 Check the method you choose against these runner behaviors:
381Check the method you choose against these runner behaviors:
382382 
383 * **Metadata endpoint**: if you deny sessions the [cloud metadata endpoint](/docs/en/self-hosted-environments-deploy#harden-your-deployment) outright, credentials served from it, such as an instance profile, don't reach Claude Code either. A file-based web identity, such as IAM Roles for Service Accounts (IRSA) on Amazon EKS or a Workload Identity Federation credential file, doesn't depend on it.
384 * **Renewal**: a session can outlive a credential, so use a method that renews itself, such as a file-based web identity
385 * **Wrapper script**: the runner starts your [wrapper script](#provision-credentials-scoped-to-the-session-creator) once per session, so credentials it exports aren't renewed. Claude Code reads AWS credentials from its environment, so if your wrapper already exports AWS credentials for other work, Claude Code can sign model requests with them.
386 </Step>
383* **Metadata endpoint**: if you deny sessions the [cloud metadata endpoint](/docs/en/self-hosted-environments-deploy#harden-your-deployment) outright, credentials served from it, such as an instance profile, don't reach Claude Code either. A file-based web identity, such as IAM Roles for Service Accounts (IRSA) on Amazon EKS or a Workload Identity Federation credential file, doesn't depend on it.
384* **Renewal**: a session can outlive a credential, so use a method that renews itself, such as a file-based web identity
385* **Wrapper script**: the runner starts your [wrapper script](#provision-credentials-scoped-to-the-session-creator) once per session, so credentials it exports aren't renewed. Claude Code reads AWS credentials from its environment, so if your wrapper already exports AWS credentials for other work, Claude Code can sign model requests with them.
387386 
388 <Step title="Set one provider's variables in the runner's environment">
389 Set exactly one provider's variables where you set the runner's other environment variables, such as the container spec or service unit, then restart the runner. The examples show them as shell exports. With [on-demand runners](#on-demand-runners), set them on the workload your `spawn-runner` hook starts.
387**3. Set one provider's variables in the runner's environment**
390388 
391 Start these runners with [`--confine-repo-settings enforce`](/docs/en/self-hosted-environments-deploy#harden-your-deployment). It refuses sessions on repositories whose committed settings it flags, so run in the default `warn` mode first and clear what it logs.
389Set exactly one provider's variables where you set the runner's other environment variables, such as the container spec or service unit, then restart the runner. The examples show them as shell exports. With [on-demand runners](#on-demand-runners), set them on the workload your `spawn-runner` hook starts.
392390 
393 <Tabs>
394 <Tab title="Amazon Bedrock">
395 Replace the region with your own:
391Start these runners with [`--confine-repo-settings enforce`](/docs/en/self-hosted-environments-deploy#harden-your-deployment). It refuses sessions on repositories whose committed settings it flags, so run in the default `warn` mode first and clear what it logs.
396392 
397 ```bash theme={null}
398 export CLAUDE_CODE_USE_BEDROCK=1
399 export AWS_REGION=us-east-1
400 ```
393**Amazon Bedrock**
401394 
402 For how Claude Code resolves the region, see [Configure Claude Code](/docs/en/amazon-bedrock#3-configure-claude-code). For which inference profile prefix Claude Code uses for your region, see [Cross-region inference profile prefixes](/docs/en/amazon-bedrock#cross-region-inference-profile-prefixes).
403 </Tab>
395Replace the region with your own:
404396 
405 <Tab title="Google Cloud's Agent Platform">
406 Replace the region and project ID with your own:
397```bash theme={null}
398export CLAUDE_CODE_USE_BEDROCK=1
399export AWS_REGION=us-east-1
400```
407401 
408 ```bash theme={null}
409 export CLAUDE_CODE_USE_VERTEX=1
410 export CLOUD_ML_REGION=global
411 export ANTHROPIC_VERTEX_PROJECT_ID=YOUR-PROJECT-ID
412 ```
402For how Claude Code resolves the region, see [Configure Claude Code](/docs/en/amazon-bedrock#3-configure-claude-code). For which inference profile prefix Claude Code uses for your region, see [Cross-region inference profile prefixes](/docs/en/amazon-bedrock#cross-region-inference-profile-prefixes).
413403 
414 To choose a region, see [Region configuration](/docs/en/google-vertex-ai#region-configuration).
415 </Tab>
416 </Tabs>
417 </Step>
404**Google Cloud's Agent Platform**
418405 
419 <Step title="Check that the variables reached a session">
420 Your own shell on the host is a different process, so check from inside a session. Start one in the environment and ask Claude to run this command:
406Replace the region and project ID with your own:
421407 
422 ```bash theme={null}
423 env | grep -E 'CLAUDE_CODE_USE_(BEDROCK|VERTEX)'
424 ```
408```bash theme={null}
409export CLAUDE_CODE_USE_VERTEX=1
410export CLOUD_ML_REGION=global
411export ANTHROPIC_VERTEX_PROJECT_ID=YOUR-PROJECT-ID
412```
425413 
426 A line that sets `CLAUDE_CODE_USE_BEDROCK` or `CLAUDE_CODE_USE_VERTEX` to `1` means the variable reached the session. If both appear, Claude Code uses Amazon Bedrock. No output means neither reached it.
414To choose a region, see [Region configuration](/docs/en/google-vertex-ai#region-configuration).
427415 
428 The command shows configuration, not traffic. To confirm the requests themselves, look for them in your cloud account's own metrics or request logs. If the first message fails instead, see troubleshooting for [Amazon Bedrock](/docs/en/amazon-bedrock#troubleshooting) or [Agent Platform](/docs/en/google-vertex-ai#troubleshooting).
429 </Step>
430</Steps>
416**4. Check that the variables reached a session**
431417 
418Your own shell on the host is a different process, so check from inside a session. Start one in the environment and ask Claude to run this command:
419 
420```bash theme={null}
421env | grep -E 'CLAUDE_CODE_USE_(BEDROCK|VERTEX)'
422```
423 
424A line that sets `CLAUDE_CODE_USE_BEDROCK` or `CLAUDE_CODE_USE_VERTEX` to `1` means the variable reached the session. If both appear, Claude Code uses Amazon Bedrock. No output means neither reached it.
425 
426The command shows configuration, not traffic. To confirm the requests themselves, look for them in your cloud account's own metrics or request logs. If the first message fails instead, see troubleshooting for [Amazon Bedrock](/docs/en/amazon-bedrock#troubleshooting) or [Agent Platform](/docs/en/google-vertex-ai#troubleshooting).
427 
432428### What differs from sessions on the Anthropic API
433429 
434430A session that sends model requests to Amazon Bedrock or Google Cloud's Agent Platform differs from a session on the Anthropic API in these ways:
from line 462
466462 
467463Sessions inherit the runner's environment, so set [`ENABLE_TOOL_SEARCH`](/docs/en/mcp#scale-with-mcp-tool-search) there to control MCP tool search for every session a runner spawns; the MCP page covers the values.
468464 
469<a id="connection-timing" />
470 
471465### Wait for MCP servers before the first turn
472466 
473467A self-hosted session waits briefly for MCP servers that are still connecting, at two separate points. A server that misses a wait has its tools missing when the first turn starts, and they become available later with no action on your part. The two waits are:
from line 610
616610 
617611A self-hosted session has no terminal attached, so an unanswered permission prompt stalls the turn until the user responds in the UI. Anthropic's control plane sends each session's tool list and permission rules with the work payload; the default configuration pre-approves routine tool calls, including `Bash`, and cloud sessions [pre-approve file edits regardless of mode](/docs/en/permission-modes#switch-permission-modes). A call that nothing pre-approves prompts through the session UI.
618612 
619<Note>
620 Only pin auto mode on an environment whose session containers run with [default-deny network egress](/docs/en/self-hosted-environments-deploy#default-deny-egress) and the rest of the [hardening section](/docs/en/self-hosted-environments-deploy#harden-your-deployment) in place. Routine tool calls, including `Bash` network requests, run without a human in the loop on both the default pre-approved tool set and in auto mode, so the network boundary is what limits where those calls can reach.
621</Note>
613> [!NOTE]
614>
615> Only pin auto mode on an environment whose session containers run with [default-deny network egress](/docs/en/self-hosted-environments-deploy#default-deny-egress) and the rest of the [hardening section](/docs/en/self-hosted-environments-deploy#harden-your-deployment) in place. Routine tool calls, including `Bash` network requests, run without a human in the loop on both the default pre-approved tool set and in auto mode, so the network boundary is what limits where those calls can reach.
622616 
623617To keep prompts to a minimum regardless of what the control plane sends, pin [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) from your wrapper script or [`command` hook](#command). Auto mode lets sessions run without routine permission prompts: a separate classifier model reviews actions before they run and blocks the ones it rejects, and explicit ask rules still force a prompt; the permission modes page covers what the classifier checks. The runner appends server-computed flags before invoking the wrapper, and for single-value flags such as `--permission-mode` the parser honors the last occurrence, so a flag you append after `"$@"` overrides the server-sent value:
624618 

self-hosted-environments-deploy Changed · +43 / -43 lines

#### When sessions fail to start on a runner with the git proxy

from line 2
22 
33> Run self-hosted runners in production: security hardening, network egress control, git credentials, Kubernetes and Compose recipes, and troubleshooting.
44 
5<Note>
6 Self-hosted environments are in public beta on Team and Enterprise plans; [Availability and limitations](/docs/en/self-hosted-environments#availability-and-limitations) covers the enablement path. This page covers running the fleet in production; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for your first runner and session.
7</Note>
5> [!NOTE]
6>
7> Self-hosted environments are in public beta on Team and Enterprise plans; [Availability and limitations](/docs/en/self-hosted-environments#availability-and-limitations) covers the enablement path. This page covers running the fleet in production; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for your first runner and session.
88 
99A [self-hosted environment](/docs/en/self-hosted-environments) runs Claude Code [cloud sessions](/docs/en/claude-code-on-the-web) on runners you deploy inside your network, and in production those sessions execute model-directed code on behalf of everyone who can dispatch a session to the environment. This page is for the operator taking a working environment to production. It works through the deployment in order: what to lock down before connecting real systems, the egress the fleet needs, how sessions authenticate to your git host, the deployment recipes themselves, and what to check when sessions misbehave.
1010 
from line 13
1313A self-hosted runner executes arbitrary, model-directed code on your infrastructure on behalf of everyone who can dispatch a session to its environment. That's any member of your Anthropic organization, and anyone who can start a [Claude Tag](https://claude.com/docs/claude-tag/overview) channel session in a scope an Owner routed to the environment. Work through each item before you connect an environment to production systems:
1414 
1515* **Ephemeral, per-session containers**: run each runner process in a fresh container or VM that's destroyed when the process exits, with `--capacity 1` and the default `--drain-grace-sec 0` so each container serves exactly one session. At a higher capacity, or with a positive drain grace, one container serves multiple sessions from the same [locked owner](/docs/en/self-hosted-environments#key-concepts); see [Runner lifecycle](/docs/en/self-hosted-environments#runner-lifecycle). Don't reuse a filesystem between runner restarts, except in the deliberate [pre-warmed checkout](#reuse-a-pre-warmed-checkout) setup, and never across owners.
16 * <span id="processes-a-stopped-session-leaves" />When the runner stops a session, it sends no signal to a process still running after its shell command exited, such as a service that daemonized. Destroying the container or VM ends that process.
16 * When the runner stops a session, it sends no signal to a process still running after its shell command exited, such as a service that daemonized. Destroying the container or VM ends that process.
1717* **No broad credentials in the image**: don't include long-lived SSH keys, cloud-provider credentials, or personal access tokens that grant more than a session needs. Mint credentials used during a session, such as push or API tokens, per session from your [wrapper script](/docs/en/self-hosted-environments-configuration#wrapper-scripts). The initial clone happens before the wrapper runs, so handle it with a [`checkout` lifecycle hook](/docs/en/self-hosted-environments-configuration#checkout), or with [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy) when all of a session's repositories are on github.com. For both, see [Configure git](#configure-git).
1818* **Keep the host's GitHub credentials away from sessions**: Claude can use any GitHub credential a session can read, with whatever access that credential grants. Keep the runner host's own broadly scoped GitHub credentials out of anything a session can read. Such a credential can be a personal access token, the token `gh auth login` saves for your account, or a `GH_TOKEN` in the runner's environment.
1919 * **With [Anthropic-managed git](#use-the-anthropic-git-proxy)**: with such a credential, Claude reaches GitHub directly instead of through Anthropic-managed git.
from line 38
3838 
3939 The guard runs regardless of [`--trust-workspace`](/docs/en/self-hosted-environments-reference#runner-cli-flags), and doesn't cover repository hooks, `.mcp.json`, or Bash rules; see [Permissions and tool approval](/docs/en/self-hosted-environments-configuration#permissions-and-tool-approval) for where those grants belong.
4040 
41<Note>
42 If your organization has [IP allowlisting](https://support.claude.com/en/articles/13200993-restrict-access-to-claude-with-ip-allowlisting) enabled, add the public egress addresses of your runners and session containers to the allowlist before you start them. If you run [on-demand runners](/docs/en/self-hosted-environments-configuration#on-demand-runners), add the orchestrator host's address too. Don't rely on the allowlist as a network control for runner or session traffic. Apply default-deny egress at your own network boundary instead.
43</Note>
41> [!NOTE]
42>
43> If your organization has [IP allowlisting](https://support.claude.com/en/articles/13200993-restrict-access-to-claude-with-ip-allowlisting) enabled, add the public egress addresses of your runners and session containers to the allowlist before you start them. If you run [on-demand runners](/docs/en/self-hosted-environments-configuration#on-demand-runners), add the orchestrator host's address too. Don't rely on the allowlist as a network control for runner or session traffic. Apply default-deny egress at your own network boundary instead.
4444 
4545## Network requirements
4646 
from line 53
5353| `api.anthropic.com` | 443, HTTPS; WSS for [Anthropic-managed git](#use-the-anthropic-git-proxy) | Runner control plane and session streaming, model inference, feature flags, product analytics, [JWKS](/docs/en/self-hosted-environments-identity) key fetches, commit signing, and Anthropic-managed git when `--use-anthropic-git-proxy` is set |
5454| Your git host, such as `github.com` or your GitHub Enterprise host | 443 or 22 | Cloning and pushing repositories on each git host the runner's sessions use. On a runner that uses [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), see [when the `github.com` path is still needed](#github-com-egress-with-the-anthropic-git-proxy). |
5555 
56<span id="github-com-egress-with-the-anthropic-git-proxy" />A runner that uses [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy) routes its `github.com` git traffic through `api.anthropic.com`, so it doesn't need the git host path for `github.com`. It still needs that path if you set `--push-outcome-on-release` or push from a `post-session` hook.
56A runner that uses [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy) routes its `github.com` git traffic through `api.anthropic.com`, so it doesn't need the git host path for `github.com`. It still needs that path if you set `--push-outcome-on-release` or push from a `post-session` hook.
5757 
5858Whether these hosts are needed depends on your configuration:
5959 
from line 158
158158* A `credential.helper` that returns a minimally scoped token
159159* `GIT_SSH_COMMAND` pointing at a narrowly scoped key
160160 
161<span id="git-must-work-without-a-prompt" />Whichever mechanism you configure must work without a prompt, because the runner's built-in clone and fetch disable the prompts that git, SSH, and Git Credential Manager would otherwise show:
161Whichever mechanism you configure must work without a prompt, because the runner's built-in clone and fetch disable the prompts that git, SSH, and Git Credential Manager would otherwise show:
162162 
163163* The runner sets `GIT_TERMINAL_PROMPT=0`, so git doesn't ask for a username or password.
164164* The runner runs SSH with `BatchMode=yes`, appended to your `GIT_SSH_COMMAND` if you set one, so SSH doesn't ask for a passphrase or host confirmation.
from line 185
185185 
186186* **github.com only**: Anthropic serves a session only when all of its repositories are on github.com, and the git proxy doesn't support GitHub Enterprise Server yet. On a runner with the git proxy, a session with a repository on another git host [fails to start](#when-anthropic-doesnt-serve-a-session).
187187* **Credentials for the session's repositories only**: Anthropic supplies git credentials for the repositories that are part of the session, not for other repositories on the same git host. A private submodule, a dependency that your package manager fetches with git, or a plugin marketplace in another repository gets no credential from Anthropic. Ask the people who create sessions to [add every repository](/docs/en/web-quickstart#start-a-task) a session needs when they create it.
188 
189 Each run of a [cloud scheduled task](/docs/en/routines) is a session with the task's repositories, so ask each task's owner to [add every repository its runs need](/docs/en/routines#edit-and-control-routines).
188190* **Branch pushes only**: a push that deletes a branch fails, and so does a push to any other kind of ref, such as a tag. For which branches a push can update, see [GitHub proxy](/docs/en/cloud-environments#github-proxy).
189191* **Connected GitHub accounts**: ask each person who creates user sessions with a repository to [connect GitHub](/docs/en/web-quickstart#connect-github). Without their own connection, those sessions [don't start](#creator-has-no-github-connection).
190192* **`--capacity 1`**: the git proxy requires one session per runner process, so run more replicas for parallelism. [Turn the Anthropic git proxy on](#turn-the-anthropic-git-proxy-on) lists the requirements.
from line 194
192194* **Host credentials for host pushes**: the runner's [`--push-outcome-on-release`](/docs/en/self-hosted-environments-reference#runner-cli-flags) push and any push your [`post-session` hook](/docs/en/self-hosted-environments-configuration#post-session) makes still use the runner host's own git credentials and its [network path to `github.com`](#github-com-egress-with-the-anthropic-git-proxy). Keep those credentials out of the [global git config](#git-proxy-replaces-global-git-config), and see [Ship git config in your image](#ship-git-config-in-your-image) for ways to supply them.
193195* **Per-session decision**: Anthropic decides for each session on the runner whether to serve its git, and a session it doesn't serve fails to start. [When sessions fail to start on a runner with the git proxy](#when-anthropic-doesnt-serve-a-session) covers the causes.
194196 
195<span id="git-proxy-replaces-global-git-config" />
197> [!WARNING]
198>
199> With `--use-anthropic-git-proxy` set, the runner deletes and replaces the global git config of the user it runs as, and keeps no backup. It does this at startup and before each session. A login or credential helper you kept there is lost. Settings that [`--configure-git`](#let-the-runner-configure-git) writes survive. Run the runner as a dedicated user or in a container, never as your own user.
196200 
197<Warning>
198 With `--use-anthropic-git-proxy` set, the runner deletes and replaces the global git config of the user it runs as, and keeps no backup. It does this at startup and before each session. A login or credential helper you kept there is lost. Settings that [`--configure-git`](#let-the-runner-configure-git) writes survive. Run the runner as a dedicated user or in a container, never as your own user.
199</Warning>
200 
201201Keep git settings that aren't secret, such as identity and `safe.directory`, in the system git configuration.
202202 
203203#### Turn the Anthropic git proxy on
from line 208
208208* **`--capacity 1`, the default**: each runner process handles one session at a time, so run more replicas for parallelism.
209209* **Git 2.32 or later**: older git ignores the per-session git configuration that the runner sets up for the git proxy.
210210 
211<Warning>
212 The [Kubernetes](#kubernetes) and [Docker Compose](#docker-compose) recipes on this page use `--capacity 4`. If you add `--use-anthropic-git-proxy` or `CLAUDE_RUNNER_USE_GIT_PROXY=1` to one of them without changing the capacity to `1`, the runner exits at startup every time your orchestrator restarts it. Set `--capacity 1` and run more replicas for parallelism. [When the runner exits](#when-the-runner-exits) shows the line the runner prints.
213</Warning>
211> [!WARNING]
212>
213> The [Kubernetes](#kubernetes) and [Docker Compose](#docker-compose) recipes on this page use `--capacity 4`. If you add `--use-anthropic-git-proxy` or `CLAUDE_RUNNER_USE_GIT_PROXY=1` to one of them without changing the capacity to `1`, the runner exits at startup every time your orchestrator restarts it. Set `--capacity 1` and run more replicas for parallelism. [When the runner exits](#when-the-runner-exits) shows the line the runner prints.
214214 
215215To turn the git proxy on, add `--use-anthropic-git-proxy` to the runner's command, or set `CLAUDE_RUNNER_USE_GIT_PROXY=1` in the runner's environment. This command, run in a shell on the runner host, starts the [quickstart](/docs/en/self-hosted-environments-quickstart#set-up-manually)'s runner with the git proxy on:
216216 
from line 228
228228* **Bot and agent sessions**: Anthropic uses your organization's GitHub App installation token.
229229* **URL rewrites**: `--git-host-rewrite` and `--git-ssh-rewrite` have no effect on a repository that the git proxy serves.
230230 
231<h4 id="when-anthropic-doesnt-serve-a-session">
232 When sessions fail to start on a runner with the git proxy
233</h4>
231#### When sessions fail to start on a runner with the git proxy
234232 
235233On a runner started with `--use-anthropic-git-proxy`, a session fails to start when Anthropic doesn't serve its git. Look in the runner's log for a git error that names an `api.anthropic.com` address containing `/git_proxy/`.
236234 
from line 239
241239 * **A repository isn't on github.com**: a session with even one repository on another git host, such as GitHub Enterprise Server, isn't served, including its github.com repositories. [Turn the Anthropic git proxy off](#turn-the-anthropic-git-proxy-off) for that environment's runners.
242240 * **Every repository is on github.com**: report the failure to [your Anthropic account team](#report-an-issue) with the session ID from the `withheld` line. Anthropic records the reason on its side.
243241* **A line containing `remote: access denied by the git proxy`**: a session that Anthropic serves can still be refused, for example when organization policy denies git access for the session, or the session isn't authorized for the repository. The runner's log then shows a line containing `remote: access denied by the git proxy`, and the rest of that line says why.
244* <span id="creator-has-no-github-connection" />**`GitHub authentication required`**: this appears when the session's creator has no working GitHub connection on claude.ai. The session's clone fails, and the git error contains `GitHub authentication required. Please reconnect your GitHub account.` Ask that person to connect or reconnect GitHub in their claude.ai settings.
242* **`GitHub authentication required`**: this appears when the session's creator has no working GitHub connection on claude.ai. The session's clone fails, and the git error contains `GitHub authentication required. Please reconnect your GitHub account.` Ask that person to connect or reconnect GitHub in their claude.ai settings.
245243 
246244After you fix the cause, start the failed sessions again.
247245 
from line 247
249247 
250248If sessions in an environment use a repository on a git host other than github.com, such as GitHub Enterprise Server, turn `--use-anthropic-git-proxy` off for that environment's runners.
251249 
252<Steps>
253 <Step title="Remove the flag">
254 Remove `--use-anthropic-git-proxy` from the runner's command. If you set `CLAUDE_RUNNER_USE_GIT_PROXY` in the runner's environment, such as a pod spec or a Compose file, remove it there. In a shell, unset it:
250**1. Remove the flag**
255251 
256 ```bash theme={null}
257 unset CLAUDE_RUNNER_USE_GIT_PROXY
258 ```
259 </Step>
252Remove `--use-anthropic-git-proxy` from the runner's command. If you set `CLAUDE_RUNNER_USE_GIT_PROXY` in the runner's environment, such as a pod spec or a Compose file, remove it there. In a shell, unset it:
260253 
261 <Step title="Give the runner git credentials">
262 Provide credentials that work without a prompt for every git host the runners' sessions use, github.com included. Any credential that was in the runner user's global git config is gone, because the runner deleted that config while `--use-anthropic-git-proxy` was set. [Ship credentials in your image](#ship-git-config-in-your-image) or use a [`checkout` lifecycle hook](/docs/en/self-hosted-environments-configuration#checkout).
263 </Step>
254```bash theme={null}
255unset CLAUDE_RUNNER_USE_GIT_PROXY
256```
264257 
265 <Step title="Open the network path">
266 Allow the runner to reach each git host the runners' sessions use on port 443 or 22. See the git host row in [Network requirements](#network-requirements).
267 </Step>
258**2. Give the runner git credentials**
268259 
269 <Step title="Restart the runners">
270 Restart the runners so that they register without the git proxy. Then start each failed session again.
271 </Step>
272</Steps>
260Provide credentials that work without a prompt for every git host the runners' sessions use, github.com included. Any credential that was in the runner user's global git config is gone, because the runner deleted that config while `--use-anthropic-git-proxy` was set. [Ship credentials in your image](#ship-git-config-in-your-image) or use a [`checkout` lifecycle hook](/docs/en/self-hosted-environments-configuration#checkout).
273261 
262**3. Open the network path**
263 
264Allow the runner to reach each git host the runners' sessions use on port 443 or 22. See the git host row in [Network requirements](#network-requirements).
265 
266**4. Restart the runners**
267 
268Restart the runners so that they register without the git proxy. Then start each failed session again.
269 
274270#### GitHub API access without the GitHub CLI
275271 
276272If your runner image doesn't include the GitHub CLI, Claude Code can provide a built-in `gh`, so Claude can still open pull requests, comment, and read CI results. The built-in `gh` is for runners that use Anthropic-managed git. It supports one command, `gh api`, which calls GitHub's REST API. Requires Claude Code v2.1.287 or later in the runner image.
from line 565
569565 
570566 By default the runner leaves these in place when the session ends, so on a disk that outlives the runner process they accumulate. Every session runs as the runner's own user, so any later session that disk serves can read them. If you keep a persistent `--base-dir`, size the volume for that growth. The same applies to any setup that restarts the runner on the same filesystem, including the [Docker Compose recipe](#docker-compose).
571567* **With `--remove-session-state`, per-session directories don't persist**: start the runner with [`--remove-session-state`](/docs/en/self-hosted-environments-reference#runner-cli-flags) to have it delete each session's per-session directories as the session ends. The deletion is best-effort: the directories stay when the runner is killed before its cleanup runs. The canonical clone and files a session wrote elsewhere on the host, such as the temporary directory, stay regardless.
572* **With the git proxy, the reset becomes a checkout**: with [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), the runner sanitizes the clone's `.git/` before each session, keeping the object store, refs, and shallow state but deleting the index, so each session pays a full working-tree checkout instead of a near-instant reset; it still never re-clones. Submodule pre-warms aren't supported under the proxy.
568* **With the git proxy, the reset becomes a checkout**: with [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), the runner sanitizes the clone's `.git/` before each session. It keeps the object store and shallow state, and deletes the index. Without the index, each session starts with a full working-tree checkout instead of a near-instant reset. The runner still fetches into the existing clone rather than cloning the repository again. Submodule pre-warms aren't supported under the proxy.
569* **With the git proxy, a `reftable` pre-warm loses its refs**: when the runner sanitizes `.git/` before each session, it keeps `.git/refs/` and removes the `.git/reftable/` directory. A clone pre-warmed in git's `reftable` ref format loses its refs that way, so pre-warm the clone in git's `files` ref format instead.
573570* **Long clones need no workaround**: the runner bounds each git operation with a 120-second no-progress watchdog and a 30-minute hard cap, not a flat timeout, so a slow cold clone that keeps reporting progress completes.
574571 
575572## Pin the version
from line 664
667664 The runner skips only a clear refusal: the host answers that the repository wasn't found, git finds no credentials for the host, or authentication fails. A network failure, a timeout, or an HTTP `403` still fails the session start, as does a refusal for a repository the session pushes results to. The runner still fails a session that skipping would leave with no repository at all. With [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), the runner skips only a repository the git proxy itself denies.
668665 
669666 The access check runs again each time the session starts on a runner, so once the runner's git identity has read access, the next start clones the repository. Before v2.1.274, each of these refusals failed the session start.
667* **A session fails to start with `This runner couldn't read`**: the runner asked the git host for a repository with its [git credentials](#configure-git) and got a not-found answer. The repository doesn't exist, or those credentials can't reach the repository.
668 * **If you administer the runner**: give its git credentials read access to the repository, then start a new session.
669 * **If you started the session**: if the repository doesn't exist, start a new session with the right repository. If the runner can't reach the repository, ask the runner's administrator to grant access, then start a new session.
670670* **Sessions take minutes to start**: the initial clone usually dominates. Watch the `claude_code_self_hosted_runner_session_init_duration_seconds` [metric](/docs/en/self-hosted-environments-reference#prometheus-metrics) to confirm, and cut the clone with a [pre-warmed checkout](#reuse-a-pre-warmed-checkout) or a smaller `CLAUDE_RUNNER_FETCH_DEPTH`.
671671* **Turns fail with a 401**: when a turn ends with a 401 or 403 from the Anthropic API, the runner fetches a fresh [`CLAUDE_CODE_OAUTH_TOKEN`](/docs/en/self-hosted-environments-configuration#wrapper-scripts) from Anthropic and passes it to the session. The failed turn isn't retried. This token is short-lived, and the runner rotates it over the session's stdin.
672672 

self-hosted-environments-identity Changed · +107 / -111 lines

from line 2
22 
33> Verify the CLAUDE_CODE_SESSION_ACCESS_TOKEN JWT so services on your network can trust requests from sessions in your self-hosted environment.
44 
5<Note>
6 Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner](/docs/en/cloud-environments#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page covers session identity verification; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy) for the fleet recipes.
7</Note>
5> [!NOTE]
6>
7> Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner](/docs/en/cloud-environments#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page covers session identity verification; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy) for the fleet recipes.
88 
99A [self-hosted environment](/docs/en/self-hosted-environments) lets Claude Code [cloud sessions](/docs/en/claude-code-on-the-web) run on infrastructure you operate instead of on Anthropic's. Because the session runs inside your network, Claude can call your internal services directly. Those services need a way to confirm that a request came from a Claude Code session in your environment, and to identify the user or service identity that created that session.
1010 
from line 54
5454 
5555Verify each incoming token against these checks:
5656 
57<Steps>
58 <Step title="Check the prefix">
59 Reject the value if it doesn't start with `sk-ant-cc-`, then remove that prefix. The remainder is a standard compact JWT.
60 </Step>
57**1. Check the prefix**
6158 
62 <Step title="Verify the signature">
63 Fetch the JWKS, select the key whose `kid` matches the token header, and verify the `ES256` signature. Reject tokens whose `alg` header is not `ES256`. If a token arrives with a `kid` that isn't in your cached key set, refetch the JWKS once before rejecting it: after a rotation, new tokens are signed with a key your cached set doesn't have yet.
64 </Step>
59Reject the value if it doesn't start with `sk-ant-cc-`, then remove that prefix. The remainder is a standard compact JWT.
6560 
66 <Step title="Verify the issuer">
67 Reject the token if `iss` is not exactly `ccr`.
68 </Step>
61**2. Verify the signature**
6962 
70 <Step title="Verify the audience against your environment">
71 The `aud` claim is an array. Reject the token unless it contains your environment ID, which has the form `ccpool_...`. The environment ID is shown in your environment's detail dialog on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments), and appears as the `ccr:pool_id` claim in any of the environment's session tokens. This check is what scopes the token to your environment and rejects tokens issued to other organizations.
72 </Step>
63Fetch the JWKS, select the key whose `kid` matches the token header, and verify the `ES256` signature. Reject tokens whose `alg` header is not `ES256`. If a token arrives with a `kid` that isn't in your cached key set, refetch the JWKS once before rejecting it: after a rotation, new tokens are signed with a key your cached set doesn't have yet.
7364 
74 <Step title="Verify the role">
75 Reject the token if `ccr:role` is not exactly `session_worker`. Other tokens issued for self-hosted environments, such as environment secrets, runner tokens, and work orders, are signed by the same key set but carry different roles.
76 </Step>
65**3. Verify the issuer**
7766 
78 <Step title="Verify expiry">
79 Reject the token if `exp` is in the past. Anthropic issues session tokens with a four-hour lifetime by default and a maximum of eight hours. The runner refreshes the token before expiry and pushes the new value to the session, so subprocesses that Claude starts after a refresh inherit it. One session can therefore present several distinct valid tokens to your service over its lifetime.
80 </Step>
67Reject the token if `iss` is not exactly `ccr`.
8168 
82 <Step title="Read the identity">
83 The creating user's identity is in the `act` claim: `act.sub` is their Anthropic user ID in the prefixed form `user:<id>`, and `act.email`, when the creating surface recorded one, is their email address. Sessions your organization's service identity creates, including Claude Tag channel sessions, carry an `agent:` subject instead, so treat a session as user-created only when `act.sub` carries the `user:` prefix, rather than testing whether identity claims are absent. See the [claims reference](#claims-reference) for the full structure and the flat duplicate claims.
84 </Step>
85</Steps>
69**4. Verify the audience against your environment**
8670 
71The `aud` claim is an array. Reject the token unless it contains your environment ID, which has the form `ccpool_...`. The environment ID is shown in your environment's detail dialog on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments), and appears as the `ccr:pool_id` claim in any of the environment's session tokens. This check is what scopes the token to your environment and rejects tokens issued to other organizations.
72 
73**5. Verify the role**
74 
75Reject the token if `ccr:role` is not exactly `session_worker`. Other tokens issued for self-hosted environments, such as environment secrets, runner tokens, and work orders, are signed by the same key set but carry different roles.
76 
77**6. Verify expiry**
78 
79Reject the token if `exp` is in the past. Anthropic issues session tokens with a four-hour lifetime by default and a maximum of eight hours. The runner refreshes the token before expiry and pushes the new value to the session, so subprocesses that Claude starts after a refresh inherit it. One session can therefore present several distinct valid tokens to your service over its lifetime.
80 
81**7. Read the identity**
82 
83The creating user's identity is in the `act` claim: `act.sub` is their Anthropic user ID in the prefixed form `user:<id>`, and `act.email`, when the creating surface recorded one, is their email address. Sessions your organization's service identity creates, including Claude Tag channel sessions, carry an `agent:` subject instead, so treat a session as user-created only when `act.sub` carries the `user:` prefix, rather than testing whether identity claims are absent. See the [claims reference](#claims-reference) for the full structure and the flat duplicate claims.
84 
8785The checks map directly onto standard JWT libraries. The examples below implement the full sequence in Node.js with [`jose`](https://www.npmjs.com/package/jose), which handles JWKS fetching, caching, and `kid` selection, and in Python with [`PyJWT`](https://pyjwt.readthedocs.io/) and its built-in JWKS client.
8886 
89<Tabs>
90 <Tab title="Node.js (jose)">
91 ```typescript theme={null}
92 import { createRemoteJWKSet, jwtVerify } from "jose";
87**Node.js (jose)**
9388 
94 const JWKS = createRemoteJWKSet(
95 new URL("https://api.anthropic.com/v1/code/.well-known/jwks.json")
96 );
89```typescript theme={null}
90import { createRemoteJWKSet, jwtVerify } from "jose";
9791 
98 const PREFIX = "sk-ant-cc-";
99 const EXPECTED_POOL_ID = "ccpool_...";
92const JWKS = createRemoteJWKSet(
93 new URL("https://api.anthropic.com/v1/code/.well-known/jwks.json")
94);
10095 
101 export async function verifySessionToken(raw: string) {
102 if (!raw.startsWith(PREFIX)) {
103 throw new Error("not a self-hosted runner session token");
104 }
105 const jwt = raw.slice(PREFIX.length);
96const PREFIX = "sk-ant-cc-";
97const EXPECTED_POOL_ID = "ccpool_...";
10698 
107 const { payload } = await jwtVerify(jwt, JWKS, {
108 issuer: "ccr",
109 audience: EXPECTED_POOL_ID,
110 algorithms: ["ES256"],
111 });
99export async function verifySessionToken(raw: string) {
100 if (!raw.startsWith(PREFIX)) {
101 throw new Error("not a self-hosted runner session token");
102 }
103 const jwt = raw.slice(PREFIX.length);
112104 
113 if (payload["ccr:role"] !== "session_worker") {
114 throw new Error("token is not a session_worker token");
115 }
105 const { payload } = await jwtVerify(jwt, JWKS, {
106 issuer: "ccr",
107 audience: EXPECTED_POOL_ID,
108 algorithms: ["ES256"],
109 });
116110 
117 const act = payload.act as { email?: string; sub?: string };
118 return {
119 sessionId: payload["ccr:session_id"] as string,
120 poolId: payload["ccr:pool_id"] as string,
121 orgId: payload["ccr:org_id"] as string,
122 creatorEmail: act?.email,
123 creatorSub: act?.sub,
124 };
125 }
126 ```
127 </Tab>
111 if (payload["ccr:role"] !== "session_worker") {
112 throw new Error("token is not a session_worker token");
113 }
128114 
129 <Tab title="Python (PyJWT)">
130 ```python theme={null}
131 import jwt
132 from jwt import PyJWKClient
115 const act = payload.act as { email?: string; sub?: string };
116 return {
117 sessionId: payload["ccr:session_id"] as string,
118 poolId: payload["ccr:pool_id"] as string,
119 orgId: payload["ccr:org_id"] as string,
120 creatorEmail: act?.email,
121 creatorSub: act?.sub,
122 };
123}
124```
133125 
134 JWKS_URL = "https://api.anthropic.com/v1/code/.well-known/jwks.json"
135 PREFIX = "sk-ant-cc-"
136 EXPECTED_POOL_ID = "ccpool_..."
126**Python (PyJWT)**
137127 
138 jwks = PyJWKClient(JWKS_URL)
128```python theme={null}
129import jwt
130from jwt import PyJWKClient
139131 
132JWKS_URL = "https://api.anthropic.com/v1/code/.well-known/jwks.json"
133PREFIX = "sk-ant-cc-"
134EXPECTED_POOL_ID = "ccpool_..."
140135 
141 def verify_session_token(raw: str) -> dict:
142 if not raw.startswith(PREFIX):
143 raise ValueError("not a self-hosted runner session token")
144 token = raw.removeprefix(PREFIX)
136jwks = PyJWKClient(JWKS_URL)
145137 
146 signing_key = jwks.get_signing_key_from_jwt(token)
147 payload = jwt.decode(
148 token,
149 signing_key.key,
150 algorithms=["ES256"],
151 issuer="ccr",
152 audience=EXPECTED_POOL_ID,
153 )
154138 
155 if payload.get("ccr:role") != "session_worker":
156 raise ValueError("token is not a session_worker token")
139def verify_session_token(raw: str) -> dict:
140 if not raw.startswith(PREFIX):
141 raise ValueError("not a self-hosted runner session token")
142 token = raw.removeprefix(PREFIX)
157143 
158 act = payload.get("act") or {}
159 return {
160 "session_id": payload["ccr:session_id"],
161 "pool_id": payload["ccr:pool_id"],
162 "org_id": payload["ccr:org_id"],
163 "creator_email": act.get("email"),
164 "creator_sub": act.get("sub"),
165 }
166 ```
167 </Tab>
168</Tabs>
144 signing_key = jwks.get_signing_key_from_jwt(token)
145 payload = jwt.decode(
146 token,
147 signing_key.key,
148 algorithms=["ES256"],
149 issuer="ccr",
150 audience=EXPECTED_POOL_ID,
151 )
152 
153 if payload.get("ccr:role") != "session_worker":
154 raise ValueError("token is not a session_worker token")
155 
156 act = payload.get("act") or {}
157 return {
158 "session_id": payload["ccr:session_id"],
159 "pool_id": payload["ccr:pool_id"],
160 "org_id": payload["ccr:org_id"],
161 "creator_email": act.get("email"),
162 "creator_sub": act.get("sub"),
163 }
164```
169165 
170166### Verify the token inside the session
171167 

self-hosted-environments-quickstart Changed · +39 / -41 lines

from line 2
22 
33> Set up your first self-hosted environment: install Claude Code, create the environment, start a runner, and route a session to it.
44 
5<Note>
6 Self-hosted environments are in public beta on Team and Enterprise plans; [Availability and limitations](/docs/en/self-hosted-environments#availability-and-limitations) covers the enablement path. This page gets your first session running; see [Self-hosted environments](/docs/en/self-hosted-environments) for what they are and [Deploy to production](/docs/en/self-hosted-environments-deploy) for hardening and fleet recipes.
7</Note>
5> [!NOTE]
6>
7> Self-hosted environments are in public beta on Team and Enterprise plans; [Availability and limitations](/docs/en/self-hosted-environments#availability-and-limitations) covers the enablement path. This page gets your first session running; see [Self-hosted environments](/docs/en/self-hosted-environments) for what they are and [Deploy to production](/docs/en/self-hosted-environments-deploy) for hardening and fleet recipes.
88 
99A [self-hosted environment](/docs/en/self-hosted-environments) runs Claude Code [cloud sessions](/docs/en/claude-code-on-the-web) on infrastructure your organization operates, executed by runner processes you deploy. This quickstart stands up your first one, the smallest that works: one runner on a single host, running one test session. There are two steps: [create the environment, start a runner, and route a session to it](#set-up-an-environment-and-runner), then [message that session from your terminal](#send-a-follow-up-message-to-a-running-session). You'll move between two surfaces: claude.ai for creating the environment, checking its status, and routing a session, and a terminal on the host for everything the runner does.
1010 
from line 65
6565 
6666Create the environment on claude.ai, start the runner from a terminal on the host, then return to claude.ai to confirm the runner appears and route a session to it. If someone who holds the Owner role already created the environment and handed you its secret, start at step 2.
6767 
68<Steps>
69 <Step title="Create an environment">
70 Go to the [**Cloud environments** page](https://claude.ai/admin-settings/cloud-environments) in admin settings. Under **Self-hosted environments**, select **New**, name the environment, and select **Create**. On the wizard's second step, select **Copy environment key** to copy the environment secret, which the admin UI labels an environment key. claude.ai shows the secret once, and you can't retrieve it later; it expires 365 days after creation. The environment's `ccpool_...` ID stays visible in its detail dialog; you'll need it for the `aud` check in [token verification](/docs/en/self-hosted-environments-identity) and for dispatching [test sessions from CI](/docs/en/self-hosted-environments-testing#run-the-test-loop).
68**1. Create an environment**
7169 
72 If you lose the secret or need to rotate it, create a new secret from the environment's **Configuration** tab, roll the new secret out to your runners, then revoke the old one. Runners holding a revoked secret fail their next authenticated poll and exit, logging `poll auth failed`, and your orchestrator restarts them with the new secret.
73 </Step>
70Go to the [**Cloud environments** page](https://claude.ai/admin-settings/cloud-environments) in admin settings. Under **Self-hosted environments**, select **New**, name the environment, and select **Create**. On the wizard's second step, select **Copy environment key** to copy the environment secret, which the admin UI labels an environment key. claude.ai shows the secret once, and you can't retrieve it later; it expires 365 days after creation. The environment's `ccpool_...` ID stays visible in its detail dialog; you'll need it for the `aud` check in [token verification](/docs/en/self-hosted-environments-identity) and for dispatching [test sessions from CI](/docs/en/self-hosted-environments-testing#run-the-test-loop).
7471 
75 <Step title="Start a runner">
76 Create the secret directory. This command and the next use `/etc/claude`, which needs root, and the secret file they create is readable only by the user who runs them. If the runner will run as another user, it exits with `error: Failed to read environment secret file <path> (EACCES: permission denied, open '<path>')`. In that case, run both commands as the runner's user with a directory that user can write to in place of `/etc/claude`, and pass the same path to `--environment-secret-file`. Any path the runner process can read works.
72If you lose the secret or need to rotate it, create a new secret from the environment's **Configuration** tab, roll the new secret out to your runners, then revoke the old one. Runners holding a revoked secret fail their next authenticated poll and exit, logging `poll auth failed`, and your orchestrator restarts them with the new secret.
7773 
78 ```bash theme={null}
79 mkdir -p /etc/claude
80 ```
74**2. Start a runner**
8175 
82 Write the environment secret to a file. The command below reads from your terminal so the secret stays out of shell history: paste the value you copied, press Enter, then Ctrl-D, and the subshell's `umask` makes the file readable only by its owner.
76Create the secret directory. This command and the next use `/etc/claude`, which needs root, and the secret file they create is readable only by the user who runs them. If the runner will run as another user, it exits with `error: Failed to read environment secret file <path> (EACCES: permission denied, open '<path>')`. In that case, run both commands as the runner's user with a directory that user can write to in place of `/etc/claude`, and pass the same path to `--environment-secret-file`. Any path the runner process can read works.
8377 
84 ```bash theme={null}
85 (umask 077 && cat > /etc/claude/environment-secret)
86 ```
78```bash theme={null}
79mkdir -p /etc/claude
80```
8781 
88 Choose a base directory, replacing `<writable-dir>` in the runner command below with an absolute path that the runner can write to or create. The runner creates the directory at startup, then checks repositories out and creates per-session directories under it. Without `--base-dir` it uses `/workspace`, which only works if that directory already exists and is writable or you start the runner as root.
82Write the environment secret to a file. The command below reads from your terminal so the secret stays out of shell history: paste the value you copied, press Enter, then Ctrl-D, and the subshell's `umask` makes the file readable only by its owner.
8983 
90 <Warning>
91 Use a directory dedicated to the runner, not your home directory or a directory of your own clones. The runner treats whatever it finds at `<writable-dir>/<repo-owner>/<repo>` as its own clone, and can reset it and overwrite or delete files in it.
92 </Warning>
84```bash theme={null}
85(umask 077 && cat > /etc/claude/environment-secret)
86```
9387 
94 If the runner can't create or write to the base directory, it exits at startup with an error naming the directory instead of registering. See [Troubleshooting](/docs/en/self-hosted-environments-deploy#troubleshooting).
88Choose a base directory, replacing `<writable-dir>` in the runner command below with an absolute path that the runner can write to or create. The runner creates the directory at startup, then checks repositories out and creates per-session directories under it. Without `--base-dir` it uses `/workspace`, which only works if that directory already exists and is writable or you start the runner as root.
9589 
96 Then start the runner with `--environment-secret-file` and `--base-dir`:
90> [!WARNING]
91>
92> Use a directory dedicated to the runner, not your home directory or a directory of your own clones. The runner treats whatever it finds at `<writable-dir>/<repo-owner>/<repo>` as its own clone, and can reset it and overwrite or delete files in it.
9793 
98 ```bash theme={null}
99 claude self-hosted-runner --environment-secret-file '/etc/claude/environment-secret' --base-dir '<writable-dir>'
100 ```
94If the runner can't create or write to the base directory, it exits at startup with an error naming the directory instead of registering. See [Troubleshooting](/docs/en/self-hosted-environments-deploy#troubleshooting).
10195 
102 The runner logs `Registered: runner_id=<runner-id>` once it has registered with your environment, then begins polling for work. Started with this command, it exits by design after serving one session, not after each turn. To serve more sessions, start the runner again each time it exits. See [If the runner exits](#if-the-runner-exits) for when that happens and what the runner logs.
103 </Step>
96Then start the runner with `--environment-secret-file` and `--base-dir`:
10497 
105 <Step title="Verify the runner appears">
106 Return to the [**Cloud environments** page](https://claude.ai/admin-settings/cloud-environments). Your environment's status changes from **No runners deployed** to **Healthy** within a few seconds of the runner starting; open the environment and select **Activity** to see the runner itself. If you don't have access to the admin page, the `Registered: runner_id=<runner-id>` line in the runner's log from the previous step gives you the same signal.
107 </Step>
98```bash theme={null}
99claude self-hosted-runner --environment-secret-file '/etc/claude/environment-secret' --base-dir '<writable-dir>'
100```
108101 
109 <Step title="Route a session to the environment">
110 <span id="route-a-session" />Start a session at claude.ai/code and select your environment from the environment picker, where self-hosted environments appear alongside Anthropic-hosted ones. For the repository, pick the one from the [prerequisites](#host-and-network): a public repository, or one this host can already clone by its HTTPS URL without being asked for credentials.
102The runner logs `Registered: runner_id=<runner-id>` once it has registered with your environment, then begins polling for work. Started with this command, it exits by design after serving one session, not after each turn. To serve more sessions, start the runner again each time it exits. See [If the runner exits](#if-the-runner-exits) for when that happens and what the runner logs.
111103 
112 The next available runner picks up the queued session and logs `Picked up session <session-id>` along with its active count and capacity, so you can confirm from the runner's own output which host took the session. Watch the session work and read Claude's replies at [claude.ai/code](https://claude.ai/code).
104**3. Verify the runner appears**
113105 
114 <span id="session-doesnt-start" />If the session doesn't start working, match what you see:
106Return to the [**Cloud environments** page](https://claude.ai/admin-settings/cloud-environments). Your environment's status changes from **No runners deployed** to **Healthy** within a few seconds of the runner starting; open the environment and select **Activity** to see the runner itself. If you don't have access to the admin page, the `Registered: runner_id=<runner-id>` line in the runner's log from the previous step gives you the same signal.
115107 
116 * **The session sits queued**: see [Troubleshooting](/docs/en/self-hosted-environments-deploy#troubleshooting).
117 * **The session fails to start with a git error**: if the error includes git's `could not read Username for` followed by your git host's URL, the runner had no HTTPS credentials for that host.
118 * **Supply HTTPS credentials**: see [Configure git](/docs/en/self-hosted-environments-deploy#configure-git)
119 * **Clone over SSH instead**: if the runner host already has an SSH key, see [Rewrite git URLs for private networks](/docs/en/self-hosted-environments-deploy#rewrite-git-urls-for-private-networks)
120 </Step>
121</Steps>
108**4. Route a session to the environment**
109 
110Start a session at claude.ai/code and select your environment from the environment picker, where self-hosted environments appear alongside Anthropic-hosted ones. For the repository, pick the one from the [prerequisites](#host-and-network): a public repository, or one this host can already clone by its HTTPS URL without being asked for credentials.
111 
112The next available runner picks up the queued session and logs `Picked up session <session-id>` along with its active count and capacity, so you can confirm from the runner's own output which host took the session. Watch the session work and read Claude's replies at [claude.ai/code](https://claude.ai/code).
113 
114If the session doesn't start working, match what you see:
115 
116* **The session sits queued**: see [Troubleshooting](/docs/en/self-hosted-environments-deploy#troubleshooting).
117* **The session fails to start with a git error**: if the error includes git's `could not read Username for` followed by your git host's URL, the runner had no HTTPS credentials for that host.
118 * **Supply HTTPS credentials**: see [Configure git](/docs/en/self-hosted-environments-deploy#configure-git)
119 * **Clone over SSH instead**: if the runner host already has an SSH key, see [Rewrite git URLs for private networks](/docs/en/self-hosted-environments-deploy#rewrite-git-urls-for-private-networks)
122120 
123121### If the runner exits
124122 

self-hosted-environments-reference Changed · +3 / -3 lines

from line 2
22 
33> Complete reference for the self-hosted runner and orchestrator: CLI flags, environment variables, and Prometheus metrics.
44 
5<Note>
6 Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner](/docs/en/cloud-environments#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page is the flag and metric reference; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy) for the fleet recipes.
7</Note>
5> [!NOTE]
6>
7> Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner](/docs/en/cloud-environments#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page is the flag and metric reference; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy) for the fleet recipes.
88 
99This page is the reference for the two processes you run in a [self-hosted environment](/docs/en/self-hosted-environments): the runner, which executes Claude Code [cloud sessions](/docs/en/claude-code-on-the-web) on your hosts, and the optional autoscaling orchestrator, which starts runners as sessions queue. Each has its own flag table. Both run on Linux or macOS hosts, which the defaults such as `/workspace` and `~/.claude` assume. Run `claude self-hosted-runner --help` for the authoritative list on your installed version.
1010 

self-hosted-environments-testing Changed · +3 / -3 lines

from line 2
22 
33> Verify a self-hosted runner image from CI: dispatch a session with the CLI, read Claude's replies through a Stop hook, and script the full loop.
44 
5<Note>
6 Self-hosted environments are in public beta on Team and Enterprise plans; [Availability and limitations](/docs/en/self-hosted-environments#availability-and-limitations) covers the enablement path. This page is the CI test recipe; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy) for the fleet recipes.
7</Note>
5> [!NOTE]
6>
7> Self-hosted environments are in public beta on Team and Enterprise plans; [Availability and limitations](/docs/en/self-hosted-environments#availability-and-limitations) covers the enablement path. This page is the CI test recipe; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy) for the fleet recipes.
88 
99In a [self-hosted environment](/docs/en/self-hosted-environments), Claude Code [cloud sessions](/docs/en/claude-code-on-the-web) run on a runner image you build and maintain. Before rolling a new image to your production environment, drive a full session against a test environment from a script: create a session, read Claude's reply, send a follow-up, and read that reply too. This is the shape of a CI smoke test that verifies your runner image, git access, and any custom tools before you promote a change.
1010 

server-managed-settings Changed · +58 / -60 lines

from line 4
44 
55Server-managed settings let organization Owners centrally configure Claude Code from [**Organization settings > Claude Code > Managed settings**](https://claude.ai/admin-settings/claude-code) in the claude.ai console. Claude Code clients fetch these settings automatically when users authenticate with an eligible credential on a platform where server-managed delivery is supported. See [Platform availability](#platform-availability) for the credentials and platforms that qualify.
66 
7<Note>
8 Server-managed settings are available for [Claude for Teams](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=server_settings_teams#team-&-enterprise) and [Claude for Enterprise](https://anthropic.com/contact-sales?utm_source=claude_code\&utm_medium=docs\&utm_content=server_settings_enterprise) customers.
9</Note>
7> [!NOTE]
8>
9> Server-managed settings are available for [Claude for Teams](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=server_settings_teams#team-&-enterprise) and [Claude for Enterprise](https://anthropic.com/contact-sales?utm_source=claude_code\&utm_medium=docs\&utm_content=server_settings_enterprise) customers.
1010 
1111## Requirements
1212 
from line 29
2929 
3030## Configure server-managed settings
3131 
32<Steps>
33 <Step title="Open the admin console">
34 In the claude.ai console, go to [**Organization settings > Claude Code > Managed settings**](https://claude.ai/admin-settings/claude-code).
32**1. Open the admin console**
3533 
36 In a Team or Enterprise organization, if the page says you don't have access, ask an [Owner or Primary Owner](#access-control) to make the change.
37 </Step>
34In the claude.ai console, go to [**Organization settings > Claude Code > Managed settings**](https://claude.ai/admin-settings/claude-code).
3835 
39 <Step title="Define your settings">
40 Add your configuration as JSON. All [settings available in `settings.json`](/docs/en/settings-reference#all-settings) are supported except those restricted to OS-level policy delivery; see [Current limitations](#current-limitations) for that short list. This includes [hooks](/docs/en/hooks), [environment variables](/docs/en/env-vars), and [managed-only settings](/docs/en/managed-settings#managed-only-settings) like `allowManagedPermissionRulesOnly`.
36In a Team or Enterprise organization, if the page says you don't have access, ask an [Owner or Primary Owner](#access-control) to make the change.
4137 
42 This example enforces a permission deny list, prevents users from bypassing permissions, and restricts permission rules to those defined in managed settings. The `Bash(curl *)` rule matches `curl` [as Claude writes it](/docs/en/permissions#bash-rule-limits), not `/usr/bin/curl` or `sh -c 'curl …'`; for network enforcement that doesn't depend on the command text, add a [`sandbox` block with `allowManagedDomainsOnly`](/docs/en/sandboxing#configure-the-sandbox-for-your-organization).
38**2. Define your settings**
4339 
44 ```json theme={null}
45 {
46 "permissions": {
47 "deny": [
48 "Bash(curl *)",
49 "Read(./.env)",
50 "Read(./.env.*)",
51 "Read(./secrets/**)"
52 ],
53 "disableBypassPermissionsMode": "disable"
54 },
55 "allowManagedPermissionRulesOnly": true
56 }
57 ```
40Add your configuration as JSON. All [settings available in `settings.json`](/docs/en/settings-reference#all-settings) are supported except those restricted to OS-level policy delivery; see [Current limitations](#current-limitations) for that short list. This includes [hooks](/docs/en/hooks), [environment variables](/docs/en/env-vars), and [managed-only settings](/docs/en/managed-settings#managed-only-settings) like `allowManagedPermissionRulesOnly`.
5841 
59 Hooks use the same format as in `settings.json`.
42This example enforces a permission deny list, prevents users from bypassing permissions, and restricts permission rules to those defined in managed settings. The `Bash(curl *)` rule matches `curl` [as Claude writes it](/docs/en/permissions#bash-rule-limits), not `/usr/bin/curl` or `sh -c 'curl …'`; for network enforcement that doesn't depend on the command text, add a [`sandbox` block with `allowManagedDomainsOnly`](/docs/en/sandboxing#configure-the-sandbox-for-your-organization).
6043 
61 This example runs an audit script after every file edit across the organization:
44```json theme={null}
45{
46 "permissions": {
47 "deny": [
48 "Bash(curl *)",
49 "Read(./.env)",
50 "Read(./.env.*)",
51 "Read(./secrets/**)"
52 ],
53 "disableBypassPermissionsMode": "disable"
54 },
55 "allowManagedPermissionRulesOnly": true
56}
57```
6258 
63 ```json theme={null}
64 {
65 "hooks": {
66 "PostToolUse": [
67 {
68 "matcher": "Edit|Write",
69 "hooks": [
70 { "type": "command", "command": "/usr/local/bin/audit-edit.sh" }
71 ]
72 }
59Hooks use the same format as in `settings.json`.
60 
61This example runs an audit script after every file edit across the organization:
62 
63```json theme={null}
64{
65 "hooks": {
66 "PostToolUse": [
67 {
68 "matcher": "Edit|Write",
69 "hooks": [
70 { "type": "command", "command": "/usr/local/bin/audit-edit.sh" }
7371 ]
7472 }
75 }
76 ```
73 ]
74 }
75}
76```
7777 
78 Because hooks execute shell commands, users in interactive sessions see a [security approval dialog](#security-approval-dialogs) before Claude Code applies them.
78Because hooks execute shell commands, users in interactive sessions see a [security approval dialog](#security-approval-dialogs) before Claude Code applies them.
7979 
80 To configure the [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) classifier so it knows which repos, buckets, and domains your organization trusts, deliver an `autoMode` block the same way; see [Configure auto mode](/docs/en/auto-mode-config) for how the `autoMode` entries affect what the classifier blocks and important warnings about the `environment`, `allow`, `soft_deny`, and `hard_deny` fields.
81 </Step>
80To configure the [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) classifier so it knows which repos, buckets, and domains your organization trusts, deliver an `autoMode` block the same way; see [Configure auto mode](/docs/en/auto-mode-config) for how the `autoMode` entries affect what the classifier blocks and important warnings about the `environment`, `allow`, `soft_deny`, and `hard_deny` fields.
8281 
83 <Step title="Save and deploy">
84 Save your changes. Claude Code clients receive the updated settings on their next startup or hourly polling cycle.
82**3. Save and deploy**
8583 
86 The editor checks your JSON against the published JSON schema for Claude Code settings. If it finds a problem in JSON that parses, it shows a warning and relabels the save button. The label is **Update with errors** when settings are already saved, and **Add with errors** when no settings are saved yet. That button still saves, because a schema warning doesn't block the save.
84Save your changes. Claude Code clients receive the updated settings on their next startup or hourly polling cycle.
8785 
88 The schema [can lag behind the newest releases](/docs/en/settings#edit-a-settings-file), so the editor can flag a key or value that the [settings reference](/docs/en/settings-reference#all-settings) documents. Claude Code receives the keys and values you saved and runs [its own validation](#invalid-entries-in-delivered-settings) when it loads them.
89 </Step>
90</Steps>
86The editor checks your JSON against the published JSON schema for Claude Code settings. If it finds a problem in JSON that parses, it shows a warning and relabels the save button. The label is **Update with errors** when settings are already saved, and **Add with errors** when no settings are saved yet. That button still saves, because a schema warning doesn't block the save.
87 
88The schema [can lag behind the newest releases](/docs/en/settings#edit-a-settings-file), so the editor can flag a key or value that the [settings reference](/docs/en/settings-reference#all-settings) documents. Claude Code receives the keys and values you saved and runs [its own validation](#invalid-entries-in-delivered-settings) when it loads them.
9189 
9290### Verify settings delivery
9391 

sessions Changed · +1 / -5 lines

### Resume a running background session

from line 19
1919| `claude --from-pr <number>` | Opens the session picker filtered to sessions linked to that pull request |
2020| `/resume` | Switches to a different session from inside an active session |
2121 
22<h3 id="resume-a-running-background-session">
23 Resume a running background session
24</h3>
22### Resume a running background session
2523 
2624When the conversation you resume with `claude --resume` or `/resume` belongs to a [background session](/docs/en/agent-view) that is still running, Claude Code opens the running session itself. With `--bg` on the command line, the resume is a [background dispatch](/docs/en/agent-view#from-your-shell) instead. Before v2.1.285, Claude Code refused and told you to open the session with `claude attach <id>`, or to stop it with `claude stop <id>` first.
2725 
from line 76
7876| `plan` | Non-interactive, under the [conditions below](#resume-in-plan-mode-with-p) | Plan mode |
7977| Any mode | Non-interactive, in any other case | The permission mode a new `claude -p` run would start in |
8078| `plan` | VS Code | Plan mode, with [the exceptions on the VS Code page](/docs/en/vs-code#resume-past-conversations) |
81 
82<a id="resume-in-plan-mode-with-p" />
8379 
8480##### Resume in plan mode with `-p`
8581 

settings-example Changed · +320 / -330 lines

## A team's shared settings ## An organization's managed settings

The two sides of this change are more than 400 edits apart, too far apart to line up, so this is the differ's own diff of it and the words inside a line are not marked.

from line 16
1616 
1717One developer's personal settings. It picks a model and effort, adjusts the terminal, and pre-approves a read-only command and one file read. Everything not listed keeps its default. A file like this goes in `~/.claude/settings.json`, where it applies to every project you open.
1818 
19<Tabs>
20 <Tab title="Copyable settings file">
21 Save this as `~/.claude/settings.json`. It's valid JSON with no comments, so you can paste it as is and delete the keys you don't want.
22 
23 ```json ~/.claude/settings.json theme={null}
24 {
25 "model": "claude-sonnet-5",
26 "modelSettings": {
27 "claude-sonnet-5": { "effortLevel": "xhigh" }
28 },
29 "editorMode": "vim",
30 "theme": "light-daltonized",
31 "statusLine": {
32 "type": "command",
33 "command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'",
34 "padding": 2
35 },
36 "spinnerTipsEnabled": false,
37 "preferredNotifChannel": "terminal_bell",
38 "permissions": {
39 "allow": [
40 "Bash(git diff *)",
41 "Read(~/.zshrc)"
42 ]
43 },
44 "autoUpdatesChannel": "stable",
45 "cleanupPeriodDays": 20
46 }
47 ```
48 </Tab>
49 
50 <Tab title="What each key does">
51 The same file with a comment above each key. Read it here; copy from the other tab, because Claude Code doesn't accept comments in a settings file.
52 
53 ```jsonc ~/.claude/settings.json theme={null}
54 {
55 // Start every session on Sonnet 5
56 "model": "claude-sonnet-5",
57 // Run Sonnet 5 above its default high level; /effort saves a level per model, and --effort sets one for a single session
58 "modelSettings": {
59 "claude-sonnet-5": { "effortLevel": "xhigh" }
60 },
61 // Vim keybindings in the prompt
62 "editorMode": "vim",
63 // The colorblind-friendly light theme
64 "theme": "light-daltonized",
65 // A status line below the prompt: model name and context used
66 "statusLine": {
67 "type": "command",
68 "command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'",
69 "padding": 2
70 },
71 // Hide the tips that rotate under the spinner
72 "spinnerTipsEnabled": false,
73 // Ring the terminal bell for notifications, such as a finished task or a waiting permission prompt
74 "preferredNotifChannel": "terminal_bell",
75 // Let Claude Code run git diff and read your .zshrc without asking
76 "permissions": {
77 "allow": [
78 "Bash(git diff *)",
79 "Read(~/.zshrc)"
80 ]
81 },
82 // Take updates from the stable channel
83 "autoUpdatesChannel": "stable",
84 // Delete session transcripts and other local session data older than 20 days
85 "cleanupPeriodDays": 20
86 }
87 ```
88 </Tab>
89</Tabs>
90 
91<h2 id="a-teams-shared-settings">
92 A team's shared settings
93</h2>
19**Copyable settings file**
20 
21Save this as `~/.claude/settings.json`. It's valid JSON with no comments, so you can paste it as is and delete the keys you don't want.
22 
23```json ~/.claude/settings.json theme={null}
24{
25 "model": "claude-sonnet-5",
26 "modelSettings": {
27 "claude-sonnet-5": { "effortLevel": "xhigh" }
28 },
29 "editorMode": "vim",
30 "theme": "light-daltonized",
31 "statusLine": {
32 "type": "command",
33 "command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'",
34 "padding": 2
35 },
36 "spinnerTipsEnabled": false,
37 "preferredNotifChannel": "terminal_bell",
38 "permissions": {
39 "allow": [
40 "Bash(git diff *)",
41 "Read(~/.zshrc)"
42 ]
43 },
44 "autoUpdatesChannel": "stable",
45 "cleanupPeriodDays": 20
46}
47```
48 
49**What each key does**
50 
51The same file with a comment above each key. Read it here; copy from the other tab, because Claude Code doesn't accept comments in a settings file.
52 
53```jsonc ~/.claude/settings.json theme={null}
54{
55 // Start every session on Sonnet 5
56 "model": "claude-sonnet-5",
57 // Run Sonnet 5 above its default high level; /effort saves a level per model, and --effort sets one for a single session
58 "modelSettings": {
59 "claude-sonnet-5": { "effortLevel": "xhigh" }
60 },
61 // Vim keybindings in the prompt
62 "editorMode": "vim",
63 // The colorblind-friendly light theme
64 "theme": "light-daltonized",
65 // A status line below the prompt: model name and context used
66 "statusLine": {
67 "type": "command",
68 "command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'",
69 "padding": 2
70 },
71 // Hide the tips that rotate under the spinner
72 "spinnerTipsEnabled": false,
73 // Ring the terminal bell for notifications, such as a finished task or a waiting permission prompt
74 "preferredNotifChannel": "terminal_bell",
75 // Let Claude Code run git diff and read your .zshrc without asking
76 "permissions": {
77 "allow": [
78 "Bash(git diff *)",
79 "Read(~/.zshrc)"
80 ]
81 },
82 // Take updates from the stable channel
83 "autoUpdatesChannel": "stable",
84 // Delete session transcripts and other local session data older than 20 days
85 "cleanupPeriodDays": 20
86}
87```
88 
89## A team's shared settings
9490 
9591One team's shared settings, committed to the repository so everyone who clones it gets the same permissions, hooks, and plugin marketplace. Save a file like this at `.claude/settings.json` at the top of the repository. What to know before you commit one:
9692 
from line 96
10096* **The hook is a script in the repo.** This file's hook runs `.claude/hooks/block-rm.sh`; [How a hook resolves](/docs/en/hooks#how-a-hook-resolves) walks through writing it.
10197* **Rules match the command and path as written.** `Bash(git push *)` doesn't match [`git -C . push`](/docs/en/permissions#bash-rule-limits). `Read(./.env)` on its own stops the file tools and commands that name the file, such as `cat .env`, but not [`grep -r` run over the directory](/docs/en/permissions#read-and-edit); the `sandbox` block in this file closes that gap, because the sandbox [adds your `Read` deny paths](/docs/en/settings-reference#sandbox-filesystem-denyread) to what every sandboxed command can't read.
10298 
103<Tabs>
104 <Tab title="Copyable settings file">
105 Save this as `.claude/settings.json` at the top of the repository and commit it. It's valid JSON with no comments, so you can paste it as is and delete the keys you don't want.
106 
107 ```json .claude/settings.json theme={null}
108 {
109 "permissions": {
110 "allow": [
111 "Bash(npm run *)"
112 ],
113 "ask": [
114 "Bash(git push *)"
115 ],
116 "deny": [
117 "Read(./.env)",
118 "Read(./.env.*)",
119 "Read(./secrets/**)"
120 ]
121 },
122 "hooks": {
123 "PreToolUse": [
99**Copyable settings file**
100 
101Save this as `.claude/settings.json` at the top of the repository and commit it. It's valid JSON with no comments, so you can paste it as is and delete the keys you don't want.
102 
103```json .claude/settings.json theme={null}
104{
105 "permissions": {
106 "allow": [
107 "Bash(npm run *)"
108 ],
109 "ask": [
110 "Bash(git push *)"
111 ],
112 "deny": [
113 "Read(./.env)",
114 "Read(./.env.*)",
115 "Read(./secrets/**)"
116 ]
117 },
118 "hooks": {
119 "PreToolUse": [
120 {
121 "matcher": "Bash",
122 "hooks": [
124123 {
125 "matcher": "Bash",
126 "hooks": [
127 {
128 "type": "command",
129 "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh"
130 }
131 ]
124 "type": "command",
125 "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh"
132126 }
133127 ]
134 },
135 "extraKnownMarketplaces": {
136 "acme-tools": {
137 "source": {
138 "source": "github",
139 "repo": "acme-corp/claude-plugins"
140 }
141 }
142 },
143 "enabledPlugins": {
144 "code-formatter@acme-tools": true
145 },
146 "sandbox": {
147 "enabled": true,
148 "filesystem": {
149 "allowWrite": [
150 "/tmp/build"
151 ]
152 },
153 "network": {
154 "allowedDomains": [
155 "registry.npmjs.org",
156 "*.example.com"
157 ]
158 }
159 },
160 "plansDirectory": "./plans"
161 }
162 ```
163 </Tab>
164 
165 <Tab title="What each key does">
166 The same file with a comment above each key. Read it here; copy from the other tab, because Claude Code doesn't accept comments in a settings file.
167 
168 ```jsonc .claude/settings.json theme={null}
169 {
170 "permissions": {
171 // Run npm scripts without asking
172 "allow": [
173 "Bash(npm run *)"
174 ],
175 // Confirm before git push commands
176 "ask": [
177 "Bash(git push *)"
178 ],
179 // Deny reads of env files and the secrets folder by the file tools and file-reading commands
180 "deny": [
181 "Read(./.env)",
182 "Read(./.env.*)",
183 "Read(./secrets/**)"
184 ]
185 },
186 // Before every Bash command, run a script in the repo that can block it
187 "hooks": {
188 "PreToolUse": [
128 }
129 ]
130 },
131 "extraKnownMarketplaces": {
132 "acme-tools": {
133 "source": {
134 "source": "github",
135 "repo": "acme-corp/claude-plugins"
136 }
137 }
138 },
139 "enabledPlugins": {
140 "code-formatter@acme-tools": true
141 },
142 "sandbox": {
143 "enabled": true,
144 "filesystem": {
145 "allowWrite": [
146 "/tmp/build"
147 ]
148 },
149 "network": {
150 "allowedDomains": [
151 "registry.npmjs.org",
152 "*.example.com"
153 ]
154 }
155 },
156 "plansDirectory": "./plans"
157}
158```
159 
160**What each key does**
161 
162The same file with a comment above each key. Read it here; copy from the other tab, because Claude Code doesn't accept comments in a settings file.
163 
164```jsonc .claude/settings.json theme={null}
165{
166 "permissions": {
167 // Run npm scripts without asking
168 "allow": [
169 "Bash(npm run *)"
170 ],
171 // Confirm before git push commands
172 "ask": [
173 "Bash(git push *)"
174 ],
175 // Deny reads of env files and the secrets folder by the file tools and file-reading commands
176 "deny": [
177 "Read(./.env)",
178 "Read(./.env.*)",
179 "Read(./secrets/**)"
180 ]
181 },
182 // Before every Bash command, run a script in the repo that can block it
183 "hooks": {
184 "PreToolUse": [
185 {
186 "matcher": "Bash",
187 "hooks": [
189188 {
190 "matcher": "Bash",
191 "hooks": [
192 {
193 "type": "command",
194 "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh"
195 }
196 ]
189 "type": "command",
190 "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh"
197191 }
198192 ]
199 },
200 // Register the team's plugin marketplace on every clone
201 "extraKnownMarketplaces": {
202 "acme-tools": {
203 "source": {
204 "source": "github",
205 "repo": "acme-corp/claude-plugins"
206 }
207 }
208 },
209 // Enable one plugin from that marketplace; a plugin from an external source such as a GitHub repository still needs each person to install it once
210 "enabledPlugins": {
211 "code-formatter@acme-tools": true
212 },
213 // Sandbox commands: writable build dir; npm and example.com pre-allowed
214 "sandbox": {
215 "enabled": true,
216 "filesystem": {
217 "allowWrite": [
218 "/tmp/build"
219 ]
220 },
221 "network": {
222 "allowedDomains": [
223 "registry.npmjs.org",
224 "*.example.com"
225 ]
226 }
227 },
228 // Keep plan files inside the repo
229 "plansDirectory": "./plans"
230 }
231 ```
232 </Tab>
233</Tabs>
234 
235<h2 id="an-organizations-managed-settings">
236 An organization's managed settings
237</h2>
193 }
194 ]
195 },
196 // Register the team's plugin marketplace on every clone
197 "extraKnownMarketplaces": {
198 "acme-tools": {
199 "source": {
200 "source": "github",
201 "repo": "acme-corp/claude-plugins"
202 }
203 }
204 },
205 // Enable one plugin from that marketplace; a plugin from an external source such as a GitHub repository still needs each person to install it once
206 "enabledPlugins": {
207 "code-formatter@acme-tools": true
208 },
209 // Sandbox commands: writable build dir; npm and example.com pre-allowed
210 "sandbox": {
211 "enabled": true,
212 "filesystem": {
213 "allowWrite": [
214 "/tmp/build"
215 ]
216 },
217 "network": {
218 "allowedDomains": [
219 "registry.npmjs.org",
220 "*.example.com"
221 ]
222 }
223 },
224 // Keep plan files inside the repo
225 "plansDirectory": "./plans"
226}
227```
228 
229## An organization's managed settings
238230 
239231A `managed-settings.json` file that shows the shape of the managed keys, with one plausible value for each. It isn't a recommended policy: pick the keys that match your own requirements and set your own values. The example sets these keys:
240232 
from line 243
251243 
252244Administrators deploy a file like this as `managed-settings.json`, or the same JSON through MDM or [server-managed settings](/docs/en/server-managed-settings). One deployed file applies to every machine or account it reaches. To give a group different values, deploy a different file or profile to that group, since [server-managed settings don't support per-group policy yet](/docs/en/server-managed-settings#current-limitations).
253245 
254<Tabs>
255 <Tab title="Copyable settings file">
256 Deploy this as `managed-settings.json`, or the same JSON through MDM or the claude.ai console. It's valid JSON with no comments; replace the example organization UUID, server URL, and marketplace with your own and delete the keys you don't want.
257 
258 ```json managed-settings.json theme={null}
246**Copyable settings file**
247 
248Deploy this as `managed-settings.json`, or the same JSON through MDM or the claude.ai console. It's valid JSON with no comments; replace the example organization UUID, server URL, and marketplace with your own and delete the keys you don't want.
249 
250```json managed-settings.json theme={null}
251{
252 "forceLoginMethod": "claudeai",
253 "forceLoginOrgUUID": [
254 "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
255 ],
256 "availableModels": [
257 "opus",
258 "sonnet"
259 ],
260 "enforceAvailableModels": true,
261 "permissions": {
262 "deny": [
263 "Bash(curl *)",
264 "Read(./.env)",
265 "Read(./secrets/**)"
266 ],
267 "disableBypassPermissionsMode": "disable"
268 },
269 "allowManagedPermissionRulesOnly": true,
270 "allowedMcpServers": [
259271 {
260 "forceLoginMethod": "claudeai",
261 "forceLoginOrgUUID": [
262 "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
272 "serverUrl": "https://api.githubcopilot.com/*"
273 }
274 ],
275 "allowManagedMcpServersOnly": true,
276 "strictKnownMarketplaces": [
277 {
278 "source": "github",
279 "repo": "acme-corp/approved-plugins"
280 }
281 ],
282 "sandbox": {
283 "enabled": true,
284 "failIfUnavailable": true,
285 "allowUnsandboxedCommands": false,
286 "network": {
287 "allowedDomains": [
288 "registry.npmjs.org",
289 "github.com"
263290 ],
264 "availableModels": [
265 "opus",
266 "sonnet"
291 "allowManagedDomainsOnly": true
292 }
293 },
294 "requiredMinimumVersion": "2.1.150",
295 "cleanupPeriodDays": 7,
296 "companyAnnouncements": [
297 "Welcome to Acme Corp! Review our code guidelines at docs.example.com"
298 ]
299}
300```
301 
302**What each key does**
303 
304The same file with a comment above each key. Read it here; copy from the other tab, because Claude Code doesn't accept comments in a settings file.
305 
306```jsonc managed-settings.json theme={null}
307{
308 // Only claude.ai logins, and only in this organization
309 "forceLoginMethod": "claudeai",
310 "forceLoginOrgUUID": [
311 "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
312 ],
313 // Only Opus and Sonnet models; with enforceAvailableModels, the Default option obeys the list too
314 "availableModels": [
315 "opus",
316 "sonnet"
317 ],
318 "enforceAvailableModels": true,
319 "permissions": {
320 // Deny curl commands and reads of the project's .env file and secrets folder on every machine
321 "deny": [
322 "Bash(curl *)",
323 "Read(./.env)",
324 "Read(./secrets/**)"
325 ],
326 // Remove the bypass-permissions mode from every session
327 "disableBypassPermissionsMode": "disable"
328 },
329 // Ignore permission rules from user, project, and local settings
330 "allowManagedPermissionRulesOnly": true,
331 // Only the GitHub MCP server, matched by URL rather than by name, since a user can
332 // name any server "github". User-added servers that don't match don't load, including
333 // every stdio server when the list has only URL entries. The allowManagedMcpServersOnly
334 // key below makes this managed list the only allowlist that applies
335 "allowedMcpServers": [
336 {
337 "serverUrl": "https://api.githubcopilot.com/*"
338 }
339 ],
340 "allowManagedMcpServersOnly": true,
341 // Plugins can come only from this marketplace
342 "strictKnownMarketplaces": [
343 {
344 "source": "github",
345 "repo": "acme-corp/approved-plugins"
346 }
347 ],
348 // Sandbox every command Claude runs, refuse to start if the sandbox can't be
349 // set up, and never let a blocked command retry outside the sandbox; network
350 // limited to npm and GitHub, and users can't add domains
351 "sandbox": {
352 "enabled": true,
353 "failIfUnavailable": true,
354 "allowUnsandboxedCommands": false,
355 "network": {
356 "allowedDomains": [
357 "registry.npmjs.org",
358 "github.com"
267359 ],
268 "enforceAvailableModels": true,
269 "permissions": {
270 "deny": [
271 "Bash(curl *)",
272 "Read(./.env)",
273 "Read(./secrets/**)"
274 ],
275 "disableBypassPermissionsMode": "disable"
276 },
277 "allowManagedPermissionRulesOnly": true,
278 "allowedMcpServers": [
279 {
280 "serverUrl": "https://api.githubcopilot.com/*"
281 }
282 ],
283 "allowManagedMcpServersOnly": true,
284 "strictKnownMarketplaces": [
285 {
286 "source": "github",
287 "repo": "acme-corp/approved-plugins"
288 }
289 ],
290 "sandbox": {
291 "enabled": true,
292 "failIfUnavailable": true,
293 "allowUnsandboxedCommands": false,
294 "network": {
295 "allowedDomains": [
296 "registry.npmjs.org",
297 "github.com"
298 ],
299 "allowManagedDomainsOnly": true
300 }
301 },
302 "requiredMinimumVersion": "2.1.150",
303 "cleanupPeriodDays": 7,
304 "companyAnnouncements": [
305 "Welcome to Acme Corp! Review our code guidelines at docs.example.com"
306 ]
307 }
308 ```
309 </Tab>
310 
311 <Tab title="What each key does">
312 The same file with a comment above each key. Read it here; copy from the other tab, because Claude Code doesn't accept comments in a settings file.
313 
314 ```jsonc managed-settings.json theme={null}
315 {
316 // Only claude.ai logins, and only in this organization
317 "forceLoginMethod": "claudeai",
318 "forceLoginOrgUUID": [
319 "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
320 ],
321 // Only Opus and Sonnet models; with enforceAvailableModels, the Default option obeys the list too
322 "availableModels": [
323 "opus",
324 "sonnet"
325 ],
326 "enforceAvailableModels": true,
327 "permissions": {
328 // Deny curl commands and reads of the project's .env file and secrets folder on every machine
329 "deny": [
330 "Bash(curl *)",
331 "Read(./.env)",
332 "Read(./secrets/**)"
333 ],
334 // Remove the bypass-permissions mode from every session
335 "disableBypassPermissionsMode": "disable"
336 },
337 // Ignore permission rules from user, project, and local settings
338 "allowManagedPermissionRulesOnly": true,
339 // Only the GitHub MCP server, matched by URL rather than by name, since a user can
340 // name any server "github". User-added servers that don't match don't load, including
341 // every stdio server when the list has only URL entries. The allowManagedMcpServersOnly
342 // key below makes this managed list the only allowlist that applies
343 "allowedMcpServers": [
344 {
345 "serverUrl": "https://api.githubcopilot.com/*"
346 }
347 ],
348 "allowManagedMcpServersOnly": true,
349 // Plugins can come only from this marketplace
350 "strictKnownMarketplaces": [
351 {
352 "source": "github",
353 "repo": "acme-corp/approved-plugins"
354 }
355 ],
356 // Sandbox every command Claude runs, refuse to start if the sandbox can't be
357 // set up, and never let a blocked command retry outside the sandbox; network
358 // limited to npm and GitHub, and users can't add domains
359 "sandbox": {
360 "enabled": true,
361 "failIfUnavailable": true,
362 "allowUnsandboxedCommands": false,
363 "network": {
364 "allowedDomains": [
365 "registry.npmjs.org",
366 "github.com"
367 ],
368 "allowManagedDomainsOnly": true
369 }
370 },
371 // Refuse to start on versions older than 2.1.150
372 "requiredMinimumVersion": "2.1.150",
373 // Delete session transcripts and other local session data after 7 days
374 "cleanupPeriodDays": 7,
375 // A message every user sees at startup
376 "companyAnnouncements": [
377 "Welcome to Acme Corp! Review our code guidelines at docs.example.com"
378 ]
379 }
380 ```
381 </Tab>
382</Tabs>
360 "allowManagedDomainsOnly": true
361 }
362 },
363 // Refuse to start on versions older than 2.1.150
364 "requiredMinimumVersion": "2.1.150",
365 // Delete session transcripts and other local session data after 7 days
366 "cleanupPeriodDays": 7,
367 // A message every user sees at startup
368 "companyAnnouncements": [
369 "Welcome to Acme Corp! Review our code guidelines at docs.example.com"
370 ]
371}
372```
383373 

settings-reference Changed · +36 / -659 lines

This page is larger than the 256 KiB this site keeps, so one side of the diff below stops where the stored text does.

The two sides of this change are more than 400 edits apart, too far apart to line up, so this is the differ's own diff of it and the words inside a line are not marked.

from line 2
22 
33> Complete reference for every Claude Code settings.json key: where each one goes, its type and default, and a paste-ready example, with an index of every key.
44 
5export const BackToIndex = ({href = '#all-settings', label = 'Back to index'}) => {
6 const [show, setShow] = useState(false);
7 useEffect(() => {
8 const onScroll = () => setShow(window.scrollY > window.innerHeight);
9 onScroll();
10 window.addEventListener('scroll', onScroll, {
11 passive: true
12 });
13 return () => window.removeEventListener('scroll', onScroll);
14 }, []);
15 return <div className="not-prose">
16 <style>{`
17 .bti-btn {
18 position: fixed; right: 20px; bottom: 20px; z-index: 40;
19 display: inline-flex; align-items: center; gap: 6px;
20 padding: 8px 12px; border-radius: 999px;
21 font-size: 13px; font-weight: 500; line-height: 1; text-decoration: none;
22 color: #1f1f1f; background: #ffffff; border: 1px solid #d9d9d9;
23 box-shadow: 0 2px 8px rgba(0,0,0,0.12);
24 opacity: 0; pointer-events: none; transform: translateY(6px);
25 transition: opacity 160ms ease, transform 160ms ease;
26 }
27 .bti-btn.bti-show { opacity: 1; pointer-events: auto; transform: translateY(0); }
28 .bti-btn:hover { border-color: #b3b3b3; }
29 .dark .bti-btn { color: #ececec; background: #1e1e1e; border-color: #3a3a3a; box-shadow: 0 2px 8px rgba(0,0,0,0.5); }
30 .dark .bti-btn:hover { border-color: #5a5a5a; }
31 @media (max-width: 1279px) { .bti-btn { bottom: 112px; } }
32 @media print { .bti-btn { display: none; } }
33 `}</style>
34 <a className={'bti-btn' + (show ? ' bti-show' : '')} href={href} aria-hidden={!show} tabIndex={show ? 0 : -1}>
35 <svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M8 13V3M3.5 7.5 8 3l4.5 4.5" /></svg>
36 {label}
37 </a>
38 </div>;
39};
40 
41export const ReferenceFilter = ({placeholder, noun, facets, facetOrder, columnHelp, children}) => {
42 const useLive = init => {
43 const [v, setV] = useState(init);
44 const ref = useRef(init);
45 return [v, ref, x => {
46 ref.current = x;
47 setV(x);
48 }];
49 };
50 const cap = s => s.charAt(0).toUpperCase() + s.slice(1);
51 const plural = s => s.endsWith('y') ? s.slice(0, -1) + 'ies' : s + 's';
52 const facetNames = facets || ['category', 'topic', 'scope', 'where'];
53 const orderOf = {};
54 Object.keys(facetOrder || ({})).forEach(k => {
55 orderOf[k] = facetOrder[k].map(x => String(x).toLowerCase());
56 });
57 const rankIn = (col, v) => {
58 const list = orderOf[col];
59 if (!list) return -1;
60 const i = list.indexOf(String(v).toLowerCase());
61 return i < 0 ? list.length : i;
62 };
63 const cmpValues = col => (a, b) => {
64 const ra = rankIn(col, a);
65 const rb = rankIn(col, b);
66 if (ra !== rb) return ra - rb;
67 return a < b ? -1 : a > b ? 1 : 0;
68 };
69 const help = columnHelp || ({});
70 const FIRST_COL_HELP = 'Click an entry to open it.';
71 const optionLabel = (f, c) => c === 'All' ? 'All ' + plural(f.label.toLowerCase()) : c;
72 const nounText = noun || 'entries';
73 const placeholderText = placeholder || 'Filter this reference';
74 const rootRef = useRef(null);
75 const tablesRef = useRef(null);
76 const searchRef = useRef(null);
77 const menuRef = useRef({});
78 const [q, qRef, setQ] = useLive('');
79 const [sel, selRef, setSel] = useLive({});
80 const [sortBy, sortRef, setSortBy] = useLive(null);
81 const [menuOpen, menuOpenRef, setMenu] = useLive(null);
82 const [facetList, setFacetList] = useState([]);
83 const [firstHead, setFirstHead] = useState('');
84 const [counts, setCounts] = useState({
85 shown: 0,
86 total: 0
87 });
88 const [disabled, setDisabled] = useState(false);
89 const menuBtn = name => menuRef.current[name] ? menuRef.current[name].querySelector(':scope > button') : null;
90 const menuList = name => menuRef.current[name] ? menuRef.current[name].querySelector('[role="listbox"]') : null;
91 const closeMenu = name => {
92 setMenu(null);
93 const btn = menuBtn(name);
94 if (btn) btn.focus();
95 };
96 const focusSelected = name => {
97 const list = menuList(name);
98 if (!list) return;
99 const btn = list.querySelector('button[aria-selected="true"]') || list.querySelector('button');
100 if (btn) btn.focus();
101 };
102 const setFacet = (name, value) => {
103 setSel(Object.assign({}, selRef.current, {
104 [name]: value
105 }));
106 apply(qRef.current);
107 closeMenu(name);
108 };
109 const sortTables = by => {
110 (tablesRef.current || []).forEach(tab => {
111 const t = tab.el;
112 const idx = tab.heads.indexOf(by);
113 const body = t.querySelector('tbody');
114 if (idx < 0 || !body) return;
115 const rows = [...body.querySelectorAll('tr')];
116 const keyOf = r => r.children[idx] ? r.children[idx].textContent.trim().toLowerCase() : '';
117 const cmp = cmpValues(by);
118 rows.map((r, i) => ({
119 r,
120 i: Number(r.dataset.sfIndex !== undefined ? r.dataset.sfIndex : i),
121 k: keyOf(r)
122 })).sort((a, b) => cmp(a.k, b.k) || a.i - b.i).forEach(x => body.appendChild(x.r));
123 [...t.querySelectorAll('thead th')].forEach((h, i) => {
124 const sortable = tab.heads[i] === tab.heads[0] || facetNames.indexOf(tab.heads[i]) > -1;
125 if (sortable) h.setAttribute('aria-sort', i === idx ? 'ascending' : 'none'); else h.removeAttribute('aria-sort');
126 });
127 });
128 };
129 const scan = () => {
130 const tables = [];
131 let el = rootRef.current ? rootRef.current.nextElementSibling : null;
132 while (el) {
133 if (el.tagName === 'H2' || el.querySelector(':scope > h2')) break;
134 const found = el.tagName === 'TABLE' ? [el] : [...el.querySelectorAll('table')];
135 found.forEach(t => {
136 const headCells = [...t.querySelectorAll('thead th, thead td')];
137 const heads = headCells.map(h => h.textContent.trim().toLowerCase());
138 if (heads.length === 0) return;
139 const facetIdx = {};
140 heads.forEach((h, i) => {
141 if (facetNames.indexOf(h) > -1) facetIdx[h] = i;
142 });
143 if (!t.dataset.sfDecorated) {
144 t.dataset.sfDecorated = '1';
145 headCells.forEach((h, i) => {
146 const text = i === 0 ? help[heads[0]] || FIRST_COL_HELP : help[heads[i]];
147 if (text) h.title = text;
148 });
149 }
150 const rows = [...t.querySelectorAll('tbody tr')].map((r, i) => {
151 if (r.dataset.sfIndex === undefined) r.dataset.sfIndex = String(i);
152 const cells = r.querySelectorAll('td');
153 const fv = {};
154 Object.keys(facetIdx).forEach(h => {
155 fv[h] = cells[facetIdx[h]] ? cells[facetIdx[h]].textContent.trim() : '';
156 });
157 return {
158 el: r,
159 text: [...cells].map(c => c.textContent).join(' ').toLowerCase(),
160 facets: fv,
161 anchors: [...r.querySelectorAll('a[href^="#"]')].map(a => a.getAttribute('href').slice(1)),
162 ids: [...r.querySelectorAll('[id]')].map(n => n.id)
163 };
164 });
165 tables.push({
166 el: t,
167 box: t.closest('[data-table-wrapper]') || t,
168 rows,
169 heads
170 });
171 });
172 el = el.nextElementSibling;
173 }
174 tablesRef.current = tables;
175 if (sortRef.current) sortTables(sortRef.current);
176 return tables;
177 };
178 const apply = query => {
179 let tables = tablesRef.current || scan();
180 if (tables.some(t => !t.el.isConnected)) tables = scan();
181 const needle = query.trim().toLowerCase();
182 const sel = selRef.current;
183 const activeFacets = Object.keys(sel).filter(h => sel[h] && sel[h] !== 'All');
184 const show = (el, on) => {
185 const want = on ? '' : 'none';
186 if (el.style.display !== want) el.style.display = want;
187 };
188 let total = 0;
189 let shown = 0;
190 const visibleTargets = new Set();
191 tables.forEach(t => {
192 let tableVisible = 0;
193 t.rows.forEach(row => {
194 total += 1;
195 const catOk = activeFacets.every(h => {
196 const v = row.facets[h];
197 return v === sel[h] || v === '' || v === undefined;
198 });
199 const match = catOk && (needle === '' || row.text.includes(needle));
200 show(row.el, match);
201 if (match) {
202 tableVisible += 1;
203 row.anchors.forEach(a => visibleTargets.add(a));
204 }
205 });
206 show(t.box, !(t.rows.length > 0 && tableVisible === 0));
207 shown += tableVisible;
208 });
209 if (shown < total && visibleTargets.size > 0) {
210 tables.forEach(t => {
211 t.rows.forEach(row => {
212 if (row.el.style.display === 'none' && row.ids.some(id => visibleTargets.has(id))) {
213 show(row.el, true);
214 show(t.box, true);
215 shown += 1;
216 }
217 });
218 });
219 }
220 setCounts({
221 shown,
222 total
223 });
224 return total;
225 };
226 const deriveFacets = tables => {
227 const seen = {};
228 tables.forEach(t => t.rows.forEach(r => {
229 Object.keys(r.facets).forEach(h => {
230 if (!seen[h]) seen[h] = [];
231 if (r.facets[h] && seen[h].indexOf(r.facets[h]) === -1) seen[h].push(r.facets[h]);
232 });
233 }));
234 const list = facetNames.filter(h => seen[h] && seen[h].length > 0).map(h => ({
235 name: h,
236 label: cap(h),
237 values: seen[h].sort(cmpValues(h))
238 }));
239 setFacetList(list);
240 const first = tables[0] ? tables[0].heads[0] : '';
241 setFirstHead(first);
242 if (!sortRef.current && first) {
243 setSortBy(first);
244 sortTables(first);
245 }
246 const init = {};
247 list.forEach(f => {
248 init[f.name] = selRef.current[f.name] || 'All';
249 });
250 setSel(init);
251 };
252 const onChange = value => {
253 setQ(value);
254 apply(value);
255 };
256 const clearAll = () => {
257 const next = {};
258 Object.keys(selRef.current).forEach(k => {
259 next[k] = 'All';
260 });
261 setSel(next);
262 setQ('');
263 apply('');
264 if (searchRef.current) searchRef.current.focus();
265 };
266 useEffect(() => {
267 const tables = scan();
268 deriveFacets(tables);
269 const total = apply('');
270 let retryTimer;
271 if (total === 0) {
272 retryTimer = setTimeout(() => {
273 tablesRef.current = null;
274 if (apply(qRef.current) > 0) deriveFacets(tablesRef.current); else setDisabled(true);
275 }, 500);
276 }
277 const onKey = e => {
278 if (e.key === 'Escape' && menuOpenRef.current !== null) closeMenu(menuOpenRef.current);
279 if (!searchRef.current) return;
280 if (e.metaKey || e.ctrlKey || e.altKey) return;
281 const active = document.activeElement;
282 const tag = active && active.tagName;
283 const editable = active && active.isContentEditable;
284 const interactive = tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || tag === 'BUTTON' || tag === 'A' || editable || active && active.getAttribute && active.getAttribute('role');
285 if (e.key === '/' && !interactive) {
286 const r = rootRef.current ? rootRef.current.getBoundingClientRect() : null;
287 if (r && r.bottom > 0 && r.top < (window.innerHeight || 0)) {
288 e.preventDefault();
289 setMenu(null);
290 searchRef.current.focus();
291 }
292 }
293 if (e.key === 'Escape' && menuOpenRef.current === null && active === searchRef.current) {
294 onChange('');
295 searchRef.current.blur();
296 }
297 };
298 const onDocClick = e => {
299 const open = menuOpenRef.current;
300 if (open !== null && menuRef.current[open] && !menuRef.current[open].contains(e.target)) setMenu(null);
301 };
302 window.addEventListener('keydown', onKey);
303 document.addEventListener('mousedown', onDocClick);
304 return () => {
305 if (retryTimer) clearTimeout(retryTimer);
306 window.removeEventListener('keydown', onKey);
307 document.removeEventListener('mousedown', onDocClick);
308 (tablesRef.current || []).forEach(t => {
309 t.box.style.display = '';
310 t.rows.forEach(row => {
311 row.el.style.display = '';
312 });
313 });
314 };
315 }, []);
316 useEffect(() => {
317 if (menuOpen !== null) focusSelected(menuOpen);
318 }, [menuOpen]);
319 if (disabled) return null;
320 const facetActive = Object.keys(sel).some(h => sel[h] && sel[h] !== 'All');
321 const sortOptions = [firstHead].concat(facetList.map(f => f.name)).filter((h, i, a) => h && a.indexOf(h) === i);
322 return <>
323 <style>{`
324 .sf-root {
325 --sf-accent: #D97757;
326 --sf-bg: #fff;
327 --sf-border: #E8E6DC;
328 --sf-text: #141413;
329 --sf-text-3: #73726C;
330 --sf-text-4: #9C9A92;
331 }
332 .dark .sf-root {
333 --sf-bg: #1a1918;
334 --sf-border: #3a3936;
335 --sf-text: #e8e6dc;
336 --sf-text-3: #9c9a92;
337 --sf-text-4: #73726c;
338 }
339 .sf-root .sf-end {
340 position: absolute;
341 right: 10px;
342 top: 50%;
343 transform: translateY(-50%);
344 }
345 .sf-root .sf-x {
346 background: none;
347 border: none;
348 cursor: pointer;
349 color: var(--sf-text-3);
350 font-size: 14px;
351 padding: 2px 4px;
352 line-height: 1;
353 }
354 `}</style>
355 <div ref={rootRef} className="sf-root" style={{
356 margin: '16px 0 8px'
357 }}>
358 <div style={{
359 display: 'flex',
360 gap: '8px',
361 flexWrap: 'wrap',
362 alignItems: 'center'
363 }}>
364 <div style={{
365 position: 'relative',
366 flex: '1 1 260px',
367 maxWidth: '480px'
368 }}>
369 <input ref={searchRef} value={q} onChange={e => onChange(e.target.value)} placeholder={placeholderText} aria-label={placeholderText} style={{
370 width: '100%',
371 padding: '8px 56px 8px 12px',
372 borderRadius: '8px',
373 border: '1px solid var(--sf-border)',
374 background: 'var(--sf-bg)',
375 color: 'var(--sf-text)',
376 fontSize: '14px',
377 outline: 'none',
378 boxSizing: 'border-box'
379 }} />
380 {q ? <button type="button" onClick={() => {
381 onChange('');
382 if (searchRef.current) searchRef.current.focus();
383 }} aria-label="Clear text" className="sf-end sf-x">
384 ×
385 </button> : <span className="sf-end" style={{
386 fontFamily: 'var(--font-mono, ui-monospace, monospace)',
387 fontSize: '11px',
388 color: 'var(--sf-text-4)',
389 border: '1px solid var(--sf-border)',
390 borderRadius: '3px',
391 padding: '0 5px',
392 pointerEvents: 'none'
393 }}>
394 /
395 </span>}
396 </div>
397 {facetList.map(f => {
398 const cur = sel[f.name] || 'All';
399 const isOpen = menuOpen === f.name;
400 return <div key={f.name} ref={el => {
401 menuRef.current[f.name] = el;
402 }} style={{
403 position: 'relative'
404 }}>
405 <button type="button" onClick={() => setMenu(isOpen ? null : f.name)} onKeyDown={e => {
406 if (e.key === 'ArrowDown') {
407 e.preventDefault();
408 if (!isOpen) setMenu(f.name); else focusSelected(f.name);
409 }
410 }} aria-haspopup="listbox" aria-expanded={isOpen} style={{
411 display: 'flex',
412 alignItems: 'center',
413 gap: '8px',
414 padding: cur !== 'All' ? '8px 30px 8px 12px' : '8px 12px',
415 borderRadius: '8px',
416 border: '1px solid ' + (cur !== 'All' ? 'var(--sf-accent)' : 'var(--sf-border)'),
417 background: 'var(--sf-bg)',
418 color: cur === 'All' ? 'var(--sf-text-3)' : 'var(--sf-text)',
419 fontSize: '13.5px',
420 cursor: 'pointer',
421 whiteSpace: 'nowrap',
422 maxWidth: '260px'
423 }}>
424 <span style={{
425 overflow: 'hidden',
426 textOverflow: 'ellipsis'
427 }}>
428 {f.label + ': ' + optionLabel(f, cur)}
429 </span>
430 <span aria-hidden="true" style={{
431 fontSize: '9px',
432 color: 'var(--sf-text-4)',
433 transform: isOpen ? 'rotate(180deg)' : 'none',
434 transition: 'transform 120ms'
435 }}>
436 ▼
437 </span>
438 </button>
439 {cur !== 'All' && <button type="button" onClick={() => setFacet(f.name, 'All')} aria-label={'Clear ' + f.label + ' filter'} title={'Clear ' + f.label + ' filter'} className="sf-end sf-x">
440 ×
441 </button>}
442 {isOpen && <div role="listbox" aria-label={f.label} onKeyDown={e => {
443 const items = [...e.currentTarget.querySelectorAll('button')];
444 const idx = items.indexOf(document.activeElement);
445 if (e.key === 'ArrowDown') {
446 e.preventDefault();
447 (items[idx + 1] || items[0]).focus();
448 } else if (e.key === 'ArrowUp') {
449 e.preventDefault();
450 (items[idx - 1] || items[items.length - 1]).focus();
451 } else if (e.key === 'Home') {
452 e.preventDefault();
453 if (items[0]) items[0].focus();
454 } else if (e.key === 'End') {
455 e.preventDefault();
456 if (items[items.length - 1]) items[items.length - 1].focus();
457 } else if (e.key === 'Tab') {
458 closeMenu(f.name);
459 }
460 }} style={{
461 position: 'absolute',
462 top: 'calc(100% + 6px)',
463 left: 0,
464 zIndex: 1000,
465 minWidth: '260px',
466 maxHeight: '340px',
467 overflowY: 'auto',
468 background: 'var(--sf-bg)',
469 border: '1px solid var(--sf-border)',
470 borderRadius: '10px',
471 boxShadow: '0 8px 24px rgba(0,0,0,0.12)',
472 padding: '5px'
473 }}>
474 {['All'].concat(f.values).map(c => {
475 const selected = cur === c;
476 return <button key={c} role="option" aria-selected={selected} tabIndex={-1} onClick={() => setFacet(f.name, c)} style={{
477 display: 'flex',
478 alignItems: 'center',
479 gap: '8px',
480 width: '100%',
481 textAlign: 'left',
482 padding: '7px 10px',
483 borderRadius: '6px',
484 border: 'none',
485 background: 'transparent',
486 color: selected ? 'var(--sf-accent)' : 'var(--sf-text)',
487 fontWeight: selected ? 600 : 400,
488 fontSize: '13.5px',
489 cursor: 'pointer'
490 }}>
491 <span aria-hidden="true" style={{
492 width: '14px',
493 color: 'var(--sf-accent)',
494 flexShrink: 0
495 }}>
496 {selected ? '✓' : ''}
497 </span>
498 {optionLabel(f, c)}
499 </button>;
500 })}
501 </div>}
502 </div>;
503 })}
504 {sortOptions.length > 1 && <div role="group" aria-label="Sort by" style={{
505 display: 'flex',
506 alignItems: 'center',
507 gap: '4px',
508 fontSize: '13px',
509 color: 'var(--sf-text-3)',
510 whiteSpace: 'nowrap'
511 }}>
512 <span style={{
513 marginRight: '4px'
514 }}>Sort by</span>
515 {sortOptions.map(o => {
516 const on = sortBy === o;
517 return <button key={o} type="button" aria-pressed={on} onClick={() => {
518 setSortBy(o);
519 sortTables(o);
520 }} style={{
521 padding: '6px 10px',
522 borderRadius: '8px',
523 border: '1px solid ' + (on ? 'var(--sf-accent)' : 'var(--sf-border)'),
524 background: 'var(--sf-bg)',
525 color: on ? 'var(--sf-text)' : 'var(--sf-text-3)',
526 fontSize: '13px',
527 cursor: 'pointer'
528 }}>
529 {o.charAt(0).toUpperCase() + o.slice(1)}
530 </button>;
531 })}
532 </div>}
533 </div>
534 <div aria-live="polite" style={{
535 margin: '8px 0 0',
536 fontSize: '13px',
537 color: 'var(--sf-text-3)',
538 minHeight: '1px'
539 }}>
540 {q.trim() === '' && !facetActive ? <>{counts.total} {nounText}</> : counts.shown === 0 ? <>
541 {q.trim() === '' ? 'No ' + nounText + ' match the selected filters.' : facetActive ? 'No ' + nounText + ' match \u201c' + q + '\u201d with the selected filters.' : 'No ' + nounText + ' match \u201c' + q + '\u201d.'}{' '}
542 <button type="button" onClick={clearAll} style={{
543 background: 'none',
544 border: 'none',
545 padding: 0,
546 color: 'var(--sf-accent)',
547 cursor: 'pointer',
548 font: 'inherit',
549 textDecoration: 'underline'
550 }}>
551 Clear filters
552 </button>
553 {children ? <> {children}</> : null}
554 </> : <>
555 Showing {counts.shown} of {counts.total} {nounText}
556 </>}
557 </div>
558 </div>
559 </>;
560};
561 
562<BackToIndex href="#all-settings" label="Back to index" />
5[Back to index](#all-settings)
5636 
5647This reference page lists each key Claude Code reads from a settings file, plus the [short group of keys](#global-config-settings) it keeps in `~/.claude.json` instead. To pick a file, or check precedence, start with [Settings files and precedence](/docs/en/settings).
5658 
566<span id="available-settings" />
567 
568<span id="scopes" />
569 
570<span id="all-settings" />
571 
5729## Settings index
57310 
57411Every key below links to its entry. Scope lists the [files](/docs/en/settings#settings-files-and-who-they-affect) it can go in: `User` is `~/.claude/settings.json`, `Project` is `.claude/settings.json`, `Local` is `.claude/settings.local.json`, and `Managed` is [what your organization deploys](/docs/en/managed-settings). `Any file` means all four, and `Global config` means [`~/.claude.json`](#global-config-settings).
575 
576<ReferenceFilter
577 noun="settings"
578 placeholder="Filter settings by key or purpose"
579 facetOrder={{ scope: ["Any file", "User, local, or managed", "User or managed", "User", "Managed", "Global config"] }}
580 columnHelp={{
581topic: "The section of this page that holds the entry. Use Sort by to group the table by topic.",
582scope: "Which settings files can set the key: user (~/.claude/settings.json), project (.claude/settings.json), local (.claude/settings.local.json), or managed (deployed by your organization). Global config keys are in ~/.claude.json instead.",
583}}
584/>
58512 
58613| Key | Description | Topic | Scope |
58714| :- | :- | :- | :- |
from line 574
1147574}
1148575```
1149576 
1150<span id="modelpicker-options" />
1151 
1152<span id="modelpicker-replacebuiltinoptions" />
1153 
1154577#### Fields for `modelPicker`
1155578 
1156579The key takes two fields, one for the rows themselves and one for whether they replace the built-in lineup or add to it.
from line 627
1204627 
1205628For the steps, including how to confirm the rates are in effect, see [Report spend at your contracted rates](/docs/en/costs#report-spend-at-your-contracted-rates).
1206629 
1207<span id="modelpricing-multiplier" />
1208 
1209<span id="modelpricing-overrides" />
1210 
1211630#### Fields for `modelPricing`
1212631 
1213632| Field | Type | What it does |
from line 976
1557976 }
1558977}
1559978```
1560 
1561<span id="exclude-sensitive-files" />
1562979 
1563980### `permissions.deny`
1564981 
from line 1703
22861703Paths use the same [prefixes](#sandbox-path-prefixes) as the `sandbox.filesystem.*` settings, and Claude Code merges the arrays from every settings scope the session loads. [Protect credentials](/docs/en/sandboxing#protect-credentials) covers what still applies from sources you exclude with `--setting-sources`. `mask` entries require Claude Code v2.1.221 or later.
22871704 
22881705`mask` substitution runs only through the sandbox proxy, so set [`sandbox.network.tlsTerminate`](#sandbox-network-tlsterminate), or [`allowPlaintextInject`](#sandbox-credentials-allowplaintextinject) for plain-HTTP test networks. `mask` applies to a single file, so list each credential file individually. Claude Code accepts but ignores the `mask` fields on a `deny` entry. [Mask credentials](/docs/en/sandboxing#mask-credentials) covers which settings sources are honored, and [Mask credential files](/docs/en/sandboxing#mask-credential-files) covers when an entry falls back to `deny`.
2289 
2290<span id="sandbox-credentials-files-extract" />
2291 
2292<span id="sandbox-credentials-files-onextractnomatch" />
2293 
2294<span id="sandbox-credentials-files-decode" />
2295 
2296<span id="sandbox-credentials-files-maskclaims" />
2297 
2298<span id="sandbox-credentials-files-maskduplicates" />
2299 
2300<span id="sandbox-credentials-files-injecthosts" />
23011706 
23021707#### Mask fields for files
23031708 
from line 1764
23591764The `name` must start with a letter or underscore and contain only letters, digits, and underscores. Claude Code merges the arrays from every settings scope the session loads, and applies `deny` when the same variable appears with both modes. [Protect credentials](/docs/en/sandboxing#protect-credentials) covers what still applies from sources you exclude with `--setting-sources`.
23601765 
23611766`mask` substitution runs only through the sandbox proxy, so set [`sandbox.network.tlsTerminate`](#sandbox-network-tlsterminate), or [`allowPlaintextInject`](#sandbox-credentials-allowplaintextinject) for plain-HTTP test networks; see [Mask credentials](/docs/en/sandboxing#mask-credentials). Claude Code accepts but ignores the `mask` fields on a `deny` entry.
2362 
2363<span id="sandbox-credentials-envvars-extract" />
2364 
2365<span id="sandbox-credentials-envvars-onextractnomatch" />
2366 
2367<span id="sandbox-credentials-envvars-decode" />
2368 
2369<span id="sandbox-credentials-envvars-maskclaims" />
2370 
2371<span id="sandbox-credentials-envvars-injecthosts" />
23721767 
23731768#### Mask fields for environment variables
23741769 
from line 2139
27442139 
27452140When more than one honored source sets it, Claude Code uses the value from the highest-precedence source: managed settings, then the `--settings` flag, then user settings.
27462141 
2747<span id="context-and-memory" />
2748 
27492142## Memory and context
27502143 
27512144Control what Claude Code loads into context, how it compacts, and where it keeps memory and plans. See [Manage context](/docs/en/context-window) and [Memory](/docs/en/memory).
from line 2261
28682261```
28692262 
28702263Exclusions apply only to user, project, and local memory files; managed policy CLAUDE.md files can't be excluded.
2871 
2872<span id="environment-variables" />
28732264 
28742265### `env`
28752266 
from line 2350
29592350 
29602351### `idleCompaction`
29612352 
2962Idle compaction [compacts](/docs/en/context-window#what-survives-compaction) a long conversation that you've stepped away from, shortly before its prompt cache expires. Your next message then sends a summary instead of the whole conversation. When you come back, the conversation shows `Compacted while idle, before the prompt cache expired`.
2353Idle compaction [compacts](/docs/en/context-window#what-survives-compaction) a long conversation that you've stepped away from, shortly before its prompt cache expires. Your next message then sends a summary instead of the whole conversation. When you come back, the conversation shows a line that starts with `Compacted while idle, before the prompt cache expired`.
29632354 
29642355Idle compaction is on by default for conversations that meet all of these conditions:
29652356 
from line 2375
29842375}
29852376```
29862377 
2378To turn idle compaction off or on from inside a session, run `/config` and change the **Idle compaction** row. Your choice is saved to your user settings and applies right away. The row requires Claude Code v2.1.302 or later. If you run one of those versions and `/config` doesn't show the row, check for these causes:
2379 
2380* **Another settings source sets the key**: a value in managed settings, the `--settings` flag, or a project or local settings file overrides your user settings. Change the key in that source instead
2381* **Feature flags are unavailable**: where Claude Code can't [fetch feature flags from Anthropic](/docs/en/env-vars#features-that-need-feature-flag-fetching), idle compaction doesn't run, so there is nothing to turn off
2382 
29872383The key requires Claude Code v2.1.290 or later. On an earlier version, which ignores this key, turn off [`autoCompactEnabled`](#autocompactenabled) to stop idle compaction.
29882384 
29892385#### Change the idle compaction threshold
from line 2449
30532449 
30542450### `taskOutputMaxChars`
30552451 
3056<Warning>
3057 Removed in v2.1.277, together with the `TaskOutput` tool it sized. Setting it has no effect on current versions. Claude reads a background task's [output file](/docs/en/tools-reference#background-commands) with `Read` instead.
3058</Warning>
2452> [!WARNING]
2453>
2454> Removed in v2.1.277, together with the `TaskOutput` tool it sized. Setting it has no effect on current versions. Claude reads a background task's [output file](/docs/en/tools-reference#background-commands) with `Read` instead.
30592455 
30602456Through v2.1.276, you set this key to the number of characters of a [background task's](/docs/en/tools-reference#background-commands) output that Claude received inline when it read the task with the `TaskOutput` tool.
30612457 
from line 2636
32402636```
32412637 
32422638See [Emoji shortcodes](/docs/en/interactive-mode#emoji-shortcodes). Requires Claude Code v2.1.217 or later.
3243 
3244<span id="file-suggestion-settings" />
32452639 
32462640### `fileSuggestion`
32472641 
from line 2680
32862680# Replace your-repo-file-index with your own file search command
32872681your-repo-file-index --query "$query" | head -20
32882682```
3289 
3290<span id="footer-link-badges" />
32912683 
32922684### `footerLinksRegexes`
32932685 
from line 2724
33322724 
33332725### `keybindingFlavor`
33342726 
3335<Warning>
3336 Deprecated since v2.1.261 and has no effect. The prompt's word-editing keys always [follow readline conventions](/docs/en/interactive-mode#make-ctrl-w-delete-back-to-whitespace), as in Bash. Claude Code still accepts `keybindingFlavor`, so a settings file that sets it stays valid.
3337</Warning>
2727> [!WARNING]
2728>
2729> Deprecated since v2.1.261 and has no effect. The prompt's word-editing keys always [follow readline conventions](/docs/en/interactive-mode#make-ctrl-w-delete-back-to-whitespace), as in Bash. Claude Code still accepts `keybindingFlavor`, so a settings file that sets it stays valid.
33382730 
33392731In v2.1.238 through v2.1.260, setting it to `"readline"` made `Ctrl+W` delete back to the previous whitespace instead of only the previous word.
33402732 
from line 3231
38393231 
38403232### `voiceEnabled`
38413233 
3842<Warning>
3843 Deprecated since v2.1.92, when the [`voice`](#voice) object replaced it. Claude Code still reads it so older settings files keep working, but new configurations should set `voice.enabled`.
3844</Warning>
3234> [!WARNING]
3235>
3236> Deprecated since v2.1.92, when the [`voice`](#voice) object replaced it. Claude Code still reads it so older settings files keep working, but new configurations should set `voice.enabled`.
38453237 
38463238Turn voice dictation on with the single Boolean form that predates the `voice` object. When both are set, `voice.enabled` applies.
38473239 
from line 3268
38763268## Git and attribution
38773269 
38783270Control the attribution Claude Code adds to commits and pull requests and how it works with git.
3879 
3880<span id="attribution-settings" />
38813271 
38823272### `attribution`
38833273 
from line 3297
39073297 
39083298### `includeCoAuthoredBy`
39093299 
3910<Warning>
3911 Deprecated since v2.0.62, when [`attribution`](#attribution) replaced it. Claude Code still reads it, but new configurations should set `attribution`.
3912</Warning>
3300> [!WARNING]
3301>
3302> Deprecated since v2.0.62, when [`attribution`](#attribution) replaced it. Claude Code still reads it, but new configurations should set `attribution`.
39133303 
39143304Use [`attribution`](#attribution) instead, which replaces this key and lets you change or hide the commit trailer, the pull request text, and the session link separately. Claude Code still honors `includeCoAuthoredBy: false` from settings files that predate `attribution`, but ignores it once you set `attribution.commit` or `attribution.pr`.
39153305 
from line 3410
40203410 }
40213411}
40223412```
4023 
4024<span id="hook-configuration" />
4025 
4026<span id="hook-and-skill-settings" />
40273413 
40283414## Hooks and automation
40293415 
from line 3621
42353621```
42363622 
42373623Requires Claude Code v2.1.219 or later; on v2.1.202 through v2.1.218, set the guideline in `/config` instead.
4238 
4239<span id="plugin-configuration" />
4240 
4241<span id="manage-plugins" />
4242 
4243<span id="plugin-settings" />
42443624 
42453625## Plugins and skills
42463626 
from line 4028
46484028 
46494029### `strictPluginOnlyCustomization.hooks`
46504030 
4651Lock the `hooks` surface. Claude Code stops running hooks from user, project, and local `settings.json`, and keeps running plugin hooks and hooks in managed settings.
4031Lock the `hooks` surface:
4032 
4033* **Stops running**:
4034 * Hooks from user, project, and local `settings.json`
4035 * Frontmatter hooks in skills, custom commands, and agents, from the sources that a [`skills`](#strictpluginonlycustomization-skills) or [`agents`](#strictpluginonlycustomization-agents) lock would block
4036* **Keeps running**: plugin hooks and hooks in managed settings
4037 
4038Skills, custom commands, and agents whose frontmatter hooks stop running still load while their own surface isn't locked.
46524039 
46534040* **Scope**: [`Managed`](#scopes)
46544041* **Type**: the string `"hooks"` in the [`strictPluginOnlyCustomization`](#strictpluginonlycustomization) array
from line 4572
51854572}
51864573```
51874574 
5188<span id="worktree-settings" />
5189 
51904575### `worktree`
51914576 
51924577Configure how Claude Code creates and manages [git worktrees](/docs/en/worktrees) for `--worktree`, the `EnterWorktree` tool, and isolated subagents and background sessions.
from line 4734
53494734 
53504735### `disableArtifact`
53514736 
5352<Warning>
5353 Deprecated, and replaced by [`enableArtifact`](#enableartifact). Claude Code still honors `disableArtifact: true` as equivalent to `enableArtifact: false`, and ignores `disableArtifact: false`.
5354</Warning>
4737> [!WARNING]
4738>
4739> Deprecated, and replaced by [`enableArtifact`](#enableartifact). Claude Code still honors `disableArtifact: true` as equivalent to `enableArtifact: false`, and ignores `disableArtifact: false`.
53554740 
53564741Use [`enableArtifact`](#enableartifact) instead to turn off the [Artifact](/docs/en/artifacts) tool, which publishes session output as a private web page on claude.ai. When you turn the **Artifacts** row off in `/config`, Claude Code writes `enableArtifact` to your user settings and clears this key.
53574742 
from line 4950
55654950A value that Desktop can't read as a list of hosts, such as `true` or an object, counts as an empty array until you correct it, except `null`, which counts as unset. Requires Claude Desktop v2.26454.0 or later.
55664951 
55674952If you set [`managedSourcesBehavior`](#managedsourcesbehavior) to `"merge"` in your highest-ranked source, Desktop combines the lists from every [admin source](/docs/en/managed-settings#how-claude-code-combines-managed-sources) and allows a host that matches any of them. If you set an empty array in one source, SSH sessions stay on for the hosts another source lists.
5568 
5569<span id="authentication-and-login" />
55704953 
55714954## Authentication and providers
55724955 
from line 5296
59135296 
59145297The desktop app ignores any other value, and a value that isn't a Boolean, such as the string `"true"` or `1`, also logs a warning.
59155298 
5916<span id="data-and-privacy" />
5917 
59185299## Privacy and telemetry
59195300 
59205301Control how long Claude Code keeps session data and what it sends. The switches that turn off usage metrics and error reports are environment variables, not settings keys: set `DISABLE_TELEMETRY`, `DISABLE_ERROR_REPORTING`, or `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` in the [`env`](#env) key or in the shell. [Telemetry services](/docs/en/data-usage#telemetry-services) says what each one stops. Two exceptions turn off from a settings file: [`feedbackDrafts`](#feedbackdrafts) below for Claude-drafted feedback, and [`feedbackSurveyRate`](#feedbacksurveyrate) below for the session survey.
from line 5384
60035384```
60045385 
60055386With the check skipped, WebFetch attempts any URL without consulting the blocklist, so pair it with [`WebFetch` permission rules](/docs/en/permissions#webfetch) if you need to restrict which domains Claude can reach.
6006 
6007<span id="managed-policy" />
60085387 
60095388## Enterprise and managed settings
60105389 
from line 5498
61195498 
61205499This key has no effect when no admin-deployed managed tier exists: the host's settings then apply as the only managed tier, still filtered to restrictive values. For the filter's limits and how the managed sources interact, see [Parent settings from embedding hosts](/docs/en/managed-settings#parent-settings-from-embedding-hosts) and [Restrict parent settings](/docs/en/claude-apps-gateway#restrict-parent-settings).
61215500 
6122<span id="compute-managed-settings-with-a-policy-helper" />
6123 
61245501### `policyHelper`
61255502 
61265503Run an executable you deploy that computes managed settings at startup, so you can derive policy from device posture, identity, or a remote service instead of a static file. Claude Code runs the helper before it accepts the first prompt and treats the settings it emits as the managed settings for the session.
from line 5821
64445821 
64455822### `permissionExplainerEnabled`
64465823 
6447<Warning>
6448 Removed in v2.1.257, together with the `Ctrl+E` command explanation on Bash and PowerShell permission prompts. Setting it has no effect on current versions.
6449</Warning>
5824> [!WARNING]
5825>
5826> Removed in v2.1.257, together with the `Ctrl+E` command explanation on Bash and PowerShell permission prompts. Setting it has no effect on current versions.
64505827 
64515828Through v2.1.256, you could press `Ctrl+E` on a Bash or PowerShell permission prompt to see a model-generated explanation of the command, and set this key to `false` to turn that shortcut off.
64525829 
from line 5851
64745851 
64755852### `teammateDefaultModel`
64765853 
6477<Warning>
6478 Removed in v2.1.234, together with its `/config` row **Default teammate model**. Setting it has no effect on current versions.
6479</Warning>
5854> [!WARNING]
5855>
5856> Removed in v2.1.234, together with its `/config` row **Default teammate model**. Setting it has no effect on current versions.
64805857 
64815858Through v2.1.233, you set this key to the model for [agent team](/docs/en/agent-teams#specify-teammates-and-models) teammates your prompt didn't name a model for: an alias such as `"sonnet"`, or `null` to follow the lead's model. For the model Claude Code picks for such teammates now, see [specify teammates and models](/docs/en/agent-teams#specify-teammates-and-models).
64825859 
64835860 

setup Changed · +340 / -370 lines

The two sides of this change are more than 400 edits apart, too far apart to line up, so this is the differ's own diff of it and the words inside a line are not marked.

from line 25
2525 
2626## Install Claude Code
2727 
28<Tip>
29 Prefer a graphical interface? The [Desktop app](/docs/en/desktop-quickstart) lets you use Claude Code without the terminal. Download it for [macOS](https://claude.ai/api/desktop/darwin/universal/dmg/latest/redirect?utm_source=claude_code\&utm_medium=docs), [Windows](https://claude.com/download?utm_source=claude_code\&utm_medium=docs), or [Linux](/docs/en/desktop-linux).
30</Tip>
28> [!TIP]
29>
30> Prefer a graphical interface? The [Desktop app](/docs/en/desktop-quickstart) lets you use Claude Code without the terminal. Download it for [macOS](https://claude.ai/api/desktop/darwin/universal/dmg/latest/redirect?utm_source=claude_code\&utm_medium=docs), [Windows](https://claude.com/download?utm_source=claude_code\&utm_medium=docs), or [Linux](/docs/en/desktop-linux).
3131 
3232To install Claude Code, open a terminal and run the command for your system. If you haven't used a terminal before, the [terminal guide](/docs/en/terminal-guide) shows how to open one and paste the command.
3333 
34<Tabs>
35 <Tab title="Native Install (Recommended)">
36 **macOS, Linux, WSL:**
37 
38 ```bash theme={null}
39 curl -fsSL https://claude.ai/install.sh | bash
40 ```
41 
42 On Windows, your prompt shows `PS C:\` when you're in PowerShell and `C:\` without the `PS` when you're in CMD.
43 
44 **Windows PowerShell:**
45 
46 ```powershell theme={null}
47 irm https://claude.ai/install.ps1 | iex
48 ```
49 
50 **Windows CMD:**
51 
52 ```batch theme={null}
53 curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd
54 ```
55 
56 The install command shows no progress while it downloads Claude Code. When the installer finishes, open a new terminal window and run `claude --version`. A working installation prints a version number. If your shell says `claude` isn't found or isn't recognized, the install directory isn't on your PATH yet: see [Fix your PATH](/docs/en/troubleshoot-install#command-not-found-claude-after-installation).
57 
58 If you see `The token '&&' is not a valid statement separator`, you're in PowerShell, not CMD. If you see `'irm' is not recognized as an internal or external command`, you're in CMD, not PowerShell.
59 
60 If the install command fails with `syntax error near unexpected token '<'`, a `403`, or any other error, see [Troubleshoot installation](/docs/en/troubleshoot-install#find-your-error) to match the error to a fix and for alternative install methods.
61 
62 [Git for Windows](https://git-scm.com/downloads/win) is recommended on native Windows so Claude Code can use the Bash tool. If Git for Windows is not installed, Claude Code uses PowerShell as the shell tool instead. WSL setups do not need Git for Windows.
63 
64 <Info>
65 Native installations automatically update in the background to keep you on the latest version.
66 </Info>
67 </Tab>
68 
69 <Tab title="Homebrew">
70 ```bash theme={null}
71 brew install --cask claude-code
72 ```
73 
74 Homebrew offers two casks. `claude-code` tracks the stable release channel, which is typically about a week behind and skips releases with major regressions. `claude-code@latest` tracks the latest channel and receives new versions as soon as they ship.
75 
76 <Info>
77 Homebrew installations do not auto-update. Run `brew upgrade claude-code` or `brew upgrade claude-code@latest`, depending on which cask you installed, to get the latest features and security fixes.
78 </Info>
79 </Tab>
80 
81 <Tab title="WinGet">
82 ```powershell theme={null}
83 winget install Anthropic.ClaudeCode
84 ```
85 
86 <Info>
87 WinGet installations do not auto-update. Run `winget upgrade Anthropic.ClaudeCode` periodically to get the latest features and security fixes.
88 </Info>
89 </Tab>
90</Tabs>
34**Native Install (Recommended)**
35 
36**macOS, Linux, WSL:**
37 
38```bash theme={null}
39curl -fsSL https://claude.ai/install.sh | bash
40```
41 
42On Windows, your prompt shows `PS C:\` when you're in PowerShell and `C:\` without the `PS` when you're in CMD.
43 
44**Windows PowerShell:**
45 
46```powershell theme={null}
47irm https://claude.ai/install.ps1 | iex
48```
49 
50**Windows CMD:**
51 
52```batch theme={null}
53curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd
54```
55 
56The install command shows no progress while it downloads Claude Code. When the installer finishes, open a new terminal window and run `claude --version`. A working installation prints a version number. If your shell says `claude` isn't found or isn't recognized, the install directory isn't on your PATH yet: see [Fix your PATH](/docs/en/troubleshoot-install#command-not-found-claude-after-installation).
57 
58If you see `The token '&&' is not a valid statement separator`, you're in PowerShell, not CMD. If you see `'irm' is not recognized as an internal or external command`, you're in CMD, not PowerShell.
59 
60If the install command fails with `syntax error near unexpected token '<'`, a `403`, or any other error, see [Troubleshoot installation](/docs/en/troubleshoot-install#find-your-error) to match the error to a fix and for alternative install methods.
61 
62[Git for Windows](https://git-scm.com/downloads/win) is recommended on native Windows so Claude Code can use the Bash tool. If Git for Windows is not installed, Claude Code uses PowerShell as the shell tool instead. WSL setups do not need Git for Windows.
63 
64> [!NOTE]
65>
66> Native installations automatically update in the background to keep you on the latest version.
67 
68**Homebrew**
69 
70```bash theme={null}
71brew install --cask claude-code
72```
73 
74Homebrew offers two casks. `claude-code` tracks the stable release channel, which is typically about a week behind and skips releases with major regressions. `claude-code@latest` tracks the latest channel and receives new versions as soon as they ship.
75 
76> [!NOTE]
77>
78> Homebrew installations do not auto-update. Run `brew upgrade claude-code` or `brew upgrade claude-code@latest`, depending on which cask you installed, to get the latest features and security fixes.
79 
80**WinGet**
81 
82```powershell theme={null}
83winget install Anthropic.ClaudeCode
84```
85 
86> [!NOTE]
87>
88> WinGet installations do not auto-update. Run `winget upgrade Anthropic.ClaudeCode` periodically to get the latest features and security fixes.
9189 
9290You can also install with [apt, dnf, or apk](/docs/en/setup#install-with-linux-package-managers) on Debian, Fedora, RHEL, and Alpine.
9391 
from line 206
208206 
209207If an npm global install can't auto-update because the npm global directory isn't writable, Claude Code shows a one-time notice at startup, and `claude doctor` lists the available fixes. See [permission errors during installation](/docs/en/troubleshoot-install#permission-errors-during-installation) for details.
210208 
211<Note>
212 Homebrew, WinGet, apt, dnf, and apk installations do not auto-update by default; see below to opt in for Homebrew and WinGet. To upgrade Homebrew manually, run `brew upgrade claude-code` or `brew upgrade claude-code@latest`, depending on which cask you installed. For WinGet, run `winget upgrade Anthropic.ClaudeCode`. For Linux package managers, see the upgrade commands in [Install with Linux package managers](#install-with-linux-package-managers).
213 
214 To have Claude Code run the upgrade command for you on Homebrew or WinGet, set [`CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE`](/docs/en/env-vars) to `1`. Claude Code then runs the upgrade in the background when a new version is available and shows a restart prompt on success. The upgrade targets only the Claude Code package and does not affect other software you have installed.
215 
216 On WinGet the upgrade may fail while Claude Code is running because Windows locks the executable. In that case Claude Code shows the manual command instead. apt, dnf, and apk continue to require a manual upgrade because those commands need elevated privileges.
217 
218 **Known issue:** Claude Code may notify you of updates before the new version is available in these package managers. If an upgrade fails, wait and try again later.
219 
220 Homebrew keeps old versions on disk after upgrades. Run `brew cleanup` periodically to reclaim disk space.
221</Note>
209> [!NOTE]
210>
211> Homebrew, WinGet, apt, dnf, and apk installations do not auto-update by default; see below to opt in for Homebrew and WinGet. To upgrade Homebrew manually, run `brew upgrade claude-code` or `brew upgrade claude-code@latest`, depending on which cask you installed. For WinGet, run `winget upgrade Anthropic.ClaudeCode`. For Linux package managers, see the upgrade commands in [Install with Linux package managers](#install-with-linux-package-managers).
212>
213> To have Claude Code run the upgrade command for you on Homebrew or WinGet, set [`CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE`](/docs/en/env-vars) to `1`. Claude Code then runs the upgrade in the background when a new version is available and shows a restart prompt on success. The upgrade targets only the Claude Code package and does not affect other software you have installed.
214>
215> On WinGet the upgrade may fail while Claude Code is running because Windows locks the executable. In that case Claude Code shows the manual command instead. apt, dnf, and apk continue to require a manual upgrade because those commands need elevated privileges.
216>
217> **Known issue:** Claude Code may notify you of updates before the new version is available in these package managers. If an upgrade fails, wait and try again later.
218>
219> Homebrew keeps old versions on disk after upgrades. Run `brew cleanup` periodically to reclaim disk space.
222220 
223221### Configure release channel
224222 
from line 296
298296 
299297To install the latest version (default):
300298 
301<Tabs>
302 <Tab title="macOS, Linux, WSL">
303 ```bash theme={null}
304 curl -fsSL https://claude.ai/install.sh | bash
305 ```
306 </Tab>
307 
308 <Tab title="Windows PowerShell">
309 ```powershell theme={null}
310 irm https://claude.ai/install.ps1 | iex
311 ```
312 </Tab>
313 
314 <Tab title="Windows CMD">
315 ```batch theme={null}
316 curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd
317 ```
318 </Tab>
319</Tabs>
299**macOS, Linux, WSL**
300 
301```bash theme={null}
302curl -fsSL https://claude.ai/install.sh | bash
303```
304 
305**Windows PowerShell**
306 
307```powershell theme={null}
308irm https://claude.ai/install.ps1 | iex
309```
310 
311**Windows CMD**
312 
313```batch theme={null}
314curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd
315```
320316 
321317To install the stable version:
322318 
323<Tabs>
324 <Tab title="macOS, Linux, WSL">
325 ```bash theme={null}
326 curl -fsSL https://claude.ai/install.sh | bash -s stable
327 ```
328 </Tab>
329 
330 <Tab title="Windows PowerShell">
331 ```powershell theme={null}
332 & ([scriptblock]::Create((irm https://claude.ai/install.ps1))) stable
333 ```
334 </Tab>
335 
336 <Tab title="Windows CMD">
337 ```batch theme={null}
338 curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd stable && del install.cmd
339 ```
340 </Tab>
341</Tabs>
319**macOS, Linux, WSL**
320 
321```bash theme={null}
322curl -fsSL https://claude.ai/install.sh | bash -s stable
323```
324 
325**Windows PowerShell**
326 
327```powershell theme={null}
328& ([scriptblock]::Create((irm https://claude.ai/install.ps1))) stable
329```
330 
331**Windows CMD**
332 
333```batch theme={null}
334curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd stable && del install.cmd
335```
342336 
343337To install a specific version number:
344338 
345<Tabs>
346 <Tab title="macOS, Linux, WSL">
347 ```bash theme={null}
348 curl -fsSL https://claude.ai/install.sh | bash -s 2.1.89
349 ```
350 </Tab>
351 
352 <Tab title="Windows PowerShell">
353 ```powershell theme={null}
354 & ([scriptblock]::Create((irm https://claude.ai/install.ps1))) 2.1.89
355 ```
356 </Tab>
357 
358 <Tab title="Windows CMD">
359 ```batch theme={null}
360 curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd 2.1.89 && del install.cmd
361 ```
362 </Tab>
363</Tabs>
339**macOS, Linux, WSL**
340 
341```bash theme={null}
342curl -fsSL https://claude.ai/install.sh | bash -s 2.1.89
343```
344 
345**Windows PowerShell**
346 
347```powershell theme={null}
348& ([scriptblock]::Create((irm https://claude.ai/install.ps1))) 2.1.89
349```
350 
351**Windows CMD**
352 
353```batch theme={null}
354curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd 2.1.89 && del install.cmd
355```
364356 
365357To confirm which version installed, run `claude --version`: the command prints the exact version you passed, such as `2.1.89 (Claude Code)`.
366358 
from line 362
370362 
371363All repositories are signed with the [Claude Code release signing key](#binary-integrity-and-code-signing). Before trusting the key, verify it as described in each tab.
372364 
373<Tabs>
374 <Tab title="apt">
375 For Debian and Ubuntu. The install commands below download the signing key with `curl` and verify it with `gpg`, which fresh Debian and Ubuntu installations may not include. If either command reports `command not found`, install both first:
376 
377 ```bash theme={null}
378 sudo apt install curl gnupg
379 ```
380 
381 Download the signing key:
382 
383 ```bash theme={null}
384 sudo install -d -m 0755 /etc/apt/keyrings
385 sudo curl -fsSL https://downloads.claude.ai/keys/claude-code.asc \
386 -o /etc/apt/keyrings/claude-code.asc
387 ```
388 
389 If this download fails, `apt update` later fails with `NO_PUBKEY BAA929FF1A7ECACE`. Confirm the key downloaded and belongs to Anthropic before continuing:
390 
391 ```bash theme={null}
392 gpg --show-keys /etc/apt/keyrings/claude-code.asc
393 ```
394 
395 The fingerprint gpg prints should be `31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE`. If gpg reports that the file can't be opened or contains no valid OpenPGP data, the download failed or returned the wrong content: confirm your network can reach `downloads.claude.ai`, then rerun the download command.
396 
397 Register the repository on the `stable` channel and install:
398 
399 ```bash theme={null}
400 echo "deb [signed-by=/etc/apt/keyrings/claude-code.asc] https://downloads.claude.ai/claude-code/apt/stable stable main" \
401 | sudo tee /etc/apt/sources.list.d/claude-code.list
402 sudo apt update
403 sudo apt install claude-code
404 ```
405 
406 To use the `latest` channel instead, both the URL path and the suite name change. Use this `deb` line:
407 
408 ```bash theme={null}
409 echo "deb [signed-by=/etc/apt/keyrings/claude-code.asc] https://downloads.claude.ai/claude-code/apt/latest latest main" \
410 | sudo tee /etc/apt/sources.list.d/claude-code.list
411 ```
412 
413 To upgrade later, run `sudo apt update && sudo apt upgrade claude-code`.
414 </Tab>
415 
416 <Tab title="dnf">
417 For Fedora and RHEL. The following commands configure the `stable` channel:
418 
419 ```bash theme={null}
420 sudo tee /etc/yum.repos.d/claude-code.repo <<'EOF'
421 [claude-code]
422 name=Claude Code
423 baseurl=https://downloads.claude.ai/claude-code/rpm/stable
424 enabled=1
425 gpgcheck=1
426 gpgkey=https://downloads.claude.ai/keys/claude-code.asc
427 EOF
428 sudo dnf install claude-code
429 ```
430 
431 To use the `latest` channel instead, set `baseurl` to the `latest` repository:
432 
433 ```ini theme={null}
434 baseurl=https://downloads.claude.ai/claude-code/rpm/latest
435 ```
436 
437 dnf downloads the key on first install and prompts you to confirm the fingerprint. Verify it matches `31DD DE24 DDFA B679 F42D 7BD2 BAA9 29FF 1A7E CACE` before accepting.
438 
439 To upgrade later, run `sudo dnf upgrade claude-code`.
440 </Tab>
441 
442 <Tab title="apk">
443 For Alpine Linux. The following commands configure the `stable` channel:
444 
445 ```sh theme={null}
446 wget -O /etc/apk/keys/claude-code.rsa.pub \
447 https://downloads.claude.ai/keys/claude-code.rsa.pub
448 echo "https://downloads.claude.ai/claude-code/apk/stable" >> /etc/apk/repositories
449 apk add claude-code
450 ```
451 
452 To switch to the `latest` channel, remove the `stable` repository line and add the `latest` repository:
453 
454 ```sh theme={null}
455 sed -i '\|downloads.claude.ai/claude-code/apk/stable|d' /etc/apk/repositories
456 echo "https://downloads.claude.ai/claude-code/apk/latest" >> /etc/apk/repositories
457 ```
458 
459 Verify the downloaded key with `sha256sum /etc/apk/keys/claude-code.rsa.pub`, which should report `395759c1f7449ef4cdef305a42e820f3c766d6090d142634ebdb049f113168b6`.
460 
461 To upgrade later, run `apk update && apk upgrade claude-code`.
462 </Tab>
463</Tabs>
365**apt**
366 
367For Debian and Ubuntu. The install commands below download the signing key with `curl` and verify it with `gpg`, which fresh Debian and Ubuntu installations may not include. If either command reports `command not found`, install both first:
368 
369```bash theme={null}
370sudo apt install curl gnupg
371```
372 
373Download the signing key:
374 
375```bash theme={null}
376sudo install -d -m 0755 /etc/apt/keyrings
377sudo curl -fsSL https://downloads.claude.ai/keys/claude-code.asc \
378 -o /etc/apt/keyrings/claude-code.asc
379```
380 
381If this download fails, `apt update` later fails with `NO_PUBKEY BAA929FF1A7ECACE`. Confirm the key downloaded and belongs to Anthropic before continuing:
382 
383```bash theme={null}
384gpg --show-keys /etc/apt/keyrings/claude-code.asc
385```
386 
387The fingerprint gpg prints should be `31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE`. If gpg reports that the file can't be opened or contains no valid OpenPGP data, the download failed or returned the wrong content: confirm your network can reach `downloads.claude.ai`, then rerun the download command.
388 
389Register the repository on the `stable` channel and install:
390 
391```bash theme={null}
392echo "deb [signed-by=/etc/apt/keyrings/claude-code.asc] https://downloads.claude.ai/claude-code/apt/stable stable main" \
393 | sudo tee /etc/apt/sources.list.d/claude-code.list
394sudo apt update
395sudo apt install claude-code
396```
397 
398To use the `latest` channel instead, both the URL path and the suite name change. Use this `deb` line:
399 
400```bash theme={null}
401echo "deb [signed-by=/etc/apt/keyrings/claude-code.asc] https://downloads.claude.ai/claude-code/apt/latest latest main" \
402 | sudo tee /etc/apt/sources.list.d/claude-code.list
403```
404 
405To upgrade later, run `sudo apt update && sudo apt upgrade claude-code`.
406 
407**dnf**
408 
409For Fedora and RHEL. The following commands configure the `stable` channel:
410 
411```bash theme={null}
412sudo tee /etc/yum.repos.d/claude-code.repo <<'EOF'
413[claude-code]
414name=Claude Code
415baseurl=https://downloads.claude.ai/claude-code/rpm/stable
416enabled=1
417gpgcheck=1
418gpgkey=https://downloads.claude.ai/keys/claude-code.asc
419EOF
420sudo dnf install claude-code
421```
422 
423To use the `latest` channel instead, set `baseurl` to the `latest` repository:
424 
425```ini theme={null}
426baseurl=https://downloads.claude.ai/claude-code/rpm/latest
427```
428 
429dnf downloads the key on first install and prompts you to confirm the fingerprint. Verify it matches `31DD DE24 DDFA B679 F42D 7BD2 BAA9 29FF 1A7E CACE` before accepting.
430 
431To upgrade later, run `sudo dnf upgrade claude-code`.
432 
433**apk**
434 
435For Alpine Linux. The following commands configure the `stable` channel:
436 
437```sh theme={null}
438wget -O /etc/apk/keys/claude-code.rsa.pub \
439 https://downloads.claude.ai/keys/claude-code.rsa.pub
440echo "https://downloads.claude.ai/claude-code/apk/stable" >> /etc/apk/repositories
441apk add claude-code
442```
443 
444To switch to the `latest` channel, remove the `stable` repository line and add the `latest` repository:
445 
446```sh theme={null}
447sed -i '\|downloads.claude.ai/claude-code/apk/stable|d' /etc/apk/repositories
448echo "https://downloads.claude.ai/claude-code/apk/latest" >> /etc/apk/repositories
449```
450 
451Verify the downloaded key with `sha256sum /etc/apk/keys/claude-code.rsa.pub`, which should report `395759c1f7449ef4cdef305a42e820f3c766d6090d142634ebdb049f113168b6`.
452 
453To upgrade later, run `apk update && apk upgrade claude-code`.
464454 
465455### Install with npm
466456 
from line 466
476466 
477467To upgrade an npm installation, run `npm install -g @anthropic-ai/claude-code@latest`. Avoid `npm update -g`, which respects the semver range from the original install and may not move you to the newest release.
478468 
479<Warning>
480 Do NOT use `sudo npm install -g` as this can lead to permission issues and security risks. If you encounter permission errors, see [troubleshooting permission errors](/docs/en/troubleshoot-install#permission-errors-during-installation).
481</Warning>
469> [!WARNING]
470>
471> Do NOT use `sudo npm install -g` as this can lead to permission issues and security risks. If you encounter permission errors, see [troubleshooting permission errors](/docs/en/troubleshoot-install#permission-errors-during-installation).
482472 
483473### Install on network storage
484474 
from line 491
501491 
502492Steps 1-3 require a POSIX shell with `gpg` and `curl`. On Windows, run them in Git Bash or WSL. Step 4 includes a PowerShell option.
503493 
504<Steps>
505 <Step title="Download and import the public key">
506 The release signing key is published at a fixed URL.
507 
508 ```bash theme={null}
509 curl -fsSL https://downloads.claude.ai/keys/claude-code.asc | gpg --import
510 ```
511 
512 Display the fingerprint of the imported key.
513 
514 ```bash theme={null}
515 gpg --fingerprint [email protected]
516 ```
517 
518 Confirm the output includes this fingerprint:
519 
520 ```text theme={null}
521 31DD DE24 DDFA B679 F42D 7BD2 BAA9 29FF 1A7E CACE
522 ```
523 </Step>
524 
525 <Step title="Download the manifest and signature">
526 Set `VERSION` to the release you want to verify.
527 
528 ```bash theme={null}
529 REPO=https://downloads.claude.ai/claude-code-releases
530 VERSION=2.1.89
531 curl -fsSLO "$REPO/$VERSION/manifest.json"
532 curl -fsSLO "$REPO/$VERSION/manifest.json.sig"
533 ```
534 </Step>
535 
536 <Step title="Verify the signature">
537 Verify the detached signature against the manifest.
538 
539 ```bash theme={null}
540 gpg --verify manifest.json.sig manifest.json
541 ```
542 
543 A valid result reports `Good signature from "Anthropic Claude Code Release Signing <[email protected]>"`.
544 
545 `gpg` also prints `WARNING: This key is not certified with a trusted signature!` for any freshly imported key. This is expected. The `Good signature` line confirms the cryptographic check passed. The fingerprint comparison in Step 1 confirms the key itself is authentic.
546 </Step>
547 
548 <Step title="Check the binary against the manifest">
549 Compare the SHA256 checksum of the binary with the value listed under `platforms.<platform>.checksum` in `manifest.json`. The commands below assume a `claude` binary in the current directory. To verify an installed native binary instead, run the command against `~/.local/share/claude/versions/VERSION`, replacing VERSION with the release you set in Step 2.
550 
551 <Tabs>
552 <Tab title="Linux">
553 ```bash theme={null}
554 sha256sum claude
555 ```
556 </Tab>
557 
558 <Tab title="macOS">
559 ```bash theme={null}
560 shasum -a 256 claude
561 ```
562 </Tab>
563 
564 <Tab title="Windows PowerShell">
565 ```powershell theme={null}
566 (Get-FileHash claude.exe -Algorithm SHA256).Hash.ToLower()
567 ```
568 </Tab>
569 </Tabs>
570 </Step>
571</Steps>
572 
573<Note>
574 Manifest signatures are available for releases from `2.1.89` onward. Earlier releases publish checksums in `manifest.json` without a detached signature.
575</Note>
494**1. Download and import the public key**
495 
496The release signing key is published at a fixed URL.
497 
498```bash theme={null}
499curl -fsSL https://downloads.claude.ai/keys/claude-code.asc | gpg --import
500```
501 
502Display the fingerprint of the imported key.
503 
504```bash theme={null}
505gpg --fingerprint [email protected]
506```
507 
508Confirm the output includes this fingerprint:
509 
510```text theme={null}
51131DD DE24 DDFA B679 F42D 7BD2 BAA9 29FF 1A7E CACE
512```
513 
514**2. Download the manifest and signature**
515 
516Set `VERSION` to the release you want to verify.
517 
518```bash theme={null}
519REPO=https://downloads.claude.ai/claude-code-releases
520VERSION=2.1.89
521curl -fsSLO "$REPO/$VERSION/manifest.json"
522curl -fsSLO "$REPO/$VERSION/manifest.json.sig"
523```
524 
525**3. Verify the signature**
526 
527Verify the detached signature against the manifest.
528 
529```bash theme={null}
530gpg --verify manifest.json.sig manifest.json
531```
532 
533A valid result reports `Good signature from "Anthropic Claude Code Release Signing <[email protected]>"`.
534 
535`gpg` also prints `WARNING: This key is not certified with a trusted signature!` for any freshly imported key. This is expected. The `Good signature` line confirms the cryptographic check passed. The fingerprint comparison in Step 1 confirms the key itself is authentic.
536 
537**4. Check the binary against the manifest**
538 
539Compare the SHA256 checksum of the binary with the value listed under `platforms.<platform>.checksum` in `manifest.json`. The commands below assume a `claude` binary in the current directory. To verify an installed native binary instead, run the command against `~/.local/share/claude/versions/VERSION`, replacing VERSION with the release you set in Step 2.
540 
541**Linux**
542 
543```bash theme={null}
544sha256sum claude
545```
546 
547**macOS**
548 
549```bash theme={null}
550shasum -a 256 claude
551```
552 
553**Windows PowerShell**
554 
555```powershell theme={null}
556(Get-FileHash claude.exe -Algorithm SHA256).Hash.ToLower()
557```
558 
559> [!NOTE]
560>
561> Manifest signatures are available for releases from `2.1.89` onward. Earlier releases publish checksums in `manifest.json` without a detached signature.
576562 
577563#### Platform code signatures
578564 
from line 572
586572 
587573To remove Claude Code, follow the instructions for your installation method. If `claude` still runs afterward, you likely have a second installation or a leftover shell alias from an older installer. See [Check for conflicting installations](/docs/en/troubleshoot-install#check-for-conflicting-installations) to find and remove it.
588574 
589<span id="native-installation" />
590 
591575### Uninstall a native installation
592576 
593577Remove the Claude Code binary and version files:
594578 
595<Tabs>
596 <Tab title="macOS, Linux, WSL">
597 ```bash theme={null}
598 rm -f ~/.local/bin/claude
599 rm -rf ~/.local/share/claude
600 ```
601 </Tab>
602 
603 <Tab title="Windows PowerShell">
604 ```powershell theme={null}
605 Remove-Item -Path "$env:USERPROFILE\.local\bin\claude.exe" -Force
606 Remove-Item -Path "$env:USERPROFILE\.local\share\claude" -Recurse -Force
607 ```
608 </Tab>
609</Tabs>
610 
611<span id="homebrew-installation" />
579**macOS, Linux, WSL**
580 
581```bash theme={null}
582rm -f ~/.local/bin/claude
583rm -rf ~/.local/share/claude
584```
585 
586**Windows PowerShell**
587 
588```powershell theme={null}
589Remove-Item -Path "$env:USERPROFILE\.local\bin\claude.exe" -Force
590Remove-Item -Path "$env:USERPROFILE\.local\share\claude" -Recurse -Force
591```
612592 
613593### Uninstall with Homebrew
614594 
from line 604
624604brew uninstall --cask claude-code@latest
625605```
626606 
627<span id="winget-installation" />
628 
629607### Uninstall with WinGet
630608 
631609Remove the WinGet package:
from line 612
634612winget uninstall Anthropic.ClaudeCode
635613```
636614 
637<span id="apt-/-dnf-/-apk" />
638 
639615### Uninstall with apt, dnf, or apk
640616 
641617Remove the package and the repository configuration:
642618 
643<Tabs>
644 <Tab title="apt">
645 ```bash theme={null}
646 sudo apt remove claude-code
647 sudo rm /etc/apt/sources.list.d/claude-code.list /etc/apt/keyrings/claude-code.asc
648 ```
649 </Tab>
650 
651 <Tab title="dnf">
652 ```bash theme={null}
653 sudo dnf remove claude-code
654 sudo rm /etc/yum.repos.d/claude-code.repo
655 ```
656 </Tab>
657 
658 <Tab title="apk">
659 ```sh theme={null}
660 apk del claude-code
661 sed -i '\|downloads.claude.ai/claude-code/apk|d' /etc/apk/repositories
662 rm /etc/apk/keys/claude-code.rsa.pub
663 ```
664 </Tab>
665</Tabs>
666 
667<span id="npm" />
619**apt**
620 
621```bash theme={null}
622sudo apt remove claude-code
623sudo rm /etc/apt/sources.list.d/claude-code.list /etc/apt/keyrings/claude-code.asc
624```
625 
626**dnf**
627 
628```bash theme={null}
629sudo dnf remove claude-code
630sudo rm /etc/yum.repos.d/claude-code.repo
631```
632 
633**apk**
634 
635```sh theme={null}
636apk del claude-code
637sed -i '\|downloads.claude.ai/claude-code/apk|d' /etc/apk/repositories
638rm /etc/apk/keys/claude-code.rsa.pub
639```
668640 
669641### Uninstall with npm
670642 
from line 648
676648 
677649### Remove configuration files
678650 
679<Warning>
680 Removing configuration files will delete all your settings, allowed tools, MCP server configurations, and session history.
681</Warning>
651> [!WARNING]
652>
653> Removing configuration files will delete all your settings, allowed tools, MCP server configurations, and session history.
682654 
683655The VS Code extension, the JetBrains plugin, and the Desktop app also write to `~/.claude/`. If any of them is still installed, the directory is recreated the next time it runs. To remove Claude Code completely, uninstall the [VS Code extension](/docs/en/vs-code#uninstall-the-extension), the JetBrains plugin, and the Desktop app before deleting these files.
684656 
685657To remove Claude Code settings and cached data:
686658 
687<Tabs>
688 <Tab title="macOS, Linux, WSL">
689 ```bash theme={null}
690 # Remove user settings and state
691 rm -rf ~/.claude
692 rm ~/.claude.json
693 
694 # Remove project-specific settings (run from your project directory)
695 rm -rf .claude
696 rm -f .mcp.json
697 ```
698 </Tab>
699 
700 <Tab title="Windows PowerShell">
701 ```powershell theme={null}
702 # Remove user settings and state
703 Remove-Item -Path "$env:USERPROFILE\.claude" -Recurse -Force
704 Remove-Item -Path "$env:USERPROFILE\.claude.json" -Force
705 
706 # Remove project-specific settings (run from your project directory)
707 Remove-Item -Path ".claude" -Recurse -Force
708 Remove-Item -Path ".mcp.json" -Force
709 ```
710 </Tab>
711</Tabs>
659**macOS, Linux, WSL**
660 
661```bash theme={null}
662# Remove user settings and state
663rm -rf ~/.claude
664rm ~/.claude.json
665 
666# Remove project-specific settings (run from your project directory)
667rm -rf .claude
668rm -f .mcp.json
669```
670 
671**Windows PowerShell**
672 
673```powershell theme={null}
674# Remove user settings and state
675Remove-Item -Path "$env:USERPROFILE\.claude" -Recurse -Force
676Remove-Item -Path "$env:USERPROFILE\.claude.json" -Force
677 
678# Remove project-specific settings (run from your project directory)
679Remove-Item -Path ".claude" -Recurse -Force
680Remove-Item -Path ".mcp.json" -Force
681```
712682 

skills Changed · +84 / -99 lines

## Choose where skills load ### Load skills in monorepos and subdirectories ### Load skills from a directory outside the project ### Use skills in Cowork and cloud sessions ### Skills synced from claude.ai #### Names reserved for synced skills ### Edit a skill during a session #### Characters a skill name can't hold

from line 6
66 
77Create a skill when you keep pasting the same instructions, checklist, or multi-step procedure into chat, or when a section of CLAUDE.md has grown into a procedure rather than a fact. Unlike CLAUDE.md content, a skill's body loads only when it's used, so long reference material costs almost nothing until you need it.
88 
9<Note>
10 For built-in commands like `/help` and `/compact`, and bundled skills like `/debug` and `/code-review`, see the [commands reference](/docs/en/commands).
9> [!NOTE]
10>
11> For built-in commands like `/help` and `/compact`, and bundled skills like `/debug` and `/code-review`, see the [commands reference](/docs/en/commands).
12>
13> **Custom commands have been merged into skills.** A file at `.claude/commands/deploy.md` and a skill at `.claude/skills/deploy/SKILL.md` both create `/deploy` and work the same way. Your existing `.claude/commands/` files keep working. Skills add optional features: a directory for supporting files, frontmatter to [control whether you or Claude invokes them](#control-who-invokes-a-skill), and the ability for Claude to load them automatically when relevant.
1114 
12 **Custom commands have been merged into skills.** A file at `.claude/commands/deploy.md` and a skill at `.claude/skills/deploy/SKILL.md` both create `/deploy` and work the same way. Your existing `.claude/commands/` files keep working. Skills add optional features: a directory for supporting files, frontmatter to [control whether you or Claude invokes them](#control-who-invokes-a-skill), and the ability for Claude to load them automatically when relevant.
13</Note>
14 
1515Claude Code skills follow the [Agent Skills](https://agentskills.io) open standard, which works across multiple AI tools. Claude Code extends the standard with additional features like [invocation control](#control-who-invokes-a-skill), [subagent execution](#run-skills-in-a-subagent), and [dynamic context injection](#inject-dynamic-context). See [Using skill frontmatter outside Claude Code](#using-skill-frontmatter-outside-claude-code) for which frontmatter fields are part of the standard and which are Claude Code extensions.
1616 
1717## Bundled skills
from line 24
2424 
2525To turn bundled skills off, use the [`disableBundledSkills`](/docs/en/settings-reference#disablebundledskills) setting.
2626 
27<Note>
28 The [`/doctor`](/docs/en/commands#all-commands) setup checkup stays typable when `disableBundledSkills` is on. To hide it, set the `DISABLE_DOCTOR_COMMAND` environment variable or a [`skillOverrides`](#override-skill-visibility-from-settings) entry of `"doctor": "off"`.
29</Note>
27> [!NOTE]
28>
29> The [`/doctor`](/docs/en/commands#all-commands) setup checkup stays typable when `disableBundledSkills` is on. To hide it, set the `DISABLE_DOCTOR_COMMAND` environment variable or a [`skillOverrides`](#override-skill-visibility-from-settings) entry of `"doctor": "off"`.
3030 
3131Bundled skills are listed alongside built-in commands in the [commands reference](/docs/en/commands), marked **Skill** in the Purpose column.
3232 
from line 82
8282| `managed-agents-onboard` | Walk through creating a new Managed Agent | Earlier than v2.1.221 |
8383| `managed-agents-onboard <url>` | Build the Managed Agent that the page at the URL describes, such as a page in the [Managed Agents docs](https://platform.claude.com/docs/en/managed-agents/overview) | v2.1.290 or later |
8484| `managed-agents-onboard <quickstart-name>` | Build one of the Console's quickstart templates, such as `deep-researcher`. If you give one word that isn't a template name, Claude lists the valid names | v2.1.290 or later |
85| `managed-agents-secure [<agent-id>]` | Read the settings of one Managed Agent and report where they match the [Secure your Managed Agents deployment](https://platform.claude.com/docs/en/managed-agents/secure-deployment) guide or differ from it. Requires the [`ant` CLI](https://platform.claude.com/docs/en/cli-sdks-libraries/cli/quickstart) | v2.1.304 or later |
8586| `prompt-audit` | Flag instructions written for older models in your prompts, skills, and tool descriptions and propose fixes as a diff | v2.1.221 or later |
8687| `cost-optimize` | Profile where your project's Claude API spend goes and propose savings from options such as prompt caching, trimming unneeded input and output tokens, batch processing, effort, and model choice, one change at a time | v2.1.247 or later |
8788| `build-eval` | Build an eval set for your Claude-powered app | v2.1.259 or later |
from line 95
9495 
9596This example creates a skill that summarizes the uncommitted changes in your git repository and flags anything risky. It pulls the live diff into the prompt before Claude reads it, so the response is grounded in your actual working tree rather than what Claude can guess from open files. Claude loads the skill automatically when you ask about your changes, or you can invoke it directly with `/summarize-changes`.
9697 
97<Steps>
98 <Step title="Create the skill directory">
99 Create a directory for the skill in your personal skills folder. Personal skills are available across all your projects.
98**1. Create the skill directory**
10099 
101 ```bash theme={null}
102 mkdir -p ~/.claude/skills/summarize-changes
103 ```
104 </Step>
100Create a directory for the skill in your personal skills folder. Personal skills are available across all your projects.
105101 
106 <Step title="Write SKILL.md">
107 Every skill needs a `SKILL.md` file with two parts: YAML frontmatter between `---` markers that tells Claude when to use the skill, and markdown content with the instructions Claude follows when the skill runs. The directory name, or the frontmatter `name` when you set one, becomes the command you type, and the `description` helps Claude decide when to load the skill automatically.
102```bash theme={null}
103mkdir -p ~/.claude/skills/summarize-changes
104```
108105 
109 Save this to `~/.claude/skills/summarize-changes/SKILL.md`:
106**2. Write SKILL.md**
110107 
111 ```yaml theme={null}
112 ---
113 description: Summarizes uncommitted changes and flags anything risky. Use when the user asks what changed, wants a commit message, or asks to review their diff.
114 ---
108Every skill needs a `SKILL.md` file with two parts: YAML frontmatter between `---` markers that tells Claude when to use the skill, and markdown content with the instructions Claude follows when the skill runs. The directory name, or the frontmatter `name` when you set one, becomes the command you type, and the `description` helps Claude decide when to load the skill automatically.
115109 
116 ## Current changes
110Save this to `~/.claude/skills/summarize-changes/SKILL.md`:
117111 
118 !`git diff HEAD`
112```yaml theme={null}
113---
114description: Summarizes uncommitted changes and flags anything risky. Use when the user asks what changed, wants a commit message, or asks to review their diff.
115---
119116 
120 ## Instructions
117## Current changes
121118 
122 Summarize the changes above in two or three bullet points, then list any risks you notice such as missing error handling, hardcoded values, or tests that need updating. If the diff is empty, say there are no uncommitted changes.
123 ```
119!`git diff HEAD`
124120 
125 The `` !`git diff HEAD` `` line uses [dynamic context injection](#inject-dynamic-context): Claude Code runs the command and replaces the line with its output before Claude sees the skill content, so the instructions arrive with the current diff already inlined.
126 </Step>
121## Instructions
127122 
128 <Step title="Test the skill">
129 Open a git project, make a small edit to any file, and start Claude Code by running `claude`. You can test the skill two ways.
123Summarize the changes above in two or three bullet points, then list any risks you notice such as missing error handling, hardcoded values, or tests that need updating. If the diff is empty, say there are no uncommitted changes.
124```
130125 
131 **Let Claude invoke it automatically** by asking something that matches the description:
126The `` !`git diff HEAD` `` line uses [dynamic context injection](#inject-dynamic-context): Claude Code runs the command and replaces the line with its output before Claude sees the skill content, so the instructions arrive with the current diff already inlined.
132127 
133 ```text theme={null}
134 What did I change?
135 ```
128**3. Test the skill**
136129 
137 **Or invoke it directly** with the skill name:
130Open a git project, make a small edit to any file, and start Claude Code by running `claude`. You can test the skill two ways.
138131 
139 ```text theme={null}
140 /summarize-changes
141 ```
132**Let Claude invoke it automatically** by asking something that matches the description:
142133 
143 Either way, Claude should respond with a short summary of your edit and a list of risks.
144 </Step>
145</Steps>
134```text theme={null}
135What did I change?
136```
146137 
147<h2 id="where-skills-live">
148 Choose where skills load
149</h2>
138**Or invoke it directly** with the skill name:
150139 
140```text theme={null}
141/summarize-changes
142```
143 
144Either way, Claude should respond with a short summary of your edit and a list of risks.
145 
146## Choose where skills load
147 
151148Where you save a skill decides which sessions load it. Save it under your home directory to get it in every project, commit it to a repository to share it with everyone who works there, or distribute it through a plugin or managed settings to reach a whole team.
152149 
153150| Location | Path | Loads in |
from line 166
169166* **Command files**: a Markdown file in `.claude/commands/` is the older format and still works. It supports the same [frontmatter](#frontmatter-reference) except `name` and `paths`. To find the name you type to invoke it, see [How a skill gets its command name](#how-a-skill-gets-its-command-name). Prefer a skill for new work, since skills also support [supporting files](#add-supporting-files).
170167* **Skill folder as a plugin**: add a `.claude-plugin/plugin.json` to a skill folder and it loads as a [plugin](/docs/en/plugins/loading#plugins-shared-through-a-repository) named `<name>@skills-dir`, so it can bundle agents, hooks, and MCP servers. In a project's `.claude/skills/`, this requires accepting the workspace trust dialog first.
171168 
172<h3 id="discovery-from-parent-and-nested-directories">
173 Load skills in monorepos and subdirectories
174</h3>
169### Load skills in monorepos and subdirectories
175170 
176171Claude Code loads project skills from `.claude/skills/` in the directory where you start it and in every parent directory up to the repository root, so starting in `packages/frontend/` still picks up skills defined at the root. When you [move the session with `/cd`](/docs/en/permissions#move-the-session-to-another-directory) on v2.1.246 or later, Claude Code adds the new directory's project skills.
177172 
from line 179
184179* `/deploy` runs the root skill. Claude Code also lists the directory-qualified variants for Claude, with an instruction to invoke the one whose directory holds the files it's working on, so the nested skill still applies to work in `apps/web/`.
185180* `/apps/web:deploy` runs the nested skill on its own. Its description names the directory it applies to.
186181 
187<h3 id="skills-from-additional-directories">
188 Load skills from a directory outside the project
189</h3>
182### Load skills from a directory outside the project
190183 
191184When you add a directory with `--add-dir` or `/add-dir`, Claude Code loads the skills in that directory's `.claude/skills/`, along with its `.claude/commands/` and `.claude/agents/`. Directories the Agent SDK adds through [`additionalDirectories`](/docs/en/agent-sdk/typescript#options) in TypeScript or [`add_dirs`](/docs/en/agent-sdk/python#claudeagentoptions) in Python load the same way, because the SDK passes them as `--add-dir`. The `permissions.additionalDirectories` setting in `settings.json` grants file access only and loads none of these.
192185 
from line 201
208201| A plugin skill and a skill at any of the locations above | Both load, because plugin skills are namespaced as `/plugin-name:skill-name` |
209202| Any of the above and the short name of a skill [synced from your claude.ai account](#how-synced-skills-behave) | The other skill or command. The synced skill is then listed and runs only under its full name. See [When a synced skill name matches another command](#when-a-synced-skill-name-matches-another-command) |
210203 
211<h3 id="skills-in-cowork-and-cloud-sessions">
212 Use skills in Cowork and cloud sessions
213</h3>
204### Use skills in Cowork and cloud sessions
214205 
215206[Cowork](https://claude.com/product/cowork) sessions and [cloud sessions](/docs/en/cloud-environments#what-carries-over-from-your-setup), including [cloud scheduled tasks](/docs/en/routines), don't read `~/.claude/skills/` on your machine. Both interactive and scheduled Cowork sessions load the skills enabled for your claude.ai account, synced at session start. Manage them from **Customize** in the Desktop app sidebar or from the skills settings on claude.ai. Cloud sessions additionally load project skills committed to the cloned repository's `.claude/skills/`.
216207 
from line 212
221212 
222213[Desktop scheduled tasks](/docs/en/desktop-scheduled-tasks) run locally on your machine, so they do load `~/.claude/skills/`.
223214 
224<h3 id="how-synced-skills-behave">
225 Skills synced from claude.ai
226</h3>
215### Skills synced from claude.ai
227216 
228217This section applies to you if you use Cowork or cloud sessions, or sign in to Claude Code in your terminal with a claude.ai account. In those sessions, Claude Code loads the skills enabled for your claude.ai account, with no setup on your part, as [Where synced skills load](#where-synced-skills-load) describes. Those skills include the ones you create or turn on in your claude.ai settings, skills your organization provides there, and Anthropic's built-in skills such as `pdf` and `xlsx`.
229218 
from line 273
284273 
285274A name that differs only by a look-alike letter from another alphabet counts as a different name, and the `claude.ai sync` label is how you tell the two apart. These checks and labels require Claude Code v2.1.228 or later.
286275 
287<h4 id="names-reserved-for-synced-skills">
288 Names reserved for synced skills
289</h4>
276#### Names reserved for synced skills
290277 
291278Claude Code reserves the name `anthropic-skills`, and every name inside that namespace such as `anthropic-skills:pdf`, for skills synced from claude.ai, so a synced skill's full name never runs anything else. The name is reserved in every session, whether or not you sign in with a claude.ai account.
292279 
from line 296
309296* In a Cowork session on your desktop, the body keeps the behavior a local skill has, except that Claude Code replaces every `!` command line with the [`disableSkillShellExecution` placeholder](#inject-dynamic-context), as it does for every skill you supply there.
310297* In any other session on your machine, Claude Code doesn't run [`!` commands](#inject-dynamic-context), doesn't attach the files that `@` references name the way it does for a local skill, and doesn't substitute the `${CLAUDE_PROJECT_DIR}` and `${CLAUDE_SESSION_ID}` placeholders, so the `@` references and both placeholders reach Claude as literal text. A `!` command line reaches Claude as literal text too, or as that placeholder when `disableSkillShellExecution` is on. This handling requires Claude Code v2.1.228 or later.
311298 
312<h3 id="live-change-detection">
313 Edit a skill during a session
314</h3>
299### Edit a skill during a session
315300 
316301Claude Code watches skill directories for file changes, except in [bare mode](/docs/en/headless#start-faster-with-bare-mode). When you add, edit, or remove a skill under `~/.claude/skills/`, the project `.claude/skills/`, or a `.claude/skills/` inside an `--add-dir` directory, Claude Code picks up the change within the current session, without a restart.
317302 
from line 441
456441 
457442For a plugin-root `SKILL.md`, there is no skill directory to take the name from, so `name` supplies the whole final segment. Without a `name` field, Claude Code falls back to the plugin's directory name.
458443 
459<h4 id="characters-a-skill-name-cant-hold">
460 Characters a skill name can't hold
461</h4>
444#### Characters a skill name can't hold
462445 
463446If a skill or custom command is missing from the `/` menu, or the menu doesn't show a skill's frontmatter `name`, check the name. Claude Code refuses a command name or a frontmatter `name` that contains characters such as these:
464447 
from line 532
549532- For usage examples, see [examples.md](examples.md)
550533```
551534 
552<Tip>Keep `SKILL.md` under 500 lines. Move detailed reference material to separate files.</Tip>
535> [!TIP]
536>
537> Keep `SKILL.md` under 500 lines. Move detailed reference material to separate files.
553538 
554539### Control who invokes a skill
555540 
from line 571
586571| `disable-model-invocation: true` | Yes | Not on its own | Description not in context, full skill loads when invoked |
587572| `user-invocable: false` | No | Yes | Description always in context, full skill loads when invoked |
588573 
589<Note>
590 In a regular session, skill descriptions are loaded into context so Claude knows what's available, but full skill content only loads when invoked. [Subagents with preloaded skills](/docs/en/sub-agents#preload-skills-into-subagents) work differently: the full skill content is injected at startup.
591</Note>
574> [!NOTE]
575>
576> In a regular session, skill descriptions are loaded into context so Claude knows what's available, but full skill content only loads when invoked. [Subagents with preloaded skills](/docs/en/sub-agents#preload-skills-into-subagents) work differently: the full skill content is injected at startup.
592577 
593578#### Where you write the skill's name
594579 
from line 721
736721 
737722Claude Code never runs these commands on your machine when they appear in skills [synced from your claude.ai account](#how-synced-skills-behave), regardless of this setting. This restriction requires Claude Code v2.1.228 or later. [How Claude Code handles the body of a synced skill](#how-claude-code-handles-the-body-of-a-synced-skill) says what Claude receives in place of the command in each kind of session.
738723 
739<Tip>
740 To request deeper reasoning when a skill runs, include `ultrathink` anywhere in the skill content. See [Use ultrathink for one-off deep reasoning](/docs/en/model-config#use-ultrathink-for-one-off-deep-reasoning).
741</Tip>
724> [!TIP]
725>
726> To request deeper reasoning when a skill runs, include `ultrathink` anywhere in the skill content. See [Use ultrathink for one-off deep reasoning](/docs/en/model-config#use-ultrathink-for-one-off-deep-reasoning).
742727 
743728#### How injected commands run
744729 
from line 771
786771 
787772Add `context: fork` to your frontmatter when you want a skill to run in isolation. Claude Code starts a new subagent of the type set in the `agent` field and gives it the skill content as its prompt. The subagent doesn't see your conversation history, so the skill's instructions have to stand on their own.
788773 
789<Note>
790 Despite the name, a skill with `context: fork` doesn't run in a [fork of the current conversation](/docs/en/sub-agents#fork-the-current-conversation), which would hand the subagent everything you've discussed so far. When the task depends on that history, fork the conversation instead of using `context: fork`.
791</Note>
774> [!NOTE]
775>
776> Despite the name, a skill with `context: fork` doesn't run in a [fork of the current conversation](/docs/en/sub-agents#fork-the-current-conversation), which would hand the subagent everything you've discussed so far. When the task depends on that history, fork the conversation instead of using `context: fork`.
792777 
793778The forked subagent runs in the [background](/docs/en/sub-agents#run-subagents-in-foreground-or-background): you keep working while it runs, and its result arrives in your conversation when it completes. Set `background: false` in the frontmatter to instead wait for the result in the turn that invoked the skill. Before v2.1.218, forked skills always blocked the turn until they finished.
794779 
from line 784
799784* When you invoke a forked skill while an earlier invocation of the same skill is still running
800785* When a [session-scoped scheduled task](/docs/en/scheduled-tasks) fires with the skill as its prompt
801786 
802When an agent in a [dynamic workflow](/docs/en/workflows) invokes a forked skill, that agent waits for and receives the result, even when the skill doesn't set `background: false`. Before v2.1.295, Claude Code didn't wait in this case, and when the skill ran in the background, its result arrived in your main conversation instead of reaching that agent.
787When an agent in a [dynamic workflow](/docs/en/workflows) invokes a forked skill, that agent waits for and receives the result, even when the skill doesn't set `background: false`. Before v2.1.295, when the skill ran in the background, that agent didn't wait for it. The skill's result then arrived in your main conversation instead of reaching the agent.
803788 
804789A backgrounded fork also runs with the [narrower tool set that applies to background subagents](/docs/en/sub-agents#run-subagents-in-foreground-or-background): the skill's subagent is a regular agent type, so the exemption for subagents that fork the conversation doesn't cover it. If your skill's steps depend on a tool outside that set, set `background: false` to keep the full tool set.
805790 
806791A forked skill that runs in the background applies its edits outside your session's [checkpoints](/docs/en/checkpointing#subagent-edits-not-restored). After one, `/rewind` restores only the files Claude had already edited in your conversation. The skill's edits to other files stay, so use git to revert them.
807792 
808<Warning>
809 `context: fork` only makes sense for skills with explicit instructions. If your skill contains guidelines like "use these API conventions" without a task, the subagent receives the guidelines but no actionable prompt, and returns without meaningful output.
810</Warning>
793> [!WARNING]
794>
795> `context: fork` only makes sense for skills with explicit instructions. If your skill contains guidelines like "use these API conventions" without a task, the subagent receives the guidelines but no actionable prompt, and returns without meaningful output.
811796 
812797Skills and [subagents](/docs/en/sub-agents) work together in two directions:
813798 
from line 879
894879 
895880**Hide individual skills** by adding `disable-model-invocation: true` to their frontmatter. This removes the skill from Claude's context entirely.
896881 
897<Note>
898 With `user-invocable: false`, you can't invoke the skill, but Claude still can. To keep Claude from invoking it through the Skill tool, set `disable-model-invocation: true`.
899</Note>
882> [!NOTE]
883>
884> With `user-invocable: false`, you can't invoke the skill, but Claude still can. To keep Claude from invoking it through the Skill tool, set `disable-model-invocation: true`.
900885 
901886### Override skill visibility from settings
902887 

slack Changed · +71 / -79 lines

### "This workspace isn't set up for Claude Tag yet" ### "The legacy Claude in Slack bot is retired"

from line 2
22 
33> Delegate coding tasks from Slack. This earlier version answers channel mentions only from Pro and Max accounts in workspaces not connected to Claude Tag.
44 
5<Warning>
6 This page documents the earlier Claude Code in Slack, which runs each session under an individual user's account. It answers channel @mentions only from Pro and Max accounts, and only in a Slack workspace that no organization has connected to [Claude Tag](https://claude.com/product/tag). Claude Tag runs @Claude as your organization's shared identity with admin-configured access.
5> [!WARNING]
6>
7> This page documents the earlier Claude Code in Slack, which runs each session under an individual user's account. It answers channel @mentions only from Pro and Max accounts, and only in a Slack workspace that no organization has connected to [Claude Tag](https://claude.com/product/tag). Claude Tag runs @Claude as your organization's shared identity with admin-configured access.
8>
9> * **Pro and Max plans:** Claude Tag isn't available on individual plans, so this page remains the setup path in a workspace that isn't connected to Claude Tag.
10> * **Team and Enterprise plans:** your existing Slack app and @Claude handle stay. [Set up Claude Tag](https://claude.com/docs/claude-tag/overview) for a new workspace; to move one that already uses this version, see [Migrate from the earlier Claude in Slack](https://claude.com/docs/claude-tag/admins/migrate-from-earlier).
11> * **Notice instead of an answer:** look up the [setup notice](#this-workspace-isnt-set-up-for-claude-tag-yet) or the [retirement notice](#the-legacy-claude-in-slack-bot-is-retired) that @Claude replied with.
712 
8 * **Pro and Max plans:** Claude Tag isn't available on individual plans, so this page remains the setup path in a workspace that isn't connected to Claude Tag.
9 * **Team and Enterprise plans:** your existing Slack app and @Claude handle stay. [Set up Claude Tag](https://claude.com/docs/claude-tag/overview) for a new workspace; to move one that already uses this version, see [Migrate from the earlier Claude in Slack](https://claude.com/docs/claude-tag/admins/migrate-from-earlier).
10 * **Notice instead of an answer:** look up the [setup notice](#this-workspace-isnt-set-up-for-claude-tag-yet) or the [retirement notice](#the-legacy-claude-in-slack-bot-is-retired) that @Claude replied with.
11</Warning>
12 
1313Claude Code in Slack brings the power of Claude Code directly into your Slack workspace. When you mention `@Claude` with a coding task, Claude automatically detects the intent and creates a Claude Code cloud session, allowing you to delegate development work without leaving your team conversations.
1414 
1515This integration is built on the existing Claude for Slack app but adds intelligent routing to Claude Code cloud sessions for coding-related requests. Each session runs under your own Claude account, using your connected repositories and your plan limits.
from line 35
3535 
3636## Setting up Claude Code in Slack
3737 
38<Steps>
39 <Step title="Install the Claude App in Slack">
40 A workspace administrator must install the Claude app from the Slack App Marketplace. Visit the [Slack App Marketplace](https://slack.com/marketplace/A08SF47R6P4) and click "Add to Slack" to begin the installation process.
41 </Step>
38**1. Install the Claude App in Slack**
4239 
43 <Step title="Connect your Claude account">
44 After the app is installed, authenticate your individual Claude account:
40A workspace administrator must install the Claude app from the Slack App Marketplace. Visit the [Slack App Marketplace](https://slack.com/marketplace/A08SF47R6P4) and click "Add to Slack" to begin the installation process.
4541 
46 1. Open the Claude app in Slack by clicking on "Claude" in your Apps section
47 2. Open the App Home tab
48 3. Click "Connect" to link your Slack account with your Claude account
49 4. Complete the authentication flow in your browser
50 </Step>
42**2. Connect your Claude account**
5143 
52 <Step title="Configure cloud sessions">
53 Ensure cloud sessions are properly configured for your account:
44After the app is installed, authenticate your individual Claude account:
5445 
55 * Visit [claude.ai/code](https://claude.ai/code) and sign in with the same account you connected to Slack
56 * Connect your GitHub account if not already connected
57 * Authenticate at least one repository that you want Claude to work with
58 </Step>
461. Open the Claude app in Slack by clicking on "Claude" in your Apps section
472. Open the App Home tab
483. Click "Connect" to link your Slack account with your Claude account
494. Complete the authentication flow in your browser
5950 
60 <Step title="Choose your routing mode">
61 After connecting your accounts, configure how Claude handles your messages in Slack. Open the Claude App Home in Slack to find the **Routing Mode** setting.
51**3. Configure cloud sessions**
6252 
63 | Mode | Behavior |
64 | :- | :- |
65 | **Code only** | Claude routes all @mentions to Claude Code sessions. Best for teams using Claude in Slack exclusively for development tasks. |
66 | **Code + Chat** | Claude analyzes each message and intelligently routes between Claude Code (for coding tasks) and Claude Chat (for writing, analysis, and general questions). Best for teams who want a single @Claude entry point for all types of work. |
53Ensure cloud sessions are properly configured for your account:
6754 
68 <Note>
69 In Code + Chat mode, if Claude routes a message to Chat but you wanted a coding session, you can click "Retry as Code" to create a Claude Code session instead. Similarly, if it's routed to Code but you wanted a Chat session, you can choose that option in that thread.
70 </Note>
71 </Step>
55* Visit [claude.ai/code](https://claude.ai/code) and sign in with the same account you connected to Slack
56* Connect your GitHub account if not already connected
57* Authenticate at least one repository that you want Claude to work with
7258 
73 <Step title="Add Claude to channels">
74 Claude is not automatically added to any channels after installation. To use Claude in a channel, invite it by typing `/invite @Claude` in that channel. Claude can only respond to @mentions in channels where it has been added.
75 </Step>
76</Steps>
59**4. Choose your routing mode**
7760 
61After connecting your accounts, configure how Claude handles your messages in Slack. Open the Claude App Home in Slack to find the **Routing Mode** setting.
62 
63| Mode | Behavior |
64| :- | :- |
65| **Code only** | Claude routes all @mentions to Claude Code sessions. Best for teams using Claude in Slack exclusively for development tasks. |
66| **Code + Chat** | Claude analyzes each message and intelligently routes between Claude Code (for coding tasks) and Claude Chat (for writing, analysis, and general questions). Best for teams who want a single @Claude entry point for all types of work. |
67 
68> [!NOTE]
69>
70> In Code + Chat mode, if Claude routes a message to Chat but you wanted a coding session, you can click "Retry as Code" to create a Claude Code session instead. Similarly, if it's routed to Code but you wanted a Chat session, you can choose that option in that thread.
71 
72**5. Add Claude to channels**
73 
74Claude is not automatically added to any channels after installation. To use Claude in a channel, invite it by typing `/invite @Claude` in that channel. Claude can only respond to @mentions in channels where it has been added.
75 
7876## How it works
7977 
8078### Automatic detection
from line 81
8381 
8482You can also explicitly tell Claude to handle a request as a coding task, even if it doesn't automatically detect it.
8583 
86<Note>
87 Claude Code in Slack only works in channels (public or private). It does not work in direct messages (DMs).
88</Note>
84> [!NOTE]
85>
86> Claude Code in Slack only works in channels (public or private). It does not work in direct messages (DMs).
8987 
9088### Context gathering
9189 
from line 93
9593 
9694This context helps Claude understand the problem, select the appropriate repository, and inform its approach to the task.
9795 
98<Warning>
99 When @Claude is invoked in Slack, Claude is given access to the conversation context to better understand your request. Claude may follow directions from other messages in the context, so users should make sure to only use Claude in trusted Slack conversations.
100</Warning>
96> [!WARNING]
97>
98> When @Claude is invoked in Slack, Claude is given access to the conversation context to better understand your request. Claude may follow directions from other messages in the context, so users should make sure to only use Claude in trusted Slack conversations.
10199 
102100### Session flow
103101 
from line 171
173171 
174172## Troubleshooting
175173 
176<h3 id="this-workspace-isnt-set-up-for-claude-tag-yet">
177 "This workspace isn't set up for Claude Tag yet"
178</h3>
174### "This workspace isn't set up for Claude Tag yet"
179175 
180176@Claude replies in the thread with this notice instead of an answer when both of these are true:
181177 
from line 180
184180 
185181Claude Tag is available on Team and Enterprise plans. To connect the workspace, start with the `@Claude connect` command the notice names, then follow [Set up Claude Tag](https://claude.com/docs/claude-tag/overview).
186182 
187<h3 id="the-legacy-claude-in-slack-bot-is-retired">
188 "The legacy Claude in Slack bot is retired"
189</h3>
183### "The legacy Claude in Slack bot is retired"
190184 
191185The notice opens with `The legacy Claude in Slack bot is retired effective October 5, 2026 and no longer responds in channels.` Your Slack workspace is connected to a Claude organization, and the earlier version is still selected for the channel, the workspace, or the organization default in that organization's Claude Tag settings. The notice doesn't depend on your own plan, so Pro and Max accounts get it too.
192186 
from line 233
239233 
240234## Related resources
241235 
242<CardGroup>
243 <Card title="Claude Code in the cloud" icon="cloud" href="/docs/en/claude-code-on-the-web">
244 Learn more about cloud sessions
245 </Card>
236> **[Claude Code in the cloud](/docs/en/claude-code-on-the-web)**
237>
238> Learn more about cloud sessions
246239 
247 <Card title="Claude for Slack" icon="slack" href="https://claude.com/claude-and-slack">
248 General Claude for Slack documentation
249 </Card>
240> **[Claude for Slack](https://claude.com/claude-and-slack)**
241>
242> General Claude for Slack documentation
250243 
251 <Card title="Claude Tag" icon="users" href="https://claude.com/docs/claude-tag/overview">
252 Organization-managed @Claude in Slack with admin-configured access
253 </Card>
244> **[Claude Tag](https://claude.com/docs/claude-tag/overview)**
245>
246> Organization-managed @Claude in Slack with admin-configured access
254247 
255 <Card title="Slack App Marketplace" icon="store" href="https://slack.com/marketplace/A08SF47R6P4">
256 Install the Claude app from the Slack Marketplace
257 </Card>
248> **[Slack App Marketplace](https://slack.com/marketplace/A08SF47R6P4)**
249>
250> Install the Claude app from the Slack Marketplace
258251 
259 <Card title="Claude Help Center" icon="circle-question" href="https://support.claude.com">
260 Get additional support
261 </Card>
262</CardGroup>
252> **[Claude Help Center](https://support.claude.com)**
253>
254> Get additional support
263255 

statusline Changed · +794 / -826 lines

#### Last miss cause

The two sides of this change are more than 400 edits apart, too far apart to line up, so this is the differ's own diff of it and the words inside a line are not marked.

from line 15
1515 
1616Here's an example of a [multi-line status line](#display-multiple-lines) that displays git info on the first line and a color-coded context bar on the second.
1717 
18<Frame>
19 <img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-multiline.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=a9d0a2fe8e446d80b1abc46da3f93270" alt="A multi-line status line showing model name, directory, git branch on the first line, and a context usage progress bar with cost and duration on the second line" width="1224" height="262" data-path="images/statusline-multiline.png" />
20</Frame>
18![A multi-line status line showing model name, directory, git branch on the first line, and a context usage progress bar with cost and duration on the second line](https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-multiline.png?fit=max\&auto=format\&n=HDAmBwgbrZVk0pOt\&q=85\&s=a9d0a2fe8e446d80b1abc46da3f93270)
2119 
2220This page walks through [setting up a basic status line](#set-up-a-status-line), explains [how the data flows](#how-status-lines-work) from Claude Code to your script, lists [all the fields you can display](#available-data), and provides [ready-to-use examples](#examples) for common patterns like git status, cost tracking, and progress bars.
2321 
from line 72
7472 
7573This walkthrough shows what `/statusline` sets up for you by manually creating a status line that displays the current model, working directory, and context window usage percentage.
7674 
77<Note>Running [`/statusline`](#use-the-%2Fstatusline-command) with a description of what you want configures all of this for you automatically.</Note>
75> [!NOTE]
76>
77> Running [`/statusline`](#use-the-%2Fstatusline-command) with a description of what you want configures all of this for you automatically.
7878 
7979These examples use Bash scripts, which work on macOS and Linux. On Windows, see [Windows configuration](#windows-configuration) for PowerShell and Git Bash examples.
8080 
81<Frame>
82 <img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-quickstart.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=88a7eab9c1038dd098ee8e284d96b7e6" alt="A status line showing model name, directory, and context percentage" width="1224" height="224" data-path="images/statusline-quickstart.png" />
83</Frame>
84 
85<Steps>
86 <Step title="Create a script that reads JSON and prints output">
87 Claude Code sends JSON data to your script via stdin. This script uses [`jq`](https://jqlang.org/), a command-line JSON parser you may need to install, to extract the model name, directory, and context percentage, then prints a formatted line.
88 
89 Save this to `~/.claude/statusline.sh` (where `~` is your home directory, such as `/Users/username` on macOS or `/home/username` on Linux):
90 
91 ```bash theme={null}
92 #!/bin/bash
93 # Read JSON data that Claude Code sends to stdin
94 input=$(cat)
95 
96 # Extract fields using jq
97 MODEL=$(echo "$input" | jq -r '.model.display_name')
98 DIR=$(echo "$input" | jq -r '.workspace.current_dir')
99 # The "// 0" provides a fallback if the field is null
100 PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
101 
102 # Output the status line - ${DIR##*/} extracts just the folder name
103 echo "[$MODEL] 📁 ${DIR##*/} | ${PCT}% context"
104 ```
105 </Step>
106 
107 <Step title="Make it executable">
108 Mark the script as executable so your shell can run it:
109 
110 ```bash theme={null}
111 chmod +x ~/.claude/statusline.sh
112 ```
113 </Step>
114 
115 <Step title="Add to settings">
116 Tell Claude Code to run your script as the status line. Add this configuration to `~/.claude/settings.json`, which sets `type` to `"command"` (meaning "run this shell command") and points `command` to your script:
117 
118 ```json theme={null}
119 {
120 "statusLine": {
121 "type": "command",
122 "command": "~/.claude/statusline.sh"
123 }
124 }
125 ```
126 
127 Your status line appears at the bottom of the interface. Claude Code reloads settings automatically and runs your script as soon as you save the file.
128 </Step>
129</Steps>
81![A status line showing model name, directory, and context percentage](https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-quickstart.png?fit=max\&auto=format\&n=HDAmBwgbrZVk0pOt\&q=85\&s=88a7eab9c1038dd098ee8e284d96b7e6)
82 
83**1. Create a script that reads JSON and prints output**
84 
85Claude Code sends JSON data to your script via stdin. This script uses [`jq`](https://jqlang.org/), a command-line JSON parser you may need to install, to extract the model name, directory, and context percentage, then prints a formatted line.
86 
87Save this to `~/.claude/statusline.sh` (where `~` is your home directory, such as `/Users/username` on macOS or `/home/username` on Linux):
88 
89```bash theme={null}
90#!/bin/bash
91# Read JSON data that Claude Code sends to stdin
92input=$(cat)
93 
94# Extract fields using jq
95MODEL=$(echo "$input" | jq -r '.model.display_name')
96DIR=$(echo "$input" | jq -r '.workspace.current_dir')
97# The "// 0" provides a fallback if the field is null
98PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
99 
100# Output the status line - ${DIR##*/} extracts just the folder name
101echo "[$MODEL] 📁 ${DIR##*/} | ${PCT}% context"
102```
103 
104**2. Make it executable**
105 
106Mark the script as executable so your shell can run it:
107 
108```bash theme={null}
109chmod +x ~/.claude/statusline.sh
110```
111 
112**3. Add to settings**
113 
114Tell Claude Code to run your script as the status line. Add this configuration to `~/.claude/settings.json`, which sets `type` to `"command"` (meaning "run this shell command") and points `command` to your script:
115 
116```json theme={null}
117{
118 "statusLine": {
119 "type": "command",
120 "command": "~/.claude/statusline.sh"
121 }
122}
123```
124 
125Your status line appears at the bottom of the interface. Claude Code reloads settings automatically and runs your script as soon as you save the file.
130126 
131127## How status lines work
132128 
133129Claude Code runs your script with [JSON session data](#available-data) on stdin and displays whatever the script prints to stdout.
134130 
135<Note>The status line runs locally and does not consume API tokens. It temporarily hides during certain UI interactions, including the help menu and permission prompts.</Note>
131> [!NOTE]
132>
133> The status line runs locally and does not consume API tokens. It temporarily hides during certain UI interactions, including the help menu and permission prompts.
136134 
137135### When the status line updates
138136 
from line 208
210208| `worktree.original_cwd` | The directory Claude was in before entering the worktree |
211209| `worktree.original_branch` | Git branch checked out before entering the worktree. Absent for hook-based worktrees |
212210 
213<Accordion title="Full JSON schema">
214 Your status line command receives this JSON structure via stdin:
215 
216 ```json theme={null}
217 {
218 "cwd": "/current/working/directory",
219 "session_id": "abc123...",
220 "session_name": "my-session",
221 "prompt_id": "550e8400-e29b-41d4-a716-446655440000",
222 "transcript_path": "/path/to/transcript.jsonl",
223 "model": {
224 "id": "claude-opus-5-5",
225 "display_name": "Opus"
226 },
227 "workspace": {
228 "current_dir": "/current/working/directory",
229 "project_dir": "/original/project/directory",
230 "added_dirs": [],
231 "git_worktree": "feature-xyz",
232 "repo": {
233 "host": "github.com",
234 "owner": "anthropics",
235 "name": "claude-code"
236 }
237 },
238 "version": "2.1.90",
239 "output_style": {
240 "name": "default"
241 },
242 "cost": {
243 "total_cost_usd": 0.01234,
244 "total_duration_ms": 45000,
245 "total_api_duration_ms": 2300,
246 "total_lines_added": 156,
247 "total_lines_removed": 23
248 },
249 "context_window": {
250 "total_input_tokens": 15500,
251 "total_output_tokens": 1200,
252 "context_window_size": 200000,
253 "used_percentage": 8,
254 "remaining_percentage": 92,
255 "current_usage": {
256 "input_tokens": 8500,
257 "output_tokens": 1200,
258 "cache_creation_input_tokens": 5000,
259 "cache_read_input_tokens": 2000
260 }
261 },
262 "exceeds_200k_tokens": false,
263 "prompt_cache": {
264 "warm": true,
265 "caching_observed": true,
266 "ttl": "1h",
267 "expires_at": 1738429200,
268 "requests": 14,
269 "misses": 2,
270 "expected_rebuilds": 1,
271 "hit_ratio": 0.91,
272 "cache_write_tokens": 352000,
273 "miss_recache_tokens": 310200,
274 "last_miss_at": 1738425230,
275 "last_miss_cause": {
276 "causes": ["tools_changed"],
277 "tools_added": 2,
278 "tools_removed": 0
279 },
280 "miss_causes": {
281 "tools_changed": 2
282 },
283 "recache_tokens_if_cold": 45000
284 },
285 "fast_mode": false,
286 "effort": {
287 "level": "high"
288 },
289 "thinking": {
290 "enabled": true
291 },
292 "rate_limits": {
293 "five_hour": {
294 "used_percentage": 23.5,
295 "resets_at": 1738425600
296 },
297 "seven_day": {
298 "used_percentage": 41.2,
299 "resets_at": 1738857600
300 },
301 "spend_limit": {
302 "used_percentage": 62.8,
303 "resets_at": 1740787200,
304 "used_usd": 314.12,
305 "limit_usd": 500,
306 "period": "monthly"
307 }
308 },
309 "vim": {
310 "mode": "NORMAL"
311 },
312 "agent": {
313 "name": "security-reviewer"
314 },
315 "pr": {
316 "number": 1234,
317 "url": "https://github.com/anthropics/claude-code/pull/1234",
318 "review_state": "pending"
319 },
320 "worktree": {
321 "name": "my-feature",
322 "path": "/path/to/.claude/worktrees/my-feature",
323 "branch": "worktree-my-feature",
324 "original_cwd": "/path/to/project",
325 "original_branch": "main"
326 }
327 }
328 ```
329 
330 **Fields that may be absent** (not present in JSON):
331 
332 * `session_name`: appears when a custom name has been set with `--name` or `/rename`, or once an AI-generated session title exists. The default display name, such as `my-app-3f`, doesn't populate it
333 * `prompt_id`: appears only after the first user input
334 * `workspace.git_worktree`: appears only when the current directory is inside a linked git worktree
335 * `workspace.repo`: appears only inside a git repository with an `origin` remote configured
336 * `effort`: appears only when the current model supports the reasoning effort parameter
337 * `vim`: appears only when vim mode is enabled
338 * `agent`: appears only when running with the `--agent` flag or agent settings configured
339 * `pr`: appears only while an open PR or GitLab merge request is found for the current branch, and is removed once it merges or closes. `pr.review_state` and `pr.kind` may be independently absent
340 * `worktree`: appears only while the session is in a [worktree session](/docs/en/worktrees). When present, `branch` and `original_branch` may also be absent for hook-based worktrees
341 * `rate_limits`: appears only for claude.ai Pro and Max subscribers, or behind a Claude apps gateway that sets a spend limit for you, and only after the first API response in the session. Each window (`five_hour`, `seven_day`, `spend_limit`) may be independently absent, and Claude Code drops a window once its `resets_at` time passes. Use `jq -r '.rate_limits.five_hour.used_percentage // empty'` to handle absence gracefully.
342 * `prompt_cache`: appears after the main conversation's first API response. See [prompt cache fields](#prompt-cache-fields)
343 
344 **Fields that may be `null`**:
345 
346 * `context_window.current_usage`: `null` before the first API call in a session, and again after `/compact` until the next API call repopulates it
347 * `context_window.used_percentage`, `context_window.remaining_percentage`: may be `null` early in the session
348 
349 Handle missing fields with conditional access and null values with fallback defaults in your scripts.
350</Accordion>
211> **Full JSON schema**
212>
213> Your status line command receives this JSON structure via stdin:
214>
215> ```json theme={null}
216> {
217> "cwd": "/current/working/directory",
218> "session_id": "abc123...",
219> "session_name": "my-session",
220> "prompt_id": "550e8400-e29b-41d4-a716-446655440000",
221> "transcript_path": "/path/to/transcript.jsonl",
222> "model": {
223> "id": "claude-opus-5-5",
224> "display_name": "Opus"
225> },
226> "workspace": {
227> "current_dir": "/current/working/directory",
228> "project_dir": "/original/project/directory",
229> "added_dirs": [],
230> "git_worktree": "feature-xyz",
231> "repo": {
232> "host": "github.com",
233> "owner": "anthropics",
234> "name": "claude-code"
235> }
236> },
237> "version": "2.1.90",
238> "output_style": {
239> "name": "default"
240> },
241> "cost": {
242> "total_cost_usd": 0.01234,
243> "total_duration_ms": 45000,
244> "total_api_duration_ms": 2300,
245> "total_lines_added": 156,
246> "total_lines_removed": 23
247> },
248> "context_window": {
249> "total_input_tokens": 15500,
250> "total_output_tokens": 1200,
251> "context_window_size": 200000,
252> "used_percentage": 8,
253> "remaining_percentage": 92,
254> "current_usage": {
255> "input_tokens": 8500,
256> "output_tokens": 1200,
257> "cache_creation_input_tokens": 5000,
258> "cache_read_input_tokens": 2000
259> }
260> },
261> "exceeds_200k_tokens": false,
262> "prompt_cache": {
263> "warm": true,
264> "caching_observed": true,
265> "ttl": "1h",
266> "expires_at": 1738429200,
267> "requests": 14,
268> "misses": 2,
269> "expected_rebuilds": 1,
270> "hit_ratio": 0.91,
271> "cache_write_tokens": 352000,
272> "miss_recache_tokens": 310200,
273> "last_miss_at": 1738425230,
274> "last_miss_cause": {
275> "causes": ["tools_changed"],
276> "tools_added": 2,
277> "tools_removed": 0
278> },
279> "miss_causes": {
280> "tools_changed": 2
281> },
282> "recache_tokens_if_cold": 45000
283> },
284> "fast_mode": false,
285> "effort": {
286> "level": "high"
287> },
288> "thinking": {
289> "enabled": true
290> },
291> "rate_limits": {
292> "five_hour": {
293> "used_percentage": 23.5,
294> "resets_at": 1738425600
295> },
296> "seven_day": {
297> "used_percentage": 41.2,
298> "resets_at": 1738857600
299> },
300> "spend_limit": {
301> "used_percentage": 62.8,
302> "resets_at": 1740787200,
303> "used_usd": 314.12,
304> "limit_usd": 500,
305> "period": "monthly"
306> }
307> },
308> "vim": {
309> "mode": "NORMAL"
310> },
311> "agent": {
312> "name": "security-reviewer"
313> },
314> "pr": {
315> "number": 1234,
316> "url": "https://github.com/anthropics/claude-code/pull/1234",
317> "review_state": "pending"
318> },
319> "worktree": {
320> "name": "my-feature",
321> "path": "/path/to/.claude/worktrees/my-feature",
322> "branch": "worktree-my-feature",
323> "original_cwd": "/path/to/project",
324> "original_branch": "main"
325> }
326> }
327> ```
328>
329> **Fields that may be absent** (not present in JSON):
330>
331> * `session_name`: appears when a custom name has been set with `--name` or `/rename`, or once an AI-generated session title exists. The default display name, such as `my-app-3f`, doesn't populate it
332> * `prompt_id`: appears only after the first user input
333> * `workspace.git_worktree`: appears only when the current directory is inside a linked git worktree
334> * `workspace.repo`: appears only inside a git repository with an `origin` remote configured
335> * `effort`: appears only when the current model supports the reasoning effort parameter
336> * `vim`: appears only when vim mode is enabled
337> * `agent`: appears only when running with the `--agent` flag or agent settings configured
338> * `pr`: appears only while an open PR or GitLab merge request is found for the current branch, and is removed once it merges or closes. `pr.review_state` and `pr.kind` may be independently absent
339> * `worktree`: appears only while the session is in a [worktree session](/docs/en/worktrees). When present, `branch` and `original_branch` may also be absent for hook-based worktrees
340> * `rate_limits`: appears only for claude.ai Pro and Max subscribers, or behind a Claude apps gateway that sets a spend limit for you, and only after the first API response in the session. Each window (`five_hour`, `seven_day`, `spend_limit`) may be independently absent, and Claude Code drops a window once its `resets_at` time passes. Use `jq -r '.rate_limits.five_hour.used_percentage // empty'` to handle absence gracefully.
341> * `prompt_cache`: appears after the main conversation's first API response. See [prompt cache fields](#prompt-cache-fields)
342>
343> **Fields that may be `null`**:
344>
345> * `context_window.current_usage`: `null` before the first API call in a session, and again after `/compact` until the next API call repopulates it
346> * `context_window.used_percentage`, `context_window.remaining_percentage`: may be `null` early in the session
347>
348> Handle missing fields with conditional access and null values with fallback defaults in your scripts.
351349 
352350### Context window fields
353351 
from line 405
407405 
408406Claude Code shows the same statistics in the terminal, on the [`/usage` command's `Prompt cache (main)` line](/docs/en/costs#prompt-cache-statistics).
409407 
410<h4 id="last-miss-cause">
411 Last miss cause
412</h4>
408#### Last miss cause
413409 
414410The `last_miss_cause` object reports what Claude Code identified as the likely cause of the most recent miss. Its `causes` array holds one or more cause names, such as `tools_changed`, `system_prompt_changed`, `ttl_expired_5m`, or `likely_server_side`. The object is `null` until the session's first miss, and again whenever Claude Code couldn't identify a cause for the most recent miss. Requires Claude Code v2.1.260 or later.
415411 
from line 428
432428 
433429Display the current model and context window usage with a visual progress bar. Each script reads JSON from stdin, extracts the `used_percentage` field, and builds a 10-character bar where filled blocks (▓) represent usage:
434430 
435<Frame>
436 <img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-context-window-usage.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=f3918a549912dc47e90f2b69e68bc847" alt="A status line showing model name and a progress bar with percentage" width="1224" height="224" data-path="images/statusline-context-window-usage.png" />
437</Frame>
438 
439<CodeGroup>
440 ```bash Bash theme={null}
441 #!/bin/bash
442 # Read all of stdin into a variable
443 input=$(cat)
444 
445 # Extract fields with jq, "// 0" provides fallback for null
446 MODEL=$(echo "$input" | jq -r '.model.display_name')
447 PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
448 
449 # Build progress bar: printf -v creates a run of spaces, then
450 # ${var// /▓} replaces each space with a block character
451 BAR_WIDTH=10
452 FILLED=$((PCT * BAR_WIDTH / 100))
453 EMPTY=$((BAR_WIDTH - FILLED))
454 BAR=""
455 [ "$FILLED" -gt 0 ] && printf -v FILL "%${FILLED}s" && BAR="${FILL// /▓}"
456 [ "$EMPTY" -gt 0 ] && printf -v PAD "%${EMPTY}s" && BAR="${BAR}${PAD// /░}"
457 
458 echo "[$MODEL] $BAR $PCT%"
459 ```
460 
461 ```python Python theme={null}
462 #!/usr/bin/env python3
463 import json, sys
464 
465 # json.load reads and parses stdin in one step
466 data = json.load(sys.stdin)
467 model = data['model']['display_name']
468 # "or 0" handles null values
469 pct = int(data.get('context_window', {}).get('used_percentage', 0) or 0)
470 
471 # String multiplication builds the bar
472 filled = pct * 10 // 100
473 bar = '▓' * filled + '░' * (10 - filled)
474 
475 print(f"[{model}] {bar} {pct}%")
476 ```
477 
478 ```javascript Node.js theme={null}
479 #!/usr/bin/env node
480 // Node.js reads stdin asynchronously with events
481 let input = '';
482 process.stdin.on('data', chunk => input += chunk);
483 process.stdin.on('end', () => {
484 const data = JSON.parse(input);
485 const model = data.model.display_name;
486 // Optional chaining (?.) safely handles null fields
487 const pct = Math.floor(data.context_window?.used_percentage || 0);
488 
489 // String.repeat() builds the bar
490 const filled = Math.floor(pct * 10 / 100);
491 const bar = '▓'.repeat(filled) + '░'.repeat(10 - filled);
492 
493 console.log(`[${model}] ${bar} ${pct}%`);
494 });
495 ```
496</CodeGroup>
431![A status line showing model name and a progress bar with percentage](https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-context-window-usage.png?fit=max\&auto=format\&n=HDAmBwgbrZVk0pOt\&q=85\&s=f3918a549912dc47e90f2b69e68bc847)
432 
433```bash Bash theme={null}
434#!/bin/bash
435# Read all of stdin into a variable
436input=$(cat)
437 
438# Extract fields with jq, "// 0" provides fallback for null
439MODEL=$(echo "$input" | jq -r '.model.display_name')
440PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
441 
442# Build progress bar: printf -v creates a run of spaces, then
443# ${var// /▓} replaces each space with a block character
444BAR_WIDTH=10
445FILLED=$((PCT * BAR_WIDTH / 100))
446EMPTY=$((BAR_WIDTH - FILLED))
447BAR=""
448[ "$FILLED" -gt 0 ] && printf -v FILL "%${FILLED}s" && BAR="${FILL// /▓}"
449[ "$EMPTY" -gt 0 ] && printf -v PAD "%${EMPTY}s" && BAR="${BAR}${PAD// /░}"
450 
451echo "[$MODEL] $BAR $PCT%"
452```
453 
454```python Python theme={null}
455#!/usr/bin/env python3
456import json, sys
457 
458# json.load reads and parses stdin in one step
459data = json.load(sys.stdin)
460model = data['model']['display_name']
461# "or 0" handles null values
462pct = int(data.get('context_window', {}).get('used_percentage', 0) or 0)
463 
464# String multiplication builds the bar
465filled = pct * 10 // 100
466bar = '▓' * filled + '░' * (10 - filled)
467 
468print(f"[{model}] {bar} {pct}%")
469```
470 
471```javascript Node.js theme={null}
472#!/usr/bin/env node
473// Node.js reads stdin asynchronously with events
474let input = '';
475process.stdin.on('data', chunk => input += chunk);
476process.stdin.on('end', () => {
477 const data = JSON.parse(input);
478 const model = data.model.display_name;
479 // Optional chaining (?.) safely handles null fields
480 const pct = Math.floor(data.context_window?.used_percentage || 0);
481 
482 // String.repeat() builds the bar
483 const filled = Math.floor(pct * 10 / 100);
484 const bar = '▓'.repeat(filled) + '░'.repeat(10 - filled);
485 
486 console.log(`[${model}] ${bar} ${pct}%`);
487});
488```
497489 
498490### Git status with colors
499491 
500492Show git branch with color-coded indicators for staged and modified files. This script uses [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors) for terminal colors: `\033[32m` is green, `\033[33m` is yellow, and `\033[0m` resets to default.
501493 
502<Frame>
503 <img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-git-context.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=f13c190724d9ec7188c17cd2f98b7bf4" alt="A status line showing model, directory, git branch, and colored indicators for staged and modified files" width="1224" height="224" data-path="images/statusline-git-context.png" />
504</Frame>
494![A status line showing model, directory, git branch, and colored indicators for staged and modified files](https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-git-context.png?fit=max\&auto=format\&n=HDAmBwgbrZVk0pOt\&q=85\&s=f13c190724d9ec7188c17cd2f98b7bf4)
505495 
506496Each script checks if the current directory is a git repository, counts staged and modified files, and displays color-coded indicators:
507497 
508<CodeGroup>
509 ```bash Bash theme={null}
510 #!/bin/bash
511 input=$(cat)
512 
513 MODEL=$(echo "$input" | jq -r '.model.display_name')
514 DIR=$(echo "$input" | jq -r '.workspace.current_dir')
515 
516 GREEN='\033[32m'
517 YELLOW='\033[33m'
518 RESET='\033[0m'
519 
520 if git rev-parse --git-dir > /dev/null 2>&1; then
521 BRANCH=$(git branch --show-current 2>/dev/null)
522 STAGED=$(git diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
523 MODIFIED=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ')
524 
525 GIT_STATUS=""
526 [ "$STAGED" -gt 0 ] && GIT_STATUS="${GREEN}+${STAGED}${RESET}"
527 [ "$MODIFIED" -gt 0 ] && GIT_STATUS="${GIT_STATUS}${YELLOW}~${MODIFIED}${RESET}"
528 
529 echo -e "[$MODEL] 📁 ${DIR##*/} | 🌿 $BRANCH $GIT_STATUS"
530 else
531 echo "[$MODEL] 📁 ${DIR##*/}"
532 fi
533 ```
534 
535 ```python Python theme={null}
536 #!/usr/bin/env python3
537 import json, sys, subprocess, os
538 
539 data = json.load(sys.stdin)
540 model = data['model']['display_name']
541 directory = os.path.basename(data['workspace']['current_dir'])
542 
543 GREEN, YELLOW, RESET = '\033[32m', '\033[33m', '\033[0m'
544 
545 try:
546 subprocess.check_output(['git', 'rev-parse', '--git-dir'], stderr=subprocess.DEVNULL)
547 branch = subprocess.check_output(['git', 'branch', '--show-current'], text=True).strip()
548 staged_output = subprocess.check_output(['git', 'diff', '--cached', '--numstat'], text=True).strip()
549 modified_output = subprocess.check_output(['git', 'diff', '--numstat'], text=True).strip()
550 staged = len(staged_output.split('\n')) if staged_output else 0
551 modified = len(modified_output.split('\n')) if modified_output else 0
552 
553 git_status = f"{GREEN}+{staged}{RESET}" if staged else ""
554 git_status += f"{YELLOW}~{modified}{RESET}" if modified else ""
555 
556 print(f"[{model}] 📁 {directory} | 🌿 {branch} {git_status}")
557 except:
558 print(f"[{model}] 📁 {directory}")
559 ```
560 
561 ```javascript Node.js theme={null}
562 #!/usr/bin/env node
563 const { execSync } = require('child_process');
564 const path = require('path');
565 
566 let input = '';
567 process.stdin.on('data', chunk => input += chunk);
568 process.stdin.on('end', () => {
569 const data = JSON.parse(input);
570 const model = data.model.display_name;
571 const dir = path.basename(data.workspace.current_dir);
572 
573 const GREEN = '\x1b[32m', YELLOW = '\x1b[33m', RESET = '\x1b[0m';
574 
575 try {
576 execSync('git rev-parse --git-dir', { stdio: 'ignore' });
577 const branch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
578 const staged = execSync('git diff --cached --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
579 const modified = execSync('git diff --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
580 
581 let gitStatus = staged ? `${GREEN}+${staged}${RESET}` : '';
582 gitStatus += modified ? `${YELLOW}~${modified}${RESET}` : '';
583 
584 console.log(`[${model}] 📁 ${dir} | 🌿 ${branch} ${gitStatus}`);
585 } catch {
586 console.log(`[${model}] 📁 ${dir}`);
587 }
588 });
589 ```
590</CodeGroup>
498```bash Bash theme={null}
499#!/bin/bash
500input=$(cat)
501 
502MODEL=$(echo "$input" | jq -r '.model.display_name')
503DIR=$(echo "$input" | jq -r '.workspace.current_dir')
504 
505GREEN='\033[32m'
506YELLOW='\033[33m'
507RESET='\033[0m'
508 
509if git rev-parse --git-dir > /dev/null 2>&1; then
510 BRANCH=$(git branch --show-current 2>/dev/null)
511 STAGED=$(git diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
512 MODIFIED=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ')
513 
514 GIT_STATUS=""
515 [ "$STAGED" -gt 0 ] && GIT_STATUS="${GREEN}+${STAGED}${RESET}"
516 [ "$MODIFIED" -gt 0 ] && GIT_STATUS="${GIT_STATUS}${YELLOW}~${MODIFIED}${RESET}"
517 
518 echo -e "[$MODEL] 📁 ${DIR##*/} | 🌿 $BRANCH $GIT_STATUS"
519else
520 echo "[$MODEL] 📁 ${DIR##*/}"
521fi
522```
523 
524```python Python theme={null}
525#!/usr/bin/env python3
526import json, sys, subprocess, os
527 
528data = json.load(sys.stdin)
529model = data['model']['display_name']
530directory = os.path.basename(data['workspace']['current_dir'])
531 
532GREEN, YELLOW, RESET = '\033[32m', '\033[33m', '\033[0m'
533 
534try:
535 subprocess.check_output(['git', 'rev-parse', '--git-dir'], stderr=subprocess.DEVNULL)
536 branch = subprocess.check_output(['git', 'branch', '--show-current'], text=True).strip()
537 staged_output = subprocess.check_output(['git', 'diff', '--cached', '--numstat'], text=True).strip()
538 modified_output = subprocess.check_output(['git', 'diff', '--numstat'], text=True).strip()
539 staged = len(staged_output.split('\n')) if staged_output else 0
540 modified = len(modified_output.split('\n')) if modified_output else 0
541 
542 git_status = f"{GREEN}+{staged}{RESET}" if staged else ""
543 git_status += f"{YELLOW}~{modified}{RESET}" if modified else ""
544 
545 print(f"[{model}] 📁 {directory} | 🌿 {branch} {git_status}")
546except:
547 print(f"[{model}] 📁 {directory}")
548```
549 
550```javascript Node.js theme={null}
551#!/usr/bin/env node
552const { execSync } = require('child_process');
553const path = require('path');
554 
555let input = '';
556process.stdin.on('data', chunk => input += chunk);
557process.stdin.on('end', () => {
558 const data = JSON.parse(input);
559 const model = data.model.display_name;
560 const dir = path.basename(data.workspace.current_dir);
561 
562 const GREEN = '\x1b[32m', YELLOW = '\x1b[33m', RESET = '\x1b[0m';
563 
564 try {
565 execSync('git rev-parse --git-dir', { stdio: 'ignore' });
566 const branch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
567 const staged = execSync('git diff --cached --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
568 const modified = execSync('git diff --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
569 
570 let gitStatus = staged ? `${GREEN}+${staged}${RESET}` : '';
571 gitStatus += modified ? `${YELLOW}~${modified}${RESET}` : '';
572 
573 console.log(`[${model}] 📁 ${dir} | 🌿 ${branch} ${gitStatus}`);
574 } catch {
575 console.log(`[${model}] 📁 ${dir}`);
576 }
577});
578```
591579 
592580### Cost and duration tracking
593581 
from line 583
595583 
596584Each script formats cost as currency and converts milliseconds to minutes and seconds:
597585 
598<Frame>
599 <img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-cost-tracking.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=925f7024c3b38be0f0eca63564bfb52f" alt="A status line showing model name, session cost, and duration" width="1224" height="224" data-path="images/statusline-cost-tracking.png" />
600</Frame>
601 
602<CodeGroup>
603 ```bash Bash theme={null}
604 #!/bin/bash
605 input=$(cat)
606 
607 MODEL=$(echo "$input" | jq -r '.model.display_name')
608 COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
609 DURATION_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
610 
611 COST_FMT=$(printf '$%.2f' "$COST")
612 DURATION_SEC=$((DURATION_MS / 1000))
613 MINS=$((DURATION_SEC / 60))
614 SECS=$((DURATION_SEC % 60))
615 
616 echo "[$MODEL] 💰 $COST_FMT | ⏱️ ${MINS}m ${SECS}s"
617 ```
618 
619 ```python Python theme={null}
620 #!/usr/bin/env python3
621 import json, sys
622 
623 data = json.load(sys.stdin)
624 model = data['model']['display_name']
625 cost = data.get('cost', {}).get('total_cost_usd', 0) or 0
626 duration_ms = data.get('cost', {}).get('total_duration_ms', 0) or 0
627 
628 duration_sec = duration_ms // 1000
629 mins, secs = duration_sec // 60, duration_sec % 60
630 
631 print(f"[{model}] 💰 ${cost:.2f} | ⏱️ {mins}m {secs}s")
632 ```
633 
634 ```javascript Node.js theme={null}
635 #!/usr/bin/env node
636 let input = '';
637 process.stdin.on('data', chunk => input += chunk);
638 process.stdin.on('end', () => {
639 const data = JSON.parse(input);
640 const model = data.model.display_name;
641 const cost = data.cost?.total_cost_usd || 0;
642 const durationMs = data.cost?.total_duration_ms || 0;
643 
644 const durationSec = Math.floor(durationMs / 1000);
645 const mins = Math.floor(durationSec / 60);
646 const secs = durationSec % 60;
647 
648 console.log(`[${model}] 💰 $${cost.toFixed(2)} | ⏱️ ${mins}m ${secs}s`);
649 });
650 ```
651</CodeGroup>
586![A status line showing model name, session cost, and duration](https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-cost-tracking.png?fit=max\&auto=format\&n=HDAmBwgbrZVk0pOt\&q=85\&s=925f7024c3b38be0f0eca63564bfb52f)
587 
588```bash Bash theme={null}
589#!/bin/bash
590input=$(cat)
591 
592MODEL=$(echo "$input" | jq -r '.model.display_name')
593COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
594DURATION_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
595 
596COST_FMT=$(printf '$%.2f' "$COST")
597DURATION_SEC=$((DURATION_MS / 1000))
598MINS=$((DURATION_SEC / 60))
599SECS=$((DURATION_SEC % 60))
600 
601echo "[$MODEL] 💰 $COST_FMT | ⏱️ ${MINS}m ${SECS}s"
602```
603 
604```python Python theme={null}
605#!/usr/bin/env python3
606import json, sys
607 
608data = json.load(sys.stdin)
609model = data['model']['display_name']
610cost = data.get('cost', {}).get('total_cost_usd', 0) or 0
611duration_ms = data.get('cost', {}).get('total_duration_ms', 0) or 0
612 
613duration_sec = duration_ms // 1000
614mins, secs = duration_sec // 60, duration_sec % 60
615 
616print(f"[{model}] 💰 ${cost:.2f} | ⏱️ {mins}m {secs}s")
617```
618 
619```javascript Node.js theme={null}
620#!/usr/bin/env node
621let input = '';
622process.stdin.on('data', chunk => input += chunk);
623process.stdin.on('end', () => {
624 const data = JSON.parse(input);
625 const model = data.model.display_name;
626 const cost = data.cost?.total_cost_usd || 0;
627 const durationMs = data.cost?.total_duration_ms || 0;
628 
629 const durationSec = Math.floor(durationMs / 1000);
630 const mins = Math.floor(durationSec / 60);
631 const secs = durationSec % 60;
632 
633 console.log(`[${model}] 💰 $${cost.toFixed(2)} | ⏱️ ${mins}m ${secs}s`);
634});
635```
652636 
653637### Display multiple lines
654638 
655639Your script can output multiple lines to create a richer display.
656640 
657<Frame>
658 <img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-multiline.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=a9d0a2fe8e446d80b1abc46da3f93270" alt="A multi-line status line showing model name, directory, git branch on the first line, and a context usage progress bar with cost and duration on the second line" width="1224" height="262" data-path="images/statusline-multiline.png" />
659</Frame>
641![A multi-line status line showing model name, directory, git branch on the first line, and a context usage progress bar with cost and duration on the second line](https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-multiline.png?fit=max\&auto=format\&n=HDAmBwgbrZVk0pOt\&q=85\&s=a9d0a2fe8e446d80b1abc46da3f93270)
660642 
661643This example combines several techniques: threshold-based colors (green under 70%, yellow 70-89%, red 90%+), a progress bar, and git branch info. Each `print` or `echo` statement creates a separate row:
662644 
663<CodeGroup>
664 ```bash Bash theme={null}
665 #!/bin/bash
666 input=$(cat)
667 
668 MODEL=$(echo "$input" | jq -r '.model.display_name')
669 DIR=$(echo "$input" | jq -r '.workspace.current_dir')
670 COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
671 PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
672 DURATION_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
673 
674 CYAN='\033[36m'; GREEN='\033[32m'; YELLOW='\033[33m'; RED='\033[31m'; RESET='\033[0m'
675 
676 # Pick bar color based on context usage
677 if [ "$PCT" -ge 90 ]; then BAR_COLOR="$RED"
678 elif [ "$PCT" -ge 70 ]; then BAR_COLOR="$YELLOW"
679 else BAR_COLOR="$GREEN"; fi
680 
681 FILLED=$((PCT / 10)); EMPTY=$((10 - FILLED))
682 printf -v FILL "%${FILLED}s"; printf -v PAD "%${EMPTY}s"
683 BAR="${FILL// /█}${PAD// /░}"
684 
685 MINS=$((DURATION_MS / 60000)); SECS=$(((DURATION_MS % 60000) / 1000))
686 
687 BRANCH=""
688 git rev-parse --git-dir > /dev/null 2>&1 && BRANCH=" | 🌿 $(git branch --show-current 2>/dev/null)"
689 
690 echo -e "${CYAN}[$MODEL]${RESET} 📁 ${DIR##*/}$BRANCH"
691 COST_FMT=$(printf '$%.2f' "$COST")
692 echo -e "${BAR_COLOR}${BAR}${RESET} ${PCT}% | ${YELLOW}${COST_FMT}${RESET} | ⏱️ ${MINS}m ${SECS}s"
693 ```
694 
695 ```python Python theme={null}
696 #!/usr/bin/env python3
697 import json, sys, subprocess, os
698 
699 data = json.load(sys.stdin)
700 model = data['model']['display_name']
701 directory = os.path.basename(data['workspace']['current_dir'])
702 cost = data.get('cost', {}).get('total_cost_usd', 0) or 0
703 pct = int(data.get('context_window', {}).get('used_percentage', 0) or 0)
704 duration_ms = data.get('cost', {}).get('total_duration_ms', 0) or 0
705 
706 CYAN, GREEN, YELLOW, RED, RESET = '\033[36m', '\033[32m', '\033[33m', '\033[31m', '\033[0m'
707 
708 bar_color = RED if pct >= 90 else YELLOW if pct >= 70 else GREEN
709 filled = pct // 10
710 bar = '█' * filled + '░' * (10 - filled)
711 
712 mins, secs = duration_ms // 60000, (duration_ms % 60000) // 1000
713 
714 try:
715 branch = subprocess.check_output(['git', 'branch', '--show-current'], text=True, stderr=subprocess.DEVNULL).strip()
716 branch = f" | 🌿 {branch}" if branch else ""
717 except:
718 branch = ""
719 
720 print(f"{CYAN}[{model}]{RESET} 📁 {directory}{branch}")
721 print(f"{bar_color}{bar}{RESET} {pct}% | {YELLOW}${cost:.2f}{RESET} | ⏱️ {mins}m {secs}s")
722 ```
723 
724 ```javascript Node.js theme={null}
725 #!/usr/bin/env node
726 const { execSync } = require('child_process');
727 const path = require('path');
728 
729 let input = '';
730 process.stdin.on('data', chunk => input += chunk);
731 process.stdin.on('end', () => {
732 const data = JSON.parse(input);
733 const model = data.model.display_name;
734 const dir = path.basename(data.workspace.current_dir);
735 const cost = data.cost?.total_cost_usd || 0;
736 const pct = Math.floor(data.context_window?.used_percentage || 0);
737 const durationMs = data.cost?.total_duration_ms || 0;
738 
739 const CYAN = '\x1b[36m', GREEN = '\x1b[32m', YELLOW = '\x1b[33m', RED = '\x1b[31m', RESET = '\x1b[0m';
740 
741 const barColor = pct >= 90 ? RED : pct >= 70 ? YELLOW : GREEN;
742 const filled = Math.floor(pct / 10);
743 const bar = '█'.repeat(filled) + '░'.repeat(10 - filled);
744 
745 const mins = Math.floor(durationMs / 60000);
746 const secs = Math.floor((durationMs % 60000) / 1000);
747 
748 let branch = '';
749 try {
750 branch = execSync('git branch --show-current', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
751 branch = branch ? ` | 🌿 ${branch}` : '';
752 } catch {}
753 
754 console.log(`${CYAN}[${model}]${RESET} 📁 ${dir}${branch}`);
755 console.log(`${barColor}${bar}${RESET} ${pct}% | ${YELLOW}$${cost.toFixed(2)}${RESET} | ⏱️ ${mins}m ${secs}s`);
756 });
757 ```
758</CodeGroup>
645```bash Bash theme={null}
646#!/bin/bash
647input=$(cat)
648 
649MODEL=$(echo "$input" | jq -r '.model.display_name')
650DIR=$(echo "$input" | jq -r '.workspace.current_dir')
651COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
652PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
653DURATION_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
654 
655CYAN='\033[36m'; GREEN='\033[32m'; YELLOW='\033[33m'; RED='\033[31m'; RESET='\033[0m'
656 
657# Pick bar color based on context usage
658if [ "$PCT" -ge 90 ]; then BAR_COLOR="$RED"
659elif [ "$PCT" -ge 70 ]; then BAR_COLOR="$YELLOW"
660else BAR_COLOR="$GREEN"; fi
661 
662FILLED=$((PCT / 10)); EMPTY=$((10 - FILLED))
663printf -v FILL "%${FILLED}s"; printf -v PAD "%${EMPTY}s"
664BAR="${FILL// /█}${PAD// /░}"
665 
666MINS=$((DURATION_MS / 60000)); SECS=$(((DURATION_MS % 60000) / 1000))
667 
668BRANCH=""
669git rev-parse --git-dir > /dev/null 2>&1 && BRANCH=" | 🌿 $(git branch --show-current 2>/dev/null)"
670 
671echo -e "${CYAN}[$MODEL]${RESET} 📁 ${DIR##*/}$BRANCH"
672COST_FMT=$(printf '$%.2f' "$COST")
673echo -e "${BAR_COLOR}${BAR}${RESET} ${PCT}% | ${YELLOW}${COST_FMT}${RESET} | ⏱️ ${MINS}m ${SECS}s"
674```
675 
676```python Python theme={null}
677#!/usr/bin/env python3
678import json, sys, subprocess, os
679 
680data = json.load(sys.stdin)
681model = data['model']['display_name']
682directory = os.path.basename(data['workspace']['current_dir'])
683cost = data.get('cost', {}).get('total_cost_usd', 0) or 0
684pct = int(data.get('context_window', {}).get('used_percentage', 0) or 0)
685duration_ms = data.get('cost', {}).get('total_duration_ms', 0) or 0
686 
687CYAN, GREEN, YELLOW, RED, RESET = '\033[36m', '\033[32m', '\033[33m', '\033[31m', '\033[0m'
688 
689bar_color = RED if pct >= 90 else YELLOW if pct >= 70 else GREEN
690filled = pct // 10
691bar = '█' * filled + '░' * (10 - filled)
692 
693mins, secs = duration_ms // 60000, (duration_ms % 60000) // 1000
694 
695try:
696 branch = subprocess.check_output(['git', 'branch', '--show-current'], text=True, stderr=subprocess.DEVNULL).strip()
697 branch = f" | 🌿 {branch}" if branch else ""
698except:
699 branch = ""
700 
701print(f"{CYAN}[{model}]{RESET} 📁 {directory}{branch}")
702print(f"{bar_color}{bar}{RESET} {pct}% | {YELLOW}${cost:.2f}{RESET} | ⏱️ {mins}m {secs}s")
703```
704 
705```javascript Node.js theme={null}
706#!/usr/bin/env node
707const { execSync } = require('child_process');
708const path = require('path');
709 
710let input = '';
711process.stdin.on('data', chunk => input += chunk);
712process.stdin.on('end', () => {
713 const data = JSON.parse(input);
714 const model = data.model.display_name;
715 const dir = path.basename(data.workspace.current_dir);
716 const cost = data.cost?.total_cost_usd || 0;
717 const pct = Math.floor(data.context_window?.used_percentage || 0);
718 const durationMs = data.cost?.total_duration_ms || 0;
719 
720 const CYAN = '\x1b[36m', GREEN = '\x1b[32m', YELLOW = '\x1b[33m', RED = '\x1b[31m', RESET = '\x1b[0m';
721 
722 const barColor = pct >= 90 ? RED : pct >= 70 ? YELLOW : GREEN;
723 const filled = Math.floor(pct / 10);
724 const bar = '█'.repeat(filled) + '░'.repeat(10 - filled);
725 
726 const mins = Math.floor(durationMs / 60000);
727 const secs = Math.floor((durationMs % 60000) / 1000);
728 
729 let branch = '';
730 try {
731 branch = execSync('git branch --show-current', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
732 branch = branch ? ` | 🌿 ${branch}` : '';
733 } catch {}
734 
735 console.log(`${CYAN}[${model}]${RESET} 📁 ${dir}${branch}`);
736 console.log(`${barColor}${bar}${RESET} ${pct}% | ${YELLOW}$${cost.toFixed(2)}${RESET} | ⏱️ ${mins}m ${secs}s`);
737});
738```
759739 
760740### Clickable links
761741 
762742This example creates a clickable link to your GitHub repository. Hold Cmd (macOS) or Ctrl (Windows/Linux) and click to open the link in your browser.
763743 
764<Frame>
765 <img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-links.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=4778a144a28cb498c99d5fa018bb374a" alt="A status line showing a clickable link to a GitHub repository" width="1224" height="224" data-path="images/statusline-links.png" />
766</Frame>
744![A status line showing a clickable link to a GitHub repository](https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-links.png?fit=max\&auto=format\&n=HDAmBwgbrZVk0pOt\&q=85\&s=4778a144a28cb498c99d5fa018bb374a)
767745 
768746Each script gets the git remote URL, converts SSH format to HTTPS, and wraps the repo name in OSC 8 escape codes. The Bash version uses `printf '%b'` which interprets backslash escapes more reliably than `echo -e` across different shells:
769747 
770<CodeGroup>
771 ```bash Bash theme={null}
772 #!/bin/bash
773 input=$(cat)
774 
775 MODEL=$(echo "$input" | jq -r '.model.display_name')
776 
777 # Convert git SSH URL to HTTPS
778 REMOTE=$(git remote get-url origin 2>/dev/null | sed 's/[email protected]:/https:\/\/github.com\//' | sed 's/\.git$//')
779 
780 if [ -n "$REMOTE" ]; then
781 REPO_NAME=$(basename "$REMOTE")
782 # OSC 8 format: \e]8;;URL\a then TEXT then \e]8;;\a
783 # printf %b interprets escape sequences reliably across shells
784 printf '%b' "[$MODEL] 🔗 \e]8;;${REMOTE}\a${REPO_NAME}\e]8;;\a\n"
785 else
786 echo "[$MODEL]"
787 fi
788 ```
789 
790 ```python Python theme={null}
791 #!/usr/bin/env python3
792 import json, sys, subprocess, re, os
793 
794 data = json.load(sys.stdin)
795 model = data['model']['display_name']
796 
797 # Get git remote URL
798 try:
799 remote = subprocess.check_output(
800 ['git', 'remote', 'get-url', 'origin'],
801 stderr=subprocess.DEVNULL, text=True
802 ).strip()
803 # Convert SSH to HTTPS format
804 remote = re.sub(r'^git@github\.com:', 'https://github.com/', remote)
805 remote = re.sub(r'\.git$', '', remote)
806 repo_name = os.path.basename(remote)
807 # OSC 8 escape sequences
808 link = f"\033]8;;{remote}\a{repo_name}\033]8;;\a"
809 print(f"[{model}] 🔗 {link}")
810 except:
811 print(f"[{model}]")
812 ```
813 
814 ```javascript Node.js theme={null}
815 #!/usr/bin/env node
816 const { execSync } = require('child_process');
817 const path = require('path');
818 
819 let input = '';
820 process.stdin.on('data', chunk => input += chunk);
821 process.stdin.on('end', () => {
822 const data = JSON.parse(input);
823 const model = data.model.display_name;
824 
825 try {
826 let remote = execSync('git remote get-url origin', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
827 // Convert SSH to HTTPS format
828 remote = remote.replace(/^git@github\.com:/, 'https://github.com/').replace(/\.git$/, '');
829 const repoName = path.basename(remote);
830 // OSC 8 escape sequences
831 const link = `\x1b]8;;${remote}\x07${repoName}\x1b]8;;\x07`;
832 console.log(`[${model}] 🔗 ${link}`);
833 } catch {
834 console.log(`[${model}]`);
835 }
836 });
837 ```
838</CodeGroup>
748```bash Bash theme={null}
749#!/bin/bash
750input=$(cat)
751 
752MODEL=$(echo "$input" | jq -r '.model.display_name')
753 
754# Convert git SSH URL to HTTPS
755REMOTE=$(git remote get-url origin 2>/dev/null | sed 's/[email protected]:/https:\/\/github.com\//' | sed 's/\.git$//')
756 
757if [ -n "$REMOTE" ]; then
758 REPO_NAME=$(basename "$REMOTE")
759 # OSC 8 format: \e]8;;URL\a then TEXT then \e]8;;\a
760 # printf %b interprets escape sequences reliably across shells
761 printf '%b' "[$MODEL] 🔗 \e]8;;${REMOTE}\a${REPO_NAME}\e]8;;\a\n"
762else
763 echo "[$MODEL]"
764fi
765```
766 
767```python Python theme={null}
768#!/usr/bin/env python3
769import json, sys, subprocess, re, os
770 
771data = json.load(sys.stdin)
772model = data['model']['display_name']
773 
774# Get git remote URL
775try:
776 remote = subprocess.check_output(
777 ['git', 'remote', 'get-url', 'origin'],
778 stderr=subprocess.DEVNULL, text=True
779 ).strip()
780 # Convert SSH to HTTPS format
781 remote = re.sub(r'^git@github\.com:', 'https://github.com/', remote)
782 remote = re.sub(r'\.git$', '', remote)
783 repo_name = os.path.basename(remote)
784 # OSC 8 escape sequences
785 link = f"\033]8;;{remote}\a{repo_name}\033]8;;\a"
786 print(f"[{model}] 🔗 {link}")
787except:
788 print(f"[{model}]")
789```
790 
791```javascript Node.js theme={null}
792#!/usr/bin/env node
793const { execSync } = require('child_process');
794const path = require('path');
795 
796let input = '';
797process.stdin.on('data', chunk => input += chunk);
798process.stdin.on('end', () => {
799 const data = JSON.parse(input);
800 const model = data.model.display_name;
801 
802 try {
803 let remote = execSync('git remote get-url origin', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
804 // Convert SSH to HTTPS format
805 remote = remote.replace(/^git@github\.com:/, 'https://github.com/').replace(/\.git$/, '');
806 const repoName = path.basename(remote);
807 // OSC 8 escape sequences
808 const link = `\x1b]8;;${remote}\x07${repoName}\x1b]8;;\x07`;
809 console.log(`[${model}] 🔗 ${link}`);
810 } catch {
811 console.log(`[${model}]`);
812 }
813});
814```
839815 
840816### Rate limit usage
841817 
from line 819
843819 
844820The `rate_limits` object is only present for claude.ai Pro and Max subscribers, or behind a Claude apps gateway with spend limits, and only after the first API response. Each script handles the absent fields gracefully, and behind a gateway prints `spend: $314.12 / $500`, or `spend: 63%` while the dollar fields are absent:
845821 
846<CodeGroup>
847 ```bash Bash theme={null}
848 #!/bin/bash
849 input=$(cat)
850 
851 MODEL=$(echo "$input" | jq -r '.model.display_name')
852 # "// empty" produces no output when rate_limits is absent
853 FIVE_H=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
854 WEEK=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
855 
856 LIMITS=""
857 [ -n "$FIVE_H" ] && LIMITS="5h: $(printf '%.0f' "$FIVE_H")%"
858 [ -n "$WEEK" ] && LIMITS="${LIMITS:+$LIMITS }7d: $(printf '%.0f' "$WEEK")%"
859 
860 # Behind a Claude apps gateway: dollars when the gateway reports them, else the percentage
861 SPEND_PCT=$(echo "$input" | jq -r '.rate_limits.spend_limit.used_percentage // empty')
862 SPEND_USD=$(echo "$input" | jq -r '.rate_limits.spend_limit | select(.used_usd != null) | "$\(.used_usd) / $\(.limit_usd)"')
863 [ -n "$SPEND_PCT" ] && LIMITS="${LIMITS:+$LIMITS }spend: ${SPEND_USD:-$(printf '%.0f' "$SPEND_PCT")%}"
864 
865 [ -n "$LIMITS" ] && echo "[$MODEL] | $LIMITS" || echo "[$MODEL]"
866 ```
867 
868 ```python Python theme={null}
869 #!/usr/bin/env python3
870 import json, sys
871 
872 data = json.load(sys.stdin)
873 model = data['model']['display_name']
874 
875 parts = []
876 rate = data.get('rate_limits', {})
877 five_h = rate.get('five_hour', {}).get('used_percentage')
878 week = rate.get('seven_day', {}).get('used_percentage')
879 
880 if five_h is not None:
881 parts.append(f"5h: {five_h:.0f}%")
882 if week is not None:
883 parts.append(f"7d: {week:.0f}%")
884 
885 # Behind a Claude apps gateway: dollars when the gateway reports them, else the percentage
886 spend = rate.get('spend_limit', {})
887 if spend.get('used_percentage') is not None:
888 if spend.get('used_usd') is not None:
889 parts.append(f"spend: ${spend['used_usd']} / ${spend['limit_usd']}")
890 else:
891 parts.append(f"spend: {spend['used_percentage']:.0f}%")
892 
893 if parts:
894 print(f"[{model}] | {' '.join(parts)}")
895 else:
896 print(f"[{model}]")
897 ```
898 
899 ```javascript Node.js theme={null}
900 #!/usr/bin/env node
901 let input = '';
902 process.stdin.on('data', chunk => input += chunk);
903 process.stdin.on('end', () => {
904 const data = JSON.parse(input);
905 const model = data.model.display_name;
906 
907 const parts = [];
908 const fiveH = data.rate_limits?.five_hour?.used_percentage;
909 const week = data.rate_limits?.seven_day?.used_percentage;
910 
911 if (fiveH != null) parts.push(`5h: ${Math.round(fiveH)}%`);
912 if (week != null) parts.push(`7d: ${Math.round(week)}%`);
913 
914 // Behind a Claude apps gateway: dollars when the gateway reports them, else the percentage
915 const spend = data.rate_limits?.spend_limit;
916 if (spend?.used_percentage != null) {
917 parts.push(spend.used_usd != null
918 ? `spend: $${spend.used_usd} / $${spend.limit_usd}`
919 : `spend: ${Math.round(spend.used_percentage)}%`);
920 }
921 
922 console.log(parts.length ? `[${model}] | ${parts.join(' ')}` : `[${model}]`);
923 });
924 ```
925</CodeGroup>
822```bash Bash theme={null}
823#!/bin/bash
824input=$(cat)
825 
826MODEL=$(echo "$input" | jq -r '.model.display_name')
827# "// empty" produces no output when rate_limits is absent
828FIVE_H=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
829WEEK=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
830 
831LIMITS=""
832[ -n "$FIVE_H" ] && LIMITS="5h: $(printf '%.0f' "$FIVE_H")%"
833[ -n "$WEEK" ] && LIMITS="${LIMITS:+$LIMITS }7d: $(printf '%.0f' "$WEEK")%"
834 
835# Behind a Claude apps gateway: dollars when the gateway reports them, else the percentage
836SPEND_PCT=$(echo "$input" | jq -r '.rate_limits.spend_limit.used_percentage // empty')
837SPEND_USD=$(echo "$input" | jq -r '.rate_limits.spend_limit | select(.used_usd != null) | "$\(.used_usd) / $\(.limit_usd)"')
838[ -n "$SPEND_PCT" ] && LIMITS="${LIMITS:+$LIMITS }spend: ${SPEND_USD:-$(printf '%.0f' "$SPEND_PCT")%}"
839 
840[ -n "$LIMITS" ] && echo "[$MODEL] | $LIMITS" || echo "[$MODEL]"
841```
842 
843```python Python theme={null}
844#!/usr/bin/env python3
845import json, sys
846 
847data = json.load(sys.stdin)
848model = data['model']['display_name']
849 
850parts = []
851rate = data.get('rate_limits', {})
852five_h = rate.get('five_hour', {}).get('used_percentage')
853week = rate.get('seven_day', {}).get('used_percentage')
854 
855if five_h is not None:
856 parts.append(f"5h: {five_h:.0f}%")
857if week is not None:
858 parts.append(f"7d: {week:.0f}%")
859 
860# Behind a Claude apps gateway: dollars when the gateway reports them, else the percentage
861spend = rate.get('spend_limit', {})
862if spend.get('used_percentage') is not None:
863 if spend.get('used_usd') is not None:
864 parts.append(f"spend: ${spend['used_usd']} / ${spend['limit_usd']}")
865 else:
866 parts.append(f"spend: {spend['used_percentage']:.0f}%")
867 
868if parts:
869 print(f"[{model}] | {' '.join(parts)}")
870else:
871 print(f"[{model}]")
872```
873 
874```javascript Node.js theme={null}
875#!/usr/bin/env node
876let input = '';
877process.stdin.on('data', chunk => input += chunk);
878process.stdin.on('end', () => {
879 const data = JSON.parse(input);
880 const model = data.model.display_name;
881 
882 const parts = [];
883 const fiveH = data.rate_limits?.five_hour?.used_percentage;
884 const week = data.rate_limits?.seven_day?.used_percentage;
885 
886 if (fiveH != null) parts.push(`5h: ${Math.round(fiveH)}%`);
887 if (week != null) parts.push(`7d: ${Math.round(week)}%`);
888 
889 // Behind a Claude apps gateway: dollars when the gateway reports them, else the percentage
890 const spend = data.rate_limits?.spend_limit;
891 if (spend?.used_percentage != null) {
892 parts.push(spend.used_usd != null
893 ? `spend: $${spend.used_usd} / $${spend.limit_usd}`
894 : `spend: ${Math.round(spend.used_percentage)}%`);
895 }
896 
897 console.log(parts.length ? `[${model}] | ${parts.join(' ')}` : `[${model}]`);
898});
899```
926900 
927901### Cache expensive operations
928902 
from line 906
932906 
933907Each script checks if the cache file is missing or older than 5 seconds before running git commands:
934908 
935<CodeGroup>
936 ```bash Bash theme={null}
937 #!/bin/bash
938 input=$(cat)
939 
940 MODEL=$(echo "$input" | jq -r '.model.display_name')
941 DIR=$(echo "$input" | jq -r '.workspace.current_dir')
942 SESSION_ID=$(echo "$input" | jq -r '.session_id')
943 
944 CACHE_FILE="/tmp/statusline-git-cache-$SESSION_ID"
945 CACHE_MAX_AGE=5 # seconds
946 
947 cache_is_stale() {
948 [ ! -f "$CACHE_FILE" ] || \
949 # stat -c %Y (Linux) or stat -f %m (macOS) prints the file's last-modified
950 # time. The Linux form must run first: on Linux, the macOS form prints a
951 # filesystem report to stdout before failing, and that output would be
952 # captured by the command substitution and break the arithmetic.
953 [ $(($(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || stat -f %m "$CACHE_FILE" 2>/dev/null || echo 0))) -gt $CACHE_MAX_AGE ]
909```bash Bash theme={null}
910#!/bin/bash
911input=$(cat)
912 
913MODEL=$(echo "$input" | jq -r '.model.display_name')
914DIR=$(echo "$input" | jq -r '.workspace.current_dir')
915SESSION_ID=$(echo "$input" | jq -r '.session_id')
916 
917CACHE_FILE="/tmp/statusline-git-cache-$SESSION_ID"
918CACHE_MAX_AGE=5 # seconds
919 
920cache_is_stale() {
921 [ ! -f "$CACHE_FILE" ] || \
922 # stat -c %Y (Linux) or stat -f %m (macOS) prints the file's last-modified
923 # time. The Linux form must run first: on Linux, the macOS form prints a
924 # filesystem report to stdout before failing, and that output would be
925 # captured by the command substitution and break the arithmetic.
926 [ $(($(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || stat -f %m "$CACHE_FILE" 2>/dev/null || echo 0))) -gt $CACHE_MAX_AGE ]
927}
928 
929if cache_is_stale; then
930 if git rev-parse --git-dir > /dev/null 2>&1; then
931 BRANCH=$(git branch --show-current 2>/dev/null)
932 STAGED=$(git diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
933 MODIFIED=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ')
934 echo "$BRANCH|$STAGED|$MODIFIED" > "$CACHE_FILE"
935 else
936 echo "||" > "$CACHE_FILE"
937 fi
938fi
939 
940IFS='|' read -r BRANCH STAGED MODIFIED < "$CACHE_FILE"
941 
942if [ -n "$BRANCH" ]; then
943 echo "[$MODEL] 📁 ${DIR##*/} | 🌿 $BRANCH +$STAGED ~$MODIFIED"
944else
945 echo "[$MODEL] 📁 ${DIR##*/}"
946fi
947```
948 
949```python Python theme={null}
950#!/usr/bin/env python3
951import json, sys, subprocess, os, time
952 
953data = json.load(sys.stdin)
954model = data['model']['display_name']
955directory = os.path.basename(data['workspace']['current_dir'])
956session_id = data['session_id']
957 
958CACHE_FILE = f"/tmp/statusline-git-cache-{session_id}"
959CACHE_MAX_AGE = 5 # seconds
960 
961def cache_is_stale():
962 if not os.path.exists(CACHE_FILE):
963 return True
964 return time.time() - os.path.getmtime(CACHE_FILE) > CACHE_MAX_AGE
965 
966if cache_is_stale():
967 try:
968 subprocess.check_output(['git', 'rev-parse', '--git-dir'], stderr=subprocess.DEVNULL)
969 branch = subprocess.check_output(['git', 'branch', '--show-current'], text=True).strip()
970 staged = subprocess.check_output(['git', 'diff', '--cached', '--numstat'], text=True).strip()
971 modified = subprocess.check_output(['git', 'diff', '--numstat'], text=True).strip()
972 staged_count = len(staged.split('\n')) if staged else 0
973 modified_count = len(modified.split('\n')) if modified else 0
974 with open(CACHE_FILE, 'w') as f:
975 f.write(f"{branch}|{staged_count}|{modified_count}")
976 except:
977 with open(CACHE_FILE, 'w') as f:
978 f.write("||")
979 
980with open(CACHE_FILE) as f:
981 branch, staged, modified = f.read().strip().split('|')
982 
983if branch:
984 print(f"[{model}] 📁 {directory} | 🌿 {branch} +{staged} ~{modified}")
985else:
986 print(f"[{model}] 📁 {directory}")
987```
988 
989```javascript Node.js theme={null}
990#!/usr/bin/env node
991const { execSync } = require('child_process');
992const fs = require('fs');
993const path = require('path');
994 
995let input = '';
996process.stdin.on('data', chunk => input += chunk);
997process.stdin.on('end', () => {
998 const data = JSON.parse(input);
999 const model = data.model.display_name;
1000 const dir = path.basename(data.workspace.current_dir);
1001 const sessionId = data.session_id;
1002 
1003 const CACHE_FILE = `/tmp/statusline-git-cache-${sessionId}`;
1004 const CACHE_MAX_AGE = 5; // seconds
1005 
1006 const cacheIsStale = () => {
1007 if (!fs.existsSync(CACHE_FILE)) return true;
1008 return (Date.now() / 1000) - fs.statSync(CACHE_FILE).mtimeMs / 1000 > CACHE_MAX_AGE;
1009 };
1010 
1011 if (cacheIsStale()) {
1012 try {
1013 execSync('git rev-parse --git-dir', { stdio: 'ignore' });
1014 const branch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
1015 const staged = execSync('git diff --cached --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
1016 const modified = execSync('git diff --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
1017 fs.writeFileSync(CACHE_FILE, `${branch}|${staged}|${modified}`);
1018 } catch {
1019 fs.writeFileSync(CACHE_FILE, '||');
1020 }
1021 }
1022 
1023 const [branch, staged, modified] = fs.readFileSync(CACHE_FILE, 'utf8').trim().split('|');
1024 
1025 if (branch) {
1026 console.log(`[${model}] 📁 ${dir} | 🌿 ${branch} +${staged} ~${modified}`);
1027 } else {
1028 console.log(`[${model}] 📁 ${dir}`);
1029 }
1030});
1031```
1032 
1033### Windows configuration
1034 
1035On Windows, Claude Code runs status line commands through Git Bash when Git Bash is installed, or through PowerShell when Git Bash is absent.
1036 
1037Git Bash treats unquoted backslashes as escape characters, so a Windows-style path such as `C:\Users\username\script.mjs` reaches the script runner with its separators removed and the command fails without a visible error. Write file paths in the `command` string with forward slashes, as shown in the examples below. The `~` shorthand also works and expands to your Windows home directory.
1038 
1039To run a PowerShell script as your status line, invoke it via `powershell`. This works whether Claude Code routes the command through Git Bash or PowerShell:
1040 
1041```json settings.json theme={null}
1042{
1043 "statusLine": {
1044 "type": "command",
1045 "command": "powershell -NoProfile -File C:/Users/username/.claude/statusline.ps1"
9541046 }
955 
956 if cache_is_stale; then
957 if git rev-parse --git-dir > /dev/null 2>&1; then
958 BRANCH=$(git branch --show-current 2>/dev/null)
959 STAGED=$(git diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
960 MODIFIED=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ')
961 echo "$BRANCH|$STAGED|$MODIFIED" > "$CACHE_FILE"
962 else
963 echo "||" > "$CACHE_FILE"
964 fi
965 fi
966 
967 IFS='|' read -r BRANCH STAGED MODIFIED < "$CACHE_FILE"
968 
969 if [ -n "$BRANCH" ]; then
970 echo "[$MODEL] 📁 ${DIR##*/} | 🌿 $BRANCH +$STAGED ~$MODIFIED"
971 else
972 echo "[$MODEL] 📁 ${DIR##*/}"
973 fi
974 ```
975 
976 ```python Python theme={null}
977 #!/usr/bin/env python3
978 import json, sys, subprocess, os, time
979 
980 data = json.load(sys.stdin)
981 model = data['model']['display_name']
982 directory = os.path.basename(data['workspace']['current_dir'])
983 session_id = data['session_id']
984 
985 CACHE_FILE = f"/tmp/statusline-git-cache-{session_id}"
986 CACHE_MAX_AGE = 5 # seconds
987 
988 def cache_is_stale():
989 if not os.path.exists(CACHE_FILE):
990 return True
991 return time.time() - os.path.getmtime(CACHE_FILE) > CACHE_MAX_AGE
992 
993 if cache_is_stale():
994 try:
995 subprocess.check_output(['git', 'rev-parse', '--git-dir'], stderr=subprocess.DEVNULL)
996 branch = subprocess.check_output(['git', 'branch', '--show-current'], text=True).strip()
997 staged = subprocess.check_output(['git', 'diff', '--cached', '--numstat'], text=True).strip()
998 modified = subprocess.check_output(['git', 'diff', '--numstat'], text=True).strip()
999 staged_count = len(staged.split('\n')) if staged else 0
1000 modified_count = len(modified.split('\n')) if modified else 0
1001 with open(CACHE_FILE, 'w') as f:
1002 f.write(f"{branch}|{staged_count}|{modified_count}")
1003 except:
1004 with open(CACHE_FILE, 'w') as f:
1005 f.write("||")
1006 
1007 with open(CACHE_FILE) as f:
1008 branch, staged, modified = f.read().strip().split('|')
1009 
1010 if branch:
1011 print(f"[{model}] 📁 {directory} | 🌿 {branch} +{staged} ~{modified}")
1012 else:
1013 print(f"[{model}] 📁 {directory}")
1014 ```
1015 
1016 ```javascript Node.js theme={null}
1017 #!/usr/bin/env node
1018 const { execSync } = require('child_process');
1019 const fs = require('fs');
1020 const path = require('path');
1021 
1022 let input = '';
1023 process.stdin.on('data', chunk => input += chunk);
1024 process.stdin.on('end', () => {
1025 const data = JSON.parse(input);
1026 const model = data.model.display_name;
1027 const dir = path.basename(data.workspace.current_dir);
1028 const sessionId = data.session_id;
1029 
1030 const CACHE_FILE = `/tmp/statusline-git-cache-${sessionId}`;
1031 const CACHE_MAX_AGE = 5; // seconds
1032 
1033 const cacheIsStale = () => {
1034 if (!fs.existsSync(CACHE_FILE)) return true;
1035 return (Date.now() / 1000) - fs.statSync(CACHE_FILE).mtimeMs / 1000 > CACHE_MAX_AGE;
1036 };
1037 
1038 if (cacheIsStale()) {
1039 try {
1040 execSync('git rev-parse --git-dir', { stdio: 'ignore' });
1041 const branch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
1042 const staged = execSync('git diff --cached --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
1043 const modified = execSync('git diff --numstat', { encoding: 'utf8' }).trim().split('\n').filter(Boolean).length;
1044 fs.writeFileSync(CACHE_FILE, `${branch}|${staged}|${modified}`);
1045 } catch {
1046 fs.writeFileSync(CACHE_FILE, '||');
1047 }
1048 }
1049 
1050 const [branch, staged, modified] = fs.readFileSync(CACHE_FILE, 'utf8').trim().split('|');
1051 
1052 if (branch) {
1053 console.log(`[${model}] 📁 ${dir} | 🌿 ${branch} +${staged} ~${modified}`);
1054 } else {
1055 console.log(`[${model}] 📁 ${dir}`);
1056 }
1057 });
1058 ```
1059</CodeGroup>
1060 
1061### Windows configuration
1062 
1063On Windows, Claude Code runs status line commands through Git Bash when Git Bash is installed, or through PowerShell when Git Bash is absent.
1064 
1065Git Bash treats unquoted backslashes as escape characters, so a Windows-style path such as `C:\Users\username\script.mjs` reaches the script runner with its separators removed and the command fails without a visible error. Write file paths in the `command` string with forward slashes, as shown in the examples below. The `~` shorthand also works and expands to your Windows home directory.
1066 
1067To run a PowerShell script as your status line, invoke it via `powershell`. This works whether Claude Code routes the command through Git Bash or PowerShell:
1068 
1069<CodeGroup>
1070 ```json settings.json theme={null}
1071 {
1072 "statusLine": {
1073 "type": "command",
1074 "command": "powershell -NoProfile -File C:/Users/username/.claude/statusline.ps1"
1075 }
1047}
1048```
1049 
1050```powershell statusline.ps1 theme={null}
1051$input_json = $input | Out-String | ConvertFrom-Json
1052$cwd = $input_json.cwd
1053$model = $input_json.model.display_name
1054$used = $input_json.context_window.used_percentage
1055$dirname = Split-Path $cwd -Leaf
1056 
1057if ($used) {
1058 Write-Host "$dirname [$model] ctx: $used%"
1059} else {
1060 Write-Host "$dirname [$model]"
1061}
1062```
1063 
1064Or, when Git Bash is installed, run a Bash script directly:
1065 
1066```json settings.json theme={null}
1067{
1068 "statusLine": {
1069 "type": "command",
1070 "command": "~/.claude/statusline.sh"
10761071 }
1077 ```
1078 
1079 ```powershell statusline.ps1 theme={null}
1080 $input_json = $input | Out-String | ConvertFrom-Json
1081 $cwd = $input_json.cwd
1082 $model = $input_json.model.display_name
1083 $used = $input_json.context_window.used_percentage
1084 $dirname = Split-Path $cwd -Leaf
1085 
1086 if ($used) {
1087 Write-Host "$dirname [$model] ctx: $used%"
1088 } else {
1089 Write-Host "$dirname [$model]"
1090 }
1091 ```
1092</CodeGroup>
1093 
1094Or, when Git Bash is installed, run a Bash script directly:
1095 
1096<CodeGroup>
1097 ```json settings.json theme={null}
1098 {
1099 "statusLine": {
1100 "type": "command",
1101 "command": "~/.claude/statusline.sh"
1102 }
1103 }
1104 ```
1105 
1106 ```bash statusline.sh theme={null}
1107 #!/usr/bin/env bash
1108 input=$(cat)
1109 cwd=$(echo "$input" | grep -o '"cwd":"[^"]*"' | cut -d'"' -f4)
1110 model=$(echo "$input" | grep -o '"display_name":"[^"]*"' | cut -d'"' -f4)
1111 dirname="${cwd##*[/\\]}"
1112 echo "$dirname [$model]"
1113 ```
1114</CodeGroup>
1072}
1073```
1074 
1075```bash statusline.sh theme={null}
1076#!/usr/bin/env bash
1077input=$(cat)
1078cwd=$(echo "$input" | grep -o '"cwd":"[^"]*"' | cut -d'"' -f4)
1079model=$(echo "$input" | grep -o '"display_name":"[^"]*"' | cut -d'"' -f4)
1080dirname="${cwd##*[/\\]}"
1081echo "$dirname [$model]"
1082```
11151083 
11161084## Subagent status lines
11171085 
11181086 

sub-agents Changed · +183 / -195 lines

### Frontmatter reference #### Trust required for inline MCP servers ### Turn fork mode off

from line 6
66 
77Each subagent runs in its own context window with a custom system prompt, specific tool access, and independent permissions. It also sends its own requests, which count toward the same [usage limits](/docs/en/costs#plan-usage-breakdown) as your main conversation. When Claude encounters a task that matches a subagent's description, it delegates to that subagent, which works independently and returns results. To see the context savings in practice, the [context window visualization](/docs/en/context-window) walks through a session where a subagent handles research in its own separate window.
88 
9<Note>
10 Subagents work within a single session. To run many independent sessions in parallel and monitor them from one place, see [background agents](/docs/en/agent-view). For separate sessions that pass messages to each other, see [cross-session messaging](/docs/en/cross-session-messaging). For a coordinated team of sessions Claude spawns and supervises, see [agent teams](/docs/en/agent-teams).
11</Note>
9> [!NOTE]
10>
11> Subagents work within a single session. To run many independent sessions in parallel and monitor them from one place, see [background agents](/docs/en/agent-view). For separate sessions that pass messages to each other, see [cross-session messaging](/docs/en/cross-session-messaging). For a coordinated team of sessions Claude spawns and supervises, see [agent teams](/docs/en/agent-teams).
1212 
1313Subagents help you:
1414 
from line 28
2828 
2929Explore and Plan start without your CLAUDE.md files and the git status snapshot to keep research fast and inexpensive. Other built-in and [custom subagents](#configure-subagents) load both, unless you set [`omitClaudeMd`](#supported-frontmatter-fields) in a custom subagent's definition to start it without the user, project, and local CLAUDE.md files. For the full breakdown of what reaches a subagent, see [what loads at startup](#what-loads-at-startup).
3030 
31<Tabs>
32 <Tab title="Explore">
33 A fast, read-only agent optimized for searching and analyzing codebases.
31**Explore**
3432 
35 * **Model**: the main conversation's model. When the main conversation runs Fable, Explore's model depends on how you connect:
36 * With a Claude subscription, an Anthropic Console account, or an [LLM gateway](/docs/en/llm-gateway) reached through `ANTHROPIC_BASE_URL`, Explore runs on the Opus model that the [`opus` alias](/docs/en/model-config#model-aliases) resolves to.
37 * On Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, [Claude Platform on AWS](/docs/en/claude-platform-on-aws), or a [Claude apps gateway](/docs/en/claude-apps-gateway), Explore stays on the main conversation's model.
38 * **Tools**: read-only tools; Write and Edit are denied
39 * **Purpose**: file discovery, code search, codebase exploration
33A fast, read-only agent optimized for searching and analyzing codebases.
4034 
41 A [user or project subagent](#choose-the-subagent-scope) named `Explore` overrides the built-in and keeps its own `model` field, so define one with `model: haiku` to run exploration on a lower-cost model. To force one model onto every subagent, Explore included, see [Run every subagent on one model](#run-every-subagent-on-one-model).
35* **Model**: the main conversation's model. When the main conversation runs Fable, Explore's model depends on how you connect:
36 * With a Claude subscription, an Anthropic Console account, or an [LLM gateway](/docs/en/llm-gateway) reached through `ANTHROPIC_BASE_URL`, Explore runs on the Opus model that the [`opus` alias](/docs/en/model-config#model-aliases) resolves to.
37 * On Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, [Claude Platform on AWS](/docs/en/claude-platform-on-aws), or a [Claude apps gateway](/docs/en/claude-apps-gateway), Explore stays on the main conversation's model.
38* **Tools**: read-only tools; Write and Edit are denied
39* **Purpose**: file discovery, code search, codebase exploration
4240 
43 Claude delegates to Explore when it needs to search or understand a codebase without making changes. This keeps exploration results out of your main conversation context.
41A [user or project subagent](#choose-the-subagent-scope) named `Explore` overrides the built-in and keeps its own `model` field, so define one with `model: haiku` to run exploration on a lower-cost model. To force one model onto every subagent, Explore included, see [Run every subagent on one model](#run-every-subagent-on-one-model).
4442 
45 When invoking Explore, Claude specifies a thoroughness level: **quick** for targeted lookups, **medium** for balanced exploration, or **very thorough** for comprehensive analysis.
46 </Tab>
43Claude delegates to Explore when it needs to search or understand a codebase without making changes. This keeps exploration results out of your main conversation context.
4744 
48 <Tab title="Plan">
49 A research agent used during [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode) to gather context before presenting a plan.
45When invoking Explore, Claude specifies a thoroughness level: **quick** for targeted lookups, **medium** for balanced exploration, or **very thorough** for comprehensive analysis.
5046 
51 * **Model**: inherits from the main conversation, unless you set `CLAUDE_CODE_SUBAGENT_MODEL` and [force it onto every subagent](#run-every-subagent-on-one-model)
52 * **Tools**: read-only tools; Write and Edit are denied
53 * **Purpose**: codebase research for planning
47**Plan**
5448 
55 When you're in plan mode and Claude needs to understand your codebase, it delegates research to the Plan subagent so that exploration output stays in a separate context window while the main conversation remains read-only.
56 </Tab>
49A research agent used during [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode) to gather context before presenting a plan.
5750 
58 <Tab title="General-purpose">
59 A capable agent for complex, multi-step tasks that require both exploration and action.
51* **Model**: inherits from the main conversation, unless you set `CLAUDE_CODE_SUBAGENT_MODEL` and [force it onto every subagent](#run-every-subagent-on-one-model)
52* **Tools**: read-only tools; Write and Edit are denied
53* **Purpose**: codebase research for planning
6054 
61 * **Model**: the [`CLAUDE_CODE_SUBAGENT_MODEL`](#choose-a-model) model if you set one and nothing assigns a model another way, otherwise the main conversation's model; [Choose a model](#choose-a-model) states the full order, and [Run every subagent on one model](#run-every-subagent-on-one-model) shows how to make the variable override those sources
62 * **Tools**: every tool [available to subagents](#available-tools)
63 * **Purpose**: complex research, multi-step operations, code modifications
55When you're in plan mode and Claude needs to understand your codebase, it delegates research to the Plan subagent so that exploration output stays in a separate context window while the main conversation remains read-only.
6456 
65 Claude delegates to general-purpose when the task requires both exploration and modification, complex reasoning to interpret results, or multiple dependent steps.
66 </Tab>
57**General-purpose**
6758 
68 <Tab title="Other">
69 Claude Code includes additional helper agents for specific tasks. These are typically invoked automatically, so you don't need to use them directly.
59A capable agent for complex, multi-step tasks that require both exploration and action.
7060 
71 | Agent | Model | When Claude uses it |
72 | :- | :- | :- |
73 | claude | None of its own; follows the [model order](#choose-a-model) when Claude spawns it as a subagent | When a task doesn't fit a more specialized agent. A catch-all with every tool [available to subagents](#available-tools). Also the default agent for a dispatched [background session](/docs/en/agent-view); [which permission mode it starts in](/docs/en/agent-view#permission-mode-model-and-effort) depends on how the session was started |
74 | statusline-setup | Sonnet | When you run `/statusline` to configure your status line |
75 | claude-code-guide | Haiku | When you ask questions about Claude Code features |
76 </Tab>
77</Tabs>
61* **Model**: the [`CLAUDE_CODE_SUBAGENT_MODEL`](#choose-a-model) model if you set one and nothing assigns a model another way, otherwise the main conversation's model; [Choose a model](#choose-a-model) states the full order, and [Run every subagent on one model](#run-every-subagent-on-one-model) shows how to make the variable override those sources
62* **Tools**: every tool [available to subagents](#available-tools)
63* **Purpose**: complex research, multi-step operations, code modifications
7864 
65Claude delegates to general-purpose when the task requires both exploration and modification, complex reasoning to interpret results, or multiple dependent steps.
66 
67**Other**
68 
69Claude Code includes additional helper agents for specific tasks. These are typically invoked automatically, so you don't need to use them directly.
70 
71| Agent | Model | When Claude uses it |
72| :- | :- | :- |
73| claude | None of its own; follows the [model order](#choose-a-model) when Claude spawns it as a subagent | When a task doesn't fit a more specialized agent. A catch-all with every tool [available to subagents](#available-tools). Also the default agent for a dispatched [background session](/docs/en/agent-view); [which permission mode it starts in](/docs/en/agent-view#permission-mode-model-and-effort) depends on how the session was started |
74| statusline-setup | Sonnet | When you run `/statusline` to configure your status line |
75| claude-code-guide | Haiku | When you ask questions about Claude Code features |
76 
7977Built-in subagents are registered by default in interactive sessions. To restrict them:
8078 
8179* To block a specific built-in type, add it to `permissions.deny` as shown in [Disable specific subagents](#disable-specific-subagents).
from line 91
9391 
9492This walkthrough creates a user-level subagent that reviews code and suggests improvements.
9593 
96<Steps>
97 <Step title="Ask Claude to create the subagent">
98 In Claude Code, describe the subagent you want and where to save it:
94**1. Ask Claude to create the subagent**
9995 
100 ```text wrap theme={null}
101 Create a personal code-improver subagent in ~/.claude/agents/ that scans
102 files and suggests improvements for readability, performance, and best
103 practices. It should explain each issue, show the current code, and
104 provide an improved version. Make it read-only and have it use Sonnet.
105 ```
96In Claude Code, describe the subagent you want and where to save it:
10697 
107 Claude writes the file with a `name`, a `description`, a `tools` list, a `model`, and a system prompt.
108 </Step>
98```text wrap theme={null}
99Create a personal code-improver subagent in ~/.claude/agents/ that scans
100files and suggests improvements for readability, performance, and best
101practices. It should explain each issue, show the current code, and
102provide an improved version. Make it read-only and have it use Sonnet.
103```
109104 
110 <Step title="Review the file">
111 Open `~/.claude/agents/code-improver.md` and confirm the frontmatter matches what you asked for. The result looks like this:
105Claude writes the file with a `name`, a `description`, a `tools` list, a `model`, and a system prompt.
112106 
113 ```markdown theme={null}
114 ---
115 name: code-improver
116 description: Scans files and suggests improvements for readability, performance, and best practices. Use after writing or modifying code.
117 tools: Read, Grep, Glob
118 model: sonnet
119 ---
107**2. Review the file**
120108 
121 You are a code improvement specialist. For each issue you find, explain
122 the problem, show the current code, and provide an improved version.
123 ```
109Open `~/.claude/agents/code-improver.md` and confirm the frontmatter matches what you asked for. The result looks like this:
124110 
125 Because the file lives in `~/.claude/agents/`, the subagent is available in every project on your machine. To scope it to one project instead, move it to that project's `.claude/agents/` directory. [Choose the subagent scope](#choose-the-subagent-scope) compares the two.
126 </Step>
111```markdown theme={null}
112---
113name: code-improver
114description: Scans files and suggests improvements for readability, performance, and best practices. Use after writing or modifying code.
115tools: Read, Grep, Glob
116model: sonnet
117---
127118 
128 <Step title="Try it out">
129 Ask Claude to delegate to the new subagent:
119You are a code improvement specialist. For each issue you find, explain
120the problem, show the current code, and provide an improved version.
121```
130122 
131 ```text wrap theme={null}
132 Use the code-improver agent to suggest improvements in this project
133 ```
123Because the file lives in `~/.claude/agents/`, the subagent is available in every project on your machine. To scope it to one project instead, move it to that project's `.claude/agents/` directory. [Choose the subagent scope](#choose-the-subagent-scope) compares the two.
134124 
135 Claude delegates to your new subagent, which scans the codebase and returns improvement suggestions. In the transcript, the delegation appears as a tool call row showing the subagent's name followed by a short task description, such as `code-improver(Suggest code improvements)`.
125**3. Try it out**
136126 
137 If Claude can't find the new subagent, restart Claude Code and try again. This happens only when `~/.claude/agents/` didn't exist before the session started, because a running session doesn't detect a newly created `agents` directory.
138 </Step>
139</Steps>
127Ask Claude to delegate to the new subagent:
140128 
129```text wrap theme={null}
130Use the code-improver agent to suggest improvements in this project
131```
132 
133Claude delegates to your new subagent, which scans the codebase and returns improvement suggestions. In the transcript, the delegation appears as a tool call row showing the subagent's name followed by a short task description, such as `code-improver(Suggest code improvements)`.
134 
135If Claude can't find the new subagent, restart Claude Code and try again. This happens only when `~/.claude/agents/` didn't exist before the session started, because a running session doesn't detect a newly created `agents` directory.
136 
141137You now have a subagent you can use in any project on your machine to analyze codebases and suggest improvements.
142138 
143139You can also write subagent files by hand, define them via CLI flags, or distribute them through plugins. The following sections cover all configuration options.
144140 
145<Note>
146 Running `/agents` prints a reminder to ask Claude or edit `.claude/agents/` and `~/.claude/agents/` directly.
147 On Claude Code v2.1.197 and earlier, `/agents` opens an interactive wizard with a **Running** tab that lists live subagents and a **Library** tab for creating, editing, and deleting them.&#x20;
148</Note>
141> [!NOTE]
142>
143> Running `/agents` prints a reminder to ask Claude or edit `.claude/agents/` and `~/.claude/agents/` directly.
144> On Claude Code v2.1.197 and earlier, `/agents` opens an interactive wizard with a **Running** tab that lists live subagents and a **Library** tab for creating, editing, and deleting them.&#x20;
149145 
150146## Configure subagents
151147 
from line 175
179175 
180176**CLI-defined subagents** are passed as JSON when launching Claude Code. They exist only for that session and aren't saved to disk, making them useful for quick testing or automation scripts. You can define multiple subagents in a single `--agents` call:
181177 
182<Tabs>
183 <Tab title="macOS, Linux, WSL">
184 ```bash theme={null}
185 claude --agents '{
186 "code-reviewer": {
187 "description": "Expert code reviewer. Use proactively after code changes.",
188 "prompt": "You are a senior code reviewer. Focus on code quality, security, and best practices.",
189 "tools": ["Read", "Grep", "Glob", "Bash"],
190 "model": "sonnet"
191 },
192 "debugger": {
193 "description": "Debugging specialist for errors and test failures.",
194 "prompt": "You are an expert debugger. Analyze errors, identify root causes, and provide fixes."
195 }
196 }'
197 ```
198 </Tab>
178**macOS, Linux, WSL**
199179 
200 <Tab title="Windows PowerShell">
201 ```powershell theme={null}
202 claude --agents @'
203 {
204 "code-reviewer": {
205 "description": "Expert code reviewer. Use proactively after code changes.",
206 "prompt": "You are a senior code reviewer. Focus on code quality, security, and best practices.",
207 "tools": ["Read", "Grep", "Glob", "Bash"],
208 "model": "sonnet"
209 },
210 "debugger": {
211 "description": "Debugging specialist for errors and test failures.",
212 "prompt": "You are an expert debugger. Analyze errors, identify root causes, and provide fixes."
213 }
214 }
215 '@
216 ```
217 </Tab>
218</Tabs>
180```bash theme={null}
181claude --agents '{
182 "code-reviewer": {
183 "description": "Expert code reviewer. Use proactively after code changes.",
184 "prompt": "You are a senior code reviewer. Focus on code quality, security, and best practices.",
185 "tools": ["Read", "Grep", "Glob", "Bash"],
186 "model": "sonnet"
187 },
188 "debugger": {
189 "description": "Debugging specialist for errors and test failures.",
190 "prompt": "You are an expert debugger. Analyze errors, identify root causes, and provide fixes."
191 }
192}'
193```
219194 
195**Windows PowerShell**
196 
197```powershell theme={null}
198claude --agents @'
199{
200 "code-reviewer": {
201 "description": "Expert code reviewer. Use proactively after code changes.",
202 "prompt": "You are a senior code reviewer. Focus on code quality, security, and best practices.",
203 "tools": ["Read", "Grep", "Glob", "Bash"],
204 "model": "sonnet"
205 },
206 "debugger": {
207 "description": "Debugging specialist for errors and test failures.",
208 "prompt": "You are an expert debugger. Analyze errors, identify root causes, and provide fixes."
209 }
210}
211'@
212```
213 
220214In [non-interactive mode](/docs/en/headless), `--agents` also accepts the path to a JSON file holding the same object, for definitions too large to pass on the command line. For example, `claude -p --agents ./agents.json "Review my changes"` reads the definitions from that file. In an interactive session, Claude Code refuses a file path. The file form requires Claude Code v2.1.281 or later.
221215 
222216Each top-level key in the JSON is an agent's name, and its value is that agent's definition. Don't start a name with `-`. A definition takes these fields:
from line 225
231225 
232226**Plugin subagents** come from [plugins](/docs/en/plugins/overview) you've installed. They load automatically alongside your custom subagents and appear in the @-mention typeahead under their scoped name. See the [plugin components reference](/docs/en/plugins/components#agents) for details on creating plugin subagents.
233227 
234<Note>
235 For security reasons, plugin subagents don't support the `hooks`, `mcpServers`, or `permissionMode` frontmatter fields. These fields are ignored when loading agents from a plugin. If you need them, copy the agent file into `.claude/agents/` or `~/.claude/agents/`. You can also add rules to [`permissions.allow`](/docs/en/settings-reference#permissions-allow) in `settings.json` or `settings.local.json`, but these rules apply to the entire session, not only the plugin subagent.
228> [!NOTE]
229>
230> For security reasons, plugin subagents don't support the `hooks`, `mcpServers`, or `permissionMode` frontmatter fields. These fields are ignored when loading agents from a plugin. If you need them, copy the agent file into `.claude/agents/` or `~/.claude/agents/`. You can also add rules to [`permissions.allow`](/docs/en/settings-reference#permissions-allow) in `settings.json` or `settings.local.json`, but these rules apply to the entire session, not only the plugin subagent.
231>
232> If you're the plugin's author, ship the hooks in the plugin's [`hooks/hooks.json`](/docs/en/plugins/components#hooks) and the MCP servers in its [`.mcp.json`](/docs/en/plugins/components#mcp-servers) instead. They apply whenever the plugin is enabled rather than only inside the subagent.
236233 
237 If you're the plugin's author, ship the hooks in the plugin's [`hooks/hooks.json`](/docs/en/plugins/components#hooks) and the MCP servers in its [`.mcp.json`](/docs/en/plugins/components#mcp-servers) instead. They apply whenever the plugin is enabled rather than only inside the subagent.
238</Note>
239 
240234You can also reuse a subagent definition as an [agent team](/docs/en/agent-teams) teammate: name the subagent type when you ask Claude to spawn the teammate, and Claude Code applies parts of that definition to it. [Use subagent definitions for teammates](/docs/en/agent-teams#use-subagent-definitions-for-teammates) says which scopes and which parts apply in each display mode.
241235 
242236### Write subagent files
from line 237
243237 
244238Subagent files use YAML frontmatter for configuration, followed by the system prompt in Markdown:
245239 
246<Note>
247 Claude Code watches `~/.claude/agents/` and `.claude/agents/`. When you add or edit a subagent file on disk, or ask Claude to write one for you, Claude Code detects the change within a few seconds and the next delegation uses the updated definition, with no restart needed.
240> [!NOTE]
241>
242> Claude Code watches `~/.claude/agents/` and `.claude/agents/`. When you add or edit a subagent file on disk, or ask Claude to write one for you, Claude Code detects the change within a few seconds and the next delegation uses the updated definition, with no restart needed.
243>
244> Three cases still need a restart:
245>
246> * The watcher covers only directories that existed when the session started, so after creating a scope's first agent file in a new `agents` directory, restart to load it.
247> * Claude Code doesn't watch `.claude/agents/` inside directories added with `--add-dir` or `/add-dir`, so after adding or editing a subagent there, restart to load the change.
248> * Sessions started with `--disable-slash-commands` don't watch these directories at all.
248249 
249 Three cases still need a restart:
250 
251 * The watcher covers only directories that existed when the session started, so after creating a scope's first agent file in a new `agents` directory, restart to load it.
252 * Claude Code doesn't watch `.claude/agents/` inside directories added with `--add-dir` or `/add-dir`, so after adding or editing a subagent there, restart to load the change.
253 * Sessions started with `--disable-slash-commands` don't watch these directories at all.
254</Note>
255 
256250```markdown .claude/agents/code-reviewer.md theme={null}
257251---
258252name: code-reviewer
from line 280
286280 
287281When the main conversation itself runs isolated in a worktree, Claude Code applies the same checks to the session and to every subagent it spawns, including subagents without `isolation: worktree`; see [How Claude Code enforces isolation](/docs/en/worktrees#how-claude-code-enforces-isolation).
288282 
289<h3 id="supported-frontmatter-fields">
290 Frontmatter reference
291</h3>
283### Frontmatter reference
292284 
293285Configure a subagent with YAML [frontmatter](/docs/en/glossary#frontmatter) between `---` markers at the top of its file, and write its system prompt as Markdown after the closing `---`. Only `name` and `description` are required.
294286 
from line 489
497489 
498490When an agent runs as the main thread with `claude --agent`, it can spawn subagents using the Agent tool. To restrict which subagent types it can spawn, use `Agent(agent_type)` syntax in the `tools` field.
499491 
500<Note>In version 2.1.63, the Task tool was renamed to Agent. Existing `Task(...)` references in settings and agent definitions still work as aliases.</Note>
492> [!NOTE]
493>
494> In version 2.1.63, the Task tool was renamed to Agent. Existing `Task(...)` references in settings and agent definitions still work as aliases.
501495 
502496```yaml theme={null}
503497---
from line 517
523517 
524518Use the `mcpServers` field to give a subagent access to [MCP](/docs/en/mcp) servers that aren't available in the main conversation. Inline servers defined here are connected when the subagent starts, subject to the [trust rule for the agent file's folder](#inline-server-trust), and disconnected when it finishes. String references share the parent session's connection.
525519 
526<Note>
527 The `mcpServers` field applies in both contexts where an agent file can run:
520> [!NOTE]
521>
522> The `mcpServers` field applies in both contexts where an agent file can run:
523>
524> * As a subagent, spawned through the Agent tool or an @-mention
525> * As the main session, launched with [`--agent`](#invoke-subagents-explicitly) or the `agent` setting
526>
527> When the agent is the main session, inline server definitions connect at startup alongside servers from [`.mcp.json`](/docs/en/mcp) and settings files, under the same [trust rule for the agent file's folder](#inline-server-trust). In `/mcp`, a remote (HTTP or SSE) server you've used before can show the [`cached` status](/docs/en/mcp#managing-your-servers) instead; Claude Code connects it when Claude first calls one of its tools.
528528 
529 * As a subagent, spawned through the Agent tool or an @-mention
530 * As the main session, launched with [`--agent`](#invoke-subagents-explicitly) or the `agent` setting
531 
532 When the agent is the main session, inline server definitions connect at startup alongside servers from [`.mcp.json`](/docs/en/mcp) and settings files, under the same [trust rule for the agent file's folder](#inline-server-trust). In `/mcp`, a remote (HTTP or SSE) server you've used before can show the [`cached` status](/docs/en/mcp#managing-your-servers) instead; Claude Code connects it when Claude first calls one of its tools.
533</Note>
534 
535529Each entry in the list is either an inline server definition or a string referencing an MCP server already configured in your session:
536530 
537531```yaml theme={null}
from line 559
565559 
566560Managed-settings restrictions apply to every subagent regardless of how it is defined. `--strict-mcp-config` doesn't filter servers you pass inline via `--agents` or the SDK `agents` option, since those are explicit caller input.
567561 
568<h4 id="inline-server-trust">
569 Trust required for inline MCP servers
570</h4>
562#### Trust required for inline MCP servers
571563 
572564Claude Code loads an [inline MCP server](#scope-mcp-servers-to-a-subagent) from an agent file in your project's `.claude/agents/` directory, or in an `--add-dir` directory's `.claude/agents/`, only after you [trust the folder the agent file came from](/docs/en/permissions#what-runs-before-you-trust-a-folder). Before v2.1.238, Claude Code loaded these servers without checking trust.
573565 
from line 616
624616 
625617If a listed skill is missing or disabled, for example by your organization's policy, Claude Code skips it and logs a warning to the debug log.
626618 
627<Note>
628 This is the inverse of [running a skill in a subagent](/docs/en/skills#run-skills-in-a-subagent). With `skills` in a subagent, the subagent controls the system prompt and loads skill content. With `context: fork` in a skill, the skill content is injected into the agent you specify. In both cases the subagent starts without your conversation history.
629</Note>
619> [!NOTE]
620>
621> This is the inverse of [running a skill in a subagent](/docs/en/skills#run-skills-in-a-subagent). With `skills` in a subagent, the subagent controls the system prompt and loads skill content. With `context: fork` in a skill, the skill content is injected into the agent you specify. In both cases the subagent starts without your conversation history.
630622 
631623#### Enable persistent memory
632624 
from line 746
754746 
755747Define hooks directly in the subagent's markdown file. These hooks only run while that specific subagent is active and are cleaned up when it finishes.
756748 
757<Note>
758 Frontmatter hooks fire when the agent is spawned as a subagent through the Agent tool or an @-mention, and when the agent runs as the main session via [`--agent`](#invoke-subagents-explicitly) or the `agent` setting. In the main-session case they run alongside any hooks defined in [`settings.json`](/docs/en/hooks).
759</Note>
749> [!NOTE]
750>
751> Frontmatter hooks fire when the agent is spawned as a subagent through the Agent tool or an @-mention, and when the agent runs as the main session via [`--agent`](#invoke-subagents-explicitly) or the `agent` setting. In the main-session case they run alongside any hooks defined in [`settings.json`](/docs/en/hooks).
760752 
761753To let a project-level subagent's frontmatter hooks run, accept the [workspace trust dialog](/docs/en/permissions#project-allow-rules-and-workspace-trust) for the folder that contains the agent file. Hooks from user-level subagents in `~/.claude/agents/` and from definitions you pass with `--agents` run without this step. If you added a folder with `--add-dir` from outside your trusted workspace's repository, trust that folder separately: its `.claude/agents/` hooks don't inherit the workspace's grant.
762754 
from line 1001
10091001 
10101002Each subagent explores its area independently, then Claude synthesizes the findings. This works best when the research paths don't depend on each other.
10111003 
1012<Warning>
1013 When subagents complete, their results return to your main conversation. Running many subagents that each return detailed results can consume significant context, and each subagent spends tokens of its own while it runs.
1014</Warning>
1004> [!WARNING]
1005>
1006> When subagents complete, their results return to your main conversation. Running many subagents that each return detailed results can consume significant context, and each subagent spends tokens of its own while it runs.
10151007 
10161008For work that needs to keep running in parallel or won't fit in one context window, run it in [separate sessions](/docs/en/agents) and let Claude [pass findings between them](/docs/en/cross-session-messaging).
10171009 
from line 1059
10671059 
10681060In the terminal, Claude Code shows nested subagents as a tree in the subagent panel below the prompt input and marks each row that still has descendants in the panel with a `(+N)` count of them. Open a row to see that subagent's siblings and direct children with a path back to `main`.
10691061 
1070<Note>
1071 Earlier versions used different defaults:
1062> [!NOTE]
1063>
1064> Earlier versions used different defaults:
1065>
1066> * **v2.1.172 through v2.1.216**: subagents could nest by default, up to five layers deep, and the limit couldn't be changed.
1067> * **v2.1.217 through v2.1.218**: the limit defaulted to one, so a subagent couldn't spawn its own unless you raised it; v2.1.219 raised the default to three.
10721068 
1073 * **v2.1.172 through v2.1.216**: subagents could nest by default, up to five layers deep, and the limit couldn't be changed.
1074 * **v2.1.217 through v2.1.218**: the limit defaulted to one, so a subagent couldn't spawn its own unless you raised it; v2.1.219 raised the default to three.
1075</Note>
1076 
10771069### Concurrent subagent limit
10781070 
10791071Two limits control subagent use, each with its own variable: this one stops Claude from spawning more subagents while too many are running, and the [depth limit](#let-subagents-spawn-their-own-subagents) caps how deeply subagents nest. There's no limit on the total number of subagents Claude can spawn over a session.
from line 1169
11771169 
11781170The `preTokens` value shows how many tokens were used before compaction occurred.
11791171 
1180<a id="subagent-auto-compact-window" />
1181 
11821172##### Set the auto-compact window for one subagent
11831173 
11841174To make one subagent compact earlier than it otherwise would, set `autoCompactWindow` in its [frontmatter](#supported-frontmatter-fields) to a token count from `100000` to `1000000`. If you set [`CLAUDE_CODE_AUTO_COMPACT_WINDOW`](/docs/en/env-vars#variables), the subagent uses that variable's window instead and the field has no effect, unless Claude Code [ignores the variable's value](/docs/en/model-config#claude_code_auto_compact_window-values). Requires Claude Code v2.1.296 or later.
from line 1192
12021192 
12031193## Fork the current conversation
12041194 
1205<Note>
1206 When [agent view is turned off](/docs/en/agent-view#turn-off-agent-view), `/subtask` isn't available and `/fork` starts the forked subagent instead; otherwise `/fork` copies the whole session into a new [background session](/docs/en/agent-view#from-inside-a-session).
1207</Note>
1195> [!NOTE]
1196>
1197> When [agent view is turned off](/docs/en/agent-view#turn-off-agent-view), `/subtask` isn't available and `/fork` starts the forked subagent instead; otherwise `/fork` copies the whole session into a new [background session](/docs/en/agent-view#from-inside-a-session).
12081198 
12091199A fork is a subagent that inherits the entire conversation so far instead of starting fresh. This drops the input isolation that subagents otherwise provide: a fork sees the same system prompt, tools, model, and message history as the main session, so you can hand it a side task without re-explaining the situation. The fork's own tool calls still stay out of your conversation and only its final result comes back, so your main context window stays clean. Use a fork when any other subagent would need too much background to be useful, or when you want to try several approaches in parallel from the same starting point.
12101200 
from line 1245
12551245 
12561246When Claude spawns a fork through the Agent tool, it can pass `isolation: "worktree"` so the fork's file edits are written to a separate git worktree instead of your checkout. A fork can't spawn further forks.
12571247 
1258<h3 id="turn-fork-mode-on-or-off">
1259 Turn fork mode off
1260</h3>
1248### Turn fork mode off
12611249 
12621250Fork mode lets Claude start a fork on its own. To stop Claude from starting forks:
12631251 
from line 1258
12701258 
12711259These examples demonstrate effective patterns for building subagents. Use them as starting points, or generate a customized version with Claude.
12721260 
1273<Tip>
1274 **Best practices:**
1275 
1276 * **Design focused subagents:** each subagent should excel at one specific task
1277 * **Write descriptions that single out one subagent:** Claude uses the description to decide when to delegate. Make each description specific enough to route to the right subagent, and keep the combined set within the [15,000-token description budget](#understand-automatic-delegation)
1278 * **Limit tool access:** grant only necessary permissions for security and focus
1279 * **Check into version control:** share project subagents with your team
1280</Tip>
1261> [!TIP]
1262>
1263> **Best practices:**
1264>
1265> * **Design focused subagents:** each subagent should excel at one specific task
1266> * **Write descriptions that single out one subagent:** Claude uses the description to decide when to delegate. Make each description specific enough to route to the right subagent, and keep the combined set within the [15,000-token description budget](#understand-automatic-delegation)
1267> * **Limit tool access:** grant only necessary permissions for security and focus
1268> * **Check into version control:** share project subagents with your team
12811269 
12821270### Code reviewer
12831271 

terminal-config Changed · +158 / -164 lines

from line 44
4444 
4545Some Claude Code shortcuts use the Option key, such as Option+Enter for a newline or Option+P to switch models. On macOS, most terminals do not send Option as a modifier by default, so these shortcuts do nothing until you enable it. The terminal setting for this is usually labeled "Use Option as Meta Key"; Meta is the historical Unix name for the key now labeled Option or Alt.
4646 
47<Tabs>
48 <Tab title="Apple Terminal">
49 Open Settings → Profiles → Keyboard and check "Use Option as Meta Key".
47**Apple Terminal**
5048 
51 If you accepted Claude Code's first-run terminal setup prompt, this is already done. That prompt runs `/terminal-setup` for you, which enables Option as Meta and turns off the audible bell in your Apple Terminal profile.
49Open Settings → Profiles → Keyboard and check "Use Option as Meta Key".
5250 
53 In [screen reader mode](/docs/en/accessibility), `/terminal-setup` leaves the bell setting unchanged so the terminal bell stays audible. If an earlier run turned the bell off, turn it back on under Settings → Profiles → Advanced → "Audible bell".
54 </Tab>
51If you accepted Claude Code's first-run terminal setup prompt, this is already done. That prompt runs `/terminal-setup` for you, which enables Option as Meta and turns off the audible bell in your Apple Terminal profile.
5552 
56 <Tab title="iTerm2">
57 Open Settings → Profiles → Keys → General and set Left Option key and Right Option key to "Esc+".
53In [screen reader mode](/docs/en/accessibility), `/terminal-setup` leaves the bell setting unchanged so the terminal bell stays audible. If an earlier run turned the bell off, turn it back on under Settings → Profiles → Advanced → "Audible bell".
5854 
59 Running `/terminal-setup` in iTerm2 enables "Applications in terminal may access clipboard" under Settings → General → Selection so the `/copy` command can write to your system clipboard. The command detects iTerm2 even when run from inside tmux. Restart iTerm2 for the change to take effect.
60 </Tab>
55**iTerm2**
6156 
62 <Tab title="VS Code">
63 Add `"terminal.integrated.macOptionIsMeta": true` to your VS Code settings.
64 </Tab>
65</Tabs>
57Open Settings → Profiles → Keys → General and set Left Option key and Right Option key to "Esc+".
6658 
59Running `/terminal-setup` in iTerm2 enables "Applications in terminal may access clipboard" under Settings → General → Selection so the `/copy` command can write to your system clipboard. The command detects iTerm2 even when run from inside tmux. Restart iTerm2 for the change to take effect.
60 
61**VS Code**
62 
63Add `"terminal.integrated.macOptionIsMeta": true` to your VS Code settings.
64 
6765For Ghostty, Kitty, and other terminals, look for an Option-as-Alt or Option-as-Meta setting in the terminal's configuration file.
6866 
6967## Get a terminal bell or notification
from line 78
8078 
8179The desktop notification reaches your local machine over SSH, so a remote session can still alert you. Ghostty and Kitty forward it to your OS notification center without further setup. iTerm2 requires you to enable forwarding:
8280 
83<Steps>
84 <Step title="Open iTerm2 notification settings">
85 Go to Settings → Profiles → Terminal.
86 </Step>
81**1. Open iTerm2 notification settings**
8782 
88 <Step title="Enable alerts">
89 Check "Notification Center Alerts", then click "Filter Alerts" and enable "Send escape sequence-generated alerts".
90 </Step>
91</Steps>
83Go to Settings → Profiles → Terminal.
9284 
85**2. Enable alerts**
86 
87Check "Notification Center Alerts", then click "Filter Alerts" and enable "Send escape sequence-generated alerts".
88 
9389If notifications still do not appear, confirm that your terminal application has notification permission in your OS settings, and if you are running inside tmux, [enable passthrough](#configure-tmux).
9490 
9591### Play a sound with a Notification hook
from line 173
177173 
178174The reference below covers the tokens you can set in `overrides`. The interactive editor in `/theme` shows the same tokens with a live preview, plus a few single-purpose accents such as onboarding screen colors that are omitted here.
179175 
180<Accordion title="Color token reference">
181 The following example combines tokens from several of the groups below: the brand accent, the plan mode border, the diff backgrounds, and the message background.
176> **Color token reference**
177>
178> The following example combines tokens from several of the groups below: the brand accent, the plan mode border, the diff backgrounds, and the message background.
179>
180> ```json ~/.claude/themes/midnight.json theme={null}
181> {
182> "name": "Midnight",
183> "base": "dark",
184> "overrides": {
185> "claude": "#a78bfa",
186> "planMode": "#38bdf8",
187> "diffAdded": "#14532d",
188> "diffRemoved": "#7f1d1d",
189> "userMessageBackground": "#1e1b4b"
190> }
191> }
192> ```
193>
194> #### Text and accent colors
195>
196> Control the primary brand accent and the foreground text shades used throughout the interface.
197>
198> | Token | Controls |
199> | :- | :- |
200> | `claude` | Primary brand accent, used for the spinner and assistant label |
201> | `text` | Default foreground text |
202> | `inverseText` | Text drawn on top of a colored background, such as status badges |
203> | `inactive` | Secondary text such as hints, timestamps, and disabled items |
204> | `subtle` | Faint borders and de-emphasized secondary text |
205> | `suggestion` | Autocomplete suggestions and selection highlight in pickers |
206> | `permission` | Dialog borders, including permission prompts and pickers |
207> | `remember` | Memory and `CLAUDE.md` indicators |
208>
209> #### Status colors
210>
211> Signal success, failure, and warning states across messages and indicators.
212>
213> | Token | Controls |
214> | :- | :- |
215> | `success` | Success messages and passing checks |
216> | `error` | Error messages and failures |
217> | `warning` | Warnings, caution messages, and the auto mode indicator |
218> | `merged` | Merged pull request status |
219>
220> #### Input box and mode indicators
221>
222> Set the input box border color and the accent shown while a permission mode or indicator is active.
223>
224> | Token | Controls |
225> | :- | :- |
226> | `promptBorder` | Input box border |
227> | `planMode` | Plan mode accent, plan messages, and plan-mode dialogs |
228> | `autoAccept` | Accept-edits mode accent |
229> | `bashBorder` | Input box border when entering a `!` shell command |
230> | `ide` | IDE connection indicator |
231> | `fastMode` | Fast mode indicator |
232> | `effortUltra` | The `ultracode` tag on the input box border while [ultracode](/docs/en/model-config#adjust-effort-level) is on. Your override of this color takes effect on Claude Code v2.1.239 or later |
233>
234> #### Diff rendering
235>
236> Color added and removed code in file edits and reviews.
237>
238> | Token | Controls |
239> | :- | :- |
240> | `diffAdded` | Background of added lines |
241> | `diffRemoved` | Background of removed lines |
242> | `diffAddedDimmed` | Background of added lines in the dimmed diff shown after you reject an edit |
243> | `diffRemovedDimmed` | Background of removed lines in the dimmed diff shown after you reject an edit |
244> | `diffAddedWord` | Word-level highlight within an added line |
245> | `diffRemovedWord` | Word-level highlight within a removed line |
246>
247> #### Fullscreen mode
248>
249> Claude Code uses `userMessageBackgroundHover`, `selectionBg`, and `composerSidebarBackground` only in [fullscreen rendering mode](/docs/en/fullscreen). It paints `userMessageBackground`, `bashMessageBackgroundColor`, and `memoryBackgroundColor` in both the default and fullscreen renderers.
250>
251> | Token | Controls |
252> | :- | :- |
253> | `userMessageBackground` | Background behind your messages in the transcript |
254> | `userMessageBackgroundHover` | Background behind a message while hovered or expanded |
255> | `bashMessageBackgroundColor` | Background behind `!` shell command entries in the transcript |
256> | `memoryBackgroundColor` | Background behind `#` memory entries in the transcript |
257> | `selectionBg` | Background of text selected with the mouse |
258> | `composerSidebarBackground` | Background of the panel beside the conversation, such as the [diff panel](/docs/en/fullscreen#watch-your-changes-in-the-diff-panel) or a [mod's pane](/docs/en/plugins/mods/interface) |
259>
260> #### Usage meter and speaker labels
261>
262> Adjust the bar shown in the `/usage` view and the labels that distinguish your messages from Claude's.
263>
264> | Token | Controls |
265> | :- | :- |
266> | `rate_limit_fill` | Filled portion of the usage meter |
267> | `rate_limit_empty` | Unfilled portion of the usage meter |
268> | `briefLabelYou` | Color of the `You` label on your messages |
269> | `briefLabelClaude` | Color of the `Claude` label on assistant messages |
270>
271> #### Shimmer variants and subagent colors
272>
273> Several tokens have a paired shimmer variant that supplies the lighter color used in the spinner's animated gradient. Override the shimmer alongside its base token if the animation looks mismatched.
274>
275> * `claude` and `claudeShimmer`
276> * `warning` and `warningShimmer`
277> * `permission` and `permissionShimmer`
278> * `promptBorder` and `promptBorderShimmer`
279> * `inactive` and `inactiveShimmer`
280> * `fastMode` and `fastModeShimmer`
281>
282> Each [subagent](/docs/en/sub-agents) and parallel task is shown in one of eight named colors so you can tell them apart in the transcript. The token names follow the pattern `<color>_FOR_SUBAGENTS_ONLY`, where `<color>` is `red`, `blue`, `green`, `yellow`, `purple`, `orange`, `pink`, or `cyan`. Override these to change what each named color looks like. For example, a subagent with `color: blue` in its definition is drawn using the `blue_FOR_SUBAGENTS_ONLY` value.
283>
284> Claude Code renders the [`ultrathink`](/docs/en/model-config#use-ultrathink-for-one-off-deep-reasoning) keyword in the prompt input with a seven-color rainbow gradient. The token names follow the pattern `rainbow_<color>` and `rainbow_<color>_shimmer`, where `<color>` is `red`, `orange`, `yellow`, `green`, `blue`, `indigo`, or `violet`.
182285 
183 ```json ~/.claude/themes/midnight.json theme={null}
184 {
185 "name": "Midnight",
186 "base": "dark",
187 "overrides": {
188 "claude": "#a78bfa",
189 "planMode": "#38bdf8",
190 "diffAdded": "#14532d",
191 "diffRemoved": "#7f1d1d",
192 "userMessageBackground": "#1e1b4b"
193 }
194 }
195 ```
196 
197 #### Text and accent colors
198 
199 Control the primary brand accent and the foreground text shades used throughout the interface.
200 
201 | Token | Controls |
202 | :- | :- |
203 | `claude` | Primary brand accent, used for the spinner and assistant label |
204 | `text` | Default foreground text |
205 | `inverseText` | Text drawn on top of a colored background, such as status badges |
206 | `inactive` | Secondary text such as hints, timestamps, and disabled items |
207 | `subtle` | Faint borders and de-emphasized secondary text |
208 | `suggestion` | Autocomplete suggestions and selection highlight in pickers |
209 | `permission` | Dialog borders, including permission prompts and pickers |
210 | `remember` | Memory and `CLAUDE.md` indicators |
211 
212 #### Status colors
213 
214 Signal success, failure, and warning states across messages and indicators.
215 
216 | Token | Controls |
217 | :- | :- |
218 | `success` | Success messages and passing checks |
219 | `error` | Error messages and failures |
220 | `warning` | Warnings, caution messages, and the auto mode indicator |
221 | `merged` | Merged pull request status |
222 
223 #### Input box and mode indicators
224 
225 Set the input box border color and the accent shown while a permission mode or indicator is active.
226 
227 | Token | Controls |
228 | :- | :- |
229 | `promptBorder` | Input box border |
230 | `planMode` | Plan mode accent, plan messages, and plan-mode dialogs |
231 | `autoAccept` | Accept-edits mode accent |
232 | `bashBorder` | Input box border when entering a `!` shell command |
233 | `ide` | IDE connection indicator |
234 | `fastMode` | Fast mode indicator |
235 | `effortUltra` | The `ultracode` tag on the input box border while [ultracode](/docs/en/model-config#adjust-effort-level) is on. Your override of this color takes effect on Claude Code v2.1.239 or later |
236 
237 #### Diff rendering
238 
239 Color added and removed code in file edits and reviews.
240 
241 | Token | Controls |
242 | :- | :- |
243 | `diffAdded` | Background of added lines |
244 | `diffRemoved` | Background of removed lines |
245 | `diffAddedDimmed` | Background of added lines in the dimmed diff shown after you reject an edit |
246 | `diffRemovedDimmed` | Background of removed lines in the dimmed diff shown after you reject an edit |
247 | `diffAddedWord` | Word-level highlight within an added line |
248 | `diffRemovedWord` | Word-level highlight within a removed line |
249 
250 #### Fullscreen mode
251 
252 Claude Code uses `userMessageBackgroundHover`, `selectionBg`, and `composerSidebarBackground` only in [fullscreen rendering mode](/docs/en/fullscreen). It paints `userMessageBackground`, `bashMessageBackgroundColor`, and `memoryBackgroundColor` in both the default and fullscreen renderers.
253 
254 | Token | Controls |
255 | :- | :- |
256 | `userMessageBackground` | Background behind your messages in the transcript |
257 | `userMessageBackgroundHover` | Background behind a message while hovered or expanded |
258 | `bashMessageBackgroundColor` | Background behind `!` shell command entries in the transcript |
259 | `memoryBackgroundColor` | Background behind `#` memory entries in the transcript |
260 | `selectionBg` | Background of text selected with the mouse |
261 | `composerSidebarBackground` | Background of the panel beside the conversation, such as the [diff panel](/docs/en/fullscreen#watch-your-changes-in-the-diff-panel) or a [mod's pane](/docs/en/plugins/mods/interface) |
262 
263 #### Usage meter and speaker labels
264 
265 Adjust the bar shown in the `/usage` view and the labels that distinguish your messages from Claude's.
266 
267 | Token | Controls |
268 | :- | :- |
269 | `rate_limit_fill` | Filled portion of the usage meter |
270 | `rate_limit_empty` | Unfilled portion of the usage meter |
271 | `briefLabelYou` | Color of the `You` label on your messages |
272 | `briefLabelClaude` | Color of the `Claude` label on assistant messages |
273 
274 #### Shimmer variants and subagent colors
275 
276 Several tokens have a paired shimmer variant that supplies the lighter color used in the spinner's animated gradient. Override the shimmer alongside its base token if the animation looks mismatched.
277 
278 * `claude` and `claudeShimmer`
279 * `warning` and `warningShimmer`
280 * `permission` and `permissionShimmer`
281 * `promptBorder` and `promptBorderShimmer`
282 * `inactive` and `inactiveShimmer`
283 * `fastMode` and `fastModeShimmer`
284 
285 Each [subagent](/docs/en/sub-agents) and parallel task is shown in one of eight named colors so you can tell them apart in the transcript. The token names follow the pattern `<color>_FOR_SUBAGENTS_ONLY`, where `<color>` is `red`, `blue`, `green`, `yellow`, `purple`, `orange`, `pink`, or `cyan`. Override these to change what each named color looks like. For example, a subagent with `color: blue` in its definition is drawn using the `blue_FOR_SUBAGENTS_ONLY` value.
286 
287 Claude Code renders the [`ultrathink`](/docs/en/model-config#use-ultrathink-for-one-off-deep-reasoning) keyword in the prompt input with a seven-color rainbow gradient. The token names follow the pattern `rainbow_<color>` and `rainbow_<color>_shimmer`, where `<color>` is `red`, `orange`, `yellow`, `green`, `blue`, `indigo`, or `violet`.
288</Accordion>
289 
290286## Switch to fullscreen rendering
291287 
292288In [screen reader mode](/docs/en/accessibility), this section doesn't apply. Claude Code always renders as plain scrolling text except in attached [background sessions](/docs/en/agent-view), and if you run `/tui fullscreen` in any other session, Claude Code prints an explanation instead of switching.
from line 293
297293 
298294Run `/tui fullscreen` to switch and save the preference. Your conversation relaunches intact and future sessions start in fullscreen unless a [fullscreen start fails](/docs/en/fullscreen#fullscreen-renderer-didnt-finish-starting). You can also set the `CLAUDE_CODE_NO_FLICKER` environment variable before starting Claude Code:
299295 
300<CodeGroup>
301 ```bash Bash and Zsh theme={null}
302 CLAUDE_CODE_NO_FLICKER=1 claude
303 ```
296```bash Bash and Zsh theme={null}
297CLAUDE_CODE_NO_FLICKER=1 claude
298```
304299 
305 ```powershell PowerShell theme={null}
306 $env:CLAUDE_CODE_NO_FLICKER = "1"; claude
307 ```
300```powershell PowerShell theme={null}
301$env:CLAUDE_CODE_NO_FLICKER = "1"; claude
302```
308303 
309 ```json ~/.claude/settings.json theme={null}
310 {
311 "env": {
312 "CLAUDE_CODE_NO_FLICKER": "1"
313 }
304```json ~/.claude/settings.json theme={null}
305{
306 "env": {
307 "CLAUDE_CODE_NO_FLICKER": "1"
314308 }
315 ```
316</CodeGroup>
309}
310```
317311 
318312## Cap response width in wide terminals
319313 

third-party-integrations Changed · +7 / -75 lines

### Use MCP for integrations

from line 2
22 
33> Learn how Claude Code can integrate with various third-party services and infrastructure to meet enterprise deployment requirements.
44 
5export const ContactSalesCard = ({surface}) => {
6 const utm = content => `utm_source=claude_code&utm_medium=docs&utm_content=${surface}_${content}`;
7 const iconArrowRight = (size = 13) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
8 <line x1="5" y1="12" x2="19" y2="12" />
9 <polyline points="12 5 19 12 12 19" />
10 </svg>;
11 const STYLES = `
12.cc-cs {
13 --cs-slate: #141413;
14 --cs-clay: #d97757;
15 --cs-clay-deep: #c6613f;
16 --cs-gray-000: #ffffff;
17 --cs-gray-700: #3d3d3a;
18 --cs-border-default: rgba(31, 30, 29, 0.15);
19 font-family: inherit;
20}
21.dark .cc-cs {
22 --cs-slate: #f0eee6;
23 --cs-gray-000: #262624;
24 --cs-gray-700: #bfbdb4;
25 --cs-border-default: rgba(240, 238, 230, 0.14);
26}
27.cc-cs-card {
28 display: flex; align-items: center; justify-content: space-between;
29 gap: 16px; padding: 14px 16px; margin: 0;
30 background: var(--cs-gray-000); border: 0.5px solid var(--cs-border-default);
31 border-radius: 8px; flex-wrap: wrap;
32}
33.cc-cs-text { font-size: 13px; color: var(--cs-gray-700); line-height: 1.5; flex: 1; min-width: 240px; }
34.cc-cs-text strong { font-weight: 550; color: var(--cs-slate); }
35.cc-cs-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
36.cc-cs-btn-clay {
37 display: inline-flex; align-items: center; gap: 8px;
38 background: var(--cs-clay-deep); color: #fff; border: none;
39 border-radius: 8px; padding: 8px 14px;
40 font-size: 13px; font-weight: 500;
41 transition: background-color 0.15s; white-space: nowrap;
42}
43.cc-cs-btn-clay:hover { background: var(--cs-clay); }
44.cc-cs-btn-ghost {
45 display: inline-flex; align-items: center; gap: 8px;
46 background: transparent; color: var(--cs-gray-700);
47 border: 0.5px solid var(--cs-border-default);
48 border-radius: 8px; padding: 8px 14px;
49 font-size: 13px; font-weight: 500;
50}
51.cc-cs-btn-ghost:hover { background: rgba(0, 0, 0, 0.04); }
52.dark .cc-cs-btn-ghost:hover { background: rgba(255, 255, 255, 0.04); }
53@media (max-width: 720px) {
54 .cc-cs-actions { width: 100%; }
55}
56`;
57 return <div className="cc-cs not-prose">
58 <style>{STYLES}</style>
59 <div className="cc-cs-card">
60 <div className="cc-cs-text">
61 <strong>Deploying Claude Code across your organization?</strong> Talk to sales about enterprise plans, SSO, and centralized billing.
62 </div>
63 <div className="cc-cs-actions">
64 <a href={`https://claude.com/pricing?${utm('view_plans')}#plans-business`} className="cc-cs-btn-ghost">
65 View plans
66 </a>
67 <a href={`https://claude.com/contact-sales?${utm('contact_sales')}`} className="cc-cs-btn-clay">
68 Contact sales {iconArrowRight()}
69 </a>
70 </div>
71 </div>
72 </div>;
73};
74 
755Organizations can deploy Claude Code through Anthropic directly or through a cloud provider. This page helps you choose the right configuration.
766 
77<ContactSalesCard surface="third_party_overview" />
7**Deploying Claude Code across your organization?** Talk to sales about enterprise plans, SSO, and centralized billing.
788 
9View plans
10Contact sales
11 
7912## Compare deployment options
8013 
8114For most organizations, Claude for Teams or Claude for Enterprise provides the best experience. Team members get access to both Claude Code and Claude on the web with a single subscription, centralized billing, and no infrastructure setup required.
from line 49
11649 
11750 <tr>
11851 <td>Billing</td>
119 <td><strong>Teams:</strong> per-seat subscription with PAYG available, see <a href="https://claude.com/pricing?utm_source=claude_code&utm_medium=docs&utm_content=third_party_pricing#team-&-enterprise">pricing</a><br /><strong>Enterprise:</strong> <a href="https://claude.com/contact-sales?utm_source=claude_code&utm_medium=docs&utm_content=third_party_enterprise">Contact Sales</a></td>
52 <td>**Teams:** per-seat subscription with PAYG available, see [pricing](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=third_party_pricing#team-&-enterprise)\
53 **Enterprise:** [Contact Sales](https://claude.com/contact-sales?utm_source=claude_code\&utm_medium=docs\&utm_content=third_party_enterprise)</td>
12054 <td>PAYG</td>
12155 <td>PAYG through AWS</td>
12256 <td>PAYG through AWS Marketplace</td>
from line 169
235169 
236170To limit which of these deployment options a managed machine may use, set [`allowedProviders`](/docs/en/settings-reference#allowedproviders) in managed settings. For example, `["bedrock"]` allows Amazon Bedrock and nothing else; a Bedrock fleet that also enables the Mantle endpoint lists `"mantle"` too. The entry says which endpoint variables also need a managed `env` pin. Requires Claude Code v2.1.285 or later.
237171 
238<h3 id="leverage-mcp-for-integrations">
239 Use MCP for integrations
240</h3>
172### Use MCP for integrations
241173 
242174MCP is a great way to give Claude Code more information, such as connecting to ticket management systems or error logs. Have one central team configure MCP servers and check a `.mcp.json` configuration into the codebase so that all users benefit. [Learn more](/docs/en/mcp).
243175 
Feedback