Package Conform Skill
Conform a package to standardized architecture, or scaffold a new one.
Worktree isolation
Before any edit, follow the worktree isolation contract. It provides the atomic live-agent claim used below.
An existing worktree alone does not prove another agent is active.
wt is the only worktree tool. Never run git worktree add, and never use a harness worktree option such as EnterWorktree or isolation: "worktree". Those write to .claude/worktrees/, which is banned. wt places every worktree at <parent>/<repo>.<branch-slug>.
Keep the primary checkout read only. Before mutation, run wt list --format=json. Reuse the task's worktree with wt switch <branch>, or create one with wt switch --create <branch> --base <base>. Read its absolute path from the JSON, then pass that path as workdir to every later command. Never share a mutation worktree between tasks.
Usage
/pkg-conform # conform existing project
/pkg-conform my-package # scaffold new package
Behavior
- New project: scaffold with all standards below
- Existing project: compare and offer to sync each component
Detection
Check for package.json in cwd to determine new vs existing.
Check for packages: in pnpm-workspace.yaml to detect monorepo vs single repo.
Check for @nuxt/module-builder in devDependencies to detect Nuxt module -> apply Nuxt-specific patterns.
Project Type Detection
Determine project type from the absolute path of the working directory:
| Path pattern | Type | Description |
|---|---|---|
*/pkg/* |
Package | Published library/module -- needs exports, build, release |
*/sites/* or */site/* |
Site | Nuxt app -- private, no exports, deploy not publish |
If path doesn't match either pattern, fall back to heuristics: private: true + nuxt in deps -> Site, otherwise Package.
The project type selects the rule set. Package-only rules (exports, obuild, test:attw, prepack, release) never apply to Sites. Site-only rules (nuxi scripts, generate, preview) never apply to Packages.
Gotchas
- Catalog version conflicts -- when migrating deps to
catalog:, check that the catalog version satisfies all consumers in the monorepo. A singlecatalog:entry shared across packages with different version requirements will break. - obuild vs tsc -- obuild doesn't do type-checking. If you remove
tscfrom the build, types won't be validated. Always keeptypecheckas a separate script. module: preservein tsconfig -- this is correct for packages built with obuild, but breaks Nuxt apps that needmodule: esnextor defer to.nuxt/tsconfig.json. Don't apply package tsconfig rules to sites.- ESM-only exports trap -- removing CJS exports breaks consumers that haven't migrated to ESM. For public packages, confirm the audience before dropping
.cjs. pnpm installafter catalog changes -- lockfile must be regenerated. If you editpnpm-workspace.yamlcatalogs, always runpnpm installbefore running any other commands.- Nuxt module
dev:prepareorder -- must run beforetypecheckortest. Missing this causes confusing "module not found" errors from auto-generated types. - Site vs Package misdetection -- path-based detection (
*/pkg/*vs*/sites/*) can fail for unusual directory structures. Always verify the detected type before applying rules. - Markdown-only CI: Test, build, and deploy events ignore
**/*.mdby default. An explicitpathslist may include Markdown. GitHub forbidspathsandpaths-ignoreon the same event.
UnJS Conventions
Always prefer UnJS ecosystem packages over Node.js builtins:
| Instead of | Use | Import |
|---|---|---|
path |
pathe |
import { join, resolve } from 'pathe' |
console.log/warn/error |
consola |
import { consola } from 'consola' |
fetch |
ofetch |
import { $fetch } from 'ofetch' |
fs.readFile (JSON) |
pkg-types |
import { readPackageJSON } from 'pkg-types' |
Object.assign defaults |
defu |
import { defu } from 'defu' |
require.resolve |
mlly |
import { resolveImports } from 'mlly' |
EventEmitter |
hookable |
import { createHooks } from 'hookable' |
yargs/commander |
citty |
import { defineCommand } from 'citty' |
cosmiconfig |
c12 |
import { loadConfig } from 'c12' |
git clone templates |
giget |
import { downloadTemplate } from 'giget' |
Principles: ESM-only, minimal deps, full TypeScript, universal (Node/browser/edge)
Package.json
Package (single repo / monorepo)
See references/pkg-package-json.md for single repo and monorepo root templates.
Site (Nuxt app)
See references/site-package-json.md for template, optional scripts, and rules.
See references/site-structure.md for Nuxt 4 directory layout.
See references/site-configs.md for nuxt.config.ts, tsconfig, eslint, .npmrc, .gitignore templates.
Test Structure
test/
unit/ # unit tests
*.test.ts
e2e/ # e2e/integration tests
*.test.ts
fixtures/ # test data
Nuxt Module (when @nuxt/module-builder detected)
Build-time vs Runtime
| Context | Location | Access | Registration |
|---|---|---|---|
| Build-time | src/module.ts |
@nuxt/kit, nuxt config |
runs during nuxi build |
| App runtime | src/runtime/app/ |
Vue, useNuxtApp() |
addPlugin(), addImports() |
| Server runtime | src/runtime/server/ |
H3, Nitro | addServerHandler(), addServerPlugin() |
| Shared | src/runtime/shared/ |
Pure JS only | import via alias |
See references/nuxt-module-template.md for full module.ts template with registration examples.
See references/nuxt-module-structure.md for directory layout and runtime rules.
References
See references/ for detailed templates:
../../references/code-comments.md- the comment contract for any code this skill writesreferences/pkg-package-json.md- single repo and monorepo package.json templatesreferences/catalogs.md- pnpm workspace catalogsreferences/configs.md- package config file templates (eslint, vitest, tsconfig, obuild)references/github-actions.md- CI/CD workflows (Package only)
Site references (when project type is Site):
references/site-package-json.md- package.json template, rules, optional scriptsreferences/site-structure.md- Nuxt 4 directory layout and conventionsreferences/site-configs.md- nuxt.config.ts, tsconfig, eslint, npmrc, editorconfig, gitignorereferences/site-github-actions.md- the shared CI gate, deploy gating, self-hosted runner rules
Nuxt module references (when @nuxt/module-builder detected):
references/nuxt-module-structure.md- directory layout and runtime rulesreferences/nuxt-module-template.md- full src/module.ts templatereferences/nuxt-configs.md- vitest, tsconfig, build.config, package.json for Nuxtreferences/nuxt-test-patterns.md- playground, fixtures, e2e tests
Sync Checklist
Shared (all project types)
-
pnpm-workspace.yaml- default catalog,ignoredBuiltDependencies,shellEmulator -
package.json-type: module, migrate deps tocatalog:, addpackageManager -
.github/workflows/test.yml- action versions and Markdown path filtering -
.editorconfig- standard config -
.gitignore- standard patterns - ESLint config - antfu +
eslint-plugin-harlanzw. Package:eslint.config.mjs; Site:eslint.config.js -
tsconfig.json- Package:module: preserve,moduleDetection: force; Site:extends .nuxt/tsconfig.json - Git hooks -
lint-stagedin devDeps,pre-commitrunslint-staged
Package-only (when in */pkg/*)
-
vitest.config.ts- coverage config, projects if unit + e2e -
tsconfig.json- addtypes: ["node", "vitest/globals"] -
build.config.ts- obuild with explicit entry points - Package exports - ESM-only (
.d.mts+.mjs), no CJS - Package scripts -
obuild,dev:prepare,test:attw,lint:fix,prepack,release -
.github/workflows/release.yml- action versions,bumpp --output=CHANGELOG.md
Site-only (when in */sites/* or */site/*)
-
package.json-private: true,engines.nodeset to latest stable even-numbered Node (e.g.>=22.0.0,>=24.0.0), noexports/main/types/files - Scripts -
dev(nuxi dev),build(nuxi prepare && nuxi build),postinstall(nuxt prepare),lint,lint:fix,typecheck(nuxt typecheck) -
pnpm.overrides-viteset to^8.0.0 -
nuxt.config.ts-future.compatibilityVersion: 5,compatibilityDate, standard module stack -
tsconfig.json- just{ "extends": "./.nuxt/tsconfig.json" } -
eslint.config.js- antfu config withnode/prefer-global/processandnode/prefer-global/bufferoff -
.npmrc-shamefully-hoist=true -
.gitignore- includes.nuxt/,.output/,.data/,.wrangler/,wrangler.toml -
.editorconfig- 2-space indent, LF, UTF-8, trim trailing whitespace (except.md) -
content.config.ts- Zod schemas for content collections (if using@nuxt/content) -
app/directory - Nuxt 4 structure (app.vue,pages/,layouts/,components/,composables/) -
.github/workflows/ci.yml- calls the shared gate and ignores Markdown-only changes. Seereferences/site-github-actions.md.
Additional Nuxt Module Checklist
When @nuxt/module-builder detected, also check (extends Package checklist):
Structure:
15. [ ] src/module.ts - main module entry exists
16. [ ] src/runtime/app/ - client/SSR code directory
17. [ ] src/runtime/server/ - Nitro server code directory
18. [ ] src/types.ts - module options types
19. [ ] playground/ - nuxt.config.ts, app.vue, pages/
20. [ ] test/fixtures/basic/ - nuxt.config.ts
Config:
21. [ ] pnpm-workspace.yaml - add nuxt: catalog
22. [ ] package.json - nuxt module exports, peerDependencies
23. [ ] tsconfig.json - extends .nuxt/tsconfig.json
24. [ ] vitest.config.ts - use defineVitestProject for e2e
25. [ ] build.config.ts - nuxt externals including #imports
26. [ ] eslint.config.mjs - ignore fixtures/playground
27. [ ] .gitignore - nuxt build dirs
Scripts:
28. [ ] typecheck - uses nuxt typecheck (not tsc)
29. [ ] dev:prepare - prepares module + playground
30. [ ] prepare:fixtures - prepares test fixtures
31. [ ] .github/workflows/test.yml - includes the prepare step and Markdown path filtering
Sync Process
Phase 0: Detect Project Type
Determine from cwd path whether this is a Package (*/pkg/*) or Site (*/sites/*, */site/*).
Phase 1: Config Review
Read the config files directly (one batch of parallel Read calls) and compare against the checklist. The comparison is cross-cutting (catalog ↔ lockfile ↔ exports ↔ tsconfig interact), so read inline to keep the whole picture in context. Exception: for a large monorepo where one read pass is unwieldy, delegate per-package walks to subagent_type=Explore.
Read for all project types:
pnpm-workspace.yaml,package.json— deps, catalogs,packageManager,type.github/workflows/*.yml— action versions against v6 standardseslint.config.{js,mjs},tsconfig.json,.editorconfig,.gitignore
If Package, also read: build.config.ts, vitest.config.ts, package exports, release scripts.
If Site, also read: nuxt.config.ts, app/ structure (pages/, layouts/, components/), .npmrc.
If Nuxt module (Package + @nuxt/module-builder), also read: src/module.ts (registration methods, resolver, options), src/runtime/app/ (composables/plugins/imports), src/runtime/server/ (handlers/plugins/middleware — verify no Vue deps), playground/ + test/fixtures/ (nuxt.config, prepare scripts, test patterns).
Phase 2: Apply Changes
Based on the review, apply necessary updates using the appropriate checklist (Package or Site).
Phase 3: Parallel Verification
Package verification:
Bash(background): pnpm install
Bash(background): pnpm lint
Bash(background): pnpm typecheck
Then sequentially (depends on install):
Bash: pnpm build
Bash: pnpm test --run
Site verification:
Bash(background): pnpm install
Bash(background): pnpm lint
Bash(background): pnpm typecheck # Uses nuxt typecheck
Then sequentially:
Bash: pnpm build # nuxi build
Nuxt module verification:
Bash: pnpm dev:prepare && pnpm prepare:fixtures
Bash(background): pnpm lint
Bash(background): pnpm typecheck # Uses nuxt typecheck
Bash: pnpm test:run