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

@bricnbrac-purl/cli

v0.1.2

Published

Purl Atelier Map capture CLI for Codex and Claude Code

Readme

@bricnbrac-purl/cli

The purl CLI connects a local Git repository to Purl's Atelier Map. It replaces the developer-local Python bootstrap in tools/atelier_capture/ with a distributable, agent-neutral bridge that captures both Codex and Claude Code activity.

The public npm package connects to https://atelierapi.bricnbrac.com by default.

Verified on macOS (this development machine): packed-tarball install, purl connect <code>, purl connect merging into a repository with pre-existing Codex/Claude Code hooks (unrelated handlers preserved byte-for-byte), both providers' hooks firing for real Claude Code activity, MapBootstrap ingestion, offline hook capture + purl flush recovery, and purl disconnect (hooks removed with backups, backend installation revoked, OS Keychain entry deleted). Windows has not yet been run on real hardware - see the handoff's Windows validation note before shipping.

Install from npm

Install globally, then connect from the Git repository you want Purl to capture:

npm install --global @bricnbrac-purl/cli
cd /path/to/your/repository
purl connect

Or run it without a global installation:

cd /path/to/your/repository
npx @bricnbrac-purl/cli connect

If Purl Web gave you a pairing code, append it to either command, for example:

purl connect ABCD-1234
# or
npx @bricnbrac-purl/cli connect ABCD-1234

Build a local package

cd cli
npm install
npm run build
npm test
npm run type-check
npm pack

Then, from a target repository:

npm install /path/to/bricnbrac-purl-cli-0.1.0.tgz --prefix ./some-local-install
./some-local-install/node_modules/.bin/purl connect

Configuration

| Variable | Default | Purpose | |---|---|---| | PURL_API_URL | https://atelierapi.bricnbrac.com | Base URL the pairing client and uploader talk to. Set http://localhost:8080 explicitly for local backend development. |

purl connect accepts --insecure-file-store as an explicit opt-in fallback: if the OS credential store (macOS Keychain / Windows Credential Manager / Linux Secret Service) is unavailable, the CLI fails with a recovery message unless this flag is passed, in which case the capture token is written to a single 0600-permission file under the Purl data directory instead. There is no silent plaintext fallback. The stable hook runner carries the current platform's native keyring package, so background capture uses the same OS credential store without requiring a file copy.

Commands

purl connect [--agent codex,claude-code] [--insecure-file-store]
                          # start pairing, open the browser
purl connect ABCD-1234    # consume a short-lived one-time code shown by Purl Web
purl status               # show paired project, providers, last capture activity
purl disconnect           # remove Purl's hooks and local credentials for this repo
purl doctor               # diagnose missing credentials, stale hooks, backend reachability
purl flush [--root <path>] # retry the local spool now
purl map init [--agent claude-code|codex] [--timeout-ms N] [--max-budget-usd N]
                          # drive a headless, read-only Claude Code or Codex session to deeply
                          # index the repo and replace the shallow bootstrap feature tree (below)
purl help                 # same as --help/-h
purl version               # same as --version/-v
purl atelier hook --provider <codex|claude-code>
                          # internal: invoked BY agent hooks, not by a human
purl atelier mcp           # internal: invoked BY agent hooks as an MCP server

purl map init

purl connect's initial project-view feature tree is built from a single shallow LLM call over a static snapshot (file tree, manifests, a handful of file excerpts) - it never reads git history or real code, so it tends to be generic on anything but a trivial repo. purl map init fixes this at the source: it shells out to a headless, read-only coding-agent session that actually explores the codebase and cross-references real routing/registration code before naming a capability, then uploads the resulting feature tree through the same event pipeline purl connect uses.

Two providers are supported, selected with --agent (defaults to claude-code). Both pin a specific model and medium reasoning effort rather than deferring to whatever the user's local CLI default happens to be, so cost and output quality stay predictable across machines:

  • claude-code (default) - claude -p, model claude-haiku-4-5-20251001 (the full id, not the haiku alias - see the module docstring in cli/src/agents/claude-code/headless-index.ts for why), --effort medium, restricted to the Read/Grep/Glob tools only, with a hard dollar cap via --max-budget-usd (default $2).
  • codex - codex exec, model gpt-5.6-terra with -c model_reasoning_effort="medium", run inside Codex's --sandbox read-only (it explores via shell commands like cat/grep/find, but any write/mutating command is sandboxed to fail rather than executing). exec is already non-interactive so there's no approval prompt to hang on. Codex's exec has no dollar-budget flag equivalent to Claude Code's - only --timeout-ms bounds a Codex run.

Both providers can never edit or run anything mutating in your repo - read-only is enforced by the agent's own tool/sandbox mechanism, not just by prompt instructions.

Prerequisites:

  • Run purl connect first - map init augments an existing pairing, it doesn't replace it.
  • The chosen provider's CLI must be installed and already authenticated on your PATH (claude --version / codex --version should work, and codex login for Codex). purl map init checks this up front and fails with an actionable message if not.

This is a foreground command you actively wait on - it can take a few minutes on a large repository and spends real agent usage. It fails loudly on any error (missing binary, timeout, unparseable output) rather than silently falling back to the shallow tree, so a real problem is never masked. If indexing succeeds but the upload fails, the result is safely queued in the local spool - retry with purl flush.

Adding a third provider means implementing the HeadlessIndexer interface (cli/src/agents/headless-indexer.ts) in a new cli/src/agents/<provider>/headless-index.ts and registering it in cli/src/commands/map-init.ts - the shared JSON schema (cli/src/agents/feature-tree-schema.ts), prompt builder (cli/src/agents/indexing-prompt.ts), loose JSON parser (cli/src/agents/json-envelope.ts), and subprocess runner (cli/src/agents/subprocess.ts) are provider-agnostic and should be reused as-is.

Re-running purl connect

Running purl connect (no pairing code) in a repository that's already connected skips the browser/device-code flow entirely - it reuses the stored capture token and just re-registers hooks and the MCP server (already idempotent no-ops when nothing's missing, so this doubles as a repair if something got deleted locally). What's printed differs by whether the CLI package itself changed since the last connect here:

  • same version - This repository is already connected to "<project>" - no need to run "purl connect" again here.
  • different version - purl CLI updated (0.1.0 -> 0.2.0) - re-synced hooks and the MCP server for "<project>" in <repoRoot>.

Passing an explicit pairing code (purl connect ABCD-1234) always runs the full pairing exchange regardless of local state - that's the escape hatch for re-pairing to a different project or after purl disconnect. The reported CLI version is also sent to the backend and shown per device in Purl Web's Connected Devices panel.

MCP server (purl atelier mcp)

purl connect also registers a local MCP server, purl-atelier-map, with each connected agent (Claude Code: ~/.claude.json's per-repo mcpServers; Codex: ~/.codex/config.toml's [mcp_servers.purl-atelier-map]). It exposes three tools an agent can call during a session - report_decision, report_activity, report_verification - that feed the backend's distillation step as a labeled, advisory hint alongside the passively-captured file/tool evidence; nothing reported through it is ever auto-trusted over what actually changed in the repo.

To make an agent aware the server exists without editing any file in your repository (no CLAUDE.md/AGENTS.md changes), the SessionStart hook emits one hookSpecificOutput.additionalContext JSON line on stdout - the same mechanism both Claude Code and Codex use to inject text into a session's context from a hook - naming the server and its three tools as available, optional capabilities. This only fires once per session, and only when Purl's MCP server is actually registered for the repo; every other hook event still writes nothing to stdout, matching the capture pipeline's normal silent behavior. See src/agents/nudge.ts for the exact wording and src/commands/hook.ts for where it's gated. It's reversible the same way the rest of a purl connect install is - purl disconnect removes the MCP registration along with the hooks, so the nudge stops firing.

Architecture

Codex native event -------\
                           > provider adapter -> canonical local event -> spool -> uploader
Claude Code native event -/
  • src/capture/types.ts - the canonical event model and ProviderAdapter interface shared by both providers.
  • src/agents/types.ts - the AgentIntegration interface each provider's hook-merge module implements (install/uninstall/isInstalled), plus the pre-resolved runner-command context the CLI core hands it.
  • src/agents/codex/ - Codex .codex/hooks.json adapter and merge logic, plus headless-index.ts (codex exec) for purl map init --agent codex.
  • src/agents/claude-code/ - Claude Code .claude/settings.local.json adapter and merge logic, plus headless-index.ts (claude -p) for purl map init (the default agent).
  • src/agents/headless-indexer.ts, feature-tree-schema.ts, indexing-prompt.ts, json-envelope.ts, subprocess.ts - the provider-neutral HeadlessIndexer contract and the schema/prompt/parsing/process-running logic both map init providers share.
  • src/pairing/ - HTTP client for the backend's device-pairing endpoints (backend/APIs.md "Device pairing" section).
  • src/storage/ - OS credential store (@napi-rs/keyring), local installation metadata under the platform Purl data directory (paths.ts resolves it per-OS; PURL_CLI_DATA_DIR overrides it for tests only), and the stable per-user runner copy hooks invoke (runner.ts).
  • src/capture/ - spool (spool.ts), uploader (uploader.ts), redaction/capping (redact.ts), git-backed baseline/workspace-delta/bootstrap helpers (git.ts, diff.ts) - all ported from tools/atelier_capture/atelier_capture.py.
  • src/commands/ - one module per CLI command.
  • src/util/ - Git-root resolution, device label/fingerprint, browser opening, and the hook-only diagnostic log file.

Security

  • The long-lived capture token never appears in a command, shell history, hook JSON, browser storage, or log line. It is exchanged once during pairing and stored only in the OS credential store (@napi-rs/keyring; macOS Keychain / Windows Credential Manager / Linux Secret Service).
  • A bare reconnect validates that token with the API before changing hooks. A revoked token starts fresh browser approval; a network/server error fails clearly and is never reported as success.
  • Hooks invoke a stable local runner (a per-user copy of the bundled CLI installed at connect time), never npx or an ephemeral cache path.
  • Hook installation always backs up the existing config, writes atomically, validates the result, and preserves every unrelated handler.

See ../backend/APIs.md for the pairing/ingestion HTTP contract and ../tools/atelier_capture/README.md for the retention and redaction policy this CLI preserves.