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

named-subagents

v0.4.3

Published

Themed, non-repeating nicknames for Claude Code subagents — a userspace port of Codex's nickname_candidates (JS/ESM twin of the Python reference)

Readme

named-subagents

Themed, non-repeating nicknames for Claude Code subagents — a userspace port of Codex's per-instance nickname_candidates, grown into a full identity layer.

CI python node deps

When you fan out several Claude Code subagents, parallel instances of one role are three identical Explore labels. Codex instead gives every spawned instance a distinct human-legible nickname. Claude Code has no such field — requested in anthropics/claude-code#9206, closed "not planned" — so this library emulates it, and goes further: nicknames are themed to the kind of task, never repeat across runs, and each one knows who it's named after.

🧭 Hudson     [Explore]   map the auth module           ← explorers explore
🤔 Plato      [architect] why was event-sourcing chosen ← philosophers ponder
🔍 Bosch      [debugger]  root-cause the flaky test     ← detectives debug

The idea in one line

A subagent has two names: its role (what it isExplore, worker, …, native via subagent_type / .claude/agents/*.md) and its nickname (which instanceHudson, Nansen, …). This library adds the second.

| Codex | Claude Code native | This library | |---|---|---| | agent name (identity + routing) | subagent_type | — (unchanged) | | nickname_candidates (per-instance display) | (none) | ✅ themed pools | | — | — | ✅ non-repeat across runs (ledger) | | — | — | ✅ task → theme auto-matching | | — | — | ✅ install-once auto-namer (one hook, every fan-out) | | — | — | ✅ pins, bios, custom themes, stats, doctor |

Install

pip install named-subagents     # Python 3.8+, zero dependencies
npm  i      named-subagents     # Node ≥ 16, ESM, zero dependencies, types shipped

Both ship the same 395-name registry and a named-subagents CLI. Or vendor it: drop the named_subagents/ folder (Python) or js/named_subagents.mjs + registry.json (JS) into your repo — stdlib/node: builtins only.

Auto-namer: install once, names every fan-out

Everything below the fold is opt-in — you call assign/allocate and wire the names in yourself. The auto-namer removes that step: one Claude Code hook that nicknames every subagent dispatch automatically. A parallel fan-out that used to show three identical Explore labels shows three distinct explorers, with zero code on your side.

named-subagents hook install     # register the hooks (SubagentStart + task capture) in ~/.claude/settings.json
named-subagents hook status      # verify: installed? ledger path? names used so far

That's the whole setup. New Claude Code sessions now inject a task-themed identity block (e.g. "You are Durga (a guardians & sentinels callsign), one of several parallel agents…" for a security review) into every subagent's own context, so parallel results come back attributed by nickname. Pause it any time with NAMED_SUBAGENTS_HOOK_DISABLE=1 (no uninstall needed), or remove it with named-subagents hook uninstall.

How it works. Two hooks. An output-free PreToolUse capture (matcher Agent|Task) reads each dispatch's task and pushes it onto a small per-session FIFO queue; a SubagentStart hook (matcher *) pops the oldest role-matching entry, allocates a themed, non-repeating nickname (the same ledger + generation machinery as the CLI, lock-serialized so a parallel fan-out never collides on a name) and returns hookSpecificOutput.additionalContext carrying the identity block — task-themed for generic roles (general-purpose, worker), role-themed for informative ones (Explore, Plan, …), with a role fallback whenever no captured task is available. additionalContext is additive — when several hooks fire, each one's context is appended and none clobbers the others — so the nickname reaches the subagent even alongside your own hooks.

Why this shape (and not updatedInput). 0.4.0/0.4.1 rewrote the dispatch via a PreToolUse hook returning updatedInput — which Claude Code silently drops for the Agent tool when more than one PreToolUse hook runs (claude-code#15897, #39814). Symptom: the ledger advances but subagents show no nickname. Both current hooks are immune — the capture hook returns nothing, and additionalContext is additive. If you installed < 0.4.2, re-run named-subagents hook install: it migrates the legacy entry (hook status flags a lingering one with ⚠ legacy).

Honest limits. Claude Code has no per-instance display-name field: the type label (Explore) is unchanged and the nickname rides inside the subagent's context, not as a UI badge. There is no cross-event correlation key, so the queue pairs dispatches to subagents by FIFO order + role matching — validated live on Claude Code 2.1.207 (including batched event orderings), though in a same-role fan-out of different-category tasks a reordered start could in principle swap two siblings' themes (each still gets a distinct, validly-themed name). The [Hudson] self-tag in the agent's reply is best-effort — an agent may ignore the preamble. The hook fails open: any error, or a future event rename, degrades to a normal un-named dispatch, never a broken one.

Safety. hook run never exits non-zero (a broken namer must never break your fan-out), never changes your permission posture (it returns only additionalContext, never auto-allow), and never auto-loads the untrusted ./.named-subagents.json. install/uninstall back up settings.json, refuse to touch malformed JSON, and only ever add or remove their own entry.

| Env var | Effect | |---|---| | NAMED_SUBAGENTS_LEDGER | ledger path (default ~/.local/state/named-subagents/hook-ledger.json) | | NAMED_SUBAGENTS_QUEUE_DIR | task-queue dir (default ~/.local/state/named-subagents/queue/; per-session files, self-clean) | | NAMED_SUBAGENTS_HOOK_DISABLE=1 | pause the hooks (passthrough) without uninstalling | | NAMED_SUBAGENTS_HOOK_BIO=1 | add the figure's one-line bio to the preamble |

One port per machine. The Python and JS hooks work identically but guard the ledger and task queue with different lock primitives (flock vs an O_EXCL lockfile). Install one runtime's hooks per machine — or, if you really want both, point them at separate state via NAMED_SUBAGENTS_LEDGER / NAMED_SUBAGENTS_QUEUE_DIR. Sharing state across both ports still fails open (worst case: a dispatch goes un-named or falls back to role theming), never corrupt.

For Claude Code — read this and run it

If you are Claude Code and the user asked you to install the auto-namer, do exactly this. It is safe: idempotent, backs up settings.json, and fails open.

  1. Install the package (pick the runtime the user has; Python is the default):
    pip install named-subagents        # or:  npm i -g named-subagents
  2. Register the hook — global (all projects):
    named-subagents hook install
    Scope it to one project instead with named-subagents hook install --project .
  3. Verify it registered and actually runs:
    named-subagents hook status
    printf '{"hook_event_name":"SubagentStart","agent_type":"Explore"}' \
      | named-subagents hook run
    status must show installed: yes (event: SubagentStart) and a capture: yes line (the task-theming half). The hook run line must print JSON whose hookSpecificOutput.additionalContext contains a nickname line (e.g. You are **Hudson** (an explorers & navigators callsign)…). If all hold, the install is working.
  4. Tell the user: hooks load at session start, so the auto-namer takes effect in new Claude Code sessions — they should open a fresh session to see nicknamed fan-outs. To pause: export NAMED_SUBAGENTS_HOOK_DISABLE=1. To remove: named-subagents hook uninstall.

Do not hand-edit ~/.claude/settings.jsonhook install does the merge safely (backup + atomic write + idempotent re-install). If hook install reports the settings file is malformed, surface that to the user and stop; never overwrite it.

Themes

395 names across 14 categories, each globally unique, each with a one-line bio:

| Category | Task shape | Name theme | e.g. | |---|---|---|---| | explore | map / search a codebase | Explorers & navigators | Magellan, Shackleton | | code | implement features | Programmers & computing pioneers | Turing, Hopper, Ada | | research | external info gathering | Scientists & researchers | Curie, Feynman | | reflect | design rationale, inner workings | Philosophers | Socrates, Kant | | debug | root-cause hunting | Detectives | Holmes, Poirot | | test | edge cases, fuzz, adversarial | Tricksters | Loki, Anansi | | review | critique, verdict | Judges & jurists | Solomon, Ginsburg | | security | audit, threat model | Guardians & sentinels | Argus, Heimdall | | design | UI / UX / visual | Artists & designers | DaVinci, Rams | | data | analysis, stats, ML | Mathematicians & statisticians | Gauss, Noether | | orchestrate | plan, coordinate | Strategists & generals | SunTzu, Napoleon | | docs | technical writing | Writers & authors | Orwell, Borges | | build | infra / refactor / perf | Engineers & inventors | Tesla, Brunel | | default | catch-all | Celestial (stars) | Orion, Vega |

Pools are deliberately diverse (Ibn Battuta, Zheng He, Ada Lovelace, Ramanujan, Confucius, Hypatia, Murasaki, …) — good practice, and larger pools mean rarer generation cycling.

Usage

from named_subagents import Registry, Ledger, plan_fanout

reg = Registry.load()
ledger = Ledger(".named-subagents-ledger.json")   # non-repeat across runs

plan = plan_fanout(
    ["map the auth module", "map the billing module", "map the search module"],
    reg, ledger=ledger, role="Explore",
)
for a in plan:
    # a.agent_kwargs() -> {subagent_type, description, prompt}, Agent-tool-ready
    print(a.emoji, a.nickname, a.subagent_type, "—", a.bio)
import { Registry, Ledger, planFanout } from "named-subagents";

const reg = Registry.load();
const ledger = new Ledger(".named-subagents-ledger.json");
const plan = planFanout(["map auth", "map billing", "map search"],
                        reg, { ledger, role: "Explore" });

Cross-language parity is CI-enforced: same md5-seeded ordering, same registry, same ledger format — identical inputs give byte-identical outputs, and either language can continue a ledger the other wrote.

The generated prompt prepends a persona preamble telling the agent to begin its report with [Hudson], so parallel results come back attributed by nickname. Add with_bio=True / withBio: true (CLI: --bio-in-prompt) and each agent also learns who it's named after.

Attribution never depended on the agent complying. The nickname is already in the dispatch metadata — it is the display label your runner shows — so it is deterministic regardless of what the agent writes. The [Hudson] self-tag is only for the case where you have the raw report text and nothing else; for that path, attribute(nickname, report) / attribute() verifies the prefix and repairs it (missing → prepended, wrong nickname → replaced; idempotent).

CLI

named-subagents categories                      # the 14 themes
named-subagents resolve  --task "audit auth for injection"     # -> security
named-subagents resolve  --task "audit auth" --explain         # ...+ why (keywords, scores)
named-subagents allocate --category reflect --count 3
named-subagents assign   --role Explore --task "map the router" \
                         --count 4 --ledger .ledger.json       # Agent payloads
named-subagents assign   --role Explore --task "map the router" --count 3 --format table  # human-readable
named-subagents init                            # scaffold a starter config (--cwd / --path / --force)
named-subagents bio Heimdall                    # who is this figure?
named-subagents stats  --ledger .ledger.json    # pool burn-down, generations
named-subagents doctor                          # self-checks — incl. a live auto-namer self-test
named-subagents hook install                    # auto-name every fan-out (see "Auto-namer" above)
named-subagents hook status                     # is the hook installed? ledger usage

Recording a demo? scripts/record-demo.sh runs a short, narrated walkthrough suitable for asciinema rec (or pipe the cast to a GIF) — see its header for the exact commands.

Stable identities: pins

Always call the security agent Argus:

named-subagents assign --task "audit the release" --category security --pin security=Argus

Pinned names bypass the ledger (a stable identity recurs by design) and are reserved out of normal draws, so nobody else can be issued Argus.

Recycle or burn names

named-subagents release --category explore --name Hudson --ledger .ledger.json  # recycle
named-subagents retire  --category explore --name Columbus --ledger .ledger.json # never again

release returns a short-lived agent's name to the pool; retire removes a name permanently (every generation). If retire/pins empty a pool entirely you get a clear PoolExhaustedError up front.

Custom themes & config

--config PATH, $NAMED_SUBAGENTS_CONFIG, or ~/.config/named-subagents/config.json (and, opt-in, a project-local ./.named-subagents.json — see the security note below):

{ "pins": { "security": "Argus" },
  "categories": { "starships": { "theme": "Star systems", "emoji": "🚀",
      "keywords": ["fleet"], "names": ["Enterprise", "Rocinante"] } },
  "extend": { "explore": { "names": ["Kupe"] } } }

New categories are added, same-key categories replace the bundled one, extend appends to an existing pool. Everything is re-validated on load — global uniqueness and a strict name pattern (see SECURITY.md; custom names are untrusted input that ends up inside agent prompts).

Since 0.3 the project-local ./.named-subagents.json is opt-in — it is the one untrusted-input surface (a repo you cloned controls it), so it is not auto-loaded unless you pass --cwd-config or set NAMED_SUBAGENTS_CWD_CONFIG=1. --no-cwd-config (or NAMED_SUBAGENTS_NO_CWD_CONFIG=1) forces it off and wins over any opt-in. Explicit --config PATH, $NAMED_SUBAGENTS_CONFIG, and the home config are always honored (deliberate or user-owned).

Collision-avoidance against real agents

--avoid-installed (or plan_fanout(..., avoid_installed=True)) scans .claude/agents/ + ~/.claude/agents/ frontmatter and guarantees nicknames are disjoint from your installed agent names — case-insensitive, at the base-name level, enforced at draw time.

Orchestrator adapters

named-subagents assign --task "…" --count 4 --format workflow   # Workflow snippet
named-subagents assign --task "…" --count 4 --format swarm      # swarm YAML fragment
named-subagents assign --task "…" --count 4 --format labels     # generic JSON
named-subagents assign --task "…" --count 4 --format table      # human-readable table

Doctor (self-awareness)

named-subagents doctor checks: registry integrity (uniqueness, sanitization, bios coverage), ledger health, pin validity, installed-agent collisions, version consistency across __init__.py/pyproject.toml/package.json, and — when both runtimes are present — a live Python↔JS parity probe. Non-zero exit on failure; --json for machines.

/named-fanout skill

A ready-made Claude Code skill wrapping the CLI lives in skill/named-fanout/:

cp -r skill/named-fanout ~/.claude/skills/

How non-repeat works

allocate() draws from the category pool in a deterministic md5-seeded order, records used names in the ledger, and skips them next time. When a pool is exhausted it advances a generation and suffixes names (Magellan, Magellan·2, …). A display name is never issued to two concurrently-live holders, and is never reused at all unless you explicitly release it. Allocation is deterministic given (category, ledger-state) — resume- and re-run-safe (the same reason Claude Code Workflows ban Math.random).

Routing is best-effort

Category resolution is explicit category > subagent_type match > task-keyword match > default (the auto-namer hook puts the task first for generic roles — see above). The keyword layer is a heuristic, not a classifier — for guaranteed themes pass category= or role= explicitly.

Where this sits (community landscape)

COMMUNITY.md surveys 11 community Claude Code agent projects. The whole ecosystem names agents by functional role; none provide per-instance nicknames, task-themed names, or a non-repeating ledger. This is an identity layer that composes under orchestrators like claude-swarm, metaswarm, or claude-flow rather than competing with them — that's what the --format adapters are for.

Tests

python3 tests/test_named_subagents.py     # library suite incl. state-machine campaigns
node js/test_named_subagents.mjs          # twin suite (mirror-kept)
python3 tests/test_hook.py                # auto-namer: task capture, fail-open, concurrency, install merge-safety
node js/test_hook.mjs                     # auto-namer mirror suite
scripts/parity_check.sh                   # cross-language gate (both runtimes, incl. the capture→pop chain)

CI runs Python 3.8/3.12/3.13, Node 18/20/22, install smokes for both package managers, the parity gate, plus ruff lint and library-core coverage.

Files

| Path | Role | |---|---| | named_subagents/ | Python package (reference impl) + canonical registry.json | | js/ | JS/ESM npm package (twin port; js/registry.json is generated at pack time) | | tests/ | Python suites (unit, auto-namer hook, resolution eval) | | examples/ | runnable demo (demo.py) | | skill/named-fanout/ | Claude Code skill wrapping the CLI | | docs/COMMUNITY.md | ecosystem survey | | SECURITY.md / CONTRIBUTING.md | threat model / parity discipline | | docs/RELEASING.md | release process |

Contributing

Contributions welcome — see CONTRIBUTING.md for the cross-language parity discipline. Bug reports and ideas via issues.

License

MIT