antfu — Claude Code skill: Anthony Fu's JS/TS conventions
Hands your agent Anthony Fu's rules for JavaScript and TypeScript work: file layout, ESLint, Knip, package publishing. Useful when starting a project or wiring a monorepo.
npx skills add antfu/skills --skill antfu -a claude-codeWhat antfu does
The skill spells out how an agent should lay out JS/TS code. Types live in types.ts, constants in constants.ts, and big files get split by responsibility. Imports stay explicit; auto-imports should be turned off, and path aliases are not introduced where none existed.
Tooling is pinned down too: @antfu/eslint-config with antislop: true (that flag pulls in eslint-plugin-slop and eslint-plugin-sonarjs as dev dependencies), Knip for dead files and exports, tsdown for builds, Vitest for tests, plus simple-git-hooks and nano-staged on pre-commit. For version lookups it points at fast-npm-meta instead of pulling the whole registry payload. Every project ends up with a ci script that runs lint, types, Knip and tests in one go.
New projects mirror the antfu/starter-ts template — the agent copies package.json, eslint.config.js, knip.json and tsdown.config.ts from there rather than inventing a layout. Releases go through npm Trusted Publishing over OIDC, so no NPM_TOKEN secret is needed. If a project already has aliases or auto-imports configured, the skill leaves them alone but does not add new ones.
What it helps with
- split types and constants into their own files
- set up ESLint with the antislop flag and no explicit any
- catch unused files and dependencies with Knip
- wire a single ci script for lint, types and tests
- publish a package over OIDC without NPM_TOKEN
Example prompts for the agent
- Scaffold a new TypeScript project following antfu/starter-ts
- Split this large file and move the types into types.ts
- Add a ci script to package.json that runs lint, typecheck, knip and tests
How to install antfu
1Skills CLI (Vercel)
npx skills add antfu/skills --skill antfu -a claude-code2Manually: put the files in place
~/.claude/skills/antfu/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/antfu/
1Skills CLI (Vercel)
npx skills add antfu/skills --skill antfu -a codex2Manually: put the files in place
.agents/skills/antfu/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add antfu/skills --skill antfu -a cursor2Manually: put the files in place
.cursor/skills/antfu/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install antfu/skills antfu2Manually: put the files in place
.github/skills/antfu/SKILL.md1Skills CLI (Vercel)
npx skills add antfu/skills --skill antfu -a gemini-cli2Platform CLI
gemini skills install https://github.com/antfu/skills --path skills/antfuManually: put the files in place
.devin/skills/antfu/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add antfu/skills --skill antfu -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about antfu
How do I install antfu in Claude Code?
Run npx skills add antfu/skills --skill antfu -a claude-code. The skill file then sits at ~/.claude/skills/antfu/SKILL.md.
What does the antfu skill do?
It gives the agent Anthony Fu's conventions for JavaScript and TypeScript: how to organize files, which tools to use, and how to publish packages.
What do I need for antislop?
The antislop: true flag in the ESLint config requires eslint-plugin-slop and eslint-plugin-sonarjs as dev dependencies.
Source text
SKILL.md · MIT
---
name: antfu
description: Anthony Fu's opinionated tooling and conventions for JavaScript/TypeScript projects. Use when setting up new projects, configuring ESLint/Prettier alternatives, monorepos, library publishing, or when the user mentions Anthony Fu's preferences.
metadata:
author: Anthony Fu
version: "2026.09.30"
---
> Reference template: [antfu/starter-ts](https://github.com/antfu/starter-ts). When scaffolding a new TypeScript project, mirror its `package.json` scripts, `eslint.config.js`, `knip.json`, `tsdown.config.ts` and workflows rather than inventing a new layout.
## Coding Practices
### Code Organization
- **Single responsibility**: Each source file should have a clear, focused scope/purpose
- **Split large files**: Break files when they become large or handle too many concerns
- **Type separation**: Always separate types and interfaces into `types.ts` or `types/*.ts`
- **Constants extraction**: Move constants to a dedicated `constants.ts` file
### Runtime Environment
- **Prefer isomorphic code**: Write runtime-agnostic code that works in Node, browser, and workers whenever possible
- **Clear runtime indicators**: When code is environment-specific, add a comment at the top of the file:
```ts
// @env node
// @env browser
```
### TypeScript
- **Explicit return types**: Declare return types explicitly when possible
- **Avoid complex inline types**: Extract complex types into dedicated `type` or `interface` declarations
### Explicitness
Favor explicit, traceable code over implicit "magic". A reader (human or agent) should be able to follow where every name comes from without running tooling.
- **Explicit imports**: Prefer explicit `import` statements. Avoid auto-imports — when a framework provides them (e.g. Nuxt/Nitro), turn them off for new projects (see [app-development](references/app-development.md)).
- **No path aliases by default**: Use relative imports (`./foo`, `../bar`). Only use path aliases (`@/`, `~/`, `#imports`, etc.) when they are *already* configured in the project; don't introduce new ones for greenfield code.
### Comments
- **Avoid unnecessary comments**: Code should be self-explanatory
- **Explain "why" not "how"**: Comments should describe the reasoning or intent, not what the code does
### Testing (Vitest)
- Test files: `foo.ts` → `foo.test.ts` (same directory)
- Use `describe`/`it` API (not `test`)
- Use `toMatchSnapshot` for complex outputs
- Use `toMatchFileSnapshot` with explicit path for language-specific snapshots
---
## Tooling Choices
### @antfu/ni Commands
| Command | Description |
|---------|-------------|
| `ni` | Install dependencies |
| `ni <pkg>` / `ni -D <pkg>` | Add dependency / dev dependency |
| `nr <script>` | Run script |
| `nu` | Upgrade dependencies |
| `nun <pkg>` | Uninstall dependency |
| `nci` | Clean install (`pnpm i --frozen-lockfile`) |
| `nlx <pkg>` | Execute package (`npx`) |
### Checking npm Package Versions
Use [`fast-npm-meta`](https://github.com/antfu/fast-npm-meta) to look up the latest version of a package — it queries a small metadata endpoint instead of downloading the full registry payload (which can be megabytes per package).
```bash
nlx fast-npm-meta version vite # 7.3.1
nlx fast-npm-meta version "nuxt@^3.5" # 3.5.22 — range-aware
nlx fast-npm-meta version vite nuxt vue # multiple at once
nlx fast-npm-meta version vite --json # JSON for scripting
nlx fast-npm-meta full vite # full version list + dist-tags
```
Prefer this over `npm view <pkg> version` when you only need the latest version, and over reading `package.json` from the registry directly.
### TypeScript Config
```json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
}
}
```
### ESLint Setup
```js
// eslint.config.js
import antfu from '@antfu/eslint-config'
export default antfu({
type: 'lib', // or 'app' (default)
pnpm: true, // enforce pnpm catalogs in package.json
antislop: true, // flag redundant/duplicated AI-style code, ban explicit `any`
})
```
Always enable `antislop: true`. It requires `eslint-plugin-slop` and `eslint-plugin-sonarjs` as dev dependencies.
For detailed configuration options: [antfu-eslint-config](references/antfu-eslint-config.md)
### Knip
Use [Knip](https://knip.dev) to catch unused files, exports and dependencies — the linter only sees one file at a time.
```json
// knip.json
{
"$schema": "https://unpkg.com/knip@6/schema.json",
"project": ["src/**/*.ts"],
"ignoreDependencies": []
}
```
Fix what Knip reports by deleting; only add to `ignoreDependencies` for tools invoked outside `package.json` scripts (e.g. `taze`).
### Scripts and the `ci` Gate
Every project exposes a `ci` script that runs all checks in one command:
```json
{
"scripts": {
"build": "tsdown",
"lint": "eslint --cache",
"typecheck": "tsc",
"knip": "knip",
"test": "pnpm run build && vitest",
"ci": "pnpm run lint && pnpm run typecheck && pnpm run knip && pnpm run test --run"
}
}
```
**Before every commit, run `nr lint --fix` to format, then `nr ci` and make sure it passes.** Do not commit with a failing `ci`; fix the root cause instead of silencing rules or adding ignores.
### Git Hooks
`simple-git-hooks` + `nano-staged`. The pre-commit hook runs the full `ci` gate; `prepare` also installs agent skills via `skills-npm`:
```json
{
"scripts": {
"prepare": "git config core.hooksPath .githooks && simple-git-hooks && skills-npm"
},
"simple-git-hooks": {
"pre-commit": "pnpm i --frozen-lockfile --ignore-scripts --offline && pnpm run ci && pnpx nano-staged"
},
"nano-staged": {
"*": "eslint --fix --no-warn-ignored"
}
}
```
Add `.githooks` to `.gitignore` and `simple-git-hooks: true` under `allowBuilds` in `pnpm-workspace.yaml`.
### skills-npm
[`skills-npm`](https://github.com/antfu/skills-npm) symlinks agent skills shipped inside installed npm packages (and fetches those declared in a `skills` field) into the agent's skills directory on every install. Install as a dev dependency and wire it into `prepare` as above; add `skills/npm-*` to `.gitignore`. Commit the generated `skills-npm-lock.json`.
### Publishing
Prefer npm Trusted Publishing (OIDC) over `NPM_TOKEN` secrets: releases run on CI from a `v*` tag via `release.yml` with `id-token: write`, and `nr release` (`bumpp`) only bumps, tags and pushes. Details: [setting-up](references/setting-up.md#publishing-with-npm-trusted-publishing-oidc).
### pnpm Catalogs
Use named catalogs in `pnpm-workspace.yaml` for version management:
| Catalog | Purpose |
|---------|---------|
| `prod` | Production dependencies |
| `inlined` | Bundler-inlined dependencies |
| `dev` | Dev tools (linter, bundler, testing) |
| `frontend` | Frontend libraries |
Avoid the default catalog. Catalog names can be adjusted per project needs.
---
## References
| Topic | Description | Reference |
|-------|-------------|-----------|
| ESLint Config | Framework support, antislop, formatters, rule overrides, VS Code settings | [antfu-eslint-config](references/antfu-eslint-config.md) |
| Project Setup | .gitignore, GitHub Actions, OIDC trusted publishing, VS Code extensions | [setting-up](references/setting-up.md) |
| App Development | Vue/Nuxt/UnoCSS conventions, auto-import control, Storybook component testing | [app-development](references/app-development.md) |
| Library Development | tsdown bundling, pure ESM publishing, publint, API snapshots | [library-development](references/library-development.md) |
| Monorepo | pnpm workspaces, centralized alias, Turborepo | [monorepo](references/monorepo.md) |
© Anthony Fu · MIT · antfu/skills