Imported from Sioood/stallning (
AGENTS.md). Install upstream withnpx skills add Sioood/stallning. Copyright stays with the author.
Stallning Monorepo
pnpm workspace + Turborepo. Shared tooling lives in packages/config/*.
Stallning is a pnpm workspace monorepo orchestrated with Turborepo. It contains a Nuxt 4 web application built on top of a layered architecture where each package extends the previous one.
Node: >= 25.8.0 · pnpm only
Principles
- Prefer the simplest implementation that fully meets current requirements. Avoid speculative abstractions, config, and indirection.
- Grow in layers: ship the smallest end-to-end version first; add capabilities on a working product. Never trade a working product for unfinished complexity.
- Keep components modular and concerns separated.
- Prefer established libraries when they reduce complexity or improve reliability. Do not reimplement common functionality without a clear reason.
- Prefer existing project dependencies before new packages or custom code. Check docs and types before assuming a gap.
- Make long-term architectural choices. Do not accept stopgaps meant to be replaced later.
Tooling
- Catalog versions in
pnpm-workspace.yaml("catalog:"); internal deps:workspace:*/workspace:^ - Format: oxfmt · Lint: oxlint + ESLint (
@stallning/eslint) · TS: strict, noany(useunknown) - Commits: conventional (
feat/fix/…) · scopes:global,config,docs,nuxt-essentials,ui,web· header ≤ 100 chars - Before push:
pnpm verify(ormake verify); pre-push also runspnpm audit --audit-level=high @stallning/webdepends on@stallning/ui(runtime) +@stallning/nuxt-essentials(dev, for i18n CLI)@stallning/uidepends on@stallning/nuxt-essentials(runtime)- All packages use
@stallning/eslint,@stallning/oxlint,@stallning/typescriptfor tooling
Layout
apps/web -> extends packages/ui
packages/ui -> extends packages/nuxt-essentials
packages/nuxt-essentials -> base Nuxt layer (modules, i18n, PWA, security)
packages/config/* -> shared eslint, oxlint, typescript configs
apps/·packages/·packages/config/(eslint, oxlint, typescript)- Runtime: Nuxt 4, Vue 3, Pinia, VueUse, Ark UI, TanStack Form, Zod v4
- Styling: Tailwind CSS v4 (Vite plugin), CVA (class-variance-authority), tailwind-merge
- Tooling: pnpm 11+, Turborepo, ESLint (flat config), oxlint, oxfmt, Vitest, Playwright
- Testing: Vitest (unit/component/visual), Playwright (E2E), Stryker (mutation)
- CI/CD: GitHub Actions, Changesets for versioning
- Security: nuxt-security (CSP, CORS, rate limiting, SRI) — disabled in dev
Rules
Package Management
- All shared dependency versions live in
pnpm-workspace.yamlcatalog — use"catalog:"in package.json - Use
workspace:*for internal runtime deps,workspace:^for internal dev deps - Never add a dependency without checking if it already exists in the catalog
.npmrcuses targeted hoisting (public-hoist-pattern) — NOTshamefully-hoistminimumReleaseAge: 1440blocks packages published less than 24h ago (supply chain protection)
Code Style
- Formatter: oxfmt (NOT the Prettier npm package) — configured in
.oxfmtrc.json. ESLint useseslint-config-prettieronly to turn off stylistic rules that overlap with oxfmt - Linting: oxlint + ESLint (flat config via
@stallning/eslint) - TypeScript: Strict mode, no
any(useunknown), no unused vars (prefix with_) - Vue:
<script setup lang="ts">always, props viadefineProps+withDefaults - Imports: Nuxt auto-imports are available in
app/directories; explicit imports in utility files
Type Safety Patterns
- Discriminated unions: All variant types use a literal
typefield for exhaustive matching assertNever: Use in switchdefaultbranch to guarantee compile-time exhaustiveness- No
any: Useunknownfor untyped boundaries, then narrow with Zod.parse()
Commit Messages
Conventional commits enforced by commitlint:
- Types:
feat,fix,refactor,perf,test,docs,chore,ci,build,style,revert - Scopes:
global,config,docs,nuxt-essentials,ui,web - Max header: 100 chars
File Structure
- Apps live in
apps/ - Packages live in
packages/(orpackages/config/for tooling) - Each Nuxt package uses the
app/directory convention (Nuxt 4) - Components:
app/components/, Composables:app/composables/, Utils:app/utils/ - Tests:
test/at package root, mirroring source structure - i18n:
i18n/locales/{locale}/with YAML/JSON translation files
Dos
- Run
pnpm installat root after adding dependencies - Use
turbofor running tasks across packages (pnpm lint,pnpm build,pnpm test) - Keep component types exported from the component SFC
<script setup>block - Use
defineModelfor two-way bindings - Use iterative approaches (explicit stack) instead of recursion
- Use optional chaining (
?.) instead of redundant null checks - Use
assertNeverin switch statements over discriminated unions - Run
pnpm mutationafter adding critical logic to validate test quality
Don'ts
- Don't use
any— preferunknownif type is unclear - Don't leave commented-out code or unused variables
- Don't create abstractions unless they reduce duplication
- Don't use Prettier (use oxfmt)
- Don't add deps without catalog entry
- Don't bypass lint-staged/husky hooks without justification
- Don't use
npmoryarn— this is a pnpm-only repo - Don't dynamically generate Tailwind classes (full strings only)
Testing Strategy
- Unit tests: Vitest in Node environment for pure logic (utils, composables)
- Component tests: Vitest +
@nuxt/test-utils+ happy-dom for Vue components - Visual regression: Vitest Browser + Playwright for screenshot comparisons
- Mutation testing: Stryker (incremental) on utils/composables only
- E2E: Playwright against built app (
pnpm build && pnpm preview) - Coverage thresholds: 75% lines/statements, 65% branches
Security
nuxt-securityis disabled in dev (enabled: !isDev) to allow DevTools- In production: strict CSP (nonce-based), CORS, HSTS, rate limiting, SRI
pnpm audit --audit-level=highruns on everygit push(husky pre-push, afterpnpm verify)- Run
make verifyorpnpm verifylocally to mirror the CI check job before pushing - Override security config per-route in consuming apps via
routeRules
Guides
Detailed implementation guides for common workflows:
- Component Development — plain Vue + CVA components (no Ark UI)
- Ark UI Components — interactive components built on Ark UI (Root/RootProvider pattern, controlled state, attr forwarding, pitfalls)
- Testing — unit, component, visual regression, and mutation testing
Common Pitfalls
- Nuxt auto-imports only work inside
app/directories — utility files outside need explicit imports - The
~uiand~nuxt-essentialsaliases resolve to package roots .npmrcuses targetedpublic-hoist-pattern— add new patterns if a dep can't be resolved- Tailwind needs full class strings (no dynamic interpolation like
bg-${color}-500) - Security headers only apply in production (
NODE_ENV=production) - Storybook hosts:
@stallning/storybook-app(UI) and@stallning/web(pnpm storybook); shared config@stallning/storybook