stablyai / stablyai/orca

Windows: Claude Code hooks are launched as `powershell -WindowStyle Hidden -EncodedCommand`, a shape Kaspersky blocks — all 11 hooks fail with "Permission denied" on every Orca start

Open
#17,876 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
69.7k
Forks
4.5k
Avg merge
16h 8m
Merged PRs (30d)
451

Description

## Summary

On Windows, Orca installs its 11 Claude Code hooks as

```
C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -EncodedCommand
```

Kaspersky refuses to launch PowerShell in that exact shape (`-WindowStyle Hidden` together with `-EncodedCommand` — a common living-off-the-land malware pattern). Plain PowerShell runs fine. So on a machine running Kaspersky, every hook event in Claude Code fails:

```
Failed with non-blocking status code: /usr/bin/bash: line 1: C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe: Permission denied
```

The decoded payload does nothing that needs PowerShell — it resolves `%USERPROFILE%\.orca\agent-hooks\claude-hook.cmd` and calls it:

```powershell
$ProgressPreference='SilentlyContinue'; $scriptPath = Join-Path $env:USERPROFILE '.orca\agent-hooks\claude-hook.cmd'; if (Test-Path -LiteralPath $scriptPath -PathType Leaf) { & $scriptPath; exit $LASTEXITCODE }; [Console]::In.ReadToEnd() | Out-Null; Write-Output '{}'; exit 0
```

`claude-hook.cmd` already prints `{}`, drains stdin and exits 0 on its own, so the PowerShell wrapper adds only the blocked launch shape.

## Environment

- Orca 1.4.190 (`ORCA_APP_VERSION`), Windows 11 Pro 26200
- 2.1.252 (Claude Code)
- Kaspersky 21.26.4.406, default settings
- Claude Code hook shell: Git Bash (`SHELL=/bin/bash.exe`)

## Reproduction

1. Install Kaspersky with default protection.
2. Launch Orca; open a Claude Code pane.
3. Any hook event (a tool call, a prompt submit, Stop) → the "Permission denied" line above.

Isolating the flag pair, from Git Bash on the same machine:

| invocation | result |
|---|---|
| `powershell.exe -NoProfile -Command "Write-Output ok"` | ok |
| `powershell.exe -NoProfile -WindowStyle Hidden -Command "Write-Output ok"` | ok |
| `powershell.exe -NoProfile -EncodedCommand ` | ok |
| `powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -EncodedCommand ` | **blocked** (the shell itself is then denied `fork`, Kaspersky kills the process tree) |

## Why it cannot be worked around locally

Orca checks whether its hooks are installed by comparing each entry's `command` string verbatim (`(m.hooks ?? []).some(p => p.command === r)`). Replacing the entries with a direct `claude-hook.cmd` call — which works — makes Orca see them as "not installed" and rewrite the PowerShell form on every launch. And Claude Code snapshots hooks at startup, so a cleanup that runs inside the session (e.g. a `SessionStart` hook) is always one session late. The only thing that ends it for a user is a cmd `AutoRun` registry hack that strips the entries before `claude` starts, which is not something to ask users for.

## Suggested fix

Either of these removes the blocked shape entirely with no loss of behaviour:

1. **Write the `.cmd` as the hook command directly**: `"command": "C:/Users//.orca/agent-hooks/claude-hook.cmd"`. Claude Code runs it fine from Git Bash; it already returns `{}` and exit 0. (The "missing file" fallback the PowerShell wrapper provides could be a one-line `.cmd` shim in a stable location.)
2. If a wrapper is kept, avoid `-EncodedCommand` together with `-WindowStyle Hidden` — e.g. `-NoProfile -ExecutionPolicy Bypass -File .ps1`, which Kaspersky allows (Claude Code's own notifier hooks run in exactly that shape on this machine without issue).

Also worth considering: match installed hooks by their target (the `claude-hook.cmd` path) rather than the exact command string, so a user-adjusted launcher isn't silently overwritten.

## Related

This is the third shape of the Windows launcher: #15416 (conhost `--headless` swallowed stdin), #15797 (`args` array ignored → bare `conhost.exe`, closed 2026-08-22). The PowerShell form fixed those but trades them for an AV block.

Contributor guide

Open the contributing guide

Research direction

The issue does not name a repository file or test. Start by locating the Windows Claude Code hook installation code that builds the PowerShell command and performs the verbatim command comparison described in the report; reproduce the Kaspersky-blocked launcher and verify the hooks still run, return `{}`, and are not rewritten on later launches.

Written by the indexing model from the issue text.

Assessment

Tech stack
powershell, typescript
Domain
cli, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.