Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Dead-code / unused-dependency detection is migrating from the legacy `unimported` tool to `knip`, which requires each package to expose an explicit, minimal `package.json#exports` (no `./*` wildcard). Read this when adding a new package or migrating an existing one.
.claude/skills/ledgerhq-knip-migration/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 11% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 6% | 0% |
| case-08 | ✗→✓ | ▲ Improved | -17% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 7% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 0% | 0% |
The repo is migrating dead-code / unused-dependency detection from the legacy unimported tool to knip, one package at a time. The root knip.json holds only the shared rules — a package needs no workspaces entry there, since knip derives its entry files from package.json (main, bin, exports) and its own defaults (entry files).
knip treats everything listed in a package's package.json#exports as an entry point. Today every package exports a ./* wildcard mapped to ./src/*.ts (via the @ledgerhq/source custom condition declared in tsconfig.base.json):
jsonc"./*": { "@ledgerhq/source": "./src/*.ts", "import": "./lib-es/*.js", "require": "./lib/*.js", "default": "./lib/*.js" }
Because of this wildcard, knip considers every top-level src/*.ts file "used" and cannot detect unused ("zombie") top-level source files — the exact thing unimported caught (it ignored exports and diffed against a curated entry list). So real knip parity for a package requires replacing the ./* wildcard with explicit, minimal subpath exports that enumerate the package's true public API.
> The few libs already pointed at knip (e.g. libs/env, libs/promise) still keep the ./* > wildcard, so they are only partially migrated — do not copy them as the template.
A new package has no consumers, so it should start in the target state — no .unimportedrc.json:
exports only. Enumerate the real public API; do not add a ./* wildcard.Mirror the . root entry per subpath, keeping the conditions: jsonc "exports": { ".": { "@ledgerhq/source": "./src/index.ts", "import": "./lib-es/index.js", "require": "./lib/index.js", "default": "./lib/index.js" }, "./logic": { "@ledgerhq/source": "./src/logic.ts", "import": "./lib-es/logic.js", "require": "./lib/logic.js", "default": "./lib/logic.js" }, "./lib-es/*": "./lib-es/*.js", "./lib/*": "./lib/*.js", "./package.json": "./package.json" }
pnpm knip --directory <relative-hop-to-root> -W <workspace-path> (--directory is the hop back to the repo root — ../.. for libs/<x>, ../../.. for libs/coin-modules/<x>; -W is the workspace path from root).
unimported./* wildcard export with explicit subpath exports (the legit public API).unimported → knip, delete .unimportedrc.json, and verifya deliberately-unused top-level src file is now reported.
Start with leaf / low-dependency packages; ledger-live-common (largest surface) comes last.
unimported stays until a package reaches knip parity — don't bulk-delete it.unimported → knip-check) is tracked separately, somatching the surrounding package's existing script name is fine.
For a new package, flag either of these and point here:
.unimportedrc.json or a script running the bare unimported binary("unimported": "unimported") — it must run knip instead; or
./* wildcard in package.json#exports — new packages must enumerate explicit exportsso knip can detect zombie files.
workspaces block in knip.json is a temporary workaroundEverything must work without touching knip.json. The workspaces entries that remain are band-aids for packages that don't declare their surface properly yet — deep imports behind a ./* wildcard, runtime entries invisible to package.json (Electron preloads, web workers), or dependencies knip can't resolve. Each one hides a package that hasn't finished the migration above.
So: don't grow this file. Fixing the package's exports is the real fix, and it lets the corresponding workspaces entry be deleted. If you truly cannot avoid an entry, keep it to the smallest possible delta and remember a configured entry replaces knip's default patterns rather than extending them.
Other measured skills in the registry, with their headline benchmark lift.