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

@hasna/hooks

v0.7.10

Published

Open source hooks library for AI coding agents - Install safety, quality, and automation hooks with a single command

Readme

@hasna/hooks

Open source hooks library for AI coding agents - Install safety, quality, and automation hooks with a single command

npm License

Install

npm install -g @hasna/hooks

CLI Usage

hooks --help
  • hooks install
  • hooks list
  • hooks search
  • hooks remove
  • hooks categories
  • hooks info
  • hooks doctor
  • hooks run

Compact Output

CLI commands default to compact, agent-friendly output. List and search commands show essential fields, cap terminal rows, and print hints for deeper inspection. Use detail flags when you need more context:

hooks list                  # compact, capped list
hooks list --all            # show all rows
hooks list --verbose        # include descriptions
hooks search git --limit 5  # cap result rows
hooks info gitguard         # full metadata for one hook
hooks docs gitguard         # README preview
hooks docs gitguard --verbose
hooks list --json           # stable machine-readable full data

MCP tools follow the same gradual disclosure pattern: list/search/log/profile tools return compact summaries by default, while explicit flags such as compact:false, verbose:true, or a detail tool like hooks_info return full records.

Codewith-native hooks

@hasna/hooks includes unprefixed Codewith-native hook names:

  • session-startSessionStart digest and additional context.
  • prompt-guardUserPromptSubmit guard for pasted fake policy/freeze/run-this-now content.
  • pre-bashPreToolUse Bash gate for staged secrets scans, scoped destructive-operation blocks, and risky-op comms checks.
  • worktree-guardPreToolUse guard for managed repos worktree boundaries and file-tool-like payloads touching protected Hasna scopes.
  • stop-syncStop turn-end heartbeat/evidence best effort.
  • knowledge-context — deterministic Knowledge context packs for SessionStart, UserPromptSubmit, and SubagentStart.

For Codewith, the installer is renderer-safe by default: it emits a TOML fragment instead of mutating managed ~/.codewith/config.toml.

hooks install session-start prompt-guard pre-bash worktree-guard stop-sync knowledge-context --target codewith

The scoped destructive-operation guard does not block every cleanup command. It blocks resolved shell/file-tool targets that threaten / or a system root (/usr, /etc, /bin, /lib, /var, /boot, /home, /Users, and the other FHS and macOS equivalents), ~/.hasna, configured workspace roots, Hasna division/scope roots, or active repo/worktree roots, including recursive rm, rsync --delete, destructive find, and destructive git clean / git reset --hard forms.

It also blocks by shape: a destructive target containing a command substitution or variable expansion immediately followed by / is checked as the shell would render it if that expansion returned empty, so rm -rf "$(anything)"/* and rm -rf "$VAR"/* are refused whatever the expansion is. Wrapped forms (bash -c, su -c, eval, ssh host '…') are unwrapped first. See hooks/pre-bash/README.md for the full rules, the deliberate exemptions (${VAR:?}, bare "$(cmd)" with no trailing separator), and the recommended safe form.

Apply that fragment through configs or the managed config renderer. A direct write path exists only for explicit local/test use:

hooks install knowledge-context --target codewith --apply-codewith --codewith-config /tmp/codewith-config.toml

Custom and remote hooks

A hook is defined by a manifest — { name, version, description, events, script, args?, timeout_ms? } — where script is a relative path or inline content. Hooks come from three sources: the bundled registry, a user custom directory, or a remote registry.

timeout_ms is an optional positive integer (milliseconds). Omit it for no timeout. A timeout of 0 is never a real value: a manifest or MCP call that passes 0 (or a negative value) is rejected, and the SDK treats a non-positive option as not provided — falling back to the manifest value, or to no timeout when the manifest has none. So timeout_ms: 0 can never mean "kill immediately"; either a positive bound applies or there is no bound at all.

Install custom hooks from a local directory, a git URL, or a manifest URL:

hooks install ./my-hook            # directory with manifest.json
hooks install [email protected]:org/hook-repo.git
hooks install https://example.com/hooks/my-hook/manifest.json

Custom hooks land in ~/.hasna/hooks/hooks/<name>/. A custom hook with the same name as a bundled hook takes precedence (visible in hooks info <name>).

Trust model. Every hook script is pinned by sha256 in ~/.hasna/hooks/hooks.lock and the SQLite hooks table. hooks run verifies the script hash before executing; if the script changed, the run is refused:

hooks trust <name>   # re-pin the current script content
hooks update         # re-register hooks and refresh pins

Registry server. hooks serve exposes the local store over HTTP — catalog, artifacts, and the published lock:

hooks serve --port 39428            # publish key resolves from HASNA_HOOKS_API_KEY / HOOKS_API_KEY only
# GET /health, GET /api/v1/catalog, GET /api/v1/hooks/:name/:version,
# PUT /api/v1/hooks (publish, requires the key), GET /api/v1/lock

Cloudflare registry (opt-in). Presence of an API URL selects the remote registry; absence means local. There is no mode concept.

hooks init --cloudflare --api-url https://registry.example.com --api-key <vault-key-name>
hooks sync            # fetch catalog + lock from the API, verify sha256, update the local store
hooks sync --dry-run  # print the plan without changing anything

hooks init --cloudflare stores the API URL and a vault key NAME in ~/.hasna/hooks/config.json — never the key value. Serve with the key resolved from the vault:

secrets exec <vault-key-name> --as HASNA_HOOKS_API_KEY -- hooks serve

Cloudflare provisioning. hooks cf deploy creates the D1 database and R2 bucket via the Cloudflare API, then prints the exact wrangler commands for the worker upload (the worker needs the workerd target, which only wrangler can bundle):

export CF_API_TOKEN=...   # resolve from the vault, never paste the value
hooks cf deploy --account-id <id> --dry-run   # plan first
hooks cf deploy --account-id <id>

The worker (src/cf/worker.ts) implements the same API routes against D1 + R2, with artifacts at hook_artifacts/<name>/<version>.json. See src/cf/wrangler.toml.example.

Storage

Hooks stores data locally by default in ~/.hasna/hooks/ and uses SQLite directly for hook event history. The package owns its database schema and migrations; it does not depend on the deprecated shared runtime or its CLI. The repo includes its own PostgreSQL migration definitions for the optional hooks storage push|pull|sync commands. Use the hooks log commands to inspect local hook event data.

hooks storage status --json
HASNA_HOOKS_DATABASE_URL=postgres://... hooks storage push --tables hook_events,feedback --json
hooks storage pull --json
hooks storage sync --json

Configure database storage with HASNA_HOOKS_DATABASE_URL or fallback HOOKS_DATABASE_URL.

Storage backend

Hooks storage has one setting with two values: which data backend, not where anything is deployed.

| HASNA_HOOKS_STORAGE_BACKEND (fallback HOOKS_STORAGE_BACKEND) | meaning | | --- | --- | | sqlite | the on-box SQLite file in ~/.hasna/hooks/ (default) | | postgresql | the PostgreSQL database named by HASNA_HOOKS_DATABASE_URL |

Leave it unset and the backend is inferred exactly as before: postgresql when a database URL is configured, sqlite otherwise. An unrecognised value is an error, not a silent fall back to SQLite.

The former deployment-mode variables HASNA_HOOKS_STORAGE_MODE and HOOKS_STORAGE_MODE, and their local / hybrid / remote / self-hosted / cloud values, are retired. They are not read; setting one raises an error naming the replacement variable and the backend to use (local became sqlite, everything else became postgresql). Deployment location was never a property of the data layer, so it is no longer expressed as one.

Runtime model

This package is an npm CLI, MCP server, and static dashboard package. Installing and running hooks needs nothing deployed anywhere — the SQLite backend is the default and requires no server.

Data Directory

Data is stored in ~/.hasna/hooks/.

License

Apache-2.0 -- see LICENSE