From mateonunez-skills
Organizes code by vertical slices in monorepos: features as workspace packages. Guides file placement, new feature addition, package structure proposals, anti-pattern avoidance.
npx claudepluginhub mateonunez/skillsThis skill uses the workspace's default tool permissions.
Layered organisation (`controllers/`, `services/`, `repositories/`) makes every change a horizontal cut across the whole codebase. One feature edit touches five folders. Tests in one place, code in another, types in a third. Then six months in, every PR is a tour through the entire repo.
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Processes PDFs: extracts text/tables/images, merges/splits/rotates pages, adds watermarks, creates/fills forms, encrypts/decrypts, OCRs scans. Activates on PDF mentions or output requests.
Share bugs, ideas, or general feedback.
Layered organisation (controllers/, services/, repositories/) makes every change a horizontal cut across the whole codebase. One feature edit touches five folders. Tests in one place, code in another, types in a third. Then six months in, every PR is a tour through the entire repo.
I organise by feature instead. A feature owns its handlers, its services, its types, its tests, its migrations, its fixtures. The seam between features is a package boundary, not a layer convention.
The canonical example is ait — see the package layout in personal/ait/references/core-architecture.md: @ait/core, @ait/connectors, @ait/postgres, @ait/qdrant, @ait/scheduler, @ait/gateway. Each one owns its slice end to end.
You are about to:
utils/ or lib/ pileWhen deciding where a new file lives, ask in order:
@scope/auth, @scope/billing).@scope/shared package, but only if the abstraction is real. Three callers is the bar. Two is duplication; one is premature.In pnpm monorepos, the slice boundary is enforced by package boundaries:
packages/
feature-a/
src/
test/
package.json # name: @scope/feature-a
feature-b/
src/
package.json # depends on "@scope/feature-a": "workspace:*"
apps/
api/
web/
Internal deps use workspace:*. Scoped names (@scope/feature) make the boundary visible at every import site.
utils/ mega-package. Becomes a junk drawer with no cohesion. Every feature ends up depending on it; nothing depends on a clear contract. Split or delete.@scope/auth-controllers, @scope/auth-services, @scope/auth-repositories. Re-creates layered organisation at the package level. Collapse to @scope/auth.@scope/billing imports from @scope/auth/src/internals/, the boundary is fake. Re-export through the package's index.ts or push the shared concept down to a third package.@scope/types package when the types belong to one feature. Types live with their owner.// package.json (root)
{
"scripts": {
"dev": "pnpm run:recursive --parallel dev",
"build": "pnpm run:recursive build",
"test": "pnpm run:recursive test",
"lint": "biome check .",
"run:recursive": "pnpm -r --workspace-concurrency=1 --if-present"
}
}
run:recursive is a reusable base — -r traverses workspaces, --workspace-concurrency=1 respects build order, --if-present skips packages that don't define the script. Override with --parallel for dev servers.
Don't rewrite the world. Pick one feature, slice it vertically into its own package, prove the pattern, then iterate. A half-finished refactor is worse than no refactor.
If the existing codebase is small (<10 files), just leave it. Premature monorepo-isation is a real failure mode.