ShellWhisperer
Natural language → shell commands. 50ms on edge.
A 1.5B parameter edge-native shell agent fine-tuned from Qwen3-1.5B. Converts natural language descriptions into safe, correct shell commands — designed to run on phones and edge devices via ONNX Runtime or llama.cpp GGUF.
Features
- Edge-native: Runs in <50ms on mobile/edge via ONNX or GGUF
- Multi-OS: Linux, macOS, Windows PowerShell prompts
- Safety-first: Built-in guardrails against destructive commands (rm -rf /, fork bombs, pipe-to-shell)
- Context-aware: Uses working directory, OS type, and recent command history
- Multiple backends: HuggingFace Transformers, ONNX Runtime, llama.cpp
- Streaming: WebSocket streaming for real-time output
- Fine-tune your own: LoRA training on Fable5 traces or custom data
Quick Start
Install
pip install shell-whisperer
# With training support:
pip install "shell-whisperer[train]"
# With GGUF inference:
pip install "shell-whisperer[gguf]"
# Everything:
pip install "shell-whisperer[train,gguf,dev]"
One-shot Prediction
# Basic usage
sw "find all python files over 100 lines"
# → find . -name "*.py" -exec wc -l {} + | awk '$1 > 100'
sw "kill the process on port 8080"
# → lsof -ti:8080 | xargs kill -9
sw --os-type macos "install ffmpeg"
# → brew install ffmpeg
sw --os-type windows "show all listening ports"
# → Get-NetTCPConnection -State Listen | Format-Table LocalPort, OwningProcess -AutoSize
Interactive Mode
sw --interactive
sw> find all python files over 100 lines
┌─────────────────────────────────────────────────────────┐
│ find . -name "*.py" -exec wc -l {} + | awk '$1 > 100' │
└─────────────────────────────────────────────────────────┘
42.5ms
sw> !os macos
OS set to: macos
sw> install ffmpeg
┌──────────────────────┐
│ brew install ffmpeg │
└──────────────────────┘
28.1ms
Start API Server
sw --serve --port 8000
# Or specify model:
sw --serve --model ./models/shell-whisperer-merged --port 8000
Fine-Tuning
Prepare Training Data
ShellWhisperer extracts training pairs from Fable5 trace formats:
from shell_whisperer.data_extractor import load_training_data
# Load from JSONL traces (auto-detects format)
pairs = load_training_data("./traces/glint_data.jsonl", fmt="auto")
# Or specify format explicitly
from shell_whisperer.data_extractor import (
extract_bash_from_glint,
extract_bash_from_armand0e,
extract_bash_from_vfable,
)
pairs = extract_bash_from_glint("./traces/glint.jsonl")
pairs = extract_bash_from_armand0e("./traces/armand0e.jsonl")
pairs = extract_bash_from_vfable("./traces/vfable.jsonl")
Supported Trace Formats
Glint — Command traces with shell intent metadata:
{"type": "shell_intent", "intent": "find all python files over 100 lines"}
{"type": "shell_command", "command": "find . -name '*.py' -exec wc -l {} + | awk '$1 > 100'", "shell": "bash", "exit_code": 0}
armand0e — Structured shell session logs:
{"event": "command_executed", "prompt": "show disk usage sorted by size", "command": "du -sh * | sort -rh", "exit_status": 0}
v-Fable — Validated Fable traces with confirmation signals:
{"role": "user", "utterance": "find all json files modified recently"}
{"role": "assistant", "tool_call": {"name": "execute_shell", "arguments": {"command": "find . -name '*.json' -mtime -7"}}, "validation": {"confirmed": true, "exit_code": 0}}
Train with LoRA
# LoRA fine-tune (default)
sw train --data ./traces/data.jsonl --epochs 3
# Full fine-tune
sw train --data ./traces/data.jsonl --full-finetune
# Custom parameters
sw train \
--data ./traces/data.jsonl \
--model-name Qwen/Qwen3-1.5B \
--output-dir ./models/my-shell-whisperer \
--epochs 5 \
--lr 1e-4 \
--batch-size 8 \
--os-type linux
Training in Python
from shell_whisperer.trainer import TrainConfig, train_lora
from shell_whisperer.data_extractor import load_training_data
# Load data
pairs = load_training_data("./traces/data.jsonl", include_builtin=True)
print(f"Training with {len(pairs)} pairs")
# Configure and train
config = TrainConfig(
model_name="Qwen/Qwen3-1.5B",
output_dir="./models/shell-whisperer-lora",
epochs=3,
learning_rate=2e-4,
lora_r=16,
use_4bit=True,
use_unsloth=True,
)
adapter_path = train_lora(config=config, training_pairs=pairs)
# Merge adapter with base model
from shell_whisperer.trainer import merge_and_save
merge_and_save(
adapter_path=adapter_path,
output_dir="./models/shell-whisperer-merged",
)
Export for Edge
# Export to ONNX
sw export --format onnx --model ./models/shell-whisperer-merged
# Export to GGUF (for llama.cpp)
sw export --format gguf --model ./models/shell-whisperer-merged
# 4-bit quantization (smallest, fastest)
sw export --format 4bit --model ./models/shell-whisperer-merged
# 8-bit quantization
sw export --format 8bit --model ./models/shell-whisperer-merged
# Export all formats
sw export --format all --model ./models/shell-whisperer-merged
Memory Estimates
| Format | RAM Required | Latency (edge) |
|---|---|---|
| FP32 | ~6.0 GB | ~200ms |
| FP16 | ~3.0 GB | ~100ms |
| 8-bit | ~1.5 GB | ~60ms |
| 4-bit | ~0.75 GB | ~50ms |
| GGUF Q4_K_M | ~0.84 GB | ~50ms |
Inference
Python API
from shell_whisperer import ShellWhisperer
# Load model
sw = ShellWhisperer(os_type="linux")
sw.load_model("./models/shell-whisperer-merged")
# Predict
result = sw.predict("find all python files over 100 lines")
print(result.command)
# → find . -name "*.py" -exec wc -l {} + | awk '$1 > 100'
# Context-aware prediction
result = sw.predict(
"find config files",
working_directory="/etc",
recent_history=["ls -la", "cd /etc"],
os_type="linux",
)
# Batch prediction
results = sw.predict_batch([
"find all python files over 100 lines",
"kill the process on port 8080",
"show disk usage sorted by size",
])
# Streaming
for token in sw.predict_stream("find all python files"):
print(token, end="", flush=True)
# Safety warnings
result = sw.predict("delete everything")
if result.safety_warnings:
for warning in result.safety_warnings:
print(f"⚠ {warning}")
sw.unload()
REST API
# Start server
sw --serve --port 8000
# Predict
curl -X POST http://localhost:8000/predict \
-H "Content-Type: application/json" \
-d '{"prompt": "find all python files over 100 lines", "os_type": "linux"}'
# Batch predict
curl -X POST http://localhost:8000/predict/batch \
-H "Content-Type: application/json" \
-d '{"prompts": ["find python files", "kill port 8080"]}'
# Health check
curl http://localhost:8000/health
# Model info
curl http://localhost:8000/info
WebSocket Streaming
const ws = new WebSocket("ws://localhost:8000/ws/stream");
ws.onopen = () => {
ws.send(JSON.stringify({
prompt: "find all python files over 100 lines",
os_type: "linux"
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.token) {
process.stdout.write(data.token);
} else if (data.done) {
console.log("\nCommand:", data.command);
if (data.safety_warnings.length) {
console.log("Warnings:", data.safety_warnings);
}
}
};
Example Training Pairs
Built-in high-quality pairs from real-world shell usage:
| Natural Language | Shell Command | Quality |
|---|---|---|
| find all python files over 100 lines | find . -name "*.py" -exec wc -l {} + | awk '$1 > 100' |
0.85 |
| kill the process on port 8080 | lsof -ti:8080 | xargs kill -9 |
0.80 |
| show disk usage sorted by size | du -sh * | sort -rh |
0.75 |
| recursively search for TODO in all python files | grep -rn "TODO" --include="*.py" . |
0.83 |
| rename all .txt files to .md | for f in *.txt; do mv "$f" "${f%.txt}.md"; done |
0.84 |
| remove all stopped docker containers | docker container prune -f |
0.73 |
| show all git commits by the current user this month | git log --author="$(git config user.name)" --since="$(date +%Y-%m-01)" --oneline |
0.88 |
| list all unique IPs that connected via SSH | grep "Accepted" /var/log/auth.log | awk '{print $11}' | sort -u |
0.84 |
Safety System
ShellWhisperer includes a built-in safety layer that:
- Blocks destructive commands:
rm -rf /, fork bombs,ddto disk - Warns on sudo: Flags commands requiring elevated privileges
- Flags pipe-to-shell: Warns about
curl | bashpatterns - Prevents chmod 777: Warns about insecure permissions
result = sw.predict("delete all files")
# Safety warning: ⚠ SAFETY: Destructive: recursive force-delete
Architecture
┌─────────────────────────────────────────┐
│ Natural Language Input │
└──────────────┬──────────────────────────┘
│
┌──────────────▼──────────────────────────┐
│ System Prompt (OS-specific) │
│ LINUX_PROMPT / MACOS_PROMPT / │
│ WINDOWS_PROMPT + Safety Rules │
└──────────────┬──────────────────────────┘
│
┌──────────────▼──────────────────────────┐
│ Qwen3-1.5B (LoRA fine-tuned) │
│ 1.5B parameters │
└──────────────┬──────────────────────────┘
│
┌──────────────▼──────────────────────────┐
│ Output Cleaning │
│ - Strip markdown/backticks │
│ - Remove model prefixes │
│ - Multi-line pipe/chain handling │
└──────────────┬──────────────────────────┘
│
┌──────────────▼──────────────────────────┐
│ Safety Check │
│ - rm -rf protection │
│ - sudo warning │
│ - pipe-to-shell detection │
└──────────────┬──────────────────────────┘
│
┌──────────────▼──────────────────────────┐
│ Shell Command Output │
└───────────────────────────────────────────┘
Project Structure
shell-whisperer/
├── pyproject.toml
├── README.md
├── src/shell_whisperer/
│ ├── __init__.py # Package init + exports
│ ├── prompts.py # OS-specific system prompts + safety rules
│ ├── data_extractor.py # Fable5 trace extraction + quality filtering
│ ├── trainer.py # LoRA/Full fine-tuning on Qwen3-1.5B
│ ├── exporter.py # ONNX, GGUF, 4-bit/8-bit export + memory estimation
│ ├── inference.py # Multi-backend inference (Transformers, ONNX, llama.cpp)
│ ├── server.py # FastAPI server (REST + WebSocket)
│ └── cli.py # CLI: sw predict, train, export, serve
└── tests/
├── test_data_extractor.py
└── test_inference.py
Development
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Lint
ruff check src/ tests/
# Type check
mypy src/
License
MIT
Ecosystem
Part of the FableForge ecosystem — 21 open-source projects built from 210K real agent traces:
| Project | Description |
|---|---|
| Anvil | Self-verified coding agent |
| VerifyLoop | Plan→Execute→Verify→Recover framework |
| ErrorRecovery | Self-healing middleware (3,725 error patterns) |
| FableForge-14B | The fine-tuned 14B model (4-stage training) |
| ShellWhisperer | 1.5B edge agent (phone/RPi, 50ms) |
| ReasonCritic | Verification model (130 benchmark tasks) |
| TraceCompiler | Compile traces → LoRA skills |
| AgentRuntime | Persistent agent daemon (systemd for AI) |
| AgentSwarm | Multi-agent from real trace transitions |
| AgentTelemetry | Datadog for agents (token tracking, costs) |
| BenchAgent | HumanEval for tool-use (107 tasks) |
| AgentDev | VSCode extension with verification |
| TraceViz | Trace replay visualizer (Next.js) |
| AgentSkills | npm for agent behaviors |
| AgentCurriculum | 5-stage progressive training |
| AgentFuzzer | Adversarial testing for agents |
| AgentConstitution | Safety guardrails from traces |
| CostOptimizer | Token cost reduction (50-80%) |
| AgentProfiler | Behavioral fingerprinting |
| TrajectoryDistiller | Trace→training data pipeline |
| Fable5-Dataset | HuggingFace dataset release |
Release files for fableforge-shell-whisperer 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fableforge_shell_whisperer-0.1.0.tar.gz | 35.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fableforge_shell_whisperer-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 69.7 kB
Release files / fableforge_shell_whisperer-0.1.0.tar.gz
| Download URL | fableforge_shell_whisperer-0.1.0.tar.gz |
|---|---|
| Size | 35.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5dc5f9a0a15c2b6bd7a875119281ec3f4b188b18740f7b055eea27a36df58007
|
|
BLAKE2b-256 checksum How to use checksums |
8a9cf1a20a1290e0dfe148995e640f17772374d953b55423cb3d7bd484084c5b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 14, 2026.
Transparency logRelease files / fableforge_shell_whisperer-0.1.0-py3-none-any.whl
| Download URL | fableforge_shell_whisperer-0.1.0-py3-none-any.whl |
|---|---|
| Size | 34.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ba4ca0e9253d579650e821919c7d9682f40fd227faabce7452b19be10e87591a
|
|
BLAKE2b-256 checksum How to use checksums |
9ebe5e1088666852bfe3cce0667091fb80c3154870401179b8b04d16e62fc5bc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 14, 2026.
Transparency log