skills/dev-npm-package/SKILL.md
Develop npm packages with Node.js and TypeScript following modern best practices. Use when: (1) Creating a new npm package, (2) Setting up package.json exports (dual ESM/CJS or ESM-only), (3) Configuring TypeScript for library authoring (Bundler or Node16 moduleResolution), (4) Building/publishing with tsup or tsc, (5) Creating CLI tools with bin field, (6) Testing with vitest, (7) CI/CD for npm publishing, (8) ESM/CJS interop issues, (9) Choosing a versioning / dist-tag / release-channel strategy — especially the pre-1.0 (0.x) ruling for what `latest` vs `next` should point at, how to tag prereleases, and avoiding the stale-`latest` footgun. Use this whenever the user mentions dist-tags, `latest`/`next`, prerelease tagging, 0.x versioning, or 'what version/release strategy should we use', even if they don't explicitly say 'npm package'. Keywords: npm package, publish to npm, library development, dist-tag, latest vs next, prerelease tagging, 0.x versioning, release strategy, semver channel.
npx skillsauth add takazudo/claude-resources dev-npm-packageInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
moduleResolution: "Bundler" (with tsup) or "Node16" (with tsc alone){
"name": "my-library",
"version": "0.1.0",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"files": ["dist"],
"sideEffects": false,
"engines": { "node": ">=18" },
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"test": "vitest",
"test:run": "vitest run",
"lint": "biome check .",
"typecheck": "tsc --noEmit",
"prepublishOnly": "npm run build"
},
"devDependencies": {
"@biomejs/biome": "^2.3",
"tsup": "^8.4",
"typescript": "^5.7",
"vitest": "^3.0"
}
}
import { defineConfig } from "tsup";
export default defineConfig({
entry: ["src/index.ts"],
format: ["cjs", "esm"],
dts: true,
splitting: false,
sourcemap: true,
clean: true,
});
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"lib": ["ES2022"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"noUncheckedIndexedAccess": true,
"noEmit": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
my-library/
src/
index.ts
index.test.ts
package.json
tsconfig.json
tsup.config.ts
vitest.config.ts
biome.json
.gitignore
LICENSE
README.md
For packages targeting modern Node.js (>=18) without CJS compatibility needs. Simpler than dual publishing.
{
"name": "@myorg/my-library",
"version": "0.1.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"files": ["dist"],
"engines": { "node": ">=18" },
"scripts": {
"build": "tsc",
"test": "vitest run",
"prepublishOnly": "tsc && vitest run"
},
"devDependencies": {
"typescript": "^5.7",
"vitest": "^3.0"
}
}
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
Important: With Node16 resolution, all relative imports must include the .js extension (even for .ts source files): import { foo } from './utils.js'.
require() ESM natively)tsc only (no bundler){
"bin": {
"my-cli": "dist/cli.js"
},
"files": ["dist"]
}
Note: npm recommends bin paths without a ./ prefix ("dist/cli.js" not "./dist/cli.js"). Modern npm normalizes this automatically, but omitting ./ avoids warnings in older npm versions. Run npm pkg fix to check for issues.
#!/usr/bin/env node
import { program } from "commander";
program
.name("my-cli")
.version("1.0.0")
.description("Description here");
program
.command("init")
.option("-t, --template <name>", "template to use", "default")
.action((options) => {
console.log(`Template: ${options.template}`);
});
program.parse();
CLI argument parsing libraries: commander (most popular, subcommands), yargs (validation, middleware), citty (lightweight ESM-first).
The default install is the latest dist-tag: a tagless npm install <pkg> (or pnpm add / pnpm dlx) dereferences latest directly — it is NOT a semver range match, so whatever latest points at is exactly what new consumers get, prerelease or not. Keeping latest on the newest shippable build is the whole game; never strand it on an old version.
Pre-1.0 (0.x) — ship clean 0.MINOR.PATCH straight to latest. Do not put a -next/-beta suffix on the everyday dev mainline. 0.x (major-zero) is itself SemVer's "anything may change" signal, so a breaking change rides a minor bump (0.2 → 0.3) and everything else a patch bump. Every release is then a clean, monotonically-increasing version that npm routes to latest automatically — a tagless install always gets the newest build, with no machinery to get stuck (esbuild, pre-1.0 Vite, Bun, Biome all do this).
Prereleases are an opt-in side channel, not the mainline. Reserve -alpha/-beta/-rc/-next plus the next (or canary) dist-tag for genuine previews — a 1.0.0-beta run-up, or a bleeding-edge line published ahead of latest. next conventionally means "ahead of/distinct from latest" — never mirror it onto latest.
In CI, derive --tag from the version string and always pass it explicitly: hyphen → --tag next, clean X.Y.Z → --tag latest. npm ≥ 11 hard-errors when you publish a prerelease without --tag; npm ≤ 10 silently routed prereleases onto latest (a silent-downgrade footgun). Never rely on the implicit default for a prerelease. At 1.0.0 the normal stable/preview split resumes automatically under this same rule — no special-casing.
Detailed mechanics, the dual-tag "advance-latest" anti-pattern that strands latest, and ^0.x range gotchas: references/publishing.md.
types before default within each condition blockimport condition for ESM, require condition for CJSmain/module/types at top level exist for backward compatibility with older toolsAlways use files as a whitelist (not .npmignore). Set to ["dist"] to publish only build output. Verify with npm pack --dry-run.
Always include a prepublishOnly script to build (and ideally test) before publishing:
{ "prepublishOnly": "npm run build && npm test" }
For tsc-only projects, you can call commands directly: "prepublishOnly": "tsc && vitest run".
For scoped packages (@myorg/pkg), configure public access via .npmrc in the project root:
access=public
Alternatively, use publishConfig in package.json:
{ "publishConfig": { "access": "public" } }
Set "sideEffects": false for pure utility libraries to enable tree-shaking. If some files have side effects, list them: "sideEffects": ["*.css"].
Use named exports (not default export of objects). Avoid classes when individual functions suffice.
npm run build # Build the package
npx publint # Validate package.json/exports
npx attw --pack . # Validate TypeScript types
npm pack --dry-run # Inspect package contents
npm publish --dry-run # Simulate publish
Read these when you need specifics:
latest/next, the pre-1.0 0.x clean-mainline ruling, the dual-tag stale-latest anti-pattern, ^0.x range mechanics), Changesets/semantic-release, GitHub Actions OIDC trusted publishing, npm provenance, publint/attw, size-limit, supply chain securitytools
Acceptance gate for a branch produced by an OpenAI Codex CLI run — usually Codex implementing a /big-plan epic that was handed off to it. Codex reports the work 'done' (or the user flags it WIP with corrections); this skill confirms the branch actually fulfils the original spec, fixes what falls short, and routes larger discoveries into GitHub issues. Use when: (1) User says '/finalize-codex-work', 'finalize codex work', 'confirm the codex work', 'check the codex branch', or 'codex said it's done', (2) A branch is the result of a Codex CLI session and needs verification against its spec issue/PR, (3) After assigning a /big-plan epic to Codex CLI. Pass -m/--merge to run /pr-complete -c at the end.
tools
Read a Figma design node directly from a share URL via the Figma REST API — no Dev Mode subscription, no MCP, no desktop app. Renders the node to PNG and dumps its full style/layout JSON so the design can be described, compared, or implemented. Use whenever the user gives a Figma design URL (figma.com/design/... or /file/...) and wants to see, read, inspect, reference, or implement that node — including `/fig-url-refer <url>`. This is the URL-based counterpart to `/figrefer` (which needs a Dev-plan desktop MCP); prefer this one when the input is a URL rather than a live desktop selection.
tools
Sync the user's Claude Code workflow skills into the OpenAI Codex CLI settings repo ($HOME/.codex) as Codex-native ports, fix the Codex .gitignore for new local state, then commit and push. Use when: (1) user says '/dev-codex-sync-settings-from-claude', 'sync codex settings', 'sync claude skills to codex', 'port skills to codex', or 'update codex from claude'; (2) after updating ~/.claude workflow skills (big-plan, x, x-as-pr, x-wt-teams) and Codex should catch up; (3) the $HOME/.codex repo has drifted behind $HOME/.claude. The ports are condensed Codex-native REWRITES, never file copies.
development
Analyze a video file (mov, mp4, webm, etc.) or a YouTube video by extracting still frames with ffmpeg and reading them chronologically with vision — Claude cannot ingest video files directly. Use whenever the user provides a video file path or YouTube URL and wants to know what happens in it: "read this video", "watch this video", "check this recording", "what happens in this .mov/.mp4", analyzing a screen recording of a UI bug, or verifying UI behavior captured in a video, even if they don't name this skill.