Practical guide · Intermediate

Safe First-Time Claude Code Setup in VS Code

The Claude Code extension, the claude command in a terminal, and the active login are separate layers. A successful sign-in or chat response in one interface does not prove that another interface uses the same executable, account, environment, or permission mode. This guide turns the first setup into a controlled experiment: identify every component, test access in an empty project, record what actually happened, and only then open a real repository.

Level: intermediate Reading and setup: 40 minutes Result: configuration record and diagnostic report

Expected result

At the end, you will have a disposable local project and a configuration record containing:

  • the VS Code version and build information;
  • the exact identifier and installed version of the Claude Code extension;
  • the absolute path and reported version of the terminal CLI;
  • the shell and environment from which each process was launched;
  • the authentication method, without credentials or tokens;
  • the initial permission or access mode;
  • observed results for reading, writing, command execution, and project-boundary checks;
  • diagnostic evidence that distinguishes extension, CLI, authentication, and permission failures.

An AI agent can do more than produce text: it may read files, edit them, and invoke local or remote tools. Consequently, “the chat works” is not a sufficient safety test. The important question is which actions the agent can actually perform in the current session.

A concrete case: one product name, three different states

A developer installs a Claude Code extension from the VS Code extension view. Later, they open the integrated terminal and install or invoke a command named claude. The extension displays a sign-in screen, while the terminal command independently requests authentication.

After signing in, the extension can answer a question about the open folder, but the terminal command fails. On another machine, the reverse happens: the CLI works while the extension cannot start a session. A report saying only “Claude Code does not work” cannot identify the failing layer.

The ambiguity becomes more serious when the interface appears to work. The developer assumes the agent is read-only because it merely answered a question. In reality, the session may also be able to edit files or run commands. Authentication establishes an identity; it does not, by itself, establish a safe local access boundary.

The test case used in this guide

Create a folder containing one readable fixture and no valuable data. Ask Claude Code to inspect the fixture, propose a change, request approval where required, create one designated file, and refuse or require additional approval for an operation outside the agreed boundary. Record the observable outcome of every step.

Diagnose four layers separately

1. VS Code host
The application build, user profile, workspace trust state, integrated shell, remote-development context, and inherited environment.
2. VS Code extension
A separately installed package with its own identifier, version, settings, logs, update cycle, and session state.
3. Terminal CLI
The executable found through PATH. Different shells, terminals, containers, or remote hosts may resolve the same command name to different files.
4. Identity and permissions
Authentication determines which identity is making requests. Permissions determine what that session may do. These are related but independent controls.

An approval gate is a deliberate pause before a sensitive action proceeds. It is evidence of an interaction policy, not proof of operating-system isolation. Likewise, successful tool calling proves that an action was available, but not that its scope was appropriate.

Step 1: prepare a genuinely isolated project

Do not begin in a work repository. Choose a new directory that contains no source code, secrets, environment files, symbolic links, mounted production data, or parent Git repository.

Linux and macOS

mkdir -p "$HOME/claude-code-isolated-check"
cd "$HOME/claude-code-isolated-check"
pwd
find . -mindepth 1 -maxdepth 1 -print
git rev-parse --show-toplevel 2>/dev/null || echo "No enclosing Git repository"

PowerShell

$Project = Join-Path $HOME "claude-code-isolated-check"
New-Item -ItemType Directory -Force -Path $Project | Out-Null
Set-Location $Project
(Get-Location).Path
Get-ChildItem -Force
git rev-parse --show-toplevel 2>$null
if ($LASTEXITCODE -ne 0) {
  "No enclosing Git repository"
}

The directory listing should be empty. If git rev-parse prints a parent repository, move the test elsewhere. A tool may treat that parent as the project root and gain a wider view than intended.

Create two controlled fixtures:

printf '%s\n' \
  'PROJECT=isolated-check' \
  'EXPECTED_MODE=approval-required' \
  'SECRET_PRESENT=no' \
  > fixture.txt

printf '%s\n' \
  'Do not modify this file during the read-only test.' \
  > protected-fixture.txt

PowerShell equivalent:

@(
  "PROJECT=isolated-check"
  "EXPECTED_MODE=approval-required"
  "SECRET_PRESENT=no"
) | Set-Content -Encoding utf8 fixture.txt

"Do not modify this file during the read-only test." |
  Set-Content -Encoding utf8 protected-fixture.txt

Record initial checksums so that later verification does not depend on visual inspection.

Linux

sha256sum fixture.txt protected-fixture.txt

macOS

shasum -a 256 fixture.txt protected-fixture.txt

PowerShell

Get-FileHash fixture.txt, protected-fixture.txt -Algorithm SHA256

Open the folder, not an individual file:

code .

Confirm the folder name in Explorer and check whether VS Code reports the workspace as trusted or restricted. For this first test, do not use a multi-root workspace, Dev Container, SSH target, or WSL window unless that remote context is specifically what you intend to diagnose.

Step 2: record VS Code and extension versions

From the same external terminal used to open the folder, run:

code --version
code --list-extensions --show-versions

Keep the complete VS Code version output because it may include a build identifier and architecture. In the extension list, identify the Claude Code package and confirm it against the extension details page inside VS Code.

  1. Open the Extensions view.
  2. Select the installed Claude Code extension.
  3. Record its full publisher and extension identifier.
  4. Record the installed version.
  5. Confirm that it is enabled in the active VS Code profile.
  6. Check for similarly named extensions that could confuse diagnosis.

The record should contain a value shaped like this, filled with observed data:

VS Code version: <complete output>
Active profile: <profile name or default>
Workspace trust: <trusted or restricted>
Claude extension: <publisher>.<extension-id>@<installed-version>

If an extension does not appear in the command output, do not immediately reinstall it. First check whether the graphical window and the terminal address the same local or remote VS Code environment. Extensions installed locally are not necessarily installed in an SSH, WSL, or container extension host.

Step 3: locate the CLI that really runs

Observability begins with the executable path. A command name alone cannot show whether the shell found a native installation, package-manager shim, alias, script, or a second copy from another runtime.

Linux and macOS

command -v claude
type -a claude
claude --version
printf 'shell=%s\n' "$SHELL"
uname -a

PowerShell

Get-Command claude -All |
  Format-List Name, CommandType, Source, Version
claude --version
$PSVersionTable.PSVersion
Get-ComputerInfo |
  Select-Object OsName, OsVersion, OsArchitecture

Repeat the location and version commands in VS Code’s integrated terminal. Compare external and integrated results literally.

Probe External terminal VS Code terminal Expected interpretation
Executable path Record observed value Record observed value Different paths mean different CLI installations or environments
CLI version Record full output Record full output A mismatch invalidates direct comparison
Shell or host Record environment Record environment Remote and local sessions must be treated separately

If several executable candidates exist, do not delete them yet. Determine which one is first in PATH, how it was installed, and whether VS Code inherited an older environment. Restarting the integrated terminal or the VS Code window may refresh environment discovery, but record the before-and-after state.

Use the installed CLI’s local help to discover supported diagnostic and authentication commands:

claude --help

Do not assume that a remembered subcommand, flag, or menu label exists in the installed version. Local help is part of the evidence.

Step 4: authenticate one interface at a time

Before signing in, create the configuration record:

CLAUDE CODE / VS CODE — INITIAL SETUP RECORD

Test date:
Operating system:
Architecture:
External shell:
Integrated shell:

VS Code version and build:
VS Code profile:
Workspace trust state:

Extension identifier:
Extension version:
Extension execution context:

CLI absolute path:
CLI version:
CLI execution context:

Extension authentication method:
CLI authentication method:
Account label, if safe to record:
Secret values recorded: NO

Initial access mode:
Approval behavior:
Network access observed:
Project boundary tested:

Read test:
Write test:
Command test:
Boundary test:
Diagnostics location:
Final status:
Open questions:

Authenticate the extension through the interface it provides. Record only the method: for example, interactive browser sign-in, organization-managed sign-in, or an environment-provided credential. Do not copy session cookies, API keys, bearer tokens, authorization codes, or complete environment dumps into the project.

Then start a separate CLI session and follow the authentication flow reported by that installed CLI. A browser page opened by both flows does not prove that they share a session. Record each interface independently:

Extension authentication:
  Method: <observed method>
  Identity label shown: <non-sensitive label or not shown>
  Time tested: <local timestamp>

CLI authentication:
  Method: <observed method>
  Identity label shown: <non-sensitive label or not shown>
  Time tested: <local timestamp>

If an organization supplies credentials or policy, note that fact without storing the credential. If the extension succeeds while the CLI fails, the correct conclusion is “authentication state differs or the CLI layer failed,” not “the account is broken.”

Step 5: select the narrowest practical access mode

Start with the installed product’s most restrictive usable mode: read-only, plan-first, or approval-required, depending on what that version exposes. Do not enable unrestricted execution merely to make the first run pass.

The principle of least privilege means granting only the access needed for the current task. An allowlist applies the same idea to actions: known-safe operations are permitted, while everything else remains blocked or requires approval.

Record the selected mode exactly as the interface displays it. If the mode is controlled through a setting, record the setting name and effective value. Do not infer a mode from the absence of a warning.

Test A: read without modification

Ask the agent:

Read fixture.txt and report its three keys and values.
Do not modify files and do not run commands unless reading requires approval.
Before using any tool, state which path you intend to access.

Verify that the response matches the fixture. Then recompute both checksums. A correct answer proves successful reading; unchanged checksums prove only that these two files were not modified.

Test B: propose a controlled write

Ask:

Propose creating result.txt with exactly this content:

READ_TEST=passed
WRITE_TEST=pending_verification

Do not create it until I explicitly approve the write.
Do not modify any existing file.

Observe whether the interface presents an approval request before the write. If it does, inspect the target path and proposed change, approve only this file, and then verify:

git diff --no-index /dev/null result.txt 2>/dev/null || true

On PowerShell, inspect the content directly:

Get-Content result.txt
Get-FileHash fixture.txt, protected-fixture.txt -Algorithm SHA256

Do not mark the write test as passed merely because the agent said it succeeded. Confirm that result.txt exists, contains exactly two expected lines, and the original fixture hashes remain unchanged.

Test C: command execution

Ask the agent to propose, but not automatically run, a harmless command that prints the current directory. The expected command depends on the shell:

pwd

or:

(Get-Location).Path

Record whether command execution was unavailable, required approval, or ran automatically. None of these outcomes should be invented in advance; the purpose is to establish the effective behavior of your configuration.

Test D: project boundary

Do not ask the agent to read a real file outside the project. Instead, ask it to explain whether it can access a deliberately nonexistent sibling path and to request approval before any attempt:

Do not access the path. Explain what permission or confirmation
would be required before attempting to read:
../claude-code-boundary-probe/nonexistent.txt

This is a policy-interface check, not proof of containment. A real sandbox is an enforced runtime boundary. A workspace preference, prompt instruction, or confirmation dialog may reduce risk but is not equivalent to operating-system isolation.

Step 6: collect diagnostics without mixing the layers

When something fails, note the time, interface, action, and visible error before restarting or reinstalling anything.

Extension diagnostics

  1. Open VS Code’s Output view.
  2. Select the channel associated with the Claude Code extension, if present.
  3. Open the extension-host log only when the component-specific channel is insufficient.
  4. Record the relevant timestamp and minimal error fragment.
  5. Check the extension’s installed version again after any reload.

CLI diagnostics

Capture these non-secret facts:

  • the exact command entered;
  • the current working directory;
  • the resolved executable path;
  • the CLI version;
  • the exit status;
  • the minimal standard-error fragment needed to recognize the failure.

On Linux and macOS, print the exit status immediately after a failed command:

printf 'exit_status=%s\n' "$?"

In PowerShell:

"exit_status=$LASTEXITCODE"

Be careful: running another command first replaces the status you intended to record.

Build a layer-by-layer matrix

Check Extension CLI Interpretation
Component starts Observed result Observed result Separates loading from authentication
Identity accepted Observed result Observed result Separates account state between interfaces
Fixture read Observed result Observed result Tests project visibility
Write approval Observed behavior Observed behavior Tests effective interaction policy
Command approval Observed behavior Observed behavior Tests execution separately from file access

If the extension and CLI produce different results, preserve that difference. It is diagnostic evidence, not noise.

Keep a small audit log of the test sequence: timestamp, interface, requested action, approval decision, observed effect, and verification method. The log should describe actions without including secrets.

Verification: when the setup is ready

Consider the isolated setup verified only when every applicable item below has an observed value.

  • VS Code’s complete version output is recorded.
  • The active profile and workspace trust state are recorded.
  • The extension identifier and installed version are recorded.
  • The extension’s local or remote execution context is known.
  • The external and integrated terminals resolve claude to known paths.
  • The CLI version is recorded for each distinct environment.
  • Extension and CLI authentication methods are recorded separately.
  • No credentials, cookies, or tokens appear in the setup record.
  • The initial permission mode is recorded using the interface’s actual label.
  • The fixture read result was verified against known content.
  • The controlled write was denied, approved, or executed with its actual behavior recorded.
  • The original fixture checksums are unchanged.
  • Command execution behavior is recorded independently of file writing.
  • The project boundary test did not expose real external data.
  • Relevant diagnostic channels and timestamps are recorded.

A compact final status can use four values:

Extension: PASS | FAIL | NOT TESTED
CLI: PASS | FAIL | NOT TESTED
Authentication consistency: CONFIRMED | DIFFERENT | UNKNOWN
Access mode: <exact observed label>
Read test: PASS | FAIL
Write test: PASS | FAIL | BLOCKED AS EXPECTED
Command test: APPROVAL REQUIRED | AUTOMATIC | UNAVAILABLE | UNKNOWN
Boundary enforcement: CONFIRMED | NOT CONFIRMED
Ready for a real repository: YES | NO

Use “boundary enforcement: confirmed” only when an actual technical control was tested. A polite refusal from the model is not sufficient evidence.

Common failure cases and what they mean

claude is not found in the integrated terminal
Compare the integrated and external PATH values, shells, and host contexts. VS Code may have inherited an earlier environment, or the CLI may exist only on another host. This is not yet an authentication failure.
The external terminal and VS Code find different executables
You have two test subjects. Record both paths and installation origins. Choose the intended one before comparing behavior.
The extension works but the CLI requests sign-in
Treat their authentication states as independent. Complete or diagnose CLI authentication without invalidating the working extension session.
The CLI works but the extension fails to load
Inspect the extension version, enabled state, execution host, workspace trust, and extension-specific output. Reinstalling the CLI is unlikely to explain an extension-host loading error.
The agent can read but cannot write
This may be the intended access mode. Check whether the write was blocked, required approval, targeted the wrong path, or failed at the operating-system permission layer.
A write occurred without the expected approval
Stop the session, preserve diagnostics, record the effective mode, and do not open a real repository. Recheck session-specific settings rather than relying on a global preference.
VS Code shows one extension version after a restart
An update may have occurred. Record the new version and repeat the controlled tests; results from two versions should not be combined as one configuration.
The project appears empty, but the agent sees unrelated files
Check for a parent repository, multi-root workspace, symbolic links, remote mounts, inherited instructions, and an incorrect working directory.
The interface says an action succeeded, but the file is absent
Verify the current directory and target path. A textual success statement is not proof of a filesystem effect.
Logs contain an authentication-looking error
Use the timestamp and component identity to determine which session generated it. Do not assume an old extension error describes the current CLI session.

Approaches that make diagnosis worse

  • Reinstalling everything at once. This removes evidence about which layer failed.
  • Granting full access to “rule out permissions.” It discards the safety boundary precisely when the configuration is least understood.
  • Testing first in a real repository. An unexpected read, write, command, or network action becomes a real incident.
  • Copying a version from a tutorial. The configuration record must describe the machine being tested.
  • Sharing a complete diagnostic archive. Logs may contain paths, prompts, account details, and credentials.
  • Treating a model refusal as containment. Reliable containment must be enforced outside the model.

Moving to a real repository

Open the real repository in a separate VS Code window. Begin with a mode no broader than the one you verified. Before the first agent task:

  • confirm the repository root with git rev-parse --show-toplevel;
  • review git status before starting;
  • remove unrelated folders from the workspace;
  • keep secrets out of open, unencrypted project files;
  • ask for a plan before permitting edits;
  • review every diff before accepting it;
  • use a reversible branch or backup where appropriate;
  • repeat the permission test after changing modes or tools.

A prompt injection is a malicious or conflicting instruction embedded in content the agent reads. The empty-project test verifies installation and access behavior; it does not make documentation, issue text, web content, tool results, or repository files trustworthy.

Plan a rollback before allowing meaningful edits. For source code, that usually means a clean Git state and a reviewable branch. Do not use version control as an excuse to expose credentials or permit irreversible external actions.

Limitations

  • An empty project is not a complete production simulation. Real repositories introduce hooks, dependencies, Git metadata, untrusted instructions, and larger file trees.
  • One approval test does not characterize every tool. File reads, writes, commands, network requests, and external integrations may follow different policies.
  • Interface labels and commands change. The installed version’s local help and visible settings take precedence over remembered instructions.
  • The record is a snapshot. Repeat the test after updating VS Code, the extension, the CLI, authentication, or access settings.
  • Workspace restriction is not necessarily a system sandbox. Confirm which boundary is technically enforced.
  • This procedure does not audit the cloud account. Organization policy, billing, retention, key management, and service-side permissions require separate review.
  • A missing visible error does not prove a clean session. Preserve timestamps and inspect the log channel belonging to the component under test.
  • No universal result is asserted here. Your completed record must contain the observations produced by your installed versions and environment.

Final result

A safe first setup is not one successful login. It is a reproducible experiment in which the VS Code host, extension, CLI, authentication, and permissions are identified and tested separately.

The durable artifact is the completed configuration record: exact versions, executable paths, authentication methods, effective access mode, diagnostic locations, and verified effects of controlled actions. With that record, you can locate a failure, repeat the configuration after an update, and decide whether the agent is ready for a real repository without relying on assumptions.

← More practical guides · Agent Lab Journal glossary →