Verified 2026-08-05
Runtime · 7 rules

How should my skill detect that it is running in Cowork?

A naive environment-variable check false-negatives in exactly the configuration that matters most, because the part of your skill that runs shell commands runs somewhere with none of those markers set. Use an ordered recipe instead: check the environment marker, then the working-directory signature, then the CLI marker. For behaviour rather than reporting, branch on which tools exist — that is the most durable signal available to a skill.

For behaviour rather than reporting, branch on which tools exist instead of on the environment.

SILENTMeasured

The tool surface is the most durable runtime signal available to a skill: it determines what the skill can actually do, it is visible without running a shell, and it stays correct when environment variables are added or removed. Concretely, the shell tool has a different name in Cowork than in the CLI — mcp__workspace__bash rather than Bash — so the presence of the Cowork-flavoured name is the signal. Use environment checks to report where you are; use tool presence to decide what to do.

Do not hard-fail when a runtime marker is missing. Fall through to the next check.

A runtime marker is an environment variable the host sets when it starts your session — CLAUDE_CODE_IS_COWORK and CLAUDECODE are the two that matter. Which markers are set has changed across releases, and they are absent entirely from the shell context. A detection routine that treats one missing variable as proof of a runtime will be wrong the next time the spawn contract changes. Order the checks so each one only adds coverage. The set is still moving: Desktop 1.28929.0 added CLAUDE_CODE_COWORK_FRAME_ARTIFACTS alongside the existing markers, and no agent before 2.1.228 could read it.

Do not detect Cowork with a bare environment-variable check.

SILENTMeasured Local sandbox

A skill spans two execution contexts. The part that runs shell commands runs in a sealed context with none of the usual markers set, so a bare environment check returns 'not Cowork' in precisely the production configuration you most needed to detect. The failure is silent and produces confidently wrong behaviour.

Caveat: This applies in the local sandbox only. In the remote sandbox there is one context and the markers are visible from the shell; the rule that says to expect the split only locally covers that case.

Detect the runtime with an ordered sequence of checks, not a single environment test.

SILENTMeasured

Run three checks in order. First, is CLAUDE_CODE_IS_COWORK set — if so you are in Cowork. Otherwise, is the working directory under /sessions/ — that catches the shell context, where the marker is absent. Otherwise, is CLAUDECODE set — then it is Claude Code, and CLAUDE_CODE_ENTRYPOINT says which: remote_cowork is Cowork running in the cloud, remote is Claude Code on the web, and anything else is the CLI. Skipping that last refinement misreads a cloud Cowork session as the CLI, because its shell sets CLAUDECODE but not CLAUDE_CODE_IS_COWORK. Each step only adds coverage and a later one never contradicts an earlier one, so a missing marker should fall through rather than fail.

Caveat: The recipe can end with neither answer. Treat that as a real third case — a surface that may be running your skill without your plugin — and check for your own files rather than assuming the CLI.

Remember your skill spans two execution contexts, and test the one you are actually in.

SILENTMeasured Local sandbox

Your skill's instructions and its file-reading and file-writing tools run in one place — call it the agent context. The shell commands it issues run somewhere else, with a different environment and a different view of the filesystem. In Cowork the agent context sees CLAUDE_CODE_IS_COWORK and host-form paths, while the shell context sees neither and works under /sessions/. A check performed in one tells you very little about the other, which is why a single probe produces confident wrong answers.

Caveat: This applies in the local sandbox only. In the remote sandbox there is one context and the markers are visible from the shell; the rule that says to expect the split only locally covers that case.

Expect the two-context split, and the missing shell markers, only in the local sandbox.

SILENTMeasured Remote sandbox

In the remote sandbox the shell and the file tools share one filesystem and one working directory, and some runtime markers are visible from the shell — CLAUDECODE and the entry-point marker, but not CLAUDE_CODE_IS_COWORK — the opposite of the local sandbox, where the shell is sealed. The ordered recipe still terminates correctly, but its early branches answer differently per sandbox: a skill that reads 'the markers are absent from the shell' as a universal fact, and treats their presence as proof it is not in Cowork, concludes wrongly in exactly the sandbox new sessions may default to. The remote sandbox does set its own distinct entry-point marker, but that value is served and has changed before; treat it as one check in the sequence, not the answer.

Caveat: One remote session, one account, relayed from another project's instrumented run. The local-sandbox half of this rule is the long-measured one.
Caveat: Re-measured 2026-09-23 on a cloud Cowork session: the shell showed CLAUDECODE=1, CLAUDE_CODE_ENTRYPOINT=remote_cowork and CLAUDE_CODE_REMOTE=true, and no CLAUDE_CODE_IS_COWORK.

Check that your own plugin's files are reachable before doing any work, and stop if they are not.

SILENTMeasured

The ordered recipe can return neither Cowork nor the CLI, and that third answer is not a curiosity — there is at least one surface that runs your skill's text without your plugin around it. Its fingerprint is cheap: every skill the account has enabled is mounted flat and read-only in one shared directory, side by side, each named for its plugin and skill together, so trimming the skills segment off your own directory gives you that shared mount rather than a plugin root. Your plugin's scripts, agents and hooks are simply not there, and sub-agent dispatch is unavailable. One line at the top of your skill — test for a file you ship and cannot work without — separates this from every supported runtime, and it is the only check that works, because a skill's shell cannot see the tool list. The trap is naming: the word a user sees when they start this surface is the same word one of Cowork's sandboxes uses, so the person who launched it cannot tell you which one you are in, and the directory your outputs go to has the same name in both.

Caveat: Observed in one session on 2026-09-22, relayed from another project's run; the specific mount layout is a detail that can change, so test for your own file rather than for that layout.
Caveat: Do not use the outputs directory name to tell surfaces apart. The same name is a built-in constant of the remote sandbox too — confirmed in the shipped application — so it identifies nothing. The absence of your plugin is the signal.

What is not established

  • Runtime markers are set by the host at spawn time and have been added and removed across releases. An ordered sequence of checks degrades safely, because a later check never contradicts an earlier one — it only adds coverage.