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

opencode-metis

v0.3.4

Published

Persistent memory system for OpenCode sessions

Readme

opencode-metis

Persistent memory system for OpenCode sessions. Captures observations, compresses them with AI, enables semantic search, and preserves context across compaction.

Features

  • AI-compressed observations — Tool outputs are distilled into structured knowledge by Gemini, OpenRouter, or Anthropic
  • Semantic search — Find relevant memories by meaning via ChromaDB vector embeddings, not just keywords
  • Context injection — Relevant past observations are automatically injected at session start
  • Compaction survival — Saves and restores context when OpenCode compacts messages
  • Multi-session support — Run multiple OpenCode sessions concurrently with session-scoped data isolation
  • Privacy protection<private> tag stripping and automatic secret detection (API keys, tokens, PEM keys) before storage
  • Crash recovery — Pending message queue with at-least-once delivery ensures no observations are lost; automatic token refresh on worker restart
  • Quality checks — TDD enforcement and file-length warnings on every edit
  • Tool redirection — Block or redirect specific tools via configuration
  • Local-only — All data stays on your machine at ~/.config/opencode/memory/; only AI compression calls leave the machine

Prerequisites

| Tool | Version | Required | Purpose | |------|---------|----------|---------| | Bun | >= 1.0.0 | Yes | Runtime for the worker daemon, plugin, and build system | | uv | latest | No | Runs chroma-mcp for semantic vector search (installed automatically by init) | | API key | — | No | One of GEMINI_API_KEY, OPENROUTER_API_KEY, or ANTHROPIC_API_KEY for AI observation compression |

ChromaDB is managed automatically via chroma-mcp (a Model Context Protocol server launched with uvx). If uv is not installed, the system falls back to SQLite FTS5 keyword search — no semantic search, but everything else works.

Installation

  1. Install globally:

    bun add -g opencode-metis
  2. Run the setup command from your project root (or any directory):

    opencode-metis init

    This merges the required plugin and mcp entries into ~/.config/opencode/opencode.json, copies framework files (agents, commands, skills) into ~/.config/opencode/, and checks for ChromaDB availability. Existing config is backed up before modification.

  3. (Optional) Set an AI provider for observation compression:

    # Pick one:
    export GEMINI_API_KEY="your-key"
    export OPENROUTER_API_KEY="your-key"
    export ANTHROPIC_API_KEY="your-key"

    Without an API key, observations are stored with basic string truncation instead of AI compression.

CLI

| Command | Description | |---------|-------------| | opencode-metis init | Configure opencode.json and copy framework files | | opencode-metis start | Start the memory worker and launch opencode | | opencode-metis stop | Stop the memory worker | | opencode-metis start-mcp | Start the MCP server (used by opencode via opencode.json config) |

Configuration

The memory system has its own optional config file at ~/.config/opencode/memory/settings.json (separate from OpenCode's config). All fields have sensible defaults:

Worker

| Key | Type | Default | Description | |-----|------|---------|-------------| | workerPort | number | 41777 | HTTP port for the worker daemon | | workerBind | string | "127.0.0.1" | Bind address for the worker |

Storage

| Key | Type | Default | Description | |-----|------|---------|-------------| | chromaDbUrl | string | "http://localhost:8000" | ChromaDB server URL | | chromaDbEnabled | boolean | true | Enable ChromaDB vector store |

AI Compression

| Key | Type | Default | Description | |-----|------|---------|-------------| | aiProvider | string | null | null | AI provider: "gemini", "openrouter", or "anthropic". Set to null to disable AI compression. | | aiModel | string | null | null | Override the default model for the selected provider | | aiCompressionTimeoutMs | number | 15000 | Timeout per AI compression call | | aiCompressionMaxRetries | number | 3 | Max retries for failed compressions | | aiSkipTools | string[] | [] | Tool names to skip during observation capture |

Enabling AI Compression

AI compression transforms raw tool outputs into structured, searchable knowledge. To enable it:

  1. Set the corresponding API key environment variable:

    # Pick one provider:
    export GEMINI_API_KEY="your-key"        # For Google Gemini
    export OPENROUTER_API_KEY="your-key"    # For OpenRouter
    export ANTHROPIC_API_KEY="your-key"     # For Anthropic Claude
  2. Create or edit ~/.config/opencode/memory/settings.json:

    {
      "aiProvider": "gemini"
    }

    Replace "gemini" with "openrouter" or "anthropic" based on your chosen provider.

  3. (Optional) Override the default model:

    {
      "aiProvider": "anthropic",
      "aiModel": "claude-sonnet-4-20250514"
    }

Default Models

| Provider | Default Model | Environment Variable | |----------|---------------|---------------------| | gemini | gemini-2.0-flash | GEMINI_API_KEY | | openrouter | anthropic/claude-haiku | OPENROUTER_API_KEY | | anthropic | claude-haiku-4-5-20251001 | ANTHROPIC_API_KEY |

Without aiProvider set, observations are stored with basic string truncation instead of AI-powered structured extraction.

Privacy

| Key | Type | Default | Description | |-----|------|---------|-------------| | secretDetectionEnabled | boolean | true | Detect and redact secrets before storage | | secretPatterns | array | [] | Additional custom secret patterns { name, pattern } |

Retention & Context

| Key | Type | Default | Description | |-----|------|---------|-------------| | recencyWindowDays | number | 90 | Days to boost recent results | | retentionDays | number | 365 | Days before observations expire | | contextTokenBudget | number | 2000 | Max tokens for context injection at session start |

Quality Checks

| Key | Type | Default | Description | |-----|------|---------|-------------| | tddEnabled | boolean | true | Enforce test-file checks on edit | | fileLengthWarn | number | 300 | Line count warning threshold | | fileLengthCritical | number | 500 | Line count critical threshold | | testFilePatterns | string[] | ["*.test.ts", "*.spec.ts", "*_test.go", "test_*.py"] | Glob patterns for test files | | toolRedirectRules | array | [] | Rules to deny or redirect specific tools |

Architecture

The system has four components, each built as a separate bundle under dist/:

  • CLI (dist/cli.cjs) — Orchestrates init, start, and stop commands
  • Plugin (dist/plugin.cjs) — Hooks into OpenCode's lifecycle events to capture observations, enforce quality checks, and inject context
  • Worker (dist/worker.cjs) — Bun HTTP daemon with bearer token auth that stores observations in SQLite (WAL mode), manages ChromaDB via chroma-mcp, runs AI compression, serves search queries, and broadcasts session-scoped SSE events
  • MCP Server (dist/mcp-server.cjs) — Exposes memory tools to the AI via the Model Context Protocol

Data Flow

OpenCode Session
  │
  ├─ session.created ──────► Worker /api/context/inject ──► SQLite + ChromaDB query ──► context injected
  │
  ├─ tool.execute.after ───► Worker /api/memory/save ────► privacy strip ──► SQLite write (session-scoped)
  │                                                         │
  │                                                         └──► AI compression queue ──► Gemini/OpenRouter/Anthropic
  │                                                                                        │
  │                                                                                        └──► update observation with structured data
  │
  ├─ session.idle ─────────► Worker /api/memory/save ────► session summary stored
  │
  ├─ SSE connection ───────► Worker /api/events?sessionId= ► session-scoped event stream
  │
  └─ session.compacted ───► Worker /api/context/inject ──► context restored after compaction

Security

  • Bearer token auth — A cryptographically random token is generated per worker instance and stored in the PID file with 0o600 permissions (owner read/write only). All non-health endpoints require Authorization: Bearer <token>.
  • Automatic token refresh — When the worker restarts, sessions automatically re-read the PID file and retry with the new token (single retry to prevent crash-loop storms).
  • Privacy stripping<private> tags are removed at the hook layer before data leaves the plugin process. Secrets (AWS keys, GitHub tokens, API keys, PEM keys, JWTs) are detected via regex and redacted with [REDACTED].
  • Secure API key transmission — Gemini API keys are sent via x-goog-api-key header rather than URL query parameters to prevent exposure in logs and proxy traces.
  • Localhost binding — The worker binds to 127.0.0.1 by default.

MCP Tools

Tools available to the AI through the MCP server:

| Tool | Description | |------|-------------| | search | Semantic or keyword search across observations. Accepts query, optional limit, type, and project filters. | | timeline | Chronological context around an observation. Accepts anchor (ID, session ID, timestamp, or query) with depth_before/depth_after. | | get_observations | Fetch full details for specific observation IDs. | | save_memory | Save a new observation with text, optional title and project. | | decisions | Search decision-type observations. Optional query and project filters. | | changes | Get recent change-type observations. Optional project and limit. |

The plugin also registers memory_search and memory_save as custom tools directly in OpenCode.

Plugin Hooks

| Hook | Purpose | |------|---------| | session.created | Injects relevant memory context at session start | | tool.execute.before | Enforces tool redirect rules (deny/redirect) before execution | | tool.execute.after | Captures tool executions as observations (with AI compression) | | session.idle | Saves session summaries when the session goes idle | | experimental.session.compacting | Saves active plan and task state before compaction | | session.compacted | Restores memory context after compaction | | file.edited | Checks for missing test files and warns on file length |

Local Development

Setup

Build and link the plugin into OpenCode's config directory using GNU Stow:

bun install
bun run build
bun run link        # stows into ~/.config/opencode/plugins/

Then add the MCP server to ~/.config/opencode/opencode.json (no plugin entry needed — stow handles that):

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["opencode-metis"],
  "mcp": {
    "mem-search": {
      "type": "local",
      "command": ["opencode-metis", "start-mcp"]
    }
  }
}

To remove the symlink:

bun run unlink      # unstow

Commands

bun test          # Run tests
bun run build     # Build all four bundles
bun run lint      # Lint and format with Biome
bun run typecheck # TypeScript type checking
bun run dev       # Watch mode for worker

Project Structure

src/
  cli/              CLI commands (init, start, stop, start-mcp) and worker lifecycle
  mcp/              MCP server and worker proxy
  plugin/           OpenCode plugin hooks, privacy filters, and custom tools
    hooks/          Hook handlers (session-start, tool-after, session-idle, etc.)
    privacy/        Secret detection, <private> tag stripping, privacy filter
    tools/          Custom MCP tools registered in OpenCode
  services/         Business logic layer
    ai-compression/ Multi-provider AI agents (Gemini, OpenRouter, Anthropic), queue processor
  shared/           Configuration, types, constants
  storage/
    sqlite/         Database, migrations, FTS5 indexing
    vector/         ChromaDB connection, chroma-mcp manager, vector sync
  worker/           HTTP server, router, routes, SSE broadcaster, auth middleware
test/               Integration and E2E tests

License

MIT