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

@ottoai/uignore

v1.2.0

Published

Unified ignore rules for AI coding tools — enforce .uignore exclusions via hooks for Claude Code and Gemini CLI

Readme

Ignoring files

This document provides an overview of the uignore (.uignore) feature for AI coding tools.

uignore gives you a single .uignore file that works across all supported AI coding tools, similar to .gitignore (used by Git). Adding paths to your .uignore file will exclude them from all supported AI tools, although they will still be visible to other services (such as Git).

Supported tools

| Tool | Hook | Config file | |---|---|---| | Claude Code | PreToolUse | .claude/settings.json | | Gemini CLI | BeforeTool | .gemini/settings.json | | Cursor | beforeReadFile | .cursor/hooks.json | | Windsurf | pre_read_code | .windsurf/hooks.json | | Codex CLI | — | coming soon |

How it works

When you add a path to your .uignore file, all supported AI tools will exclude matching files and directories from their operations. For example, when Claude Code tries to read a file, any paths in your .uignore will be automatically blocked before the tool call executes.

uignore registers a native hook with each AI tool. Before every file operation, the hook checks whether the target path matches any rule in your .uignore. If it does, the hook exits with code 2 — a hard block — and the tool is prevented from accessing the file.

uignore also supports hierarchical .uignore files. Starting from the target file's directory, it searches upward through parent directories to find all .uignore files up to the project root. Patterns from all discovered files are combined, with files closer to the root applied first.

A global ~/.uignore file is also supported and loaded before project-level files, giving you personal rules that apply across every project.

Since hooks are registered in config files that can be committed to git, the whole team gets the same rules automatically.

Installation

npm install -g @ottoai/uignore

Quick start (no global install)

You can use uignore without a global install via npx:

npx @ottoai/uignore init
npx @ottoai/uignore install --yes

Note: because npx resolves a new path each time, the hook command written into your settings files will point to the npx cache. Run uignore install once with a global install (npm install -g @ottoai/uignore) for a stable path, or use uignore update after each npm update -g.

How to use .uignore

To enable .uignore:

  1. Run uignore init in your project root to create a .uignore file.
  2. Run uignore install to register the hook with all supported AI tools.
  3. Commit both files to git so your whole team benefits.
uignore init      # creates a .uignore template
uignore install   # registers hooks for all supported tools
git add .uignore .claude/settings.json .gemini/settings.json .cursor/hooks.json .windsurf/hooks.json
git commit -m "chore: add uignore"

To add a file or directory to .uignore:

  1. Open your .uignore file.
  2. Add the path or file you want to ignore, for example: secrets/ or apikeys.txt.

You can update your .uignore file at any time. Changes take effect immediately — no restart required.

To remove a path from .uignore, delete the relevant line.

.uignore syntax

For the most part, .uignore follows the conventions of .gitignore files:

  • Blank lines and lines starting with # are ignored.
  • Standard glob patterns are supported (such as *, ?, and []).
  • Putting a / at the end will only match directories.
  • Putting a / at the beginning anchors the path relative to the .uignore file.
  • ! negates a pattern.

.uignore examples

You can use .uignore to ignore directories and files:

# Exclude your secrets/ directory and all subdirectories
secrets/

# Exclude your apikeys.txt file
apikeys.txt

You can use wildcards in your .uignore file with *:

# Exclude all .pem files
*.pem

# Exclude all .env files
.env
.env.*

You can exclude files and directories from exclusion with !:

# Exclude all .md files except README.md
*.md
!README.md

You can use hierarchical .uignore files for subtree-specific rules:

project/
  .uignore          # global rules — apply everywhere
  src/
    .uignore        # src-specific rules
    secrets/
      .uignore      # fine-grained rules for this subtree only

CLI reference

uignore init [dir] [-i] [--force]

Creates a .uignore file. Without flags, copies a sensible default template.

uignore init            # create .uignore from template
uignore init --interactive  # guided category selection
uignore init ./myapp    # create in ./myapp
uignore init --force    # overwrite an existing .uignore

With --interactive (-i), you are prompted to include/exclude each category: secrets, dependencies, build output, lock files, editor noise, and test coverage.

Use --force (or -f) to overwrite an existing .uignore without prompting.

uignore add <pattern> [--global]

Appends a pattern to your .uignore (or ~/.uignore with --global). Skips duplicates.

uignore add secrets/        # add to project .uignore
uignore add *.pem --global  # add to ~/.uignore

uignore remove <pattern> [--global]

Removes a pattern from your .uignore (or ~/.uignore with --global). Exits with code 1 if the pattern is not found.

uignore remove secrets/        # remove from project .uignore
uignore remove *.pem --global  # remove from ~/.uignore

uignore check <file> [dir]

Tests whether a file would be blocked by the active .uignore rules, including ~/.uignore.

uignore check secrets/apikey.txt
# ✗ Blocked: "secrets/apikey.txt" matches a rule in .uignore

uignore check src/index.ts
# ✓ Allowed: "src/index.ts"

uignore list [dir]

Lists all .uignore files found in the project tree and their active rules, including ~/.uignore if it exists.

uignore list
~/.uignore (2 rules)
  *.pem
  .env

.uignore (3 rules)
  secrets/
  node_modules/
  dist/

src/.uignore (1 rule)
  internal/

uignore validate [dir]

Validates all .uignore files for syntax errors and warns on patterns that do not match any git-tracked file (possible typo or obsolete rule). Exits with code 1 if issues found.

uignore validate
# ✓ .uignore: 5 rules, all valid
# ⚠  .uignore:3: pattern "scret/" does not match any tracked file

uignore diff [dir] [--all] [--json]

Shows which git-tracked files are blocked by the active .uignore rules. Useful for auditing rules before rolling out to a team.

uignore diff         # show only blocked files
uignore diff --all   # show blocked and allowed files
uignore diff --json  # machine-readable JSON output (for CI pipelines)
uignore diff --all --json  # JSON with both blocked and allowed arrays

With --json, the output format is:

{
  "total": 42,
  "blocked": ["secrets/key.pem"],
  "allowed": ["src/index.ts"]
}

The "allowed" key is only included when --all is also passed.

uignore doctor [dir]

Diagnoses common configuration problems:

  • Hook script exists at its compiled path
  • .uignore file exists and has valid syntax
  • Each tool's settings file exists and is valid JSON
  • uignore hook is registered for Claude Code and Gemini CLI
  • Hook path in settings still points to an existing file
uignore doctor
# ✓ Hook script found: /usr/local/lib/node_modules/@ottoai/uignore/dist/hook.js
# ✓ .uignore found and syntax valid
# ✓ Claude Code: hook registered and path valid
# ✗ Gemini CLI: hook path no longer exists — run `uignore update` to fix

Exits with code 1 if any issues are found.

uignore install [dir] [--claude] [--gemini] [--cursor] [--windsurf] [--codex] [--all] [-y] [--dry-run]

Registers the uignore hook with the specified AI tool(s). Defaults to --all.

  • If the tool's settings file does not exist, it is created automatically.
  • If it already exists, you are prompted to choose:
    • [1] auto — merge automatically
    • [2] manual — show a snippet to merge yourself
uignore install              # install for all supported tools
uignore install --claude     # Claude Code only
uignore install --gemini     # Gemini CLI only
uignore install --cursor     # Cursor only
uignore install --windsurf   # Windsurf only
uignore install --codex      # Codex CLI (coming soon)
uignore install --yes        # auto-merge without prompting (CI-friendly)
uignore install --dry-run    # preview what would be written

uignore update [dir] [--claude] [--gemini] [--cursor] [--windsurf] [--all]

Re-writes the hook command in each tool's settings file with the current absolute path. Run this after upgrading @ottoai/uignore to fix stale hook paths.

uignore update          # update all tools
uignore update --claude # Claude Code only

uignore uninstall [dir] [--claude] [--gemini] [--cursor] [--windsurf] [--codex] [--all]

Removes the uignore hook from the specified AI tool(s). Defaults to --all.

uignore uninstall              # remove from all supported tools
uignore uninstall --claude     # Claude Code only
uignore uninstall --gemini     # Gemini CLI only
uignore uninstall --cursor     # Cursor only
uignore uninstall --windsurf   # Windsurf only

uignore status [dir] [--claude] [--gemini] [--cursor] [--windsurf] [--all]

Shows whether the hook is installed for each tool and lists the active rules from .uignore.

uignore status
~/.uignore : found (/Users/you/.uignore)
.uignore   : found (/your/project/.uignore)

Claude Code
  Hook installed : yes
  Settings file  : /your/project/.claude/settings.json

Gemini CLI
  Hook installed : yes
  Settings file  : /your/project/.gemini/settings.json

Cursor
  Hook installed : yes
  Settings file  : /your/project/.cursor/hooks.json

Windsurf
  Hook installed : yes
  Settings file  : /your/project/.windsurf/hooks.json

Active rules (5):
  .env
  .env.*
  *.pem
  secrets/
  node_modules/

uignore install-git-hook [dir]

Installs a git pre-commit warning hook. Before each commit, it checks whether any staged files match .uignore rules and prints a warning — without blocking the commit.

uignore install-git-hook    # install in current repo
uignore uninstall-git-hook  # remove it

uignore completions <bash|zsh|fish>

Prints a shell completion script. Add to your shell config to get tab completion for all subcommands and flags.

# bash — add to ~/.bashrc
eval "$(uignore completions bash)"

# zsh — add to ~/.zshrc
eval "$(uignore completions zsh)"

# fish — add to ~/.config/fish/config.fish
uignore completions fish | source

uignore help [command]

Shows help for a specific command, or lists all available commands.

uignore help         # list all commands
uignore help init    # show detailed help for uignore init
uignore help diff    # show detailed help for uignore diff

uignore version

Prints the installed version.

uignore version
# @ottoai/uignore v1.1.0

dir defaults to the current directory.

Manual hook configuration

If you prefer to configure the hook yourself, add the following to each tool's settings file.

Claude Code — .claude/settings.json

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Read|Edit|Write|Glob|Grep|NotebookEdit",
        "hooks": [
          {
            "type": "command",
            "command": "node /path/to/uignore/dist/hook.js"
          }
        ]
      }
    ]
  }
}

Gemini CLI — .gemini/settings.json

{
  "hooks": {
    "BeforeTool": [
      {
        "matcher": "read_file|write_file|replace|read_many_files|grep_search|glob|list_directory",
        "hooks": [
          {
            "type": "command",
            "command": "node /path/to/uignore/dist/hook.js",
            "name": "uignore",
            "description": "Blocks file access matching .uignore rules"
          }
        ]
      }
    ]
  }
}

Cursor — .cursor/hooks.json

{
  "version": 1,
  "hooks": {
    "beforeReadFile": [{ "command": "node /path/to/uignore/dist/hook.js" }],
    "beforeTabFileRead": [{ "command": "node /path/to/uignore/dist/hook.js" }],
    "afterFileEdit": [{ "command": "node /path/to/uignore/dist/hook.js" }],
    "afterTabFileEdit": [{ "command": "node /path/to/uignore/dist/hook.js" }]
  }
}

Windsurf — .windsurf/hooks.json

{
  "hooks": {
    "pre_read_code": [{ "command": "node /path/to/uignore/dist/hook.js" }],
    "pre_write_code": [{ "command": "node /path/to/uignore/dist/hook.js" }]
  }
}

uignore install writes all files automatically with the correct absolute path.

Development

npm install        # install dependencies
npm run build      # compile TypeScript → dist/
npm test           # run all tests

The source is TypeScript. Tests use Node's built-in test runner with tsx for on-the-fly compilation — no separate compile step needed to run tests.

Project structure

src/
  hook.ts                   # PreToolUse / BeforeTool / beforeReadFile / pre_read_code hook
  pre-commit.ts             # git pre-commit warning script
  cli/
    index.ts                # entry point — USAGE + command dispatch
    shared.ts               # shared helpers (parseFlags, loadSettings, …)
    hook-script.ts          # HOOK_SCRIPT path constant
    completions.ts          # bash / zsh / fish completion strings
    commands/
      add.ts                # uignore add
      remove.ts             # uignore remove
      help.ts               # uignore help
      check.ts              # uignore check
      completions.ts        # uignore completions
      diff.ts               # uignore diff
      doctor.ts             # uignore doctor
      git-hook.ts           # uignore install-git-hook / uninstall-git-hook
      init.ts               # uignore init
      install.ts            # uignore install
      list.ts               # uignore list
      status.ts             # uignore status
      uninstall.ts          # uignore uninstall
      update.ts             # uignore update
      validate.ts           # uignore validate
      version.ts            # uignore version
    tools/
      types.ts              # shared interfaces
      claude.ts             # Claude Code hook config
      gemini.ts             # Gemini CLI hook config
      cursor.ts             # Cursor install / uninstall / status / update
      windsurf.ts           # Windsurf install / uninstall / status / update
      codex.ts              # Codex coming-soon stubs
  templates/
    .uignore                # default template created by `uignore init`
  __tests__/
    hook.test.ts            # integration tests
dist/                       # compiled output (tsc)