# Knip

> Use when configuring or debugging Knip — creating or editing a knip config, tuning entry/project globs, silencing false positives, or reading Knip output. Mentions of knip, `knip.jsonc`, "unused exports", or "unused dependencies" are triggers.

- Skill: `liaoann/knip` (Agent Skill)
- Install (CLI): `npx skillmds@latest add liaoann/knip`
- Raw SKILL.md: https://api.skillmd.com/api/skills/liaoann/knip/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: liaoann (https://skillmd.com/u/liaoann)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/liaoann/knip

---


# Knip

Setting up Knip (unused files / exports / dependencies detector) so it keeps catching real dead code without drowning you in false positives.

## Scope

Use when the change touches a Knip config or responds to Knip output. Guidance, not a procedure.

## Rules

### Config lives in `knip.jsonc`

Not `knip.json`, not `knip.ts`, not a `package.json` key. JSONC allows comments, and every ignore entry needs a written reason next to it — a bare package name in `ignoreDependencies` is unreviewable a month later.

```jsonc
{
  "$schema": "https://unpkg.com/knip@6/schema-jsonc.json",
  "entry": ["src/index.ts"],
  "project": ["src/**/*.{ts,tsx}"]
}
```

Only use `knip.ts` when the config needs a `compilers` function, which cannot be expressed as data.

### Never set `ignoreExportsUsedInFile`

An exported `type`/`interface` that only its own file's functions use is a local type wearing an `export` keyword. Knip's default (`false`) reports it — that is the finding you want. Setting it to `true`, or granularly to `{ "interface": true, "type": true }`, deletes the check. Same for excluding the `types` issue type to make CI green.

The fix for such a finding is to remove the `export` keyword. If one export genuinely must stay public, tag that symbol (`/** @public */` plus `"tags": ["-public"]`) rather than relaxing the global option.

### shadcn/ui: ignore the generated directory

```jsonc
{
  // Generated by shadcn/ui — not hand-authored, do not prune.
  "ignore": ["src/components/ui/**"]
}
```

Those files are vendored and ship with unused variants; pruning them just loses the next `shadcn add`. Match the path to the project's `components.json` alias.

Consequence: `ignore` drops the files from the graph, so packages used *only* there (radix, `class-variance-authority`, `clsx`, `tailwind-merge`, `lucide-react`, …) now report as unused — add the ones the project has to `ignoreDependencies`. If that list gets long, use `"entry": ["src/components/ui/**"]` instead: exports go unreported and imports still count.

### CSS-imported packages go in `ignoreDependencies`

Tailwind v4 is wired from CSS (`@import "tailwindcss"`), which Knip does not scan, so it reports a false positive.

```jsonc
{
  "ignoreDependencies": [
    // Imported from CSS, invisible to the module graph.
    "tailwindcss",
    "tw-animate-css"
  ]
}
```

Grep the stylesheets before deleting any dependency Knip flags.

### Fix noise in this order

`entry`/`project` globs → plugin config → targeted ignores (`ignoreDependencies`, `ignoreFiles`, a tag) → `ignore` last. Never quiet a finding by widening top-level `exclude`; that kills the check repo-wide to hide one instance.

## Completion Criterion

- Config is `knip.jsonc` with the `schema-jsonc.json` `$schema` (unless `compilers` forces `knip.ts`).
- `ignoreExportsUsedInFile` is unset, and `types` is not excluded.
- Every `ignore` / `ignoreFiles` / `ignoreDependencies` entry has a comment saying why.
- shadcn's `components/ui/**` is ignored or set as `entry`; if ignored, its exclusive dependencies are listed.
- CSS-imported packages are in `ignoreDependencies`, not removed from `package.json`.
- `knip` runs clean with no check disabled repo-wide.

