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

@dawkinsuke/hooks

v0.1.4

Published

Claude Code hooks toolkit — write hooks with TypeScript, compile to settings.local.json

Downloads

959

Readme

@dawkinsuke/hooks

Claude Code hooks toolkit — write hooks in TypeScript, compile to settings.local.json.

Write TypeScript extensions → hx build → hooks & settings ready

test_2x

Prerequisites

  • Bun v1.0.0 or later

Install

bun install -g @dawkinsuke/hooks

Optionally, install the Claude Code plugin so Claude can create hooks for you in natural language:

# In Claude Code
/plugin marketplace add ashigirl96/hx-cli
/plugin install hook-creator@dawkinsuke-hx-cli

Quick Start

With the plugin installed, ask Claude to create hooks in natural language:

> /hook-creator Create a hook that blocks git push

This generates .claude/extensions/block-push/index.ts:

import { defineExtension, deny } from "@dawkinsuke/hooks"

export default defineExtension((cc) => {
	cc.on("PreToolUse", "Bash", async (input) => {
		if (input.tool_input.command && /git\s+push/.test(input.tool_input.command)) {
			return deny("git push is blocked by hook policy")
		}
	})
})

You can keep going — just describe what you want:

> Create a hook that runs bun test before git commit when src/ files are staged
import { execSync } from "node:child_process"
import { defineExtension, modifyInput } from "@dawkinsuke/hooks"

export default defineExtension((cc) => {
	cc.on("PreToolUse", "Bash", async (input) => {
		const command = input.tool_input.command as string | undefined
		if (!command || !/git\s+commit/.test(command)) return

		const staged = execSync("git diff --cached --name-only", { encoding: "utf-8" })
		const hasSrcChanges = staged
			.split("\n")
			.filter(Boolean)
			.some((f) => f.startsWith("src/"))

		if (hasSrcChanges) {
			return modifyInput({ ...input.tool_input, command: `bun test && ${command}` })
		}
	})
})

The hook-creator skill automatically runs hx build — hooks are active immediately 🎉 Use hx activate to toggle extensions on/off.

Writing Extensions

Create .claude/extensions/<name>/index.ts (or use hx new <name> to scaffold):

import { defineExtension, deny, addContext } from "@dawkinsuke/hooks"

export default defineExtension((cc) => {
	// Command hook — compiled to .mjs, full Bun runtime access
	cc.on("PreToolUse", "Bash", async (input) => {
		if (input.tool_input.command?.match(/rm\s+-rf\s+\//)) {
			return deny("Destructive command blocked")
		}
	})

	// HTTP hook — POST to a URL on events (declarative, not compiled)
	cc.http("PostToolUse", {
		matcher: "Bash",
		url: "http://localhost:8080/audit",
		timeout: 5,
	})

	// Prompt hook — single-turn LLM evaluation (declarative)
	cc.prompt("PreToolUse", {
		matcher: "Edit",
		prompt: "Ensure the edit does not introduce security vulnerabilities.",
	})

	// Agent hook — multi-turn LLM verification (declarative)
	cc.agent("PostToolUse", {
		matcher: "Write",
		prompt: "Verify the file is syntactically correct.",
	})
})

| Type | Method | Compiled | Use Case | | ------- | ------------- | -------- | ---------------------------------------------------------------------- | | Command | cc.on() | .mjs | Programmatic logic with full Bun access | | HTTP | cc.http() | No | Forward events to an external webhook | | Prompt | cc.prompt() | No | LLM single-turn evaluation (PreToolUse/PostToolUse/PermissionRequest) | | Agent | cc.agent() | No | LLM multi-turn verification (PreToolUse/PostToolUse/PermissionRequest) |

Output Helpers

| Helper | Effect | Events | | -------------------- | -------------------------------------- | ------------------------------ | | deny(reason?) | Block tool / deny permission | PreToolUse, PermissionRequest | | allow(reason?) | Auto-approve tool / grant permission | PreToolUse, PermissionRequest | | ask(reason?) | Prompt user for confirmation | PreToolUse | | addContext(text) | Inject text into Claude's conversation | Most events | | modifyInput(input) | Rewrite tool input | PreToolUse, PermissionRequest | | accept(content?) | Accept elicitation | Elicitation, ElicitationResult | | decline() | Decline elicitation | Elicitation, ElicitationResult | | cancel() | Cancel elicitation | Elicitation, ElicitationResult |

Helpers are chainable:

deny("Dangerous").context("See docs for allowed commands")
allow().input({ command: "ls -la" }).context("Modified for safety")
addContext("warning").visible() // macOS notification

Examples

See examples/ for 16 copy-ready extensions covering every hook pattern.

CLI

hx build              Build all enabled extensions
hx init               Create .claude/extensions/ with a sample extension
hx new <name>         Scaffold a new extension
hx list               List all extensions and their status
hx activate           Toggle extensions on/off (interactive)
hx update             Update hx to the latest version
hx clean              Remove all hx artifacts
hx completions        Generate shell completion scripts

How It Works

.claude/extensions/my-ext/index.ts    # You write this
        ↓ hx build
.claude/hooks/my-ext.mjs              # Compiled hook script (one per extension)
.claude/settings.local.json           # Hook entries merged in
        ↓ Claude Code reads
Hooks fire on tool use, prompts, sessions, etc.
  1. Discover — Find all .ts files in .claude/extensions/
  2. Collect — Execute the factory to record hook registrations
  3. Bundle — Compile to a single .mjs per extension via Bun.build
  4. Merge — Write hook entries into settings.local.json (hx-managed hooks are tagged and never touch user hooks)

Acknowledgments

This project was inspired by pi-mono by Mario Zechner — particularly the coding-agent extension system.

License

MIT