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

meowcode

v0.1.6

Published

MeowCode — a Claude Code–style agentic coding CLI (framework skeleton).

Readme

MeowCode

A Claude Code–style agentic coding CLI. This is the framework skeleton — the terminal UI, streaming loop, slash-command system, and a pluggable provider layer are all in place. Real agent capabilities (tools, file edits, planning) are meant to be layered on next.

Quick start

npm install
npm run dev        # interactive session (mock provider, no API key needed)

Build a standalone CLI:

npm run build      # bundles to dist/cli.js with a shebang
node dist/cli.js   # or: npm link  → then run `meowcode`

Using a real model

export ANTHROPIC_API_KEY=sk-ant-...
npm run dev        # provider auto-switches to `anthropic`

The Anthropic provider streams via the Messages API using the built-in fetch (no SDK dependency).

Non-interactive / scripting

meowcode -p "explain this repo"
echo "write a haiku about tps reports" | meowcode

Commands

/help · /model <id> · /provider <mock|anthropic> · /config · /entry · /clear · /version · /exit

Config location

All user state lives in ~/.meowcode/ — settings.json, sessions/, memory/, credentials.json, history.json, and entries/.

This tool was called AnyCode before; its config directory was ~/.anycode. That directory is no longer read. On the next interactive start, if ~/.anycode still holds data and ~/.meowcode is empty, MeowCode asks whether to merge it in — a dialog with the same keys and mouse support as the rest of the UI (↑↓/wheel to move, 1–2 to jump, ↵/click to confirm, y/n to answer directly, esc to skip). The merge is additive and never deletes the old directory, so you can check the result and remove ~/.anycode yourself. To skip the prompt:

mv ~/.anycode ~/.meowcode     # or merge by hand

Non-interactive runs (print mode, pipes, CI) print a one-line notice with the same mv hint instead of prompting.

Entries (front-ends)

An entry is a complete front-end of its own — its own UI and wiring, driven by the same agent — not a per-user profile. A plugin can ship one that replaces the whole terminal UI (a web front-end, a tray app); the built-in terminal UI is itself just an entry, named tui, materialized as ~/.meowcode/entries/tui/ on first start.

meowcode                     # start the default entry, or the built-in tui
meowcode tui                 # ...explicitly, the built-in terminal UI
meowcode meowui              # start a specific entry (argv shorthand)
meowcode --entry meowui      # same, as an explicit flag
meowcode entry install <pkg> # install one (the only way an entry appears)
meowcode entry use meowui    # make it the default (~/.meowcode/entry.json)

/entry opens an interactive menu over the installed set — pick an entry, then set it as the startup default, show its details, or remove it. The subcommands stay for scripting: /entry list, /entry current, /entry default [name|off], /entry remove <name> [--force].

An entry cannot be hand-authored, and there is no "new entry" anywhere in the UI: the list is a readout of what you have installed, because what you can install is decided by the plugins you have. meowcode entry install <source> is the only way one appears, and entry new refuses with that pointer.

Entries bind at startup (no hot-switching), so every message says a restart is needed. Sessions and memory are shared across entries — switching front-ends does not fork your history.

~/.meowcode/
  settings.json                 ← global base config
  sessions/  memory/  projects/ history.json   ← shared by every entry
  entry.json                    ← { "default": "<name>" }; absent ⇒ tui
  entries/
    tui/    entry.json  settings.json          ← built-in, materialized at startup
    meowui/ entry.json  settings.json  skills/ commands/ agents/

Each entry dir holds:

  • entry.json — manifest and highest-precedence config overrides (description, model, provider, theme, settings, hooks, mcpServers, permissions, customProviders, launcher).
  • settings.json — the entry's saved config. Layering, low → high: built-in defaults → global ~/.meowcode/settings.json → entry settings.json → entry entry.json.
  • skills/, commands/, agents/ — per-entry content, same lookup order.

Credentials, usage stats, and the update cache stay machine-global. The API key always comes from the environment (ANTHROPIC_API_KEY), never from an entry.

Installing shared entries

meowcode entry install creates an entry from a template shipped in an npm package, a git URL, or a local path:

meowcode entry install <npm-package>
meowcode entry install <git-url>
meowcode entry install <local-path>
meowcode entry remove <name>    # delete an entry's directory

A template package declares its entry in package.json under "meowcode.entry"; install never overwrites existing files, so reinstalling keeps your local edits.

{
  "name": "my-meowcode-entry",
  "meowcode": {
    "entry": {
      "description": "A Web front-end for the same agent",
      "model": "claude-opus-4-8",
      "settings": { "effort": "high" },
      "content": {
        "skills": { "review": "# Review\n..." },
        "commands": { "standup.md": "Summarize what changed since yesterday." },
        "agents": { "explorer": "You are a code exploration agent." }
      }
    }
  }
}

Entries with a launcher (has-entry plugins)

Most entries are just config + content for the normal TUI. An entry can also change how MeowCode starts: its entry.json may declare a launcher front-end (e.g. a web UI). When that entry is active, meowcode skips the Ink TUI entirely and spawns the launcher instead — the plugin drives the agent through a JSON-RPC bridge, and the transcript lands in the shared sessions/ (/resume-compatible, same as in the TUI).

The plugin is the JSON-RPC client and MeowCode the server, over the child's stdin/stdout (newline-delimited JSON-RPC 2.0 — same framing as MCP servers, opposite direction). On startup the host sends one initialize request — { protocolVersion: "2025-meowcode-launcher-1", serverInfo: { name, version } } — which the plugin answers (any result value), then:

| Direction | Method | Payload → result | |---|---|---| | plugin → host | agent/turn | { prompt } → { turn }: runs one full agent turn (tool loop included), streaming progress as agent/event notifications | | host → plugin | agent/event | { turn, event }: one AgentEvent (text / thinking / tool_use / tool_result / usage / error / workflow / agent) | | plugin → host | agent/abort | {} → { aborted }: stops the in-flight turn (the TUI's esc); aborted: false when nothing is running | | plugin → host | tools/list | {} → { tools } (Anthropic-format schemas) | | plugin → host | tools/call | { name, input } → the ToolResult | | plugin → host | config/get | {} → the effective AppConfig (apiKey stripped) | | plugin → host | session/state | {} → { sessionId, messages, usage } | | plugin → host | session/reset | {} → { sessionId }: drops the transcript (like /clear) |

Rules of the road:

  • entry.json form: "launcher": { "command": "node", "args": ["launcher.js"] }. A relative command/args string that names a file in the entry dir is anchored there — that is where the installer puts it — while anything else (a bare node/bun on PATH, a flag, a path for the launched program itself) is passed through untouched. cwd defaults to the entry dir; a relative cwd is written through as-is, and spawn resolves it against the parent's cwd, so "cwd": "." is how a manifest asks for the session's own directory. Optional env adds variables. meowcode entry install copies the files referenced by relative launcher.args into the entry, as it does for relative mcpServers args.
  • One turn at a time — a second agent/turn while one streams is rejected. agent/abort stops the running one: the pending agent/turn still resolves with { turn }, the partial answer is committed with meta.interrupted, and no ⚠ error row is added — so a plugin learns the turn is over from the turn's own resolution and needs no abort event. The bridge reads lines one at a time, so an abort sent before agent/turn has installed its controller finds nothing to stop and answers { aborted: false }.
  • Headless: turns run with bypassPermissions (the plugin owns interaction and gates before calling), but the entry's permissions deny/allow rules still bind. Hooks fire when the entry brings them; there is nobody to answer requestUserInput, so the ask_user tool reports that by itself.
  • Session state lives in the host, so the plugin can stay stateless. Print mode (-p / --print) bypasses the launcher and uses the normal non-interactive path.
  • Entry mcpServers start with the host (the TUI never mounts in launcher mode). Discovery is async — a server's mcp__* tools appear in tools/list once its handshake completes (same "next turn" semantics as the TUI), so a plugin should re-list before its first agent/turn if it needs them.
  • /entry and meowcode entry list mark these entries [launcher].

Known gaps, for plugin authors who need to plan around them: the host never reports the session cwd (declare "cwd": "." in launcher — a relative cwd resolves against the parent's, so that yields the session's own directory; omit it and process.cwd() is the entry dir), there is no interactive permission prompt (an ask decision falls through to allow), abort produces no event (only the turn's resolution), and there is no fs/* method — read what the host's tools would, or read the filesystem directly.

Minimal plugin sketch (node) — answer initialize, run one turn, exit (the host saves the transcript):

// launcher.js — toy front-end: one turn, log events to stderr, exit
const readline = require('node:readline');
let id = 1;
const pending = new Map();
const send = (o) => process.stdout.write(JSON.stringify(o) + '\n');
readline.createInterface({ input: process.stdin }).on('line', (line) => {
  const msg = JSON.parse(line);
  if (msg.id && !msg.method) pending.get(msg.id)?.(msg.result); // host reply
  else if (msg.method === 'agent/event') process.stderr.write(`[${msg.params.turn}] ${msg.params.event.type}\n`);
  else if (msg.id) send({ jsonrpc: '2.0', id: msg.id, result: {} }); // initialize
});
const call = (method, params) => new Promise((res) => {
  const i = id++; pending.set(i, res);
  send({ jsonrpc: '2.0', id: i, method, params });
});
(async () => {
  await call('config/get', {});
  const { turn } = await call('agent/turn', { prompt: 'Say hi in one line.' });
  const state = await call('session/state', {});
  process.stderr.write(`turn ${turn}: ${state.messages.at(-1)?.content}\n`);
  process.exit(0);
})();

Layout

src/
  cli.tsx            entry point (arg parsing, print mode, render)
  app.tsx            Ink app: layout, global keys, elapsed timer
  theme.ts           colors + symbols
  types.ts           Message / Provider / Command contracts
  config.ts          ~/.meowcode/settings.json load/save
  hooks/useChat.ts   conversation state machine + streaming
  components/        Banner · Message · StatusLine · PromptInput
  commands/          slash-command registry
  providers/         mock (offline) + anthropic (real, fetch/SSE)
  lib/               markdown rendering · spinner frames · token estimate

Extending

  • Add a command: push a SlashCommand onto registry in src/commands/index.ts.
  • Add a provider: implement the Provider interface and register it in src/providers/index.ts.
  • Add tools / file edits: give a provider access to a tool loop and render tool messages in src/components/Message.tsx (the role already exists).