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

copilot-tap-extension

v2.0.4

Published

Copilot CLI extension for background event emitters, event streams, and session injection.

Downloads

1,719

Readme


Copilot CLI already runs background tasks, but their output sits idle until you check it. This extension adds filtering and auto-injection on top of that capability.

Background commands and agent prompts produce output line by line. An EventFilter decides what to drop, what to store, and what to push into your session. Important events arrive without you asking.

| Without this extension | With it | | --- | --- | | You check background output manually | Important lines are pushed into your conversation | | No way to filter noisy output | Rules drop noise, keep context, inject signal | | No scheduled prompt re-runs | Prompts repeat on a timer or fire when idle | | Output stays in the background task | Matched events arrive in your session as they happen |

Who is this for?

  • You tail logs and want failures injected into your session while you keep coding.
  • You maintain a repo and want PR reviews, CI failures, or new issues surfaced automatically.
  • You run long builds and want to know when they finish or break -- without watching.
  • You poll an API or dashboard and want the agent to react when something changes.
  • You re-ask the same prompt periodically and want it on a timer or running whenever idle.
  • You build external tools in any language and want them available inside Copilot without touching the SDK.

Get started

Prerequisites: Node.js ≥ 20 and Copilot CLI.

Important: This extension requires Copilot CLI to be running with experiments enabled. Extensions and background-task features are gated behind this flag.

How to enable experiments:

# Start Copilot CLI with experiments on
copilot --experimental

Or, if Copilot CLI is already running, type the following inside the session:

/experimental

Once enabled, the experimental state persists across sessions -- you only need to do this once. You can also disable it at any time with copilot --no-experimental.

Install via npx (recommended)

# Install globally (available in all projects)
npx copilot-tap-extension

# Install locally (project-scoped, committed with your repo)
npx copilot-tap-extension --local

This installs the bundled extension, the /tap-loop skill, the /tap-monitor skill, the /tap-goal skill, and the agent instructions to the appropriate Copilot directory. Run npx copilot-tap-extension --help for all options.

To update to the latest version, re-run the same command with --force:

npx copilot-tap-extension --force

Install from source

git clone https://github.com/amitse/copilot-tap-extension
cd copilot-tap-extension
npm install
cp tap.config.example.json tap.config.json
copilot

On Windows, replace cp with copy.

The config file tells the extension which emitters to auto-start. The example defines a heartbeat emitter:

{
  "emitters": [
    {
      "name": "heartbeat",
      "command": "node ./examples/heartbeat.mjs",
      "autoStart": true,
      "eventFilter": [
        { "match": "booting", "outcome": "drop" },
        { "match": "warning|error", "outcome": "inject" },
        { "match": ".*", "outcome": "keep" }
      ]
    }
  ]
}

This runs the heartbeat script on session start, drops boot messages, injects warnings and errors, and keeps everything else in the stream.

Once inside the session, describe what you want in natural language. You can also use /tap-loop to set up scheduled prompts directly:

"Watch my build logs and tell me if anything fails"

"/tap-loop 5m check for new PR review comments"

"/tap-monitor tail -f /var/log/app.log"

"/tap-goal migrate the repo to the new API and keep going until tests pass"

"Tail the API logs, inject errors, drop health checks"

The agent translates these into emitter and filter configurations behind the scenes.

How it works

An EventEmitter is a background worker attached to your session. There are two kinds:

  • A CommandEmitter runs a shell command and captures stdout line by line.
  • A PromptEmitter runs an agent prompt -- once, on a recurring interval, or whenever the session is idle.

Each emitter writes to an EventStream, an in-memory log of accepted output. The stream is created automatically and shares the emitter's name.

For CommandEmitters, an EventFilter decides what happens to each line. It is an ordered list of regex rules -- first match wins:

| Outcome | What happens | | --- | --- | | drop | Discarded. Never enters the stream. | | keep | Stored in the EventStream for later review. | | surface | Stored and shown in the session timeline. | | inject | Stored, shown, and pushed into your conversation. |

Outcomes are inclusive: inject implies surface, and surface implies keep. Only drop is outside this chain.

PromptEmitter output bypasses the filter and always injects.

A SessionInjector controls whether stream updates are pushed into your session proactively. Enable it when you want important events to arrive as they happen.

Filters are hot-swappable while the emitter runs. ownership="modelOwned" lets the agent tune rules; ownership="userOwned" locks them to your specification.

Emitters are temporary by default and last only for the current session. Set lifespan="persistent" to save an emitter to config and restore it next session.

Run schedules control timing: continuous (command runs until stopped), timed (repeats on an interval), oneTime (runs once), or idle (prompt re-runs when the session has nothing else to do).

Extend with providers

External processes can register tools with your Copilot session through the Provider Interface. A provider connects via WebSocket to the ※ tap gateway (port 9400), authenticates with a token, and declares tools — no Copilot SDK knowledge required.

# Provider connects and registers a "greet" tool
TAP_PROVIDER_TOKEN=ptk-... node my-provider.mjs

Once connected, the tool appears alongside the existing ※ tap tools. Copilot can invoke it like any other tool, and the call is routed through the gateway to your provider.

Providers can be written in any language that supports WebSocket — Node.js, Python, Go, Rust, or anything else.

Provider guide — Quick start, protocol reference, and examples.

What you can do

Watch something in the background

Tell Copilot to watch a log, build, or command. It creates a CommandEmitter, filters the output, and only interrupts you when something needs attention.

"Start a deploy watcher that tails our CI logs.
 Drop health checks, inject any failures or rollbacks."

You keep coding. Twenty minutes later, Copilot interrupts: "Run 48291: deployment rollback triggered on prod."

Monitor a command with self-tuning filters

Use /tap-monitor to run a shell command continuously while a companion agent periodically reads the output and updates the filter expressions to separate noise from signal automatically.

/tap-monitor tail -f /var/log/app.log
/tap-monitor 10m docker logs -f mycontainer

The command stream starts with a sensible initial notifyPattern. Every few minutes (configurable) the companion reviews recent log lines and calls tap_set_event_filter if the patterns need adjustment. The filter tightens itself based on real output — no manual tuning required.

Loop a prompt on a schedule

A PromptEmitter re-runs an agent prompt at a fixed interval. Useful for PR comments, CI status, or ticket queues.

/tap-loop 15m Check for new failing CI runs or PR review comments.
         Summarize only actionable items.

Every 15 minutes the agent scans and reports back. No news means no interruption.

Run a prompt when idle

Use /tap-loop idle to re-run a prompt whenever the session has nothing else to do. Set maxRuns to cap iterations.

/tap-loop idle Scan for new issues labeled urgent. Summarize what changed.

The prompt fires immediately, then re-fires after each idle period. It stops after reaching the iteration limit.

Work toward a goal autonomously

Use /tap-goal to create an idle goal loop that keeps advancing a concrete objective until it finishes, hits a blocker, or reaches its iteration budget. Goals are explicit, control commands are user-owned, and the loop should stop itself only when the objective is actually complete or blocked.

/tap-goal migrate the repo to the new API and keep going until tests pass

The skill creates a temporary idle PromptEmitter with a self-contained goal prompt. Each iteration inspects its own emitter state, assesses progress, takes the next small action, validates when relevant, and stops the emitter when the goal is complete or blocked. As the remaining iteration budget gets low, the prompt shifts into wrap-up mode so it leaves a useful handoff instead of starting broad new work.

Goal loops default to 50 iterations unless you specify another budget.

Use /tap-goal status to list current goal emitters.

Use /tap-goal stop <name> or /tap-goal clear <name> to stop a specific goal emitter. If there is exactly one active goal-* emitter, the skill can stop it without a name; otherwise run /tap-goal status first and then stop the goal by name.

Use /tap-goal resume <objective> to start a new loop from an objective. Stopped goal loops do not preserve resumable internal state; resuming creates a new emitter from the supplied objective.

Because /tap-goal uses an idle PromptEmitter, it is best when the session has natural idle gaps. For always-busy autopilot-style flows, prefer a timed prompt loop or hook/session-injector based delivery so follow-up context can still reach the session.

Tune the filter live

The recommended approach is a keep-all bootstrap: start with no EventFilter rules so all output flows into the stream. Read the stream history to learn what the output looks like, then add rules progressively:

1. Drop the noise:    { "match": "health_check|heartbeat", "outcome": "drop" }
2. Inject the signal: { "match": "error|failure|rollback",  "outcome": "inject" }
3. Keep the rest:     { "match": ".*",                       "outcome": "keep" }

Rules can be added or changed while the emitter is running. You never need to restart it to adjust filtering.

Repo layout

.github/
  extensions/tap/extension.mjs  # extension entry point (loads the runtime)
  skills/tap-loop/                  # /tap-loop skill for scheduled and idle prompts
  skills/tap-monitor/               # /tap-monitor skill for self-tuning command monitors
  skills/tap-goal/                  # /tap-goal skill for autonomous goal loops
  skills/tap-create-provider/       # /tap-create-provider skill for scaffolding external tool providers
  copilot-instructions.md       # agent guidance for using this extension
src/
  emitter/                      # supervisor, lifecycle, spawn, line router
  streams/                      # EventStream store and notification dispatcher
  provider/                     # WebSocket gateway for external tool providers
  tools/                        # tool definitions (emitters, streams, filters)
  config/                       # persistent config store (tap.config.json)
  format/                       # display formatters for emitters and streams
  session/                      # session port abstraction
  util/                         # normalization, text, time, path helpers
  hooks.mjs                     # session lifecycle hooks
  tap-runtime.mjs               # runtime factory (wires everything together)
tap.svg                         # ※ mark — the tap icon
docs/
  evolution-of-tap-icon.html    # design evolution: 20 agents, 20 metaphors, one mark
examples/heartbeat.mjs          # demo CommandEmitter
evals/                          # eval harness and test cases
tap.config.example.json         # starter config (copy to tap.config.json)
PLAN.md                         # ubiquitous language and design decisions

Further reading

| Document | When to read it | | --- | --- | | Reference | Look up tool parameters, config fields, or the event pipeline | | Provider guide | Add external tools to Copilot via the WebSocket provider interface | | Use cases and patterns | Recipes for deploy watchers, PR monitors, log tailers, and more | | Evals | Run or extend the automated test suite | | Copilot instructions | Understand or customize how the agent uses this extension | | Implementation plan | Ubiquitous language and naming conventions for contributors | | Evolution of the ※ icon | 20 metaphors, 10 variants, one mark — the design story behind ※ tap |

Contributing

Before opening a PR, run the local checks:

npm run check              # syntax check
npm run evals:smoke        # smoke test
npm run evals:validate-modes  # interactive vs prompt-mode gap

The runtime has no production dependencies. Dev dependencies (@github/copilot-sdk, yaml) are used for the eval harness and extension loading.

If you add a new tool or change the event pipeline, update the reference. If you add a new workflow pattern, add it to use cases.

License

MIT