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

@lostgradient/skillset

v0.0.7

Published

Compile one canonical set of agent skills, subagents, MCP servers, instructions, hooks, and model defaults into Claude Code and OpenAI Codex configuration.

Downloads

464

Readme

skillset

A CLI that maintains one canonical set of agent configuration — skills, subagents, MCP servers, instructions files, lifecycle hooks, and model defaults — and compiles it into the dialects Claude Code and OpenAI Codex each understand.

Author everything once under a single source root; skillset emits per-tool output, translating Claude-only dynamic features into prose fallbacks for Codex. User scope (default) writes into your home directory; --scope project writes the same kinds into the current repo:

| Source | Claude (user) | Codex (user) | Claude (project) | Codex (project) | | -------------------------- | -------------------------------- | --------------------------------------------------- | ----------------------- | -------------------- | | ./skills/<name>/SKILL.md | ~/.claude/skills/<name>/ | ~/.agents/skills/<name>/ (+ agents/openai.yaml) | .claude/skills/ | .agents/skills/ | | ./agents/<name>.md | ~/.claude/agents/<name>.md | ~/.codex/agents/<name>.toml | .claude/agents/ | .codex/agents/ | | ./mcp-servers.yaml | mcpServers in ~/.claude.json | [mcp_servers.*] in ~/.codex/config.toml | .mcp.json | .codex/config.toml | | ./instructions.md | ~/.claude/CLAUDE.md | ~/.codex/AGENTS.md | CLAUDE.md | AGENTS.md | | ./hooks.yaml | ~/.claude/settings.json | ~/.codex/hooks.json | .claude/settings.json | .codex/hooks.json | | ./defaults.yaml | ~/.claude/settings.json | ~/.codex/config.toml | .claude/settings.json | .codex/config.toml |

The source root is the current directory, or $SKILLSET_DIRECTORY when set (configuration resolves through @lostgradient/environmentalist, so .env files and skillset.config.* files work too). Any kind may be absent.

Usage

skillset [sync] [--dry-run] [--prune] [--force] [--scope user|project] [--target claude|codex] [--kind <kind>] [--json]
skillset doctor [--targets] [--json]
skillset list [--json]
skillset show <name> [--target claude|codex] [--json]
skillset new <skill|agent> <name>
skillset remove <skill|agent> <name>
skillset get <skill|agent> <name> [<field-path>] [--json]
skillset set <skill|agent> <name> <field-path> <value>
skillset import <skill|agent|instructions> [name] [--from claude|codex]
skillset mcp
  • sync (the default) compiles every source into both tools. It only ever overwrites or removes what it previously generated: file outputs carry a generated by skillset marker, config entries are tracked in the ledger at ~/.config/skillset/state.json (with content hashes and timestamps). Hand-installed entries are skipped as unmanaged; managed outputs that were hand-edited since the last sync are skipped as drifted — both with loud warnings, both overridable with --force. Shared config files are backed up to <file>.skillset-backup before the first write of a run.
  • doctor validates every source and exits non-zero on errors; sync refuses to write while any error remains. doctor --targets flips direction: it audits the ledger against disk and reports every managed output as clean, drift, or missing — run it before syncing on a machine where you may have hand-tweaked compiled output.
  • import adopts an existing hand-installed item: it reverse-compiles the installed skill/agent/instructions file into a source (Codex agent TOML becomes union frontmatter + body; agents/openai.yaml folds into the openai: block), then force-syncs that one item so the pre-existing targets gain markers and ledger entries.
  • The CRUD commands exist so a coding agent can manage sources without hand-parsing directories: list for inventory + status, show for a compiled preview, new/remove for scaffolding and deletion, and get/set for frontmatter fields via dot paths. set values are parsed as YAML and the write is schema-validated first.
  • mcp serves every operation above as an MCP tool over stdio (list_sources, run_doctor, check_targets, sync, show_source, new_source, remove_source, get_field, set_field, import_source) — register it with claude mcp add skillset -- skillset mcp or codex mcp add skillset -- skillset mcp so an agent can manage its own configuration directly, with the same ownership/drift rules as the CLI.
  • Every command supports --json (stable shapes, documented in documentation/ui-readiness.md); text output respects NO_COLOR / FORCE_COLOR and disables color on non-TTY pipes. Run skillset <command> --help for the full reference on any command.

Skills (./skills/<name>/SKILL.md)

One frontmatter block holds the union of what both tools support:

---
name: my-skill # shared, must match the directory name
description: What and when. # shared
license: MIT # shared spec fields
allowed-tools: Read, Grep # shared

model: sonnet # Claude-only fields → Claude output only
when_to_use: ...
disable-model-invocation: true

openai: # Codex-only → emitted as agents/openai.yaml
  interface:
    display_name: 'My Skill'
    short_description: ...
  policy:
    allow_implicit_invocation: false
---

The full Claude-only set: when_to_use, argument-hint, disable-model-invocation, user-invocable, disallowed-tools, model, effort, context, agent, background, hooks, paths, and shell. Shared fields (name, description, arguments, allowed-tools, plus the agentskills.io spec's license/compatibility/metadata) are emitted to both tools. The openai block supports interface (display_name, short_description, icon_small, icon_large, brand_color, default_prompt), policy.allow_implicit_invocation, and dependencies.tools. disable-model-invocation: true implies policy.allow_implicit_invocation: false unless an explicit openai.policy is set.

Supporting files (scripts/, references/, assets/, …) are copied verbatim into both targets. See skills/example-skill/.

Agents (./agents/<name>.md)

Claude-style subagent markdown — frontmatter plus a body that becomes the system prompt:

---
name: reviewer
description: Reviews diffs.
tools: Read, Grep # becomes prose in the Codex output (set codex.tools for the native form)
model: haiku # Claude model family
permissionMode: plan # plan → read-only, acceptEdits → workspace-write
codex: # Codex models are a different family, so they're explicit
  model: gpt-5.6-luna
  model_reasoning_effort: low
  sandbox_mode: read-only # explicit value wins over permissionMode mapping
---

Claude gets the file nearly verbatim at ~/.claude/agents/<name>.md (supported fields: tools, disallowedTools, model, permissionMode, maxTurns, skills, mcpServers, hooks, memory, background, effort, isolation, color, initialPrompt). Codex gets ~/.codex/agents/<name>.toml with the body as developer_instructions (Codex fallbacks applied), tools/disallowedTools folded in as a "Tool guidance" prose section, and the codex: block's settings (model, model_reasoning_effort, model_verbosity, sandbox_mode, nickname_candidates).

Codex agents also natively support hooks, mcp_servers, skills, and tools tables — but with different schemas from Claude's same-named frontmatter, so skillset does not auto-translate them: set codex.hooks / codex.mcp_servers / codex.skills / codex.tools and they are emitted verbatim as TOML tables (doctor reminds you when the Claude-side field is set without its Codex counterpart). Only maxTurns, memory, background, isolation, and initialPrompt have no documented Codex equivalent and are dropped with a warning. See agents/example-agent.md.

MCP servers (./mcp-servers.yaml)

YAML because it is one authoring language across all skillset sources and keeps comments; both targets are transformations anyway (Claude speaks JSON, Codex speaks TOML).

servers:
  neon:
    url: https://mcp.neon.tech/mcp # url → http transport, command → stdio
    headers:
      Authorization: 'Bearer ${NEON_KEY}' # → bearer_token_env_var for Codex
    timeout: 600000 # Claude ms → Codex tool_timeout_sec
    claude: { oauth: { callbackPort: 8080 } } # merged into the Claude entry
    codex: { startup_timeout_sec: 120 } # merged into the Codex section; wins

Codex mappings: ${VAR}-only header values → env_http_headers, static headers → http_headers, ${VAR}-only env values → env_vars (literals stay in env). SSE and WebSocket transports are rejected at the source level — Claude still accepts them, but Codex supports only stdio and streamable HTTP, and a union source must compile for both. Claude-only extras (headersHelper, oauth, …) go through the claude: block; Codex-only extras (auth, required, enabled_tools, …) through codex:. Writes are surgical: the Claude edit touches only the managed mcpServers entries in ~/.claude.json; the Codex edit splices only the managed [mcp_servers.<name>] line spans in ~/.codex/config.toml, so comments and formatting elsewhere survive byte-for-byte.

Instructions (./instructions.md)

Plain markdown compiled to CLAUDE.md (Claude) and AGENTS.md (Codex), with the same #if directives for provider-specific sections. No fallback rewriting applies — instructions have no inline-shell or argument semantics — but doctor warns when a Claude @path memory import sits outside an #if claude guard, since AGENTS.md has no import syntax.

Hooks (./hooks.yaml)

Both tools use a near-identical lifecycle-hook config shape, so one source compiles to both — the hooks key of Claude's settings.json and Codex's hooks.json:

hooks:
  PreToolUse:
    - matcher: Bash # optional regex/matcher
      command: ./scripts/check.sh
      timeout: 10 # seconds, in both tools
      statusMessage: Checking…
      claude: { async: true } # per-target handler overrides, merged last
      codex: { timeout: 20 }
  FileChanged:
    - command: ./scripts/watch.sh
      targets: [claude] # Claude-only event — restriction required

Codex supports 11 events (SessionStart/End, PreToolUse, PermissionRequest, PostToolUse, UserPromptSubmit, Stop, PreCompact/PostCompact, SubagentStart/Stop), all of which exist in Claude's larger set; doctor errors when a Codex-targeted hook uses a Claude-only event. Ownership is entry-level via the ledger: hand-written hooks in the same files are never touched, and a managed entry you hand-edit is treated as drifted. Heads-up on every Codex hook write: Codex trust-hashes its hook config, so changed hooks must be re-trusted via /hooks.

Defaults (./defaults.yaml)

Model/effort defaults, applied as surgical key-level edits (model/effortLevel in Claude's settings.json; top-level model/model_reasoning_effort/model_verbosity scalars in Codex's config.toml, comments preserved). Keys the user set by hand are skipped as unmanaged.

claude: { model: opus, effort: high }
codex: { model: gpt-5.6-sol, model_reasoning_effort: high }

Templating

Target-conditional blocks work in skill and agent bodies (and instructions.md) via HTML comment directives, removed from compiled output:

<!-- #if claude -->

- Repo: !`basename "$(git rev-parse --show-toplevel)"`

<!-- #else -->

- Run `git rev-parse --show-toplevel` to find the repo root.

<!-- #endif -->

<!-- #if codex --> works the same way, and blocks nest.

Codex fallbacks

Codex has no inline-shell preprocessing, argument substitution, or ${CLAUDE_*} expansion, so unguarded Claude-only features are rewritten automatically in the Codex output:

| Claude feature | Codex output | | ------------------------------------------------- | ---------------------------------------------------------- | | !`command` and ```! fences | Plain code spans plus a note telling the agent to run them | | $ARGUMENTS, $0, $ARGUMENTS[1], named $foo | Prose ("the arguments the user provided", …) | | ${CLAUDE_SKILL_DIR} / ${CLAUDE_PROJECT_DIR} | "this skill's directory" / "the project root" | | ${CLAUDE_SESSION_ID} / ${CLAUDE_EFFORT} | Dropped — doctor warns; guard these with #if claude |

doctor flags every automatic rewrite so you can decide whether the translation is good enough or deserves an explicit #if block.

Releases

Tag-driven (v*.*.*): the release workflow publishes to npm with provenance and attaches standalone executables (built with bun build --compile) for linux-x64/arm64, darwin-x64/arm64, and windows-x64 to the GitHub release.

Development

bun run dev          # watch mode
bun test             # tests (100% coverage enforced for src/)
bun run validate     # full gate: format, lint, typecheck, tests, build, package checks

Git hooks (Lefthook) format and lint on commit and run the full validate gate on push; CI runs the same gate on every push and pull request. Published src/ code must stay Node-compatible — Bun-only APIs belong in scripts/ and tests.