Bug: Inline command execution (!) in slash commands fails with permission error

Resolved 💬 2 comments Opened Nov 14, 2025 by NickMainAtPI Closed Nov 14, 2025

Bug Description

Slash commands that contain inline command execution syntax (!command``) fail with a permission error. The error occurs before the slash command can even execute, making inline command syntax unusable in custom slash commands.

Error Message

Error: Bash command permission check failed for pattern "!`git status --porcelain`": 
This Bash command contains multiple operations. The following part requires approval: 
export CLAUDE_PROJECT_DIR=/path/to/project

Root Cause

The issue occurs in the interaction between two functions:

  1. Command wrapper (An5() in minified code) - Wraps every inline command with export CLAUDE_PROJECT_DIR=<path>; <command>
  2. Permission checker - Sees the wrapped command as "multiple operations" and blocks compound commands containing export

Code flow:

// Inline command extracted from !`git status`
command = "git status --porcelain"

// An5() wraps with export
wrappedCommand = "export CLAUDE_PROJECT_DIR=/path; git status --porcelain"

// Permission checker blocks compound commands with export
if (!permissionCheck(wrappedCommand)) throw Error();  // ❌ Fails here

Reproduction Steps

Step 1: Create a test slash command

Create .claude/commands/test-inline.md:

---
description: Test inline command execution
---

## Context

- Current branch: !`git branch --show-current`
- Git status: !`git status --porcelain`

## Instructions

If you see this text, the bug is fixed!

Step 2: Execute the command

/test-inline

Step 3: Observe the error

The command fails with the permission error shown above. The slash command never executes because the inline commands fail during preprocessing.

Expected Behavior

  1. Inline command !git status`` should be recognized
  2. Command should execute with CLAUDE_PROJECT_DIR set in the environment
  3. Output should be injected into the slash command context
  4. Slash command should continue execution

Actual Behavior

  1. Inline command !git status`` is recognized
  2. Command is wrapped with export CLAUDE_PROJECT_DIR=...;
  3. Permission check sees compound command and blocks it
  4. Execution fails - slash command cannot continue

Proposed Fixes

Fix 1: Use Environment Variables Instead of Export (Recommended)

Instead of wrapping commands with export, pass environment variables via spawn options (like hooks already do):

// Current (broken)
command = `export CLAUDE_PROJECT_DIR=/path; ${command}`
exec(command)

// Proposed (works)
let env = {...process.env, CLAUDE_PROJECT_DIR: projectDir};
spawn(command, [], {env, shell: true})

This is exactly how hooks work and they don't have this issue.

Fix 2: Whitelist Self-Generated Export Pattern

Add an exception in the permission checker for the specific pattern Claude Code generates:

if (command.startsWith('export CLAUDE_PROJECT_DIR=') && 
    command.includes('; ') && 
    isSelfGenerated) {
  return {behavior: "allow"};
}

Fix 3: Skip Export for Read-Only Commands

Only add export wrapper for commands that actually need it (plugins, certain tools).

Impact

Affected features:

  • ❌ All slash commands with inline command execution (!...``)
  • ❌ Dynamic context in slash commands (git status, branch info, etc.)
  • ❌ Any automation relying on inline bash execution

Workaround:
Remove inline commands and have Claude execute them directly in the conversation:

<!-- Instead of this (broken): -->
- Current branch: !`git branch --show-current`

<!-- Use this (works): -->
First, run `git branch --show-current` to get the current branch.

System Information

  • Platform: macOS (also likely affects Linux/Windows)
  • Node Version: v20.18.1
  • Shell: bash

Additional Context

Why This is a Security Improvement

The proposed fix (using spawn environment) is actually more secure than the current approach:

  • Current: Export appears in command string → visible in logs, ps output
  • Fixed: Environment set internally → not visible in command line

Consistency with Existing Code

Hooks and plugins already use spawn environment correctly:

// From hook execution code
let env = {...process.env, CLAUDE_PROJECT_DIR: projectDir};
spawn(command, [], {env, shell: true})

Inline commands should follow the same pattern.

Related Patterns

The inline command patterns that trigger this bug:

  • Single-line: !command` (e.g., !git status`)
  • Multi-line: `!\ncommand\n`

Both patterns are recognized by regex but fail when wrapped with export.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗