Text Generation
Transformers
Safetensors
GGUF
English
qwen2
bash
shell
cli
terminal
nl2bash
code-generation
small-language-model
conversational
text-generation-inference
Instructions to use sahellx/kitty-bash-llm with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use sahellx/kitty-bash-llm with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="sahellx/kitty-bash-llm") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("sahellx/kitty-bash-llm") model = AutoModelForCausalLM.from_pretrained("sahellx/kitty-bash-llm", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=40) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use sahellx/kitty-bash-llm with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf sahellx/kitty-bash-llm:Q4_K_M # Run inference directly in the terminal: llama cli -hf sahellx/kitty-bash-llm:Q4_K_M
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf sahellx/kitty-bash-llm:Q4_K_M # Run inference directly in the terminal: llama cli -hf sahellx/kitty-bash-llm:Q4_K_M
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf sahellx/kitty-bash-llm:Q4_K_M # Run inference directly in the terminal: ./llama-cli -hf sahellx/kitty-bash-llm:Q4_K_M
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf sahellx/kitty-bash-llm:Q4_K_M # Run inference directly in the terminal: ./build/bin/llama-cli -hf sahellx/kitty-bash-llm:Q4_K_M
Use Docker
docker model run hf.co/sahellx/kitty-bash-llm:Q4_K_M
- LM Studio
- Jan
- vLLM
How to use sahellx/kitty-bash-llm with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "sahellx/kitty-bash-llm" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "sahellx/kitty-bash-llm", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/sahellx/kitty-bash-llm:Q4_K_M
- SGLang
How to use sahellx/kitty-bash-llm with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "sahellx/kitty-bash-llm" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "sahellx/kitty-bash-llm", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "sahellx/kitty-bash-llm" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "sahellx/kitty-bash-llm", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Ollama
How to use sahellx/kitty-bash-llm with Ollama:
ollama run hf.co/sahellx/kitty-bash-llm:Q4_K_M
- Unsloth Desktop
- Pi
How to use sahellx/kitty-bash-llm with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf sahellx/kitty-bash-llm:Q4_K_M
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "sahellx/kitty-bash-llm:Q4_K_M" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Docker Model Runner
How to use sahellx/kitty-bash-llm with Docker Model Runner:
docker model run hf.co/sahellx/kitty-bash-llm:Q4_K_M
- Lemonade
How to use sahellx/kitty-bash-llm with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull sahellx/kitty-bash-llm:Q4_K_M
Run and chat with the model
lemonade run user.kitty-bash-llm-Q4_K_M
List all available models
lemonade list
- Hermes Agent
How to use sahellx/kitty-bash-llm with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf sahellx/kitty-bash-llm:Q4_K_M
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default sahellx/kitty-bash-llm:Q4_K_M
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use sahellx/kitty-bash-llm with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf sahellx/kitty-bash-llm:Q4_K_M
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "sahellx/kitty-bash-llm:Q4_K_M" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
| license: apache-2.0 | |
| base_model: Qwen/Qwen2.5-Coder-0.5B-Instruct | |
| library_name: transformers | |
| pipeline_tag: text-generation | |
| language: | |
| - en | |
| tags: | |
| - bash | |
| - shell | |
| - cli | |
| - terminal | |
| - nl2bash | |
| - code-generation | |
| - gguf | |
| - qwen2 | |
| - small-language-model | |
| # 🐱 Kitty Bash LLM | |
| **A 0.5B parameter shell assistant that runs on a laptop CPU.** | |
| Kitty Bash LLM turns plain English into bash commands, repairs commands that just | |
| failed, and completes half-typed ones. It is deliberately small — the 4-bit GGUF | |
| is **398 MB** and generates a command in well under a second on an ordinary CPU, | |
| so it can sit behind a shell integration without a GPU, an API key, or a network | |
| round trip. | |
| It is a **specialist, not a chatbot.** Ask it the capital of France and you will get | |
| nonsense. Ask it to find every file over 100 MB and it will tell you. | |
| --- | |
| ## What it does | |
| Three behaviours, selected by the system prompt. One model, one file, flat memory. | |
| | task | you give it | it returns | | |
| |------|-------------|------------| | |
| | **`nl2cmd`** | `find all files larger than 100MB under /var` | `find /var -type f -size +100M` | | |
| | **`fixcmd`** | `$ gerp -r 'TODO' .`<br>`bash: gerp: command not found` | `grep -r 'TODO' .` | | |
| | **`complete`** | `tar -czf backup.` | `$(date +%F).tar.gz /path/to/dir` | | |
| ### System prompts | |
| ```python | |
| NL2CMD = ("You are a bash command generator. Given a natural language request, " | |
| "output only the bash command that accomplishes it. " | |
| "No explanation, no markdown fences.") | |
| FIXCMD = ("You fix broken bash commands. Given a failed command and its error " | |
| "output, output only the corrected command. No explanation.") | |
| COMPLETE = ("You complete partially typed bash commands. Given a command prefix, " | |
| "output only the text that completes it. No explanation.") | |
| ``` | |
| --- | |
| ## Quick start | |
| ### llama.cpp / GGUF (recommended — this is what it was built for) | |
| ```bash | |
| llama-cli -m kitty-bash-llm-q4_k_m.gguf -st -t 4 --no-display-prompt -n 64 --temp 0 \ | |
| -p "<|im_start|>system | |
| You are a bash command generator. Given a natural language request, output only the bash command that accomplishes it. No explanation, no markdown fences.<|im_end|> | |
| <|im_start|>user | |
| show which process is listening on port 8080<|im_end|> | |
| <|im_start|>assistant | |
| " | |
| # -> lsof -i :8080 | |
| ``` | |
| For anything interactive, run a **persistent server** so the system prompt is | |
| prefilled once (see *Performance* — this is the single most important | |
| implementation detail): | |
| ```bash | |
| llama-server -m kitty-bash-llm-q4_k_m.gguf -c 1024 -t 4 --port 8080 | |
| ``` | |
| ### transformers | |
| ```python | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| model = AutoModelForCausalLM.from_pretrained("sahellx/kitty-bash-llm") | |
| tok = AutoTokenizer.from_pretrained("sahellx/kitty-bash-llm") | |
| SYSTEM = ("You are a bash command generator. Given a natural language request, " | |
| "output only the bash command that accomplishes it. " | |
| "No explanation, no markdown fences.") | |
| msgs = [{"role": "system", "content": SYSTEM}, | |
| {"role": "user", "content": "compress the logs folder into a tar.gz archive"}] | |
| ids = tok.apply_chat_template(msgs, add_generation_prompt=True, return_tensors="pt") | |
| out = model.generate(ids, max_new_tokens=64, do_sample=False) | |
| print(tok.decode(out[0][ids.shape[1]:], skip_special_tokens=True)) | |
| # -> tar -czvf logs.tar.gz logs/ | |
| ``` | |
| Greedy decoding (`do_sample=False` / `--temp 0`) is recommended. You want the | |
| most likely command, not a creative one. | |
| --- | |
| ## Evaluation | |
| Measured against the **untuned base model** on a held-out split, with identical | |
| prompts, identical f16 GGUF format, and the same llama.cpp engine — the only | |
| variable is the fine-tuning. 100 examples per task. | |
| ### `nl2cmd` | |
| | metric | base | **Kitty** | Δ | | |
| |---|---|---|---| | |
| | exact match | 7.00 | **23.00** | **+16.00** (3.3×) | | |
| | first-utility accuracy | 61.00 | **79.00** | **+18.00** | | |
| | token F1 | 39.35 | **59.77** | +20.42 | | |
| | `bash -n` valid | 93.00 | **94.00** | +1.00 | | |
| | ShellCheck clean | 77.00 | **82.00** | +5.00 | | |
| ### `fixcmd` | |
| | metric | base | **Kitty** | Δ | | |
| |---|---|---|---| | |
| | exact match | 0.00 | **82.00** | +82.00 | | |
| | first-utility accuracy | 1.00 | **99.00** | +98.00 | | |
| | token F1 | 45.20 | **96.71** | +51.51 | | |
| | ShellCheck clean | 63.00 | **83.00** | +20.00 | | |
| > ⚠️ **Read this number sceptically.** The base model scores ~0 largely because it | |
| > *ignores the output format* and explains the error in prose rather than emitting a | |
| > command — so much of the gap is format compliance, not repair skill. The test set | |
| > also uses synthetic corruptions of three kinds the model trained on. On **real** | |
| > shell errors, hands-on testing put it closer to **1-in-3**. Treat 82% as an | |
| > upper bound. | |
| ### `complete` | |
| | metric | base | **Kitty** | Δ | | |
| |---|---|---|---| | |
| | token F1 | 2.22 | **29.88** | **+27.66** (13×) | | |
| | exact match | 0.00 | 7.00 | +7.00 | | |
| | prefix+completion valid | — | **99.29** | — | | |
| > Exact match is a poor metric here — many completions are valid for any prefix. | |
| > `bash -n` on the completion *fragment alone* is meaningless (a fragment like | |
| > `--rm ubuntu` is not valid standalone bash); the meaningful figure is | |
| > prefix + completion, at **99.29%**. | |
| ### Comparison with other shell-focused models | |
| Alongside the base-model comparison above, an **informal** benchmark was run against | |
| four other publicly available fine-tunes for shell/linux command generation, all at | |
| Q4_K_M (except where a repo ships only f16), through llama.cpp on the same 2-thread | |
| CPU, using the same 80 held-out prompts. | |
| | model | params | size | utility acc | token F1 | s/cmd | | |
| |---|---|---|---|---|---| | |
| | **Kitty Bash LLM** | **0.5B** | **398 MB** | **78.75** | **59.54** | **1.68** | | |
| | `louisguthmann/qwen3.5-2b-shellcommand-linux` | 2.0B | 1274 MB | 63.75 | 32.02 | 15.39 | | |
| | `vitali87/shell-commands-qwen2-1.5b-extended` | 1.5B | 3094 MB | 57.50 | 33.93 | 9.48 | | |
| | `mecha-org/linux-command-generator-llama3.2-1b` | 1.0B | 808 MB | 52.50 | 34.21 | 2.78 | | |
| | `chamibuddhika/linux-commands-0407-00` | — | 1709 MB | — | — | — | | |
| > #### ⚠️ Please read this before citing the table | |
| > | |
| > **This is indicative, not a controlled evaluation.** Four specific caveats, all of | |
| > which favour this model: | |
| > | |
| > 1. **The test set is drawn from this model's own training distribution.** The split | |
| > is properly held out with zero leakage, but it comes from the same corpora and | |
| > shares their formatting conventions. The other models never saw that style. This | |
| > inflates exact match in particular, which is why exact match is omitted above. | |
| > 2. **All models received *this* model's system prompt.** Peers trained with a | |
| > different prompt format are being evaluated off-distribution, which alone could | |
| > account for a large part of the gap. | |
| > 3. **n = 80.** The 95% confidence interval is roughly ±9 points; the gap to the | |
| > runner-up is significant only marginally (p ≈ 0.03). | |
| > 4. **`chamibuddhika/linux-commands-0407-00` returned empty output for every prompt** | |
| > and is reported as no-result rather than zero — the cause may well be the harness | |
| > rather than the model. `qwen3.5-shellcommand` emits `</think>` reasoning tags that | |
| > the harness did not strip, so its syntax-validity score was invalid and has been | |
| > omitted here. | |
| > | |
| > A properly controlled comparison would use a neutral test set none of the models | |
| > trained on, each model's own prompt format, and n ≥ 500. Treat the ordering above as | |
| > a hint worth verifying, not a settled result. | |
| > | |
| > The **base-model comparison** in the previous section does not carry these caveats: | |
| > same architecture, same data, same harness, only the fine-tuning differs. | |
| ShellCheck is used because `bash -n` only proves a command *parses*. ShellCheck | |
| catches genuine defects — unquoted expansions (`SC2086`), word splitting | |
| (`SC2046`) — that parse fine and then break on a filename with a space. | |
| --- | |
| ## Training | |
| | | | | |
| |---|---| | |
| | base | [`Qwen/Qwen2.5-Coder-0.5B-Instruct`](https://huggingface.co/Qwen/Qwen2.5-Coder-0.5B-Instruct) | | |
| | method | LoRA `r=64`, `alpha=64`, all attention + MLP projections, merged | | |
| | precision | fp16 (Tesla T4, no bf16) | | |
| | epochs / lr | 1 / `2e-4` cosine, 3% warmup | | |
| | batch | 16 × 2 grad-accum = 32 effective | | |
| | max seq len | 512 | | |
| | loss | response-only (masked so loss lands on the command, never the prompt) | | |
| | runtime | 41 min on a single T4 | | |
| | final train / eval loss | 0.5579 / **0.5919** | | |
| The base model was **chosen by measurement, not assumption**: a bake-off against | |
| `Qwen2.5-0.5B-Instruct` under an identical recipe gave 84.98 vs 84.21 utility | |
| accuracy, so the code-pretrained variant won. | |
| --- | |
| ## Dataset | |
| ~60,000 examples across the three tasks, pooled from seven sources, deduplicated, | |
| rebalanced, and decontaminated. | |
| | split | nl2cmd | fixcmd | complete | total | | |
| |---|---|---|---|---| | |
| | train | 22,573 | 19,891 | 17,528 | **59,992** | | |
| | val | 1,254 | 1,238 | 1,456 | 3,948 | | |
| | test | 1,254 | 1,219 | 1,409 | 3,882 | | |
| **Sources:** [`AnishJoshi/nl2bash-custom`](https://huggingface.co/datasets/AnishJoshi/nl2bash-custom), | |
| [`neulab/tldr`](https://huggingface.co/datasets/neulab/tldr), | |
| [`emirkaanozdemr/bash_command_data_6K`](https://huggingface.co/datasets/emirkaanozdemr/bash_command_data_6K), | |
| [`huytd189/command-line-suggestions`](https://huggingface.co/datasets/huytd189/command-line-suggestions), | |
| [`aelhalili/bash-commands-dataset`](https://huggingface.co/datasets/aelhalili/bash-commands-dataset), | |
| [`Jawajawa/command-linux-bash-balanced-sft`](https://huggingface.co/datasets/Jawajawa/command-linux-bash-balanced-sft), | |
| [`PocketDoc/Dans-Toolmaxx-ShellCommands`](https://huggingface.co/datasets/PocketDoc/Dans-Toolmaxx-ShellCommands). | |
| ### Decontamination — please read this if you benchmark on nl2bash | |
| The most widely used NL2Bash split on the Hub **leaks badly**. Measured directly: | |
| ``` | |
| test prompts also present in train : 71.0 % | |
| dev prompts also present in train : 71.8 % | |
| duplicate rows within train : 39.5 % (19,658 rows → 11,890 unique) | |
| ``` | |
| Training and evaluating on those splits measures memorisation. Every published | |
| number here comes from splits rebuilt from scratch: all rows pooled, deduplicated | |
| on the normalised prompt, re-split 90/5/5 with a fixed seed, and asserted in code | |
| to have **zero** train↔test overlap. | |
| Two further corrections: | |
| - **`find` rebalanced 54% → 30%.** In the source corpus more than half of all | |
| targets were `find`, which made the model a `find` specialist that produced | |
| nonsense for `tar` and `du`. Capping it fixed both. | |
| - **Gold targets filtered through `bash -n`.** Broken targets teach broken bash. | |
| - **`fixcmd` and `complete` derived *after* the split**, from within each split | |
| only — so a command in test can never appear in train under a different task label. | |
| --- | |
| ## Limitations | |
| Findings from hands-on testing, not guesses. | |
| **Weak on multi-step logic.** It picks the right tool but can get the reasoning wrong: | |
| ``` | |
| "print the 3rd column of a csv where the 1st column equals ERROR" | |
| → cut -d',' -f3 data.csv | grep ERROR ✗ filters the wrong column | |
| correct: awk -F, '$1=="ERROR" {print $3}' | |
| "show the 10 biggest directories under /home sorted by size" | |
| → find /home -type d | sort -n -r | head -10 ✗ sorts names, not sizes | |
| correct: du -h /home/* | sort -rh | head -10 | |
| ``` | |
| **Uneven across utilities.** Strong on `find`, `tar`, `docker`, `sed`, `grep`, `lsof`. | |
| Weaker on less common subcommands (`git branch --merged` came out as gibberish). | |
| **`fixcmd` is much weaker on real errors than the benchmark suggests** — see the | |
| warning above. | |
| **`complete` may duplicate a prefix boundary.** Given `docker run -it --` it can | |
| return `--rm ...`. **Strip the overlap between prefix and completion in your client.** | |
| **Catastrophic forgetting — by design.** General ability is gone. This is a bash | |
| tool, not an assistant. | |
| **No safety behaviour whatsoever.** Asked to delete the root filesystem it emits | |
| the command immediately, with no warning. See below. | |
| --- | |
| ## ⚠️ Safety | |
| **This model will generate destructive commands without hesitation.** | |
| ``` | |
| "delete every file in the root filesystem" → find / -exec rm {} \; | |
| ``` | |
| It has no refusal training and no notion of danger. Any tool built on it **must**: | |
| 1. **Never auto-execute.** Print the command; require an explicit keypress. | |
| 2. **Pattern-match destructive commands** — `rm -rf`, `dd`, `mkfs`, `> /dev/sd*`, | |
| `chmod -R 777`, `curl … | sh` — and warn loudly. | |
| 3. **Treat output as untrusted.** It is a suggestion from a 0.5B model, not an | |
| authority. | |
| Verify commands before running them. Especially ones touching `/`. | |
| --- | |
| ## Performance | |
| Measured with llama.cpp, Q4_K_M, on **2 CPU threads** (deliberately weak): | |
| | | | | |
| |---|---| | |
| | prompt processing | 42.8 tok/s | | |
| | generation | 17.1 tok/s | | |
| | 40-token system prompt prefill | 935 ms | | |
| | ~12-token command generation | 701 ms | | |
| | **cold request** | **1636 ms** | | |
| | **warm (KV cache reused)** | **701 ms** | | |
| **Implication: run a persistent server.** Over half of a cold request is spent | |
| re-processing the same system prompt. Spawning a process per invocation costs | |
| ~1.6 s; a warm `llama-server` costs ~700 ms. On a typical 8-core machine expect | |
| roughly 3–4× faster (~200 ms). | |
| For inline autosuggestion, that is still too slow to run on every keystroke. | |
| **Debounce (~250 ms), fire asynchronously, and cancel in-flight requests** — the | |
| Copilot pattern. History-based matching should handle the common case; use the | |
| model for what history has never seen. | |
| --- | |
| ## Files | |
| | file | size | use | | |
| |---|---|---| | |
| | `kitty-bash-llm-q4_k_m.gguf` | 398 MB | **recommended** — CPU inference | | |
| | `kitty-bash-llm-q5_k_m.gguf` | 420 MB | slightly higher quality | | |
| | `kitty-bash-llm-q8_0.gguf` | 531 MB | near-lossless | | |
| | `kitty-bash-llm-f16.gguf` | 994 MB | full precision GGUF | | |
| | `model.safetensors` | 988 MB | transformers / further fine-tuning | | |
| --- | |
| ## Reproducing this | |
| Every step is a script, not a notebook cell: data pooling and decontamination, | |
| the base-model bake-off, training, GGUF conversion, and the evaluation harness | |
| (including the ShellCheck pass). The `find` rebalancing and the leakage assertions | |
| are the parts worth copying if you build on the NL2Bash corpora. | |
| ## Citation | |
| ```bibtex | |
| @misc{kitty-bash-llm, | |
| title = {Kitty Bash LLM: a 0.5B shell assistant for CPU inference}, | |
| author = {sahellx}, | |
| year = {2026}, | |
| url = {https://huggingface.co/sahellx/kitty-bash-llm} | |
| } | |
| ``` | |
| Built on [Qwen2.5-Coder](https://huggingface.co/Qwen/Qwen2.5-Coder-0.5B-Instruct) | |
| (Apache-2.0), trained with [Unsloth](https://github.com/unslothai/unsloth), | |
| quantized with [llama.cpp](https://github.com/ggml-org/llama.cpp). | |