Workspace Guardrails
Workspace guardrails solve one specific problem: launching an AI coding agent in the wrong repo with the wrong account.
If you work on client repos alongside personal projects, you may have noticed that claude, codex, and gemini are always willing to start regardless of which account is currently active. Nothing stops you from opening a client repo and accidentally running it under your personal key - or vice versa.
The workspace feature lets you bind a repo or directory to an expected aisw context. The shell hook then checks that binding before each agent launch and either warns you or blocks the launch entirely.
Concepts
Section titled “Concepts”Workspace binding maps a location to an expected context. The location can be:
- A specific git repo (stored locally in
.git/info/aisw.json, never committed) - A directory path or path prefix on your filesystem
- A git remote URL pattern (
github.com/acme/*) - The fallback default for any location not matched by a more specific rule
Guard mode controls what happens when the active context does not match the expected one:
warn(default): print a warning before the agent launches. The launch proceeds.strict: block the launch entirely with a remediation command.
Resolution order (most specific wins):
- Repo-local binding (
.git/info/aisw.json) - User path rule (
~/.aisw/workspaces.json, longest prefix match) - User git-remote rule (most-specific pattern match)
- Default context
Step 1: Create contexts for your workspaces
Section titled “Step 1: Create contexts for your workspaces”Workspace guardrails work on top of existing contexts. Create a context for each work mode you want to protect:
# Work at a client using a dedicated Claude team accountaisw context create client-acme \ --claude acme-claude \ --codex acme-codex \ --gemini acme-gemini
# Personal projects using personal accountsaisw context create personal \ --claude personal-claude \ --codex personal-codex \ --gemini personal-geminiStep 2: Install the shell hook
Section titled “Step 2: Install the shell hook”The workspace check runs as part of the shell hook. Install or update it:
# Bashecho 'eval "$(aisw shell-hook bash)"' >> ~/.bashrc && source ~/.bashrc
# Zshecho 'eval "$(aisw shell-hook zsh)"' >> ~/.zshrc && source ~/.zshrc
# Fishecho 'aisw shell-hook fish | source' >> ~/.config/fish/config.fish
# PowerShellAdd-Content $PROFILE "`naisw shell-hook pwsh | Out-String | Invoke-Expression"Step 3: Bind your repos
Section titled “Step 3: Bind your repos”Bind the current repo to a context:
cd ~/clients/acme-apiaisw workspace bind . --context client-acmeThis writes .git/info/aisw.json inside the repo. The file is in the git directory, not the working tree, so it is never committed or pushed.
Bind by git remote pattern to cover all repos under an organization:
aisw workspace bind --git-remote "github.com/acme/*" --context client-acmeBind a filesystem path prefix:
aisw workspace bind ~/clients --context client-acmeSet a default context for any location without a more specific binding:
aisw workspace bind --default --context personalStep 4: Set guard mode
Section titled “Step 4: Set guard mode”# Print a warning but allow the launch (default)aisw workspace guard --mode warn
# Block the launch and require explicit context switchaisw workspace guard --mode strictHow it works in practice
Section titled “How it works in practice”Once the shell hook is active and a binding is set, the claude, codex, and gemini commands in your shell become wrapper functions. Each time you run one of them, the hook calls aisw workspace check first.
With warn mode, in a mismatched repo you see:
Workspace guard warning: expected context 'client-acme', current state 'personal-claude, personal-codex, personal-gemini' (mismatch). Run 'aisw context use client-acme' before launching claude.The agent still launches. You can proceed if the mismatch is intentional.
With strict mode:
Error: workspace guard refused to launch claude. Expected context: 'client-acme' Current state: 'personal-claude, personal-codex, personal-gemini' Status: mismatch Run 'aisw context use client-acme'.The agent does not launch. Fix the context and try again:
aisw context use client-acmeclaudeThe prompt check also runs when you change directories, so you get a heads-up as soon as you cd into a bound repo rather than only when you try to launch an agent.
Commands
Section titled “Commands”aisw workspace bind
Section titled “aisw workspace bind”aisw workspace bind [PATH] --context <name>aisw workspace bind --git-remote <PATTERN> --context <name>aisw workspace bind --default --context <name>Create or update a workspace binding.
| Flag | Effect |
|---|---|
PATH | Path to bind. Defaults to .. Inside a git repo, writes to .git/info/aisw.json. Outside a repo, writes a path rule to ~/.aisw/workspaces.json. |
--context NAME | Context to expect at this location. Must already exist. |
--git-remote PATTERN | Bind by git remote URL pattern. Supports * wildcards. Cannot be combined with PATH or --default. |
--default | Set the fallback context for locations without a more specific rule. Cannot be combined with PATH or --git-remote. |
Repo-local bindings take priority over all user-level rules. They are written to .git/info/aisw.json, which is excluded from the working tree by default and never committed.
# Bind the current repo locallyaisw workspace bind . --context client-acme
# Bind a specific repo by pathaisw workspace bind ~/clients/acme-api --context client-acme
# Bind all repos under a GitHub organization by remote patternaisw workspace bind --git-remote "github.com/acme/*" --context client-acme
# Bind a path prefix (any subdirectory of ~/work maps to the work context)aisw workspace bind ~/work --context work
# Set a default context for unlisted locationsaisw workspace bind --default --context personalaisw workspace status
Section titled “aisw workspace status”aisw workspace status [--json]Show the resolved binding for the current directory: which rule matched, which context is expected, what is currently active, and what command to run if there is a mismatch.
aisw workspace statusaisw workspace status --jsonExample output:
Workspace Workspace: /Users/alice/clients/acme-api Repo root: /Users/alice/clients/acme-api Expected: client-acme Active: claude acme-claude, codex acme-codex, gemini acme-gemini Active context: client-acme Status: match Matched rule: repo_local:/Users/alice/clients/acme-api/.git/info/aisw.jsonJSON output:
{ "workspace": "/Users/alice/clients/acme-api", "repo_root": "/Users/alice/clients/acme-api", "matched_rule": "repo_local:/Users/alice/clients/acme-api/.git/info/aisw.json", "expected_context": "client-acme", "active_context": "client-acme", "active_profiles": { "claude": "acme-claude", "codex": "acme-codex", "gemini": "acme-gemini" }, "status": "match", "recommended_command": null}Status values:
| Status | Meaning |
|---|---|
match | Active profiles match the expected context. |
mismatch | A binding is set but the active profiles do not match. |
no_expected_context | No binding applies to the current location. |
invalid_context | The bound context name no longer exists. |
ambiguous_active | Multiple contexts match the current active profiles. |
unmanaged | A binding is set but no tool has an active profile. |
aisw workspace doctor
Section titled “aisw workspace doctor”aisw workspace doctor [--json]Validate workspace rules and the current workspace state. Checks that every context referenced by a rule still exists, and reports the resolved status for the current directory.
aisw workspace doctoraisw workspace doctor --jsonaisw workspace guard
Section titled “aisw workspace guard”aisw workspace guard --mode warn|strictSet the default guard mode. The setting is saved to ~/.aisw/workspaces.json.
aisw workspace guard --mode warn # warn but allow launchaisw workspace guard --mode strict # block launch on mismatchCommon scenarios
Section titled “Common scenarios”You work in client repos and a personal repo
Section titled “You work in client repos and a personal repo”# Create one context per work modeaisw context create acme --claude acme-claude --codex acme-codex --gemini acme-geminiaisw context create personal --claude personal-claude --codex personal-codex --gemini personal-gemini
# Bind each client repo locallycd ~/clients/acme-api && aisw workspace bind . --context acmecd ~/clients/acme-ui && aisw workspace bind . --context acme
# Set personal as the default for everything elseaisw workspace bind --default --context personal
# Use strict mode so mistakes are caught before they happenaisw workspace guard --mode strictYou have many repos under a GitHub organization
Section titled “You have many repos under a GitHub organization”# Instead of binding each repo individually, bind by remote patternaisw workspace bind --git-remote "github.com/acme-corp/*" --context acme-corpAny repo with a remote matching that pattern will resolve to the acme-corp context automatically, even repos you clone in the future.
You want to check state without launching an agent
Section titled “You want to check state without launching an agent”aisw workspace statusaisw workspace status --jsonYou temporarily need to override the guard
Section titled “You temporarily need to override the guard”In warn mode, just proceed - the warning is informational. In strict mode, switch context first:
aisw context use personalclaude # launches with personal contextTo switch back:
aisw context use acmeStorage
Section titled “Storage”Workspace configuration is stored in two places:
| Location | Contains |
|---|---|
.git/info/aisw.json | Repo-local binding. Stays with the repo’s git directory. Never committed. |
~/.aisw/workspaces.json | User-level path rules, git-remote rules, default context, and guard mode. |
Both files have 0600 permissions on Unix systems.
Related
Section titled “Related”- Commands - full flag reference including
workspace check - Shell integration - how the prompt check and agent wrappers work
- Quickstart - getting started with profiles and contexts