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

@saluzi/min

v1.2.1

Published

Min Agent: minimum agent for edge devices

Readme

min-agent

A minimal coding agent for edge devices. TypeScript, ESM, runs on Bun or Node.

@saluzi/min implements the smallest usable coding-agent core: a reasoning loop, a handful of file/shell tools, opt-in context-window compaction, path-safe permissions, a model/provider layer, and a plugin system that makes tools, hooks, prompts, skills, and compaction replaceable or extendable — all without hot-loading.

Install

bun install

Build

bun run build      # tsc → dist/

CLI

bun run dist/cli.js [options] [message]

Or after bun link / global install:

min-agent [options] [message]
mina     [options] [message]

Options:

| Option | Description | Default | | --- | --- | --- | | --model <spec> | Model spec [provider:]model-id (see Models) | resolved (see Models) | | --base-url <url> | Override the API base URL for this run (writes the provider's dedicated <PROVIDER>_BASE_URL) | — | | --cwd <path> | Working directory | current directory | | --project-root <path> | Project root (for path safety) | cwd | | --max-turns <n> | Max LLM iterations per user turn | 100 | | --permission <mode> | default | acceptWrite | bypass | default | | --skills-dir <path> | Directory to scan for SKILL.md files | resolved (see Skills) | | --plugins-dir <path> | Directory to scan for file plugins (plugin.json) | resolved (see Plugins) | | --trust-plugin <name> | Trust a named file plugin's code for this run (repeatable) | — | | --trust-plugins | Trust all file plugins for this run (automation) | — | | --compact | Enable context compaction (see Compaction) | off | | --compact=<path> | Enable compaction with an external strategy module | — | | --debug | Enable debug logging to ~/.min/debug.log | — | | --help | Show help | — |

Pass a positional message for single-shot mode; omit it for interactive mode.

Interactive mode

  • Model banner — startup prints ▶ model <id> · provider <p> so a misrouted model is visible immediately (single-shot mode stays quiet).
  • Turn counter — a turn is one user input. The ─── Turn N ─── banner appears once per message you send and grows across the session; tool calls and extra LLM round-trips within a run do not advance it.
  • Multi-line paste — pasting multi-line text inserts a compact tag into the input line, e.g. You: summarize this [Pasted text #1 +12 lines]. The full pasted content is stored and sent to the model when you press Enter (single-line pastes are inserted verbatim). Works via bracketed-paste mode on modern terminals, with a timing fallback elsewhere.
  • Ctrl+C — aborts the current run; press twice at the prompt to exit. During an approval prompt it denies the tool call and aborts.

Examples

# OpenAI — zero flags: the single provider with credentials is picked up
OPENAI_API_KEY=sk-... OPENAI_MODEL=gpt-4o min-agent

# Claude via Anthropic proxy
ANTHROPIC_BASE_URL=https://my-proxy.com ANTHROPIC_AUTH_TOKEN=sk-... \
  min-agent --model claude-sonnet-4-5

# Local Ollama model (OpenAI-compatible) with a small context window.
# Model ids containing a colon need the openai_compat: prefix.
OPENAI_COMPATIBLE_BASE_URL=http://localhost:11434/v1 \
  OPENAI_COMPATIBLE_API_KEY=ollama MIN_MAX_CONTEXT_TOKENS=32768 \
  OPENAI_MAX_TOKENS=4096 min-agent --model openai_compat:qwen2.5-coder:7b

# DeepSeek (OpenAI-compatible endpoint; the base URL is required)
OPENAI_COMPATIBLE_API_KEY=sk-... \
  OPENAI_COMPATIBLE_BASE_URL=https://api.deepseek.com/v1 \
  min-agent --model deepseek-chat

# Enable auto-compaction, triggering at 50% of the context window
MIN_AUTOCOMPACT_PCT=50 min-agent --compact --model gpt-4o

# Enable compaction with an external strategy module
min-agent --compact=./my-compact.mjs --model gpt-4o

# Bypass permissions (sandboxed/automation only)
min-agent --permission bypass --model gpt-4o

# Load file plugins and trust a code plugin
min-agent --trust-plugin web-tools --model gpt-4o

Environment variables

Auth

| Var | Provider | | --- | --- | | OPENAI_API_KEY | openai | | ANTHROPIC_API_KEY | anthropic | | ANTHROPIC_AUTH_TOKEN | anthropic proxies (sent as Bearer) | | OPENAI_COMPATIBLE_API_KEY | openai_compatible |

Model selection (when --model is omitted)

| Var | Effect | | --- | --- | | MIN_DEFAULT_MODEL | Default model spec, same syntax as --model (highest priority) | | OPENAI_MODEL | Model id for the openai provider (requires OPENAI_API_KEY) | | ANTHROPIC_MODEL | Model id for the anthropic provider (requires ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN) | | OPENAI_COMPATIBLE_MODEL | Model id for the openai_compatible provider (requires OPENAI_COMPATIBLE_API_KEY) |

When --model is omitted, the agent picks the single builtin provider that has credentials and uses its <PROVIDER>_MODEL. Zero or multiple providers with credentials → an actionable startup error listing the three configuration ways. Fail loud beats guessing.

Base URL

| Var | Effect | | --- | --- | | OPENAI_BASE_URL | Redirects the openai provider | | ANTHROPIC_BASE_URL | Redirects the anthropic provider | | OPENAI_COMPATIBLE_BASE_URL | Redirects the openai_compatible provider only | | <PROVIDER>_BASE_URL | Redirects a plugin-registered provider |

Legacy: OPENAI_BASE_URL still redirects openai_compatible when OPENAI_COMPATIBLE_BASE_URL is unset. --base-url always writes the dedicated variable, so redirecting a compat provider never hijacks the official openai endpoint.

Debug

| Var | Effect | | --- | --- | | MIN_DEBUG | 1 / true / verbose enables debug logging | | MIN_DEBUG_FILE | Override log file path (default ~/.min/debug.log) | | MIN_DEBUG=stderr | Mirror logs to stderr in human-readable form |

Context window & compaction (compaction is OFF by default — enable with --compact / --compact=<path>; the compaction vars below only take effect once it is on)

| Var | Effect | | --- | --- | | MIN_MAX_CONTEXT_TOKENS | Override the effective context window (any model). Highest priority. | | MIN_AUTO_COMPACT_WINDOW | Secondary context window override, for local/custom models. | | OPENAI_MAX_TOKENS | Override max output tokens for openai + openai_compatible. | | MIN_MAX_OUTPUT_TOKENS | Generic max output tokens override (any provider). | | MIN_AUTOCOMPACT_PCT | Percentage threshold (0–100) to trigger autocompact. Only makes it more aggressive. | | MIN_KEEP_RECENT_TOKENS | Override keep-recent token budget (default 20000). | | MIN_RESERVE_TOKENS | Override reserve token budget (default 16384). | | DISABLE_COMPACT | Disable compaction entirely — even when --compact is passed. | | DISABLE_AUTO_COMPACT | Disable auto-compact only. |

Models

A model spec is [provider:]model-id, split at the first colon. There is no per-model builtin table — snapshot-dated ids rot, and aliases silently mapped to stale models. Resolution order:

  1. Explicit provider prefixanthropic:claude-sonnet-4-5, openai_compat:qwen2.5-coder:7b (openai_compat is an input alias for openai_compatible; the rest of the string is the model id, colons included). The provider must be registered (builtin or plugin) — an unknown prefix fails at startup with the registered-provider list instead of silently routing your API key to a fallback host.
  2. Plugin-registered models — a bare id matching some provider's models: [...] entry (see Custom providers).
  3. Brand inference (prefix-only, never version-aware): claude* → anthropic, gpt* / chatgpt* / o<digit>* → openai.
  4. Fallback — any other bare id resolves to openai_compatible, so local models work by id: --model qwen2.5-coder with OPENAI_COMPATIBLE_BASE_URL pointed at Ollama/vLLM.

Bare ids that contain a colon (Ollama-style qwen2.5-coder:7b) must be prefixed explicitly (openai_compat:qwen2.5-coder:7b) — a colon always means "provider prefix".

When --model is omitted, the default is MIN_DEFAULT_MODEL or the single builtin provider with credentials (<PROVIDER>_MODEL + key, see Environment variables).

Context window / max output tokens default to 128k / 16k for every model; override via MIN_MAX_CONTEXT_TOKENS / OPENAI_MAX_TOKENS / MIN_MAX_OUTPUT_TOKENS (see the env tables), or declare exact values on a plugin provider's model entries.

Migration (v1.2)

  • Aliases removedclaude, sonnet, gpt4, deepseek no longer map to fixed snapshots. Use full ids (claude-sonnet-4-5) or specs.
  • Colon-containing ids need a prefix--model qwen2.5-coder:7b--model openai_compat:qwen2.5-coder:7b.
  • DeepSeek is no longer a builtin endpoint — set OPENAI_COMPATIBLE_BASE_URL=https://api.deepseek.com/v1 (see the example above).
  • Metadata changes — claude models previously carried a 200k context window from the builtin table; they now use the 128k default (auto-compact triggers earlier). Override with MIN_MAX_CONTEXT_TOKENS if you rely on the larger window.
  • Multiple keys — with credentials for 2+ providers set, the agent used to silently default to gpt-4o; it now fails at startup and asks you to pick explicitly.

Programmatic API

import { MinAgent } from "@saluzi/min";

const agent = new MinAgent({
  // model is optional: omitted → MIN_DEFAULT_MODEL, or the single builtin
  // provider with credentials. String specs use the [provider:]model-id
  // syntax; a prebuilt ModelConfig object also works.
  model: "gpt-4o",
  cwd: process.cwd(),
  permissionMode: "default",  // requires askUser for default/acceptWrite
  askUser: async (toolCall, reason) => true,
  // plugins: [...],           // configuration-time only
  // skillsDir: "./.min/skills",
  // compaction: true,         // enable auto-compaction (off by default);
  //                            // or "./my-compact.mjs" for an external strategy
});

const result = await agent.run("Add a vitest test for src/utils/env.ts");
console.log(result.totalTurns, result.stoppedReason);

agent.abort();                 // stop the current run at the next safe point
agent.installSigintHandler();  // Ctrl+C → abort

Key exports: MinAgent, createMinAgent, resolveModel, createDefaultTools (and each tool factory), MinimalSession, plus the plugin/skills helpers shown under Plugins and Skills.

Tools

Built-in tools: bash, read, write, edit, grep, ls, find.

  • Concurrent writes to the same file are serialized automatically.
  • File operations are confined to the working/project root (no traversal outside it).
  • Tool output is truncated to protect the context window.
  • read shows at most the first 2000 lines (or first 50KB) of a file and includes the next startLine to continue from; files above 5MB are refused.

Permissions

Three modes:

  • default — ask before bash / write / edit (requires askUser).
  • acceptWrite — writes allowed, still asks for shell.
  • bypass — no prompts (sandbox/automation only).

Approval is requested through your askUser callback; output is also guarded against context overflow.

Compaction

Compaction is a replaceable plugin and is off by default — the core loop never compacts unless a strategy is registered. Enable the builtin auto-compaction with --compact (or compaction: true in the programmatic API): when the context approaches the window limit, the loop finds a cut point, summarizes the dropped messages through the main model, and keeps a recent tail. Thresholds are tuned by the env vars above; DISABLE_COMPACT=1 is a kill switch that wins over the flag.

Point at an external compact with --compact=<path> (or compaction: "./my-compact.mjs"). The module's default export is either a CompactionStrategy object or a bare async function; both receive the current entries, model config, and stream function, and return a CompactionResult (or null when nothing should be compacted):

// my-compact.mjs — an external compaction strategy
export default async (entries, model, streamFn) => {
  // …your own threshold / cut-point / summarizer logic…
  return {
    type: "compaction",
    summary: "…",
    tokensBefore: 12345,
    retainedTail: [/* AgentMessage[] kept after the cut */],
  };
};

Any plugin can also replace compaction at setup time via host.setCompactionStrategy(...) (last-writer-wins), and tune it with host.setCompactionSettings(...) — including the "compaction" field of a plugin.json manifest. A failed external-module load warns and leaves compaction off (it never silently falls back to the builtin summarizer).

Plugins

A plugin runs at construction time and may replace or add capabilities — no hot-loading. Through the PluginHost a plugin can:

  • register/replace tools
  • add beforeToolCall / afterToolCall hooks
  • add prompt sections, guidelines, context files, or replace the prompt builder
  • replace the stream function, session storage, or compaction strategy
  • add context transforms, steering/follow-up message sources
  • register custom model providers (host.registerProvider)
  • publish capabilities on a CapabilityBus (consumed by other plugins)

Programmatic plugins

import { MinAgent, type MinPlugin } from "@saluzi/min";

const myPlugin: MinPlugin = {
  name: "style",
  setup(host) {
    host.addTool(myTool);
    host.beforeToolCall(myGuard);
    host.addPromptSection({ heading: "Style", body: "Prefer named exports." });
  },
};

new MinAgent({ model: "gpt-4o", plugins: [myPlugin] });

File plugins

Drop a plugin.json manifest into .min/plugins/<name>/ (project-local) or ~/.min/plugins/<name>/ (user-global). A manifest can declare tools, providers, prompt sections, guidelines, and more — declaratively (no code) or with code modules (tools[].module, module, provider.streamModule).

.min/plugins/
  web-tools/
    plugin.json          # manifest: tool declarations + refs
    web_search.mjs       # tool execute implementation
{
  "name": "web-tools",
  "tools": {
    "items": [
      {
        "name": "web_search",
        "description": "Search the web.",
        "parameters": { "type": "object", "properties": { "q": { "type": "string" } }, "required": ["q"] },
        "module": "./web_search.mjs"
      }
    ]
  }
}
// web_search.mjs — export default a ToolExecutor
export default async (args, ctx) => {
  ctx.onUpdate?.("searching...");
  return { content: "results...", isError: false };
};

Code modules require trust (--trust-plugin <name> or trust.json); untrusted plugins apply only their declarative fields — with one exception: the provider field also requires trust (it redirects LLM traffic and attaches credentials). See ~/.min/plugins/plugins.md for the full guide (auto-generated on first run).

Custom providers

Register a provider via host.registerProvider(def) or the provider field in plugin.json. api: "openai" | "anthropic" reuses the builtin protocol; streamModule gives a fully custom stream handler. Security rules: provider registration requires trust; apiKeyEnv/authTokenEnv must stay in the provider's namespace (<PROVIDER>_API_KEY / <PROVIDER>_AUTH_TOKEN); builtin provider names (openai / anthropic / openai_compatible) are reserved — redirect them with their dedicated *_BASE_URL vars or --base-url.

{
  "name": "gemini-bridge",
  "provider": {
    "name": "gemini",
    "api": "openai",
    "baseUrl": "https://generativelanguage.googleapis.com/v1",
    "apiKeyEnv": "GEMINI_API_KEY",
    "models": [{ "modelId": "gemini-1.5-pro", "contextWindow": 1000000, "maxOutputTokens": 8192 }]
  }
}

Then min-agent --model gemini-1.5-pro resolves via the provider's model list. ModelConfig.provider is a plain string, so any provider name works.

Skills

Agent-Skills convention: each skill is a SKILL.md file with YAML frontmatter (name, description, optional disable-model-invocation). Only frontmatter is loaded — the body is read on demand by the LLM via the read tool (lazy loading). Skills are injected as a pure plugin that adds a catalog section to the system prompt.

Resolution (first non-empty existing directory wins):

  1. --skills-dir / MinAgentConfig.skillsDir
  2. MIN_SKILLS_DIR env var
  3. <cwd>/.min/skills/ (project-local)
  4. ~/.min/skills/ (user-global)

Selecting / disabling skills (allowlist / denylist)

An optional skills.config.json in the skills directory selects or excludes skills by their frontmatter name (mirrors workflows.config.json):

{ "enabled": ["plan", "review"], "disabled": ["experimental"] }

--skill <name> (repeatable) / MinAgentConfig.skills sets an explicit allowlist that overrides enabled — only the named skills load:

min-agent --skill plan --skill review --model gpt-4o
mkdir -p .min/skills
cat > .min/skills/release.md <<'EOF'
---
name: release
description: Cut a new release bump and tag.
---
1. Run `bun run build`.
2. Bump version in package.json.
3. Commit and tag.
EOF

Scripts

bun run build        # tsc
bun run test         # vitest run
bun run test:watch   # vitest
bun run typecheck    # tsc --noEmit
bun run start        # tsx src/index.ts

License

MIT