Shell command translation layer for AI agent IDEs — no external services required.
Project description
██████╗██╗ ██╗███████╗██╗ ██╗ ███████╗ █████╗ ██████╗ ███████╗
██╔════╝██║ ██║██╔════╝██║ ██║ ██╔════╝██╔══██╗██╔════╝ ██╔════╝
╚█████╗ ███████║█████╗ ██║ ██║ ███████╗███████║██║ ███╗█████╗
╚═══██╗██╔══██║██╔══╝ ██║ ██║ ╚════██║██╔══██║██║ ██║██╔══╝
██████╔╝██║ ██║███████╗███████╗███████╗███████║██║ ██║╚██████╔╝███████╗
╚═════╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝
The shell translation layer for AI coding agents
fast rule engine · optional vector memory · MCP server · hooks · local-first · zero token waste
What it does · Quickstart · Setup · Translation Reference · Configuration · CLI · Architecture
ShellSage intercepts Bash-style tool calls made by your AI coding agent (Claude Code, Cursor, Windsurf, Kiro, Cline …) and silently rewrites bash syntax into correct PowerShell/CMD before the shell sees it. It works immediately with a local rule engine and a SQLite memory that learns from your sessions — no external services required.
No API key. No cloud. No Docker. Runs entirely on your machine.
What it does
| Without ShellSage | With ShellSage |
|---|---|
Agent writes ls -la → PowerShell fails → retry loop → 45k wasted tokens |
Agent writes ls -la → silently becomes Get-ChildItem -Force → works ✓ |
| 3 bash failures per session = ~135k wasted tokens | 0 failures · 0 wasted tokens |
| Error traces pollute all future turns | Errors never reach the LLM context |
How it translates:
- Rule-based translation — 100+ regex patterns covering common bash constructs. Instant, zero DB dependency.
- SQLite memory — BM25-style lookup over 400+ curated seed translations plus anything learned from your own sessions. Stored locally in
~/.shellsage/memory.db. - Passthrough — if no translation is needed (native PowerShell, git, docker), the command passes through unchanged.
Quickstart
# 1. Install
pip install "shellsage-mcp[mcp]"
# 2. One-command setup (detects your IDE automatically)
shellsage setup
The setup wizard:
- detects which IDE/agent you have (Claude Code, Cursor, Windsurf)
- seeds the local SQLite database with 400+ curated translations
- starts the background MCP server
- registers the MCP server with your IDE
- optionally installs Claude Code hooks for transparent pre-execution translation
If you have multiple IDEs installed it will ask which to configure.
Setup Guides
shellsage setup handles everything automatically. The manual steps below are for reference or scripted environments.
Claude Code (recommended — hooks + MCP)
Claude Code supports hooks (silently rewrite before execution) and MCP (tools the model can call).
Option A — automatic (recommended)
shellsage setup
Option B — manual
# 1. Start the background server
shellsage start
# 2. Register the MCP server
claude mcp add --transport sse shellsage http://127.0.0.1:7842/sse
# 3. Install project hooks (run inside your project directory)
shellsage hooks install
shellsage hooks install creates .claude/hooks/pre_tool_use.py and post_tool_use.py and prints the settings snippet to add to .claude/settings.json.
What each hook does:
pre_tool_use.py— translates the command before execution; caches original→translated to a temp filepost_tool_use.py— reads the cache and records the outcome to local SQLite memory
Cursor
shellsage setup # auto-writes ~/.cursor/mcp.json
Or add manually to ~/.cursor/mcp.json:
{
"mcpServers": {
"shellsage": {
"url": "http://127.0.0.1:7842/sse"
}
}
}
Start the server first: shellsage start
Windsurf
shellsage setup # auto-writes ~/.codeium/windsurf/mcp_config.json
Or add manually to ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"shellsage": {
"serverUrl": "http://127.0.0.1:7842/sse"
}
}
}
Start the server first: shellsage start
Other IDEs (stdio transport)
For any MCP-compatible IDE that supports stdio transport:
{
"mcpServers": {
"shellsage": {
"command": "shellsage",
"args": ["mcp"]
}
}
}
Command Translation Reference
The rule engine handles these commands immediately. If vector memory is installed, shellsage init loads a limited curated seed set by default; use shellsage init --all to load the complete corpus.
File Listing
| bash | PowerShell |
|---|---|
ls |
Get-ChildItem |
ls -la |
Get-ChildItem -Force |
ls -l |
Get-ChildItem | Format-List |
ls -R |
Get-ChildItem -Recurse |
ls *.py |
Get-ChildItem *.py |
ls -la src/ |
Get-ChildItem -Force 'src/' |
ls ~ |
Get-ChildItem $HOME |
Find / Locate
| bash | PowerShell |
|---|---|
find . -name '*.py' |
Get-ChildItem -Recurse -Filter '*.py' |
find . -type f |
Get-ChildItem -Recurse -File |
find . -type d |
Get-ChildItem -Recurse -Directory |
find . -type f -name '*.log' |
Get-ChildItem -Recurse -File -Filter '*.log' |
find src/ -name '*.py' |
Get-ChildItem -Path 'src/' -Recurse -Filter '*.py' |
find . -mtime -7 |
Get-ChildItem -Recurse | Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-7) } |
find . -size +1M |
Get-ChildItem -Recurse | Where-Object { $_.Length -gt 1MB } |
find . -name '*.tmp' -delete |
Get-ChildItem -Recurse -Filter '*.tmp' | Remove-Item -Force |
find . -name '*.pyc' -delete |
Get-ChildItem -Recurse -Filter '*.pyc' | Remove-Item -Force |
Grep / Search
| bash | PowerShell |
|---|---|
grep 'error' app.log |
Select-String -Pattern 'error' -Path 'app.log' |
grep -r 'TODO' . |
Get-ChildItem -Recurse | Select-String -Pattern 'TODO' |
grep -rn 'import' src/ |
Get-ChildItem -Recurse 'src/' | Select-String -Pattern 'import' |
grep -i 'error' app.log |
Select-String -Pattern 'error' -Path 'app.log' -CaseSensitive:$false |
grep -v 'debug' app.log |
Get-Content 'app.log' | Where-Object { $_ -notmatch 'debug' } |
grep -c 'error' app.log |
(Select-String -Pattern 'error' -Path 'app.log').Count |
grep -l 'TODO' *.py |
Select-String -Pattern 'TODO' -Path '*.py' | Select-Object -ExpandProperty Path -Unique |
grep 'error' *.log |
Select-String -Pattern 'error' -Path '*.log' |
grep -r 'password' . --include='*.py' |
Get-ChildItem -Recurse -Filter '*.py' | Select-String -Pattern 'password' |
View Files
| bash | PowerShell |
|---|---|
cat README.md |
Get-Content 'README.md' |
cat file1.txt file2.txt |
Get-Content 'file1.txt', 'file2.txt' |
head -n 20 file.txt |
Get-Content 'file.txt' -TotalCount 20 |
tail -n 50 app.log |
Get-Content 'app.log' -Tail 50 |
tail -f server.log |
Get-Content -Wait 'server.log' |
File Management
| bash | PowerShell |
|---|---|
mkdir -p src/utils |
New-Item -ItemType Directory -Force -Path 'src/utils' |
rm -rf node_modules |
Remove-Item -Recurse -Force 'node_modules' |
rm -rf dist/ |
Remove-Item -Recurse -Force 'dist/' |
rm -f output.log |
Remove-Item -Force 'output.log' |
cp -r src/ backup/ |
Copy-Item -Recurse 'src/' 'backup/' |
cp config.json config.json.bak |
Copy-Item 'config.json' 'config.json.bak' |
mv old.txt new.txt |
Move-Item 'old.txt' 'new.txt' |
touch .gitkeep |
New-Item -ItemType File -Force '.gitkeep' |
ln -s src dest |
New-Item -ItemType SymbolicLink -Name 'dest' -Target 'src' |
Text Processing
| bash | PowerShell |
|---|---|
wc -l file.txt |
(Get-Content 'file.txt').Count |
sort file.txt |
Get-Content 'file.txt' | Sort-Object |
sort -u file.txt |
Get-Content 'file.txt' | Sort-Object -Unique |
sort -r file.txt |
Get-Content 'file.txt' | Sort-Object -Descending |
sort file.txt | uniq -c |
Get-Content 'file.txt' | Group-Object | Select-Object Count, Name |
sed -i 's/foo/bar/g' file.txt |
(Get-Content 'file.txt') -replace 'foo','bar' | Set-Content 'file.txt' |
sed '/^#/d' file.txt |
Get-Content 'file.txt' | Where-Object { $_ -notmatch '^#' } |
awk '{print $1}' file.txt |
Get-Content 'file.txt' | ForEach-Object { ($_ -split '\s+')[0] } |
Echo / Redirect
| bash | PowerShell |
|---|---|
echo 'hello world' |
Write-Output 'hello world' |
echo $PATH |
$env:PATH |
echo $HOME |
$env:USERPROFILE |
echo 'line' > file.txt |
Set-Content 'file.txt' 'line' |
echo 'line' >> file.txt |
Add-Content 'file.txt' 'line' |
Environment Variables
| bash | PowerShell |
|---|---|
export NODE_ENV=production |
$env:NODE_ENV = 'production' |
export PORT=3000 |
$env:PORT = '3000' |
export DATABASE_URL=postgres://localhost/db |
$env:DATABASE_URL = 'postgres://localhost/db' |
unset NODE_ENV |
Remove-Item Env:\NODE_ENV |
env |
Get-ChildItem Env: |
printenv PATH |
$env:PATH |
Process Management
| bash | PowerShell |
|---|---|
ps aux |
Get-Process |
ps aux | grep node |
Get-Process | Where-Object { $_.Name -match 'node' } |
pgrep python |
Get-Process -Name '*python*' |
pkill node |
Stop-Process -Name 'node' -Force |
kill -9 1234 |
Stop-Process -Id 1234 -Force |
killall python |
Stop-Process -Name 'python' -Force |
sleep 5 |
Start-Sleep 5 |
nohup python app.py & |
Start-Process -NoNewWindow python -ArgumentList 'app.py' -RedirectStandardOutput 'nohup.out' |
Network
| bash | PowerShell |
|---|---|
curl https://example.com |
Invoke-WebRequest -Uri 'https://example.com' |
curl -s https://api.github.com |
Invoke-RestMethod 'https://api.github.com' |
curl -o file.zip https://example.com/a.zip |
Invoke-WebRequest -Uri 'https://example.com/a.zip' -OutFile 'file.zip' |
curl -X POST URL -d '{"k":"v"}' |
Invoke-RestMethod -Method POST -Uri URL -Body '{"k":"v"}' -ContentType 'application/json' |
curl -H 'Authorization: Bearer TOKEN' URL |
Invoke-RestMethod -Uri URL -Headers @{ Authorization = 'Bearer TOKEN' } |
wget https://example.com/file.zip |
Invoke-WebRequest -Uri 'https://example.com/file.zip' -OutFile 'file.zip' |
ping google.com |
Test-Connection -ComputerName 'google.com' |
ping -c 4 google.com |
Test-Connection -ComputerName 'google.com' -Count 4 |
netstat -tulpn |
Get-NetTCPConnection | Where-Object { $_.State -eq 'Listen' } |
nslookup google.com |
Resolve-DnsName 'google.com' |
Archive / Compression
| bash | PowerShell |
|---|---|
tar -czf archive.tar.gz dist/ |
Compress-Archive -Path 'dist/' -DestinationPath 'archive.zip' |
tar -xzf archive.tar.gz |
Expand-Archive -Path 'archive.zip' -DestinationPath '.' |
tar -xzf archive.tar.gz -C out/ |
Expand-Archive -Path 'archive.zip' -DestinationPath 'out/' |
zip -r archive.zip src/ |
Compress-Archive -Path 'src/' -DestinationPath 'archive.zip' |
unzip archive.zip -d output/ |
Expand-Archive -Path 'archive.zip' -DestinationPath 'output/' |
Disk / System Info
| bash | PowerShell |
|---|---|
df -h |
Get-PSDrive -PSProvider FileSystem |
du -sh . |
(Get-ChildItem -Recurse | Measure-Object -Property Length -Sum).Sum / 1MB |
du -sh node_modules/ |
(Get-ChildItem -Recurse 'node_modules/' | Measure-Object -Property Length -Sum).Sum / 1MB |
uname -a |
Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion |
hostname |
$env:COMPUTERNAME |
whoami |
$env:USERNAME |
date |
Get-Date |
date '+%Y-%m-%d' |
Get-Date -Format 'yyyy-MM-dd' |
uptime |
(Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime |
Permissions
| bash | PowerShell |
|---|---|
chmod +x script.sh |
# Rename to script.ps1 or use Set-ExecutionPolicy |
chmod 755 dir/ |
# Use icacls for Windows ACL management |
chown user file |
# Use icacls: icacls 'file' /setowner 'user' |
sudo command |
# Run PowerShell as Administrator, then: command |
Python
| bash | PowerShell |
|---|---|
python3 script.py |
python script.py |
python3 -m pytest |
python -m pytest |
python3 -m pytest -v |
python -m pytest -v |
python3 -m pip install -r requirements.txt |
python -m pip install -r requirements.txt |
python3 -m pip install -e . |
python -m pip install -e . |
python3 -m venv .venv |
python -m venv .venv |
source .venv/bin/activate |
.venv\Scripts\Activate.ps1 |
python3 -m pip freeze > requirements.txt |
python -m pip freeze | Set-Content 'requirements.txt' |
which python3 |
(Get-Command python).Source |
Node.js / npm
| bash | PowerShell |
|---|---|
npm install |
npm install |
npm install package |
npm install package |
npm run build |
npm run build |
npm run dev |
npm run dev |
npm test |
npm test |
npx tsc |
npx tsc |
yarn install |
yarn install |
yarn add package |
yarn add package |
Docker
| bash | PowerShell |
|---|---|
docker ps -a |
docker ps -a |
docker build -t myapp . |
docker build -t myapp . |
docker run -d -p 8080:8080 myapp |
docker run -d -p 8080:8080 myapp |
docker exec -it mycontainer bash |
docker exec -it mycontainer bash |
docker logs -f mycontainer |
docker logs -f mycontainer |
docker-compose up -d |
docker-compose up -d |
docker-compose down |
docker-compose down |
docker system prune -f |
docker system prune -f |
Git
Git commands are identical on all platforms — ShellSage passes them through unchanged.
| bash / PowerShell |
|---|
git init · git clone URL · git status · git add . |
git commit -m 'message' · git push origin main · git pull |
git checkout -b feature/name · git merge branch · git rebase main |
git log --oneline -10 · git diff --stat · git stash |
Directory Navigation
| bash | PowerShell |
|---|---|
pwd |
Get-Location |
cd src/ |
Set-Location 'src/' |
cd .. |
Set-Location .. |
cd ~ |
Set-Location $HOME |
pushd src/ |
Push-Location 'src/' |
popd |
Pop-Location |
Pipes and Redirects
| bash | PowerShell |
|---|---|
ls | grep '.py' |
Get-ChildItem | Where-Object { $_.Name -match '\.py' } |
ls | wc -l |
(Get-ChildItem).Count |
cat file.txt | sort | uniq |
Get-Content 'file.txt' | Sort-Object -Unique |
find . -name '*.py' | xargs grep 'import' |
Get-ChildItem -Recurse -Filter '*.py' | Select-String -Pattern 'import' |
command > /dev/null 2>&1 |
command > $null 2>&1 |
Configuration
All settings can be overridden via environment variables:
| Variable | Default | Description |
|---|---|---|
SHELLSAGE_DB_PATH |
~/.shellsage/memory.db |
SQLite database path |
SHELLSAGE_PORT |
7842 |
Background MCP server port |
SHELLSAGE_HOST |
127.0.0.1 |
Background MCP server host |
SHELLSAGE_SCORE_THRESHOLD |
0.1 |
Minimum score to accept a stored-translation hit |
SHELLSAGE_SEED_LIMIT |
75 |
Number of seed examples loaded by shellsage init |
SHELLSAGE_SEED_CONFIDENCE |
0.95 |
Confidence assigned to seed translations |
SHELLSAGE_OUTCOME_CONFIDENCE |
0.99 |
Confidence assigned when a command succeeds in practice |
Example — custom port:
export SHELLSAGE_PORT=8888
shellsage setup --port 8888
MCP Tools
The MCP server exposes 4 tools that your AI agent can call directly:
| Tool | Description |
|---|---|
translate_command(command, project_root) |
Translate a bash command for the current shell |
store_command_result(original, translated, shell, os_name, project_type, exit_code, error_snippet) |
Record command outcome when vector memory is installed |
get_shell_context(project_root) |
Return detected OS/shell/project environment |
get_stats() |
Health check — return Qdrant collection counts |
CLI Reference
shellsage setup # Interactive one-command install wizard (auto-detects IDE)
shellsage setup --port 8888 # Wizard with custom port
shellsage init # Seed the local SQLite DB (75 examples by default)
shellsage init --all # Load the complete 400+ seed corpus
shellsage translate "ls -la" # Translate a single command
shellsage translate "ls -la" --json-out # Machine-readable output
shellsage stats # Show local DB counts
shellsage replay # Show recent failure patterns
shellsage start # Start background MCP server (HTTP/SSE)
shellsage stop # Stop background MCP server
shellsage status # Show daemon and DB status
shellsage mcp # Start MCP server in foreground (stdio)
shellsage mcp --http # Start MCP server in foreground (HTTP/SSE)
shellsage hooks install # Write pre/post hook scripts to .claude/hooks/
shellsage --version # Print version
Architecture
Claude Code / Cursor / Windsurf / Kiro / Cline
│ bash command
▼
┌───────────────────────────────────────────────┐
│ PreToolUse Hook (.claude/hooks/pre_*.py) │ ← Claude Code only
│ ───────────────────────────────────────── │
│ 1. Rule-based translation (100+ patterns) │
│ 2. SQLite hybrid search (BM25 + learned) │
│ 3. Passthrough if no match │
│ Caches: original → translated (temp file) │
└───────────────────────────────────────────────┘
│ corrected PowerShell command
▼
[Shell execution]
│
▼
┌───────────────────────────────────────────────┐
│ PostToolUse Hook (.claude/hooks/post_*.py) │ ← Claude Code only
│ Reads cache, records outcome to SQLite │
│ Success → upsert translation (conf=0.99) │
│ Failure → upsert failure pattern │
└───────────────────────────────────────────────┘
│
▼
SQLite (~/.shellsage/memory.db) — always local, zero config
├─ translations (400+ seeds + session-learned)
└─ failures (error patterns for replay)
─────────────────────────────────────────────────
MCP server (http://127.0.0.1:7842/sse)
├─ translate_command → rules + SQLite lookup
├─ store_command_result → write back to SQLite
├─ get_shell_context → OS / shell / project detection
└─ get_stats → health check
Module map:
| Module | Role |
|---|---|
config.py |
Env-var-backed settings (single source of truth) |
models.py |
ShellContext, Translation, CommandOutcome — zero deps |
rules.py |
100+ regex patterns (instant, no DB needed) |
seed.py |
400+ curated bash→PS pairs; init loads a bounded set by default |
store.py |
SQLite: translations + failures, BM25-style lookup |
translator.py |
2-tier resolution: rules → SQLite lookup → passthrough |
server.py |
FastMCP server (4 tools, stdio or HTTP/SSE) |
daemon.py |
Background process management (start / stop / status) |
setup_wizard.py |
Interactive installer with IDE auto-detection |
cli.py |
Click CLI (10 commands) |
Development
git clone https://github.com/shellsage/shellsage.git
cd shellsage
pip install -e ".[mcp,dev]"
# Run tests
pytest
# Lint
ruff check shellsage/ tests/
ruff format shellsage/ tests/
# Type check
mypy shellsage/
# Validate seed data
python -c "from shellsage.seed import SEED_TRANSLATIONS; print(len(SEED_TRANSLATIONS))"
# Test a translation (no Qdrant needed)
shellsage translate "find . -name '*.py'"
See CONTRIBUTING.md for full contribution guidelines.
License
MIT — see LICENSE.
Security
Please report vulnerabilities to security@shellsage.dev — see SECURITY.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shellsage_mcp-0.3.1.tar.gz.
File metadata
- Download URL: shellsage_mcp-0.3.1.tar.gz
- Upload date:
- Size: 56.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
40890463de0f5aacf4814fa655a7341ccab98a995be8c53bac75c23142819346
|
|
| MD5 |
79ac618ec59d8957b08efb1b9cd50708
|
|
| BLAKE2b-256 |
f648e678d0260eef2d08dd66d589036175b9cbe397eb42237fc61dcff7a9ab4c
|
File details
Details for the file shellsage_mcp-0.3.1-py3-none-any.whl.
File metadata
- Download URL: shellsage_mcp-0.3.1-py3-none-any.whl
- Upload date:
- Size: 48.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
01c86b49661001b7cbeca87837b83d9d0a8c7793f55c8417bfed29abf6915e2f
|
|
| MD5 |
f6b130f6dfe4236db0a95bb0ecbd3b63
|
|
| BLAKE2b-256 |
6062fcc6fc56f0eb7fe0f6b211b6346d75bd65f572955b933430c23534fc7bdd
|