name: pre-publish description: 'Validate a package before publishing to npm. Catches workspace dependency leaks, missing dist files, source leaks, and bundling issues. TRIGGER when: publishing to npm, modifying package.json dependencies of a publishable package, changing bundler config (vite.config, tsup.config), debugging npm install failures (E404, missing packages), or reviewing release-please PRs.'
Pre-Publish Validation Skill
Mandatory checklist before publishing any package to npm. This skill exists
because workspace dependencies have leaked into published packages before,
breaking npm install for consumers.
For deeper incident context, load
../rendered-pack-708fda04/SKILL.md when debugging check:pack failures,
Vite SSR packaging, or private @moltnet/* dependency leaks. That rendered
pack links the pi-extension recurrence to the earlier legreffier,
pi-extension, agent-runtime, check-pack, and Vite 8 incidents.
When to trigger
- Before running
pnpm publishornpm publishon any package - Before merging a PR that modifies
package.jsonof a publishable package - When adding or moving dependencies in a publishable package
- When modifying a package's Vite/bundler config
- Any time release-please creates a release PR
Publishable packages
A package is publishable if it has a files field in package.json and is
not marked "private": true. Current published packages:
@themoltnet/sdk(libs/sdk)@themoltnet/design-system(libs/design-system)@themoltnet/pi-extension(libs/pi-extension)@themoltnet/agent-runtime(libs/agent-runtime)@themoltnet/cli(packages/cli)@themoltnet/github-agent(packages/github-agent)@themoltnet/legreffier(packages/legreffier-cli)
Checklist
1. Workspace dependency placement
Private workspace packages (@moltnet/*) must NEVER appear in dependencies
of a publishable package. They are not published to npm and will cause
npm install to fail.
Rule: If a @moltnet/* package is imported in source code and the build
bundles it (Vite SSR, esbuild, etc.), it belongs in devDependencies.
Check:
grep -n '@moltnet/' <package>/package.json
Expected: @moltnet/* entries appear only under devDependencies, never
under dependencies.
Published workspace packages (@themoltnet/*) with workspace:* are fine
in dependencies — pnpm rewrites workspace:* to concrete versions on
publish.
2. Build produces a valid bundle
For bundled packages (Vite SSR), verify the bundle doesn't contain runtime imports to private workspace packages:
# Build the package and its workspace deps first
pnpm --filter <package> build
# Check the bundle has no @moltnet/ imports
grep '@moltnet/' <package>/dist/index.js
Expected: zero matches. All @moltnet/* code should be inlined.
3. Run check:pack
pnpm --filter <package> run check:pack
This validates:
dist/index.jsexists in the tarballdist/index.d.tsexists (for library packages)- No
src/files leak into the tarball - No
@moltnet/imports in.d.tsfiles - No
@moltnet/packages independencies
4. Verify the tarball contents
npm pack --dry-run --json 2>/dev/null | jq '.[0].files[].path'
Check that:
- Only expected files are included (typically
dist/andpackage.json) - No source files, test files, or config files leaked
5. Test install (optional but recommended for major releases)
npm pack
mkdir /tmp/test-install && cd /tmp/test-install
npm init -y
npm install <tarball-path>
node -e "import('<package-name>')"
Common mistakes and how they happen
Workspace deps in dependencies (the legreffier incident)
What happened: @moltnet/api-client, @moltnet/crypto-service, and
@themoltnet/design-system were listed in dependencies instead of
devDependencies in @themoltnet/legreffier. Vite correctly bundled them
into dist/index.js, but pnpm publish rewrote workspace:* to version
numbers and shipped a package.json that referenced unpublished packages.
Why it wasn't caught: The check:pack script only checked for
@moltnet/ imports in .d.ts files and src/ leaks in the tarball. It
didn't check the dependencies field itself.
Prevention: The check:pack script now validates that no @moltnet/*
packages appear in dependencies. Run it before every publish.
Private task/runtime deps in pi-extension (recurring release failure)
What happened: Release CI for @themoltnet/pi-extension@0.21.0 passed
the Vite build, then failed check:pack because @moltnet/tasks appeared
in dependencies.
Why this matters: @moltnet/tasks is private. Consumers cannot install it
from npm. If pi-extension imports it and Vite bundles it via ssr.noExternal,
it belongs in devDependencies, not dependencies.
Prevention:
- Treat
@moltnet/taskslike every other private@moltnet/*package. - Keep bundled private workspace deps in
devDependencies. - Keep published
@themoltnet/*packages independenciesonly when runtime consumers need to install them. - If the failure says
private workspace packages in dependencies, fixpackage.jsonplacement before changing Vite.
SDK pattern (correct)
@themoltnet/sdk does it right:
@moltnet/api-clientand@moltnet/crypto-serviceindevDependenciesssr.noExternal: [/@moltnet\//]invite.config.tsto explicitly bundle- Only
@noble/*(published) packages independencies
Integration with CI
The check:pack step runs in the release workflow before pnpm publish.
If it fails, the publish is blocked. This is the last line of defense.
# From .github/workflows/release.yml
- run: pnpm --filter <package> run check:pack
- run: pnpm --filter <package> publish --no-git-checks --access public --provenance
Reminders
- Moving a dependency from
dependenciestodevDependenciesrequires updating the lockfile (pnpm install) - Vite SSR mode bundles workspace-symlinked packages by default (no explicit
noExternalneeded), but explicit is better than implicit - The
filesfield inpackage.jsoncontrols what goes in the tarball — keep it minimal