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

@snevins/nudges

v0.6.6

Published

Harness-agnostic nudges for agent workflows.

Downloads

651

Readme

@snevins/nudges

Portable retry nudges for Git, Codex, and Claude Code hooks.

nudges does one thing: when a configured event fires, it prints the next message for that event, blocks the action, and records progress. Rerun the same action to get the next message. After all messages for that event context have fired, the action passes.

Supported event adapters:

  • Git pre-commit
  • Git pre-push
  • Codex Stop for main-agent final responses
  • Codex subagent completion through transcript-classified Stop payloads
  • Codex ExitPlanMode via Plan Mode Stop payloads
  • Claude Code Stop for main-agent final responses
  • Claude Code SubagentStop
  • Claude Code ExitPlanMode

Requirements

  • Node.js 18+
  • Git
  • husky v9+ — only for the Git hooks (pre-commit / pre-push); initialize it with npx husky init first.
  • direnvrecommended for the Claude and Codex agent hooks. Their hook commands are bare (nudges stop-hook claude, nudges stop-hook codex) and only resolve when node_modules/.bin is on the PATH the agent launched with. direnv's .envrc (which nudges init manages) keeps it there for every launch from the repo. Without direnv, launch the agent via pnpm exec / npm exec instead (e.g. pnpm exec claude).

There are no runtime npm dependencies. husky and direnv are system tools, not npm packages — nudges integrates with them (writes .husky/* and .envrc) but cannot install them for you, because both do their real work in the shell, not in node_modules.

Why direnv (the UX decision)

The Claude hook commands used to carry a PATH="$CLAUDE_PROJECT_DIR/..." prefix so they were self-contained under any launch. We made them bare for a terser, less noisy .claude/settings.json. The cost: a hook now inherits PATH only from how claude was launched — Claude Code offers no committed-config way to put node_modules/.bin on a hook's PATH (settings env doesn't expand variables, CLAUDE_ENV_FILE reaches only the Bash tool, and the hook's working directory follows the session's cd, all verified empirically). direnv is the one option that makes plain claude just work for the whole team from a single committed .envrc, rather than requiring everyone to remember a special launch command. To keep the tradeoff safe, nudges init also installs a SessionStart guard that warns loudly when node_modules/.bin is missing from PATH, so a wrong launch (or a teammate who hasn't run direnv allow) fails visibly instead of silently skipping the hooks.

Quick start

Install nudges as a dev dependency, then set up the current repository:

npm i -D @snevins/nudges
npx nudges init

@snevins/nudges is a private scoped package. Make sure your npm client is authenticated to an account with access (npm login) before installing. The published tarball ships a prebuilt CLI, so there is no build step on install.

nudges init writes a starter .nudges.json (if one does not already exist), installs the Git hooks, and wires agent hooks for any provider it detects (.claude/ or .codex/). Re-run nudges init after cloning the repository on a new machine — Git hooks and agent hook commands use machine-local paths.

After nudges init, confirm the install took effect:

npx nudges --version            # CLI built and runnable
npx nudges status               # state reachable

If you use Codex or Claude Code, also check that the detected agent hooks were written as expected (.codex/hooks.json, .claude/settings.json). See the post-install checklist for the full set of checks.

npx nudges init                 # Git hooks + detected agent hooks
npx nudges init --no-agents     # Git hooks only
npx nudges init --claude --codex # force both providers
npx nudges init --force         # overwrite an existing .nudges.json

Then edit .nudges.json:

{
  "pre-commit": [
    "Diff: review the staged diff before committing.",
    "Validation: confirm tests or a targeted validation command were run."
  ],
  "pre-push": [
    "Branch: check that the branch contains only intended commits."
  ],
  "stop": [
    "Status: run git status and confirm only intended files changed."
  ],
  "subagent-stop": [
    "Subagent: verify the reported result names changed files, validation, and unresolved assumptions."
  ],
  "exit-plan-mode": [
    "Plan: review the plan before leaving plan mode."
  ],
  "user-prompt-submit": [
    "Prompt: rewrite the submitted prompt into a concise, actionable prompt for the next assistant. Do not answer the submitted prompt. Preserve the user's intent. Preserve any $skill_name or /command_name activations exactly. Include useful file references with verified line ranges when they would help, and omit file references rather than inventing unverified lines. Return only the rewritten prompt."
  ]
}

nudges init runs the installers below for you. You can also run them individually — for example to install a single Git hook, or to add an agent provider that was not present when you first ran init. The Git installers require husky to be initialized first (npx husky init):

npx nudges install git pre-commit
npx nudges install git pre-push
npx nudges install agent-stop codex
npx nudges install agent-subagent-stop codex
npx nudges install agent-exit-plan codex
npx nudges install agent-user-prompt-submit codex
npx nudges install agent-stop claude
npx nudges install agent-subagent-stop claude
npx nudges install agent-exit-plan claude
npx nudges install agent-user-prompt-submit claude

Installers invoke the bare nudges command (the CLI shim the package manager creates in node_modules/.bin from package.json bin) — no node, no dist path — so a committed .husky/* or .claude/settings.json works unchanged across git worktrees, machines, and dependency upgrades. Git hook installs require husky (v9+) to be initialized in the repo (run npx husky init first); they append a fenced managed block to the husky hook files .husky/pre-commit and .husky/pre-push that runs nudges event <hook> "$@" (husky v9 already puts node_modules/.bin on PATH, so the bare command resolves). Claude agent hook commands are the bare nudges stop-hook claude form. A Claude Code hook inherits the PATH that claude was launched with, so node_modules/.bin must be on PATH when you launch claude. The recommended, reliable way is direnv: nudges init writes a managed block to .envrc (PATH_add node_modules/.bin), so once you've installed direnv, hooked it into your shell, and run direnv allow, every claude launched from the repo — plain claude included — resolves the bare hooks. Without direnv, launch through your package runner instead: pnpm exec claude (or npm exec -- claude). Either way, nudges init also installs a SessionStart launch guard that warns loudly when nudges is not on PATH, so a wrong launch is obvious instead of silently skipping the hooks. The project root resolves from CLAUDE_PROJECT_DIR (which Claude Code exports to every hook), so no NUDGE_PROJECT_DIR is needed.

direnv is a system tool (not an npm dependency — it works at the shell level), so nudges manages the .envrc block and detects direnv but cannot install it for you; manage the block directly with nudges install direnv / nudges uninstall direnv. Codex agent hook commands are bare too (nudges stop-hook codex) and resolve the same way: node_modules/.bin comes from the launch PATH (the same .envrc serves codex — launch codex from the repo with direnv active), and the project root comes from the hook payload's cwd. Codex has no CLAUDE_PROJECT_DIR, but it sends cwd in the payload, which nudges chdirs to before resolving the root via git — so no NUDGE_PROJECT_DIR is needed. The SessionStart launch guard is claude-only (codex has no equivalent event), so for codex a missing node_modules/.bin surfaces as a normal hook error rather than a pre-emptive warning. The managed block coexists with any other commands already in the husky hook file; re-running install replaces only the managed block. If husky is not initialized, nudges install git exits with an error telling you to run npx husky init first; nudges init skips the Git hooks (with a message) and still writes .nudges.json and wires agent hooks.

Hook installation writes to .husky/pre-commit, .husky/pre-push, .codex/hooks.json, or .claude/settings.json. In sandboxed workspaces or mounted repos, those paths may require elevated write access from the host environment. Agent config updates are serialized per provider config and written through a temporary file before replacement, so parallel agent hook installs should leave valid JSON.

Configure nudges

.nudges.json must be a JSON object. Each event key maps to an array of strings:

{
  "pre-commit": [],
  "pre-push": [],
  "stop": [],
  "subagent-stop": [],
  "exit-plan-mode": [],
  "user-prompt-submit": []
}

Missing config, missing event keys, and empty arrays are no-ops. user-prompt-submit is different from the retry-style events: it accepts zero or one configured message. One message is used as an optimizer instruction for the first submitted prompt in a provider session. Put rewrite policy in that message, including whether to preserve $skill_name or /command_name activations and whether to include verified file references. The optimized prompt is injected as canonical replacement instructions for Codex. Claude Code does not honor UserPromptSubmit context as a replacement, so nudges blocks and erases the original prompt with an optimized prompt to resubmit. Later prompts in the same session are no-ops. More than one message blocks prompt processing as a config error.

When a nudge blocks an action, it prints:

NUDGE pre-commit 1/2

Review the staged diff before committing.

Address this nudge, then rerun the gated command.

State is keyed by event, context, and config hash. Changing .nudges.json starts a new nudge cycle.

For a config with two pre-commit nudges, the first commit passes on the third attempt: two blocked attempts, then success.

Commands

nudges init [--claude] [--codex] [--no-agents] [--force]
nudges event <event> [hook-arg...]
nudges stop-hook <codex|claude>
nudges subagent-stop-hook <codex|claude>
nudges exit-plan-hook <codex|claude>
nudges user-prompt-submit-hook <codex|claude>
nudges install git [pre-commit] [pre-push]
nudges install agent-stop <codex|claude>
nudges install agent-subagent-stop <codex|claude>
nudges install agent-exit-plan <codex|claude>
nudges install agent-user-prompt-submit <codex|claude>
nudges install agent-launch-guard claude
nudges install direnv
nudges uninstall <git|agent-stop|agent-subagent-stop|agent-exit-plan|agent-user-prompt-submit|agent-launch-guard|direnv> [...]
nudges status [event]
nudges reset [event]
nudges update
nudges whats-new [--version <version>]
nudges --version

Use nudges event directly for custom wrappers:

NUDGE_CONTEXT=session-123:/path/to/artifact nudges event before-submit

Use stable context values such as session IDs, artifact paths, branch names, commit ranges, or workflow IDs. Do not use attempt numbers, timestamps, process IDs, temp paths, or random values.

Wrappers launched outside the repository can pin config and state explicitly:

NUDGE_CONFIG_FILE=/path/to/.nudges.json \
NUDGE_STATE_DIR=/path/to/repo/.git/nudges \
nudges event before-submit

Status & reset

Inspect recorded progress for the current config:

nudges status
nudges status pre-commit

Clear recorded progress (start a fresh cycle):

nudges reset
nudges reset pre-commit

Remove managed hook entries (Git, Codex, or Claude):

nudges uninstall git
nudges uninstall agent-stop claude
nudges uninstall agent-exit-plan claude

uninstall only removes hooks nudges install wrote. Unmanaged hooks are preserved.

Hook behavior

Git hook state is stored under .git/nudges/state.json. A changed staged content, pushed ref set, or nudge config starts a new cycle.

pre-push reads stdin to identify pushed refs. husky runs the hook file with sh -e "$hook" "$@", forwarding both the remote args and the pushed-ref stdin to .husky/pre-push, so nudges event pre-push "$@" receives them automatically. To run other pre-push checks alongside nudges, add them as extra command lines in .husky/pre-push. Stdin is consumed once, so if more than one command needs the pushed-ref stdin, capture it once and replay it to each:

stdin_file=$(mktemp "${TMPDIR:-/tmp}/pre-push.XXXXXX")
trap 'rm -f "$stdin_file"' EXIT
cat > "$stdin_file"

nudges event pre-push "$@" < "$stdin_file"
other-pre-push-check "$@" < "$stdin_file"

Agent stop hooks read provider JSON on stdin, run the shared stop event, and emit provider-native blocking JSON when a nudge fires. The installers configure project files:

  • Codex: .codex/hooks.json
  • Claude Code: .claude/settings.json

Codex does not expose a distinct ExitPlanMode hook event. nudges install agent-exit-plan codex installs a Codex Stop hook instead, and nudges exit-plan-hook codex only runs the shared exit-plan-mode event when the Stop payload's transcript_path and turn_id identify a Plan Mode turn in the Codex transcript. Other Codex Stop payloads are ignored by that adapter. The general nudges stop-hook codex path ignores plan-mode Stop payloads so the stop event remains for non-plan Codex stops.

Subagent hooks run the shared subagent-stop event. Claude Code uses its native SubagentStop lifecycle event. Codex installs another managed Stop hook and classifies subagent completions from the transcript referenced by transcript_path: thread_source: "subagent" or source.subagent.thread_spawn routes to subagent-stop, while thread_source: "user" or source: "cli" routes to main stop. If Codex transcript classification is unavailable, ambiguous Stop payloads remain compatible with the existing main stop behavior.

Claude Code exit-plan hooks use a PreToolUse matcher for ExitPlanMode and run the shared exit-plan-mode event.

User prompt submit hooks run before a prompt is submitted. Codex installs a managed UserPromptSubmit entry in .codex/hooks.json; Claude Code installs the same lifecycle event in .claude/settings.json. On Codex success, nudges emits:

{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"Use the following optimized prompt as the canonical user request for this turn.\nTreat the originally submitted prompt only as source material for this replacement; do not answer it separately.\n\n<optimized_prompt>\n<optimizer output>\n</optimized_prompt>"}}

For Claude Code success, nudges emits a blocking response whose reason contains the optimized prompt to resubmit. Claude Code erases the original blocked prompt from context.

The nested optimizer is guarded by NUDGES_USER_PROMPT_SUBMIT_ACTIVE=1 to avoid recursion. Codex is run with codex exec from the project directory and Claude Code is run with claude -p in non-persistent print mode. Both nested providers return plain-text optimized prompts. Neither provider exposes a native UserPromptSubmit field that mutates the submitted prompt in place. Installer entries omit timeout, so Codex uses its default 600 seconds and Claude Code uses its default 30 seconds. Failures block the submitted prompt with an actionable reason; in Claude Code this also means the original prompt is erased from Claude's context.

Update

nudges is published to npm, so updates go through your package manager:

npm i -D @snevins/nudges@latest        # latest release
npm i -D @snevins/[email protected]         # a pinned version

nudges update prints this guidance. After updating, re-run nudges init in each repository so the managed hooks point at the refreshed install.

Show the latest changelog entry bundled with the install:

npx nudges whats-new
npx nudges whats-new --version 0.5.2

Stop prompt examples

Use Label: action prompts so the blocked retry is easy to scan.

Final response:

{
  "stop": [
    "Status: run git status and confirm only intended files changed.",
    "Validation: name the exact tests or checks run in the final response.",
    "Handoff: call out any remaining risk or skipped validation before ending."
  ]
}

Plan mode:

{
  "exit-plan-mode": [
    "Scope: confirm the plan maps every requested item to a concrete artifact.",
    "Questions: ask the user before implementation if ownership, naming, release path, or validation is ambiguous."
  ]
}

Subagent workflows:

{
  "subagent-stop": [
    "Subagent: verify the reported result names changed files, validation, and unresolved assumptions.",
    "Recursive review: if a subagent reviewed another agent's work, require the review to cite the artifact it inspected."
  ]
}

Prompt submit optimization:

{
  "user-prompt-submit": [
    "Prompt: rewrite the submitted prompt into a concise, actionable prompt for the next assistant. Do not answer the submitted prompt. Preserve the user's intent. Preserve any $skill_name or /command_name activations exactly. Include useful file references with verified line ranges when they would help, and omit file references rather than inventing unverified lines. Return only the rewritten prompt."
  ]
}

Clarifying questions are useful when the next edit would choose a public name, change install behavior, publish a release, require credentials, or pick between incompatible validation gates. Ask before acting in those cases; otherwise keep working from the repo evidence.

Codex Stop hooks cover final responses, plan-mode exits, and subagent completion paths. Plan-mode exits are identified from the Stop payload's transcript_path/turn_id pair and routed to exit-plan-mode; subagent completion is identified from transcript session metadata and routed to subagent-stop. Raw Codex Stop stdin is still ambiguous, so classification is best-effort and older synthetic payloads without transcript metadata continue through the main stop event.

Upgrading from 0.3.x

The 0.4.x rewrite collapses the previous bash + Node implementation into a single Node CLI. After upgrading:

  • Re-run nudges install git to refresh the managed block in the husky hook files .husky/pre-commit and .husky/pre-push (the old wrappers referenced the removed nudge-event binary). Initialize husky first with npx husky init if you have not already.
  • Remove old managed Codex/Claude hook entries with NUDGES_MANAGED=1 that call nudge-stop-hook or nudge-exit-plan-hook, then run nudges install agent-stop <provider> and nudges install agent-exit-plan claude. The rewrite does not keep compatibility shims for the removed hook binaries. If those legacy managed entries are still present, agent hook install exits with a cleanup message instead of adding another hook. Unmanaged custom hook entries are preserved; remove or update any custom calls to removed binaries yourself because nudges will not convert them.

The old binaries (nudge-event, nudge-stop-hook, nudge-exit-plan-hook) are gone. Custom callers should switch to nudges event ..., nudges stop-hook ..., and nudges exit-plan-hook ....

Develop

See CONTRIBUTING.md for setup, validation, packaging, and CI guidance.

Run the full CI-equivalent local check:

pnpm install
pnpm run verify

Run live Codex and Claude CLI checks when those CLIs are available:

scripts/qa-agent-hooks.sh --live

Build the CLI:

pnpm run build

pnpm run build compiles src/nudges.ts to dist/nudges.js. The same build runs automatically through the prepare script when the package is packed or published, so the npm tarball ships a prebuilt dist/.

The release workflow runs after the test workflow passes on a push to main. It publishes @snevins/nudges to npm when package.json carries a version not yet on npm, and independently creates the matching v<version> GitHub release when it does not exist. Both checks are idempotent, so a re-run after a partial failure fills in only what is missing. Bump the version in package.json to cut a release.

Post-install checklist:

npx nudges --version
npx nudges status
sed -n '1,80p' .husky/pre-commit
sed -n '1,120p' .codex/hooks.json

More detail

See docs/spec.md for the exact behavior contract, exit codes, context derivation, adapter outputs, and package contents.