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-instincts

v0.1.1

Published

OpenCode V2 plugin that observes session activity and distils it into ECC-compatible instincts, plus slash commands for the ECC instinct-cli.py workflow.

Downloads

488

Readme

opencode-instincts

An OpenCode V2 plugin that observes session activity (tool calls and prompts) into a per-project log, periodically has a cheap model distil that log into small learned "instincts," and exposes ECC's instinct CLI as slash commands. Nothing becomes live on its own: /evolve only writes inert markdown, the observer is off by default, and the plugin never injects instincts into a prompt.

Install

Add the plugin to your opencode.json (project or global at ~/.config/opencode/opencode.json):

{
  "$schema": "https://opencode.ai/config.json",
  "plugins": ["opencode-instincts"]
}

To set options, use the object form instead of the bare string:

{
  "$schema": "https://opencode.ai/config.json",
  "plugins": [
    {
      "package": "opencode-instincts",
      "options": {
        "observer": { "enabled": true, "model": "anthropic/claude-haiku-4-5" }
      }
    }
  ]
}

Restart OpenCode. Requires Python 3 on PATH (the plugin runs ECC's vendored instinct-cli.py as a subprocess; nothing else is installed).

Options

All options are optional; defaults shown:

{
  "disabled": false,
  "dataDir": null,
  "python": "python3",
  "observePrompts": true,
  "ignoreTools": ["todowrite", "todoread", "question", "instincts"],
  "ignoreAgents": [],
  "observer": {
    "enabled": false,
    "model": null,
    "runIntervalMinutes": 5,
    "minObservations": 20,
    "maxAnalysisLines": 300,
    "notify": true
  }
}

| Option | Description | | --- | --- | | disabled | Skip registering everything (hooks, commands, tool). | | dataDir | Absolute path to use instead of the default ECC data directory; relative values are ignored with a warning. Maps to CLV2_HOMUNCULUS_DIR for the vendored CLI. | | python | Python interpreter to run instinct-cli.py with. | | observePrompts | Record user prompt text (scrubbed, clipped to 2000 chars) as user_prompt observations. | | ignoreTools / ignoreAgents | Tool names / agent names to never observe. | | observer.enabled | Turn on the background distillation run described below. | | observer.model | "provider/model" to run generate.text with. When unset, the host's default model is used (and a one-time warning is logged). | | observer.runIntervalMinutes / observer.minObservations | Minimum time and minimum new observation count between automatic runs. | | observer.maxAnalysisLines | How many of the most recent observation lines to show the model per run. | | observer.notify | Post a synthetic summary message into the triggering session after a background run. |

What gets written, and where

Everything lands in ECC's existing on-disk layout so instinct-cli.py (and anything else that reads it) sees the same data; the only additions are the two bookkeeping files marked below:

${CLV2_HOMUNCULUS_DIR or $XDG_DATA_HOME/ecc-homunculus or ~/.local/share/ecc-homunculus}/
└── projects/<project-hash>/
    ├── observations.jsonl          # tool_start / tool_complete / user_prompt records
    ├── observations.archive/       # rotated out at 10 MB
    ├── observer.log                # plugin addition: one line per observer run
    ├── .observer-state.json        # plugin addition: last run time + line count for the gate
    └── instincts/
        ├── personal/<id>.md        # written by the observer
        └── inherited/<id>.md       # written by `/instinct-import`

Project identity (the <project-hash> above, or the shared global bucket when there is no git project) is resolved by calling ECC's own detect_project() in vendor/ecc/instinct-cli.py, so this plugin and the CLI agree unless resolution fails, in which case a warning is logged and the shared global bucket is used.

Commands

| Command | Does | | --- | --- | | /instinct-status | Show instinct status (project-scoped + global). | | /instinct-import <file\|url> [--dry-run] [--min-confidence N] [--scope project\|global] | Import instincts from a local file or an https:// URL. | | /instinct-export [--output file] [--domain d] [--min-confidence N] [--scope project\|global\|all] | Export instincts to stdout or a file. | | /evolve [--generate] [--limit N] | Cluster instincts into skill/command/agent candidates; --generate writes them under evolved/. | | /promote [id] [--dry-run] | Promote a specific instinct, or auto-promote everything that qualifies, to global scope. | | /instinct-projects | List known projects and their instinct counts. | | /instinct-prune [--dry-run] [--max-age N] | Delete pending instincts older than the TTL. | | /instinct-observe | Run the observer right now, ignoring the enabled/interval/threshold gates. |

Every command runs instinct-cli.py directly and posts its output back into the session as a synthetic message (resume: false); none of them start a model turn. /instinct-import and /promote pass --force, since typing the command is the confirmation that would otherwise come from the CLI's own y/N prompt.

The instincts tool

The plugin also registers a tool named instincts that the agent can call mid-turn. It only exposes the read-only subset of the CLI: status, projects, export (to stdout only, no --output), evolve (preview only, no --generate), promote --dry-run, and prune --dry-run. It never passes --force and can never import, write, or delete anything.

Observer behaviour and cost

When observer.enabled is true, every observed tool call or prompt checks whether enough time (runIntervalMinutes) and enough new observations (minObservations) have passed since the last run. When both are true, the plugin:

  1. Reads the most recent observation lines (clipped per-field and capped in total size) plus the existing instinct list, so the model updates instincts instead of duplicating them.
  2. Calls ctx.generate.text({ prompt, model }) once, with no tools available to the model.
  3. Parses <instinct>...</instinct> blocks out of the response, validates each one (id, trigger, confidence, domain, no stray --- in the body), and writes the valid ones to instincts/personal/.
  4. Logs the result to observer.log and, if observer.notify is true, posts a summary into the session.

This is at most one generate.text call per interval, on whatever model you set in observer.model. /instinct-observe runs the same logic on demand, bypassing the enabled/interval/threshold gates (it still needs a model, from observer.model or the host default).

Privacy

  • Observations contain scrubbed tool inputs/outputs and prompt text (the same credential-redaction pattern ECC's shell hook uses), clipped to a few thousand characters each.
  • Everything stays on disk under the data directory above. Nothing is sent anywhere except the one generate.text call per observer run, which goes to whichever model you configured.
  • Only instincts (small learned patterns), never raw observations, can be exported with /instinct-export.

How this differs from ECC

ECC's continuous-learning-v2 skill is built for Claude Code: a bash hook (observe.sh) appends observations on every PreToolUse/PostToolUse event, and a separate daemon (observer-loop.sh) periodically spawns a real Claude Code sub-agent (with Read/Write tools) that analyzes observations and writes instinct files itself.

OpenCode has no hook-daemon model and ctx.generate.text has no tools, so this plugin:

  • Observes through OpenCode's own tool.hook/session.hook("prompt") instead of a shell script.
  • Runs the observer in-process, as a single generate.text call triggered from inside the plugin (no daemon, no separate process to start or supervise). Since the model has no tools, the plugin itself parses the model's markdown output and writes the instinct files.
  • Installs nothing beyond the plugin itself: no hooks are added to any Claude Code settings.json, no scripts are copied anywhere.

vendor/ecc/instinct-cli.py (status/import/export/evolve/promote/projects/prune) is used unmodified, so instincts written by this plugin are fully compatible with ECC's own tooling.

Attribution

The instinct model, CLI, and observer prompt are from affaan-m/ECC, vendored verbatim at commit ef648e01899ba3e8dc6371642deaaf64b4477775 under vendor/ecc/ (MIT License; see NOTICE).

Licence

MIT. See LICENSE.