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

@theglitchking/claude-plugin-runtime

v0.1.0

Published

Shared postinstall + SessionStart + CLI-subcommand runtime for Claude Code plugins distributed via npm. Handles skill symlinking, update policy config, hook registration with plugin/npm dedup, and update nudge/auto-apply.

Readme

@theglitchking/claude-plugin-runtime

Shared postinstall + SessionStart + CLI-subcommand runtime for Claude Code plugins distributed via npm. One small package (~13 KB, zero runtime deps) that handles the boilerplate every plugin in the Glitch Kingdom marketplace needs:

  • Skill symlinking — bundled skills in node_modules/ get linked into <project>/.claude/skills/ so Claude Code can discover them.
  • Default policy config — writes <project>/.claude/<plugin>.json with { "updatePolicy": "nudge" } if it doesn't exist.
  • Hook registration with dedup — registers a SessionStart hook in <project>/.claude/settings.json, but skips when the Claude Code plugin marketplace version is already enabled globally, or when the project already has a matching hook.
  • SessionStart update checkoff / nudge / auto policies with a 3s network budget, 6h cache, CI-skip, and plugin/npm dedup at runtime.
  • CLI subcommand registrationupdate, policy, status, relink for terminal parity with the slash commands.

Install

npm install --save @theglitchking/claude-plugin-runtime

Usage

Three entry points. Each plugin wires them up once and inherits every behavior change made to this package.

Postinstall

In scripts/link-skills.js (or whatever you call your postinstall script):

#!/usr/bin/env node
import { runPostinstall } from "@theglitchking/claude-plugin-runtime";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");

runPostinstall({
  packageName: "@theglitchking/my-plugin",
  pluginName: "my-plugin",
  configFile: "my-plugin.json",
  skillsDir: "skills",
  packageRoot,
  hookCommand: "node ./node_modules/@theglitchking/my-plugin/hooks/session-start.js",
});

Wire it in package.json:

{
  "scripts": { "postinstall": "node scripts/link-skills.js" }
}

SessionStart hook

In hooks/session-start.js:

#!/usr/bin/env node
import { runSessionStart } from "@theglitchking/claude-plugin-runtime";

await runSessionStart({
  packageName: "@theglitchking/my-plugin",
  pluginName: "my-plugin",
  configFile: "my-plugin.json",
  reconcile: (projectRoot) => {
    // Plugin-specific setup: .mcp.json reconciliation, scaffolding, etc.
    // Thrown errors are caught and logged — the update check still runs.
  },
});

Register it in the plugin's hooks/hooks.json:

{
  "hooks": {
    "SessionStart": [
      { "hooks": [
          { "type": "command",
            "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start.js\"" }
      ]}
    ]
  }
}

CLI subcommands

In your commander-based CLI:

import { program } from "commander";
import { registerUpdateCommands } from "@theglitchking/claude-plugin-runtime";
import { fileURLToPath } from "node:url";
import { dirname, resolve, join } from "node:path";
import { spawnSync } from "node:child_process";

registerUpdateCommands(program, {
  packageName: "@theglitchking/my-plugin",
  pluginName: "my-plugin",
  configFile: "my-plugin.json",
  onAfterUpdate: (cwd) => {
    const linker = join(cwd, "node_modules", "@theglitchking", "my-plugin", "scripts", "link-skills.js");
    spawnSync(process.execPath, [linker], {
      cwd,
      env: { ...process.env, INIT_CWD: cwd },
      stdio: "inherit",
    });
  },
});

Policy resolution

  1. <ENV_PREFIX>_UPDATE_POLICY env var (one-shot override).
  2. <project>/.claude/<configFile>updatePolicy.
  3. Default: nudge.

ENV_PREFIX defaults to the upper-snake form of pluginName (e.g. semantic-pagesSEMANTIC_PAGES). Override via envPrefix if you want something different.

Env opt-outs

| Variable | Effect | |----------|--------| | <PREFIX>_SKIP_LINK=1 | Skip skill symlinking in postinstall. | | <PREFIX>_SKIP_HOOK_REGISTER=1 | Skip settings.json hook registration. | | <PREFIX>_UPDATE_POLICY=off\|nudge\|auto | One-shot policy override. |

Dedup between plugin and npm install

When a user has both a Claude Code plugin install and the npm dep:

  • At install time: runPostinstall scans ~/.claude/settings.jsonenabledPlugins. If <pluginName>@*: true, it skips registering the project-level hook.
  • At runtime: runSessionStart checks process.env.CLAUDE_PLUGIN_ROOT. If it's set (the hook was invoked by the plugin marketplace) and the project's .claude/settings.json contains a SessionStart entry whose command includes the plugin name, the plugin instance defers to the project-registered one.

Detection is substring-based on the plugin name — no magic tags or marker fields. Any command string in settings.json containing the plugin name is treated as "someone else is handling this," and we step aside.

Full authoring recipe

See docs/PLUGIN_AUTHORING_SCAFFOLD.md for a copy-paste template for a new plugin, including file layout, commander CLI wiring, slash commands, and the testing checklist.

Reference implementation

@theglitchking/semantic-pages is the canonical consumer. Diff its scripts/link-skills.js and hooks/session-start.js against a new plugin to see the minimal per-plugin delta (just the reconcile body and the config field names).

License

MIT