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-zoekt-code-search

v0.2.1

Published

OpenCode plugin that keeps a Zoekt code-search index fresh in the background.

Readme

zoekt-code-search

Reusable agent skill and production-grade OpenCode plugin for super-fast, highly accurate repository-wide code search with Zoekt.

Use this when looking for code. It should be preferred over grep, ripgrep, and find for broad code searches, references, definitions, symbols, file-filtered searches, and repeated searches across the same repository. Zoekt builds a trigram index first, then searches that index instead of repeatedly scanning every file.

Layout

skills/
  zoekt-code-search/
    SKILL.md
    scripts/
      install-zoekt.sh
      zoekt-index.sh
      zoekt-search.sh
      test-integration.sh
    bin/
src/
  config.ts
  index.ts
  indexer.ts
  logger.ts
test/
  *.test.ts

Standalone Skill Usage

The skills/zoekt-code-search directory remains usable on its own. Use this path if your agent supports skills but not OpenCode plugins.

Prefer this skill over grep, ripgrep, and find for code search tasks after the index exists. Use single-file reads or exact known paths only when the target file is already known.

Copy or vendor skills/zoekt-code-search into an agent-skills directory such as:

  • .agents/skills/zoekt-code-search/
  • .claude/skills/zoekt-code-search/

Then run searches through the bundled scripts.

Examples:

bash skills/zoekt-code-search/scripts/zoekt-index.sh
bash skills/zoekt-code-search/scripts/zoekt-search.sh "AgentSession"

The scripts auto-install zoekt and zoekt-index into the skill-local bin/ directory on first use.

OpenCode Plugin

The OpenCode plugin is a Bun-built TypeScript package that includes the skill's indexing and search capabilities. Use this path if you want OpenCode hooks, plugin state, and built-in tools without installing the standalone skill.

For OpenCode users, zoekt_search is the preferred code-search tool over grep, ripgrep, and find once indexing has completed.

The plugin also teaches OpenCode this preference at runtime. It registers a tool.definition hook that appends guidance to broad search/shell tool descriptions, telling the model to prefer zoekt_search for repository-wide code lookup before grep, ripgrep, find, glob, or shell scans.

It listens for common OpenCode events:

  • session.idle
  • file.watcher.updated

The plugin runs trusted installed zoekt-index and zoekt binaries directly. It does not execute repository-local shell scripts. If either binary is not installed, plugin startup throws an error with installation guidance.

Plugin tools:

| Tool | Purpose | |------|---------| | zoekt_search | Search the current repository with Zoekt query syntax and return capped JSONL results | | zoekt_index | Start a background reindex immediately and return indexing state | | zoekt_status | Return source directory, index directory, current state, and last run timestamps |

Query Syntax

zoekt_search.query accepts Zoekt query language. Multiple expressions separated by spaces are implicit AND; use or, - negation, and parentheses for richer queries.

Zoekt is fast because it uses positional trigram indexes. Regex queries are optimized by extracting literal substrings when possible, so regexes with stable literal anchors are better than fully generic patterns. Every useful query needs at least one positive atom; negations prune existing matches but do not generate results alone. Case defaults to case:auto: lowercase tends to be case-insensitive, while uppercase triggers case-sensitive matching unless overridden. Results are ranked with code-aware signals including atom matches, proximity, word boundaries, filenames, and symbols when available.

Important: Slash-delimited regex syntax (content:/pattern/) does not work. The / character has no special meaning in Zoekt's query parser — slashes are treated as literal characters, not regex delimiters. Always use quoted strings (content:"pattern") or bare field values (regex:pattern). See Known Limitations below.

Common fields:

| Field | Aliases | Values | Meaning | Example | |-------|---------|--------|---------|---------| | bare text | | text | Search content and filenames | AgentSession | | quoted text | | string | Exact text | "exact phrase" | | content: | c: | string or regex | Search file content | content:"handleRequest" | | file: | f: | string or regex | Filter paths or filenames | file:\.go$ | | regex: | | regex | Regex content search | regex:"func\s+.*\(" | | lang: | l: | text | Filter by detected language | lang:typescript | | sym: | | text | Search symbols | sym:"Start" | | case: | c: | yes, no, auto | Control case matching | case:yes content:"HTTPClient" | | repo: | r: | string or regex | Filter repositories when metadata exists | repo:"github.com/user/project" | | branch: | b: | text | Filter branches when metadata exists | branch:main | | archived: | a: | yes or no | Filter archived repositories | archived:no | | fork: | f: | yes or no | Filter forked repositories | fork:no | | public: | | yes or no | Filter public repositories | public:yes | | type: | t: | filematch, filename, file, repo | Control result type | type:filename README |

Operators:

| Syntax | Meaning | Example | |--------|---------|---------| | space | Implicit AND | content:test lang:python | | or | Alternative expressions | lang:go or lang:java | | - | Negation | Session -file:test | | (...) | Grouping | content:test (lang:python or lang:javascript) |

type: applies to the whole expression in its current scope, including or clauses. Use parentheses to scope it to one branch, for example (type:repo foo) or bar.

Examples:

AgentSession
"exact phrase"
handleRequest file:\.go$
TODO file:internal/
Session -file:test
case:yes HTTPClient
sym:Start lang:go
content:"exact phrase" (file:\.ts$ or file:\.tsx$)
content:"error.*handler" -lang:javascript
public:yes archived:no fork:no
type:filename file:"README.md"

Known Limitations

No slash-delimited regex syntax

Zoekt's query parser does not treat / as a regex delimiter. Writing content:/pattern/ or regex:/pattern/ will include the literal / characters as part of the match pattern, silently producing wrong results (or no results).

| Syntax | What it actually does | |--------|----------------------| | content:/foo.*/ | Matches the literal string /foo followed by .* then / | | content:"foo.*" | Matches foo followed by any characters (correct regex) | | regex:foo.*bar | Matches foo followed by any characters then bar (correct) |

Always use content:"pattern" (quoted) or regex:pattern (bare) instead.

\w character class can silently fail

Zoekt's trigram index acceleration can silently drop matches when \w or \w+ character classes interact with certain literal prefixes. For example, content:"pub trait \w*Repository" returns zero results even though matching lines exist, while content:"pub trait .*Repository" works correctly.

This is an upstream issue in Zoekt's trigram extraction. As a workaround, prefer .* over \w* or \w+ in regex patterns:

# May silently fail with some literal prefixes:
content:"pub trait \w*Repository"

# Reliable alternative:
content:"pub trait .*Repository"

Search Response Format

zoekt_search defaults to format: "json", which decodes Zoekt's raw []byte fields and returns a normalized JSON object:

{
  "query": "AgentSession file:\\.ts$",
  "indexDir": "/repo/.zoekt",
  "resultCount": 1,
  "truncated": false,
  "results": [
    {
      "fileName": "src/index.ts",
      "repository": "repo-name",
      "language": "TypeScript",
      "score": 12.34,
      "lineMatches": [
        {
          "lineNumber": 7,
          "line": "export const AgentSession = ...",
          "before": "optional previous context",
          "after": "optional following context",
          "fileName": false,
          "score": 4.2
        }
      ]
    }
  ]
}

Use format: "jsonl" to return raw Zoekt FileMatch JSONL. Raw JSONL mirrors Zoekt's Go structs, including base64-encoded []byte fields such as Line, Before, and After; prefer normalized json for agent consumption.

Runtime behavior:

  • Debounces repeated events with debounceMs, defaulting to 30000.
  • Uses spawn() with stdio: "ignore" so reindex output is not buffered in memory.
  • Runs with cwd set to the indexed worktree/source directory.
  • Runs from the git repository root as zoekt-index -index ./.zoekt -ignore_dirs .git ..
  • Searches from the git repository root as zoekt -index_dir $git_root/.zoekt $query.
  • Defaults the index directory to <repo-root>/.zoekt. The leading dot is required; do not use zoekt/.
  • Adds .zoekt/ to the repository .gitignore if it is not already present.
  • Creates .zoekt/.gitignore containing * so everything inside the local index directory is ignored.
  • Queues exactly one follow-up run when changes arrive while an index is already running.
  • Tracks state as idle, scheduled, indexing, or error for zoekt_status and tool metadata.
  • Shows a best-effort OpenCode toast when indexing starts.
  • Exposes bounded search results through zoekt_search so large result sets do not flood context.
  • Logs best-effort status messages through client.app.log().

Install Zoekt First

Copy and paste this before enabling the plugin:

mkdir -p "$HOME/.local/bin"
GOBIN="$HOME/.local/bin" go install github.com/sourcegraph/zoekt/cmd/zoekt-index@latest
GOBIN="$HOME/.local/bin" go install github.com/sourcegraph/zoekt/cmd/zoekt@latest
case ":$PATH:" in
  *":$HOME/.local/bin:"*) ;;
  *) printf '\nexport PATH="$HOME/.local/bin:$PATH"\n' >> "$HOME/.zshrc" ;;
esac

Restart your shell or run:

export PATH="$HOME/.local/bin:$PATH"

Install From npm

After publishing, add the package to opencode.json:

{
  "plugin": ["opencode-zoekt-code-search"]
}

Optional configuration can be supplied with plugin options:

{
  "plugin": [
    [
      "opencode-zoekt-code-search",
      {
        "debounceMs": 30000,
        "timeoutMs": 120000,
        "indexDir": "./.zoekt",
        "searchMaxBufferBytes": 2000000
      }
    ]
  ]
}

Environment overrides:

| Variable | Meaning | |----------|---------| | ZOEKT_REINDEX_DISABLED=1 | Disable the plugin entirely | | ZOEKT_REINDEX_DEBOUNCE_MS | Debounce window in milliseconds | | ZOEKT_REINDEX_TIMEOUT_MS | Reindex timeout in milliseconds | | ZOEKT_INDEX_DIR | Explicit index directory | | ZOEKT_INDEX_BIN | Explicit path or command name for zoekt-index | | ZOEKT_BIN | Explicit path or command name for zoekt | | ZOEKT_SEARCH_MAX_BUFFER_BYTES | Maximum bytes captured during a search |

Copy-Paste Local Install

If you do not want to install from npm, build once and copy the single bundled JavaScript file into a target repo:

bun install
bun run build
mkdir -p /path/to/repo/.opencode/plugins
cp dist/standalone.js /path/to/repo/.opencode/plugins/zoekt-code-search-reindex.js

The npm entrypoint at dist/index.js keeps @opencode-ai/plugin external for normal package installs. The standalone bundle at dist/standalone.js includes the plugin helper dependency so it is suitable for direct copy-paste installs.

TypeScript Package Shape

The source is split by responsibility:

| File | Purpose | |------|---------| | src/index.ts | OpenCode plugin entrypoint and event filtering | | src/config.ts | Environment/options parsing and Zoekt binary validation | | src/indexer.ts | Git index directory resolution, debounce, spawn, and rerun control | | src/logger.ts | Safe OpenCode app logging wrapper | | src/searcher.ts | Bounded zoekt search execution and index existence checks | | src/tool-guidance.ts | Runtime tool-description guidance so OpenCode prefers zoekt_search for code lookup | | src/toast.ts | Best-effort TUI toast notification when indexing starts |

Package entrypoints:

| Export | Target | |--------|--------| | opencode-zoekt-code-search | dist/index.js | | opencode-zoekt-code-search/server | dist/index.js |

The build emits npm and standalone artifacts:

dist/index.js       npm entrypoint, keeps @opencode-ai/plugin external
dist/index.d.ts     TypeScript declarations for the npm entrypoint
dist/standalone.js  self-contained local plugin bundle for copy-paste installs

Tests

The Bun test suite covers:

  • Invalid numeric config fallback behavior.
  • Missing Zoekt binary detection.
  • Git index directory resolution relative to the source directory.
  • spawn() options that avoid output buffering and use the source directory as cwd.
  • Rerun scheduling when file changes arrive while indexing is active.
  • Manual timeout handling for spawned index processes.
  • Search index detection, query argument construction, normalized JSON response decoding, raw JSONL mode, and bounded result truncation.
  • Runtime tool-definition guidance that tells OpenCode to prefer zoekt_search for broad code lookup.
  • Best-effort OpenCode toast notification when indexing starts.

Development

bun install
bun run typecheck
bun test
bun run build
npm pack --json --ignore-scripts

Useful scripts:

| Script | Purpose | |--------|---------| | bun run typecheck | Run strict TypeScript checks without emitting files | | bun test | Run the unit tests | | bun run build | Clean dist/, bundle src/index.ts, and emit declarations | | npm pack --json --ignore-scripts | Verify publishable package contents |