npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

termita

v2.10.0

Published

Local AI first copilot for your console — chat with a model that operates your shell, one approved command at a time. Local (LM Studio/Ollama), or OpenAI/Anthropic.

Readme

🏴‍☠️ termita 🇦🇷

npm version node license

Local AI first copilot for your console.

 🏴‍☠️  TERMITA  🇦🇷

 ████████╗███████╗██████╗ ███╗   ███╗██╗████████╗ █████╗
 ╚══██╔══╝██╔════╝██╔══██╗████╗ ████║██║╚══██╔══╝██╔══██╗
    ██║   █████╗  ██████╔╝██╔████╔██║██║   ██║   ███████║
    ██║   ██╔══╝  ██╔══██╗██║╚██╔╝██║██║   ██║   ██╔══██║
    ██║   ███████╗██║  ██║██║ ╚═╝ ██║██║   ██║   ██║  ██║
    ╚═╝   ╚══════╝╚═╝  ╚═╝╚═╝     ╚═╝╚═╝   ╚═╝   ╚═╝  ╚═╝

 Local AI first copilot for your console

termita is a chat with a model that runs in your terminal and can operate the shell. It proposes one command at a time, explains what it does and why, and waits for you to approve it. The command runs, its real output streams into the chat, the model reads the result and responds. Then it's your turn again. One Node process, runnable over SSH on a headless box.

It talks to a local model by default — LM Studio, Ollama, or anything speaking the OpenAI-compatible API — and can also point at OpenAI or Anthropic.

termita replies in the language you write to it — English or Spanish.

  • Every command that changes something is approved by you, or by an allowlist you build. rm -rf, dd, mkfs, fork bombs and the like always prompt, even in auto-approve mode.
  • Real stdout/stderr streams in line by line — you read what actually happened, not a summary of it.
  • Runs against any OpenAI-compatible endpoint (LM Studio, Ollama, vLLM…), or OpenAI / Anthropic directly.
  • Pure Node, no native build. npm i -g and run it.

Requirements

  • Node.js ≥ 20 (uses native fetch, ReadableStream, AbortController)
  • A model endpoint: a local server (LM Studio / Ollama), or an OpenAI / Anthropic API key.

Check Node:

node --version   # should be v20 or higher

Install

macOS / Linux (recommended)

npm install -g termita
termita

That's it. (If termita isn't found, make sure your npm global bin is on $PATHnpm bin -g shows the dir.)

Windows

termita's shell tool targets a POSIX shell (sh). On Windows, run it inside WSL2 (Ubuntu) or Git Bash, where sh is available:

# In WSL2 / Git Bash:
npm install -g termita
termita

Native PowerShell/cmd is not supported yet (the shell tool assumes sh -c).

Over SSH (the main use case)

Install on the box, then just run it — termita is a TUI that works fine over SSH:

ssh you@your-vps
npm install -g termita
termita

From source

git clone https://github.com/andycufari/termita2.git
cd termita2
npm install
npm run build
npm link          # makes `termita` available globally
termita

First run & config

Just run termita. On first launch an interactive setup wizard walks you through it — pick your provider (Local / OpenAI / Anthropic), enter the endpoint or API key, and choose a model (fetched live from the server when possible). No JSON editing required.

Re-run the wizard anytime with /setup inside termita.

Settings are saved to:

  • Config: ~/.config/termita/config.json (respects $XDG_CONFIG_HOME)
  • System facts: ~/.config/termita/system.json (re-probe with termita init)
termita doctor    # check provider, endpoint, model, node version
termita init      # re-probe system facts

You can still edit config.json by hand if you prefer — here's the shape:

config.json

{
  "llm": {
    "provider": "openai-compatible",          // "openai-compatible" | "openai" | "anthropic"
    "endpoint": "http://localhost:1234/v1",   // base URL (for openai-compatible)
    "model": "your-model-id",
    "apiKey": "lm-studio",                     // ignored by LM Studio; required by OpenAI/Anthropic
    "maxTokens": 4096,
    "reasoning": false                          // thinking trace on/off (Qwen etc.)
  },
  "ui": { "theme": "neon" },
  "policy": { "allowlist": [], "autoRunReadOnly": true, "autoApprove": false }
}

Provider presets

LM Studio (default — local, no key needed):

"llm": {
  "provider": "openai-compatible",
  "endpoint": "http://localhost:1234/v1",
  "model": "qwen3.6-27b",
  "apiKey": "lm-studio"
}

Serving LM Studio on your LAN? Enable "Serve on Local Network" (binds 0.0.0.0) and open the port, then point endpoint at http://<host>:1234/v1.

Ollama (local):

"llm": {
  "provider": "openai-compatible",
  "endpoint": "http://localhost:11434/v1",
  "model": "llama3.1",
  "apiKey": "ollama"
}

OpenAI:

"llm": {
  "provider": "openai",
  "model": "gpt-4o",
  "apiKey": "sk-..."        // or set OPENAI_API_KEY in your env
}

Anthropic (Claude):

"llm": {
  "provider": "anthropic",
  "model": "claude-sonnet-4-6",
  "apiKey": "sk-ant-..."    // or set ANTHROPIC_API_KEY in your env
}

API keys can come from the config or environment variables (OPENAI_API_KEY, ANTHROPIC_API_KEY) — env wins if both are set.


Using it

You chat. When something needs the system, termita proposes a command and waits.

Approval

| Action | Key | What it does | |------------|-------|----------------------------------------------------| | Run | R | run this step once | | Edit | E | tweak the command inline, then re-gate it | | Always | A | allowlist this kind of command, then run it | | No | N | decline; the model is told and reacts |

Navigate with ↑ / ↓ and Enter, or hit the letter directly.

Auto-approve mode (TAB or /auto): commands run without asking — except dangerous patterns, which always prompt with a red warning. File writes auto-run too.

Esc interrupts anything: a streaming reply, a running command, or a prompt.

Run a command yourself — !

Type a line starting with ! to run a command yourself — no model round-trip, no approval prompt. What happens depends on the command:

Plain commands (!ls, !git status, !grep …) run inside termita. Their output streams straight into the transcript — it stays there, scrollable, like any command output. When it finishes, a small menu lets you pick what the model hears:

─────────────────────────────────────────────
 !ls -la finished
   ▸ tell termita you ran it (command only)
     share command + output
     silent — tell termita nothing
   ↑↓ select · enter · f share+output · e silent
─────────────────────────────────────────────

Pick enter/f/e, then you're back at the input with that choice staged — add a note ("the config isn't here") or just press enter to send it as-is. So a throwaway lookup stays private (e), and a result worth acting on gets shared, optionally with your own take.

Full-screen programs (!vim, !htop, !tail -f, !python, …) need the whole terminal. termita suspends, hands it off (fully interactive — vim keys, prompts, live TUIs, exactly as in your shell), and redraws when you exit (:wq, quit, or Ctrl-C):

you  › !vim notes.txt      # your editor, full-screen
you  › !git commit         # opens $EDITOR, prompts work
you  › !tail -f app.log    # live stream — Ctrl-C to stop, back to termita
you  › !cd ../other-repo   # sticks — termita follows you there

These own the screen, so there's no captured output to share — the model just learns you ran them. termita picks which path automatically; !!command forces the full-terminal mode for an interactive program it didn't recognize. A !cd always sticks, so termita follows you. (No native PTY — stays pure Node.)

Attaching files — @

Reference a file with @path in your message (or drag-and-drop it onto the terminal — that pastes the path, which termita picks up the same way). What happens depends on the file type, decided at send time:

you  › summarize @notes.md and @src/config.py
you  › what's wrong in this screenshot? @~/Desktop/error.png
  • Text files (.md, .txt, source code, .json, …) are read and inlined into your message, so any model can see the actual contents — including a text-only local model. Big files are trimmed to a head+tail (the model can read the rest).
  • Images (.png, .jpg, .webp, .gif) are attached using the standard OpenAI multimodal format (a base64 image_url part). This needs a vision model — with a text-only model loaded (most local Qwens), termita surfaces a clear "this model has no vision — load a vision model" message rather than a raw error. Point termita at a vision model (a Qwen-VL / Llava in LM Studio, or OpenAI/Anthropic) and images just work.

Paths may be quoted (@"my file.md"), use ~, or be relative to the current directory. An @mention that isn't a real file is left in your message untouched.

Slash commands

| Command | Action | |----------------------|-------------------------------------------------| | /help | show commands + keys | | /setup | re-run the provider / model setup wizard | | /model [id] | list models / switch the active model | | /reasoning on\|off | toggle the thinking trace | | /auto | toggle auto-approve (same as TAB) | | /clean | wipe transcript + history (fresh context) | | /compact | summarize history into a note, free context | | /context [n] | show / set the model context window (gauge) | | /mouse [on\|off] | wheel-scroll vs native drag-select / copy | | /memory […] | show / manage what termita remembers | | /cognito [on\|off] | incognito — no memory saved or recalled | | /allow | list allowlist rules | | /websearch [key] | set/show Brave web-search key (off to clear) | | /credits | who made this | | /quit | exit (also Ctrl-C twice) |

Memory

termita can remember durable facts you can't discover by running a command — a preference, a project detail, a constraint. Just tell it:

you  › remember I use pnpm here and deploy with fly
term › noted.   → memory: "Uses pnpm; deploys via fly.io"

The model distills what you said into one line and saves it (via a memory tool, no approval needed). Notes are project-scoped by default (tied to the current directory) or global (machine-wide prefs). Saved notes are shown to the model at the top of every turn, so it stops re-asking.

Manage them by hand too:

/memory                 list active notes (global + this project)
/memory add <note>      save a project note   (add -g for a global one)
/memory forget <n>      drop note #n
/memory clear           wipe everything

Stored at ~/.config/termita/memory.json. termita does not auto-save things it can probe (whether a tool is installed, a service is running) — it runs a command for those.

Incognito: /cognito on is a session blackout — nothing is saved and nothing is recalled, so the model runs as if memory were empty. /cognito off brings it back. Resets to off each launch. The footer shows 🕶️ while it's on.

Web search

Give the model live web access with a Brave Search API key: /websearch <key> (or set BRAVE_API_KEY). Once a key is set, the model calls a read-only websearch tool on its own whenever an answer needs current info — latest versions, release dates, recent events, unfamiliar errors — and cites the URLs it used. Without a key the tool stays hidden from the model.

Scrolling output

termita runs in the alternate screen (no resize ghosting), so it draws its own scrollable transcript. Scroll with the mouse wheel, PageUp/PageDown, Ctrl+↑/Ctrl+↓, Home (oldest) / End (latest) — it scrolls by rows, so the wheel moves evenly and Home lands on real history instead of a blank top. New output auto-follows the bottom until you scroll up.

To select/copy text with the mouse while wheel-scroll is on, hold a modifier while dragging — Option on macOS (iTerm2/Terminal.app), Shift on most Linux terminals. Prefer plain drag-to-select? Run /mouse off to release mouse capture (native selection + copy-paste work everywhere; the wheel then uses your terminal's own scrollback). /mouse on brings wheel-scroll back. The setting persists.


Safety model

  • shell and write ask before running; read and grep auto-run (read-only).
  • Allowlist is persisted per normalized command shape. Broad/slow commands (find, du, tar, rsync…) key on command + path, so blessing find . does not also bless find /.
  • A built-in danger list (rm -rf, mkfs, dd of=/dev/…, fork bombs, curl | sh, destructive git, …) can never be silently auto-allowed — it always prompts with a loud warning, even in auto-approve.
  • Remote endpoints: command output is fed back to the model. If your endpoint is non-local, that output leaves your machine. Keep it local (LM Studio/Ollama) for fully offline use.

Architecture

Three clean layers, each swappable and testable:

UI (Ink/React)        renders the transcript, captures input + approval keys
Engine                the copilot loop — owns history, drives the LLM, runs ONE
                      tool per round, awaits a human decision, feeds result back
Providers + Tools     streaming clients (OpenAI-compatible / OpenAI / Anthropic),
                      the shell/read/write/grep tools, the approval gate

The engine emits events and awaits decisions; the UI renders events and supplies decisions. They talk through an async interface, never direct calls.

src/
  cli.js              entry: init | doctor | TUI
  app.jsx             Ink root
  engine/             copilot loop + event bus
  providers/          openai.js, anthropic.js + a factory
  tools/              shell (live stream + cwd), fs (read/write/grep), schemas
  policy/gate.js      allowlist + danger list + auto-approve
  config/             config.json + system probe
  prompt/system.js    persona + machine facts
  ui/                 theme, components, banner
build.js              esbuild bundle → dist/cli.js

Development

npm run build     # bundle src → dist/cli.js
npm run dev       # build + run
npm start         # run the built bundle

License

MIT