mirror of
https://github.com/wshobson/agents.git
synced 2026-03-18 17:47:16 +00:00
Add domain expert agents with comprehensive skill sets: - service-mesh-expert (cloud-infrastructure): Istio/Linkerd patterns, mTLS, observability - event-sourcing-architect (backend-development): CQRS, event stores, projections, sagas - vector-database-engineer (llm-application-dev): embeddings, similarity search, hybrid search - monorepo-architect (developer-essentials): Nx, Turborepo, Bazel, pnpm workspaces - threat-modeling-expert (security-scanning): STRIDE, attack trees, security requirements Update all documentation to reflect correct counts: - 67 plugins, 99 agents, 107 skills, 71 commands
453 lines
11 KiB
Markdown
453 lines
11 KiB
Markdown
---
|
|
name: nx-workspace-patterns
|
|
description: Configure and optimize Nx monorepo workspaces. Use when setting up Nx, configuring project boundaries, optimizing build caching, or implementing affected commands.
|
|
---
|
|
|
|
# Nx Workspace Patterns
|
|
|
|
Production patterns for Nx monorepo management.
|
|
|
|
## When to Use This Skill
|
|
|
|
- Setting up new Nx workspaces
|
|
- Configuring project boundaries
|
|
- Optimizing CI with affected commands
|
|
- Implementing remote caching
|
|
- Managing dependencies between projects
|
|
- Migrating to Nx
|
|
|
|
## Core Concepts
|
|
|
|
### 1. Nx Architecture
|
|
|
|
```
|
|
workspace/
|
|
├── apps/ # Deployable applications
|
|
│ ├── web/
|
|
│ └── api/
|
|
├── libs/ # Shared libraries
|
|
│ ├── shared/
|
|
│ │ ├── ui/
|
|
│ │ └── utils/
|
|
│ └── feature/
|
|
│ ├── auth/
|
|
│ └── dashboard/
|
|
├── tools/ # Custom executors/generators
|
|
├── nx.json # Nx configuration
|
|
└── workspace.json # Project configuration
|
|
```
|
|
|
|
### 2. Library Types
|
|
|
|
| Type | Purpose | Example |
|
|
|------|---------|---------|
|
|
| **feature** | Smart components, business logic | `feature-auth` |
|
|
| **ui** | Presentational components | `ui-buttons` |
|
|
| **data-access** | API calls, state management | `data-access-users` |
|
|
| **util** | Pure functions, helpers | `util-formatting` |
|
|
| **shell** | App bootstrapping | `shell-web` |
|
|
|
|
## Templates
|
|
|
|
### Template 1: nx.json Configuration
|
|
|
|
```json
|
|
{
|
|
"$schema": "./node_modules/nx/schemas/nx-schema.json",
|
|
"npmScope": "myorg",
|
|
"affected": {
|
|
"defaultBase": "main"
|
|
},
|
|
"tasksRunnerOptions": {
|
|
"default": {
|
|
"runner": "nx/tasks-runners/default",
|
|
"options": {
|
|
"cacheableOperations": [
|
|
"build",
|
|
"lint",
|
|
"test",
|
|
"e2e",
|
|
"build-storybook"
|
|
],
|
|
"parallel": 3
|
|
}
|
|
}
|
|
},
|
|
"targetDefaults": {
|
|
"build": {
|
|
"dependsOn": ["^build"],
|
|
"inputs": ["production", "^production"],
|
|
"cache": true
|
|
},
|
|
"test": {
|
|
"inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"],
|
|
"cache": true
|
|
},
|
|
"lint": {
|
|
"inputs": ["default", "{workspaceRoot}/.eslintrc.json"],
|
|
"cache": true
|
|
},
|
|
"e2e": {
|
|
"inputs": ["default", "^production"],
|
|
"cache": true
|
|
}
|
|
},
|
|
"namedInputs": {
|
|
"default": ["{projectRoot}/**/*", "sharedGlobals"],
|
|
"production": [
|
|
"default",
|
|
"!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
|
|
"!{projectRoot}/tsconfig.spec.json",
|
|
"!{projectRoot}/jest.config.[jt]s",
|
|
"!{projectRoot}/.eslintrc.json"
|
|
],
|
|
"sharedGlobals": [
|
|
"{workspaceRoot}/babel.config.json",
|
|
"{workspaceRoot}/tsconfig.base.json"
|
|
]
|
|
},
|
|
"generators": {
|
|
"@nx/react": {
|
|
"application": {
|
|
"style": "css",
|
|
"linter": "eslint",
|
|
"bundler": "webpack"
|
|
},
|
|
"library": {
|
|
"style": "css",
|
|
"linter": "eslint"
|
|
},
|
|
"component": {
|
|
"style": "css"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Template 2: Project Configuration
|
|
|
|
```json
|
|
// apps/web/project.json
|
|
{
|
|
"name": "web",
|
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
"sourceRoot": "apps/web/src",
|
|
"projectType": "application",
|
|
"tags": ["type:app", "scope:web"],
|
|
"targets": {
|
|
"build": {
|
|
"executor": "@nx/webpack:webpack",
|
|
"outputs": ["{options.outputPath}"],
|
|
"defaultConfiguration": "production",
|
|
"options": {
|
|
"compiler": "babel",
|
|
"outputPath": "dist/apps/web",
|
|
"index": "apps/web/src/index.html",
|
|
"main": "apps/web/src/main.tsx",
|
|
"tsConfig": "apps/web/tsconfig.app.json",
|
|
"assets": ["apps/web/src/assets"],
|
|
"styles": ["apps/web/src/styles.css"]
|
|
},
|
|
"configurations": {
|
|
"development": {
|
|
"extractLicenses": false,
|
|
"optimization": false,
|
|
"sourceMap": true
|
|
},
|
|
"production": {
|
|
"optimization": true,
|
|
"outputHashing": "all",
|
|
"sourceMap": false,
|
|
"extractLicenses": true
|
|
}
|
|
}
|
|
},
|
|
"serve": {
|
|
"executor": "@nx/webpack:dev-server",
|
|
"defaultConfiguration": "development",
|
|
"options": {
|
|
"buildTarget": "web:build"
|
|
},
|
|
"configurations": {
|
|
"development": {
|
|
"buildTarget": "web:build:development"
|
|
},
|
|
"production": {
|
|
"buildTarget": "web:build:production"
|
|
}
|
|
}
|
|
},
|
|
"test": {
|
|
"executor": "@nx/jest:jest",
|
|
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
|
"options": {
|
|
"jestConfig": "apps/web/jest.config.ts",
|
|
"passWithNoTests": true
|
|
}
|
|
},
|
|
"lint": {
|
|
"executor": "@nx/eslint:lint",
|
|
"outputs": ["{options.outputFile}"],
|
|
"options": {
|
|
"lintFilePatterns": ["apps/web/**/*.{ts,tsx,js,jsx}"]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Template 3: Module Boundary Rules
|
|
|
|
```json
|
|
// .eslintrc.json
|
|
{
|
|
"root": true,
|
|
"ignorePatterns": ["**/*"],
|
|
"plugins": ["@nx"],
|
|
"overrides": [
|
|
{
|
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
|
"rules": {
|
|
"@nx/enforce-module-boundaries": [
|
|
"error",
|
|
{
|
|
"enforceBuildableLibDependency": true,
|
|
"allow": [],
|
|
"depConstraints": [
|
|
{
|
|
"sourceTag": "type:app",
|
|
"onlyDependOnLibsWithTags": [
|
|
"type:feature",
|
|
"type:ui",
|
|
"type:data-access",
|
|
"type:util"
|
|
]
|
|
},
|
|
{
|
|
"sourceTag": "type:feature",
|
|
"onlyDependOnLibsWithTags": [
|
|
"type:ui",
|
|
"type:data-access",
|
|
"type:util"
|
|
]
|
|
},
|
|
{
|
|
"sourceTag": "type:ui",
|
|
"onlyDependOnLibsWithTags": ["type:ui", "type:util"]
|
|
},
|
|
{
|
|
"sourceTag": "type:data-access",
|
|
"onlyDependOnLibsWithTags": ["type:data-access", "type:util"]
|
|
},
|
|
{
|
|
"sourceTag": "type:util",
|
|
"onlyDependOnLibsWithTags": ["type:util"]
|
|
},
|
|
{
|
|
"sourceTag": "scope:web",
|
|
"onlyDependOnLibsWithTags": ["scope:web", "scope:shared"]
|
|
},
|
|
{
|
|
"sourceTag": "scope:api",
|
|
"onlyDependOnLibsWithTags": ["scope:api", "scope:shared"]
|
|
},
|
|
{
|
|
"sourceTag": "scope:shared",
|
|
"onlyDependOnLibsWithTags": ["scope:shared"]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Template 4: Custom Generator
|
|
|
|
```typescript
|
|
// tools/generators/feature-lib/index.ts
|
|
import {
|
|
Tree,
|
|
formatFiles,
|
|
generateFiles,
|
|
joinPathFragments,
|
|
names,
|
|
readProjectConfiguration,
|
|
} from '@nx/devkit';
|
|
import { libraryGenerator } from '@nx/react';
|
|
|
|
interface FeatureLibraryGeneratorSchema {
|
|
name: string;
|
|
scope: string;
|
|
directory?: string;
|
|
}
|
|
|
|
export default async function featureLibraryGenerator(
|
|
tree: Tree,
|
|
options: FeatureLibraryGeneratorSchema
|
|
) {
|
|
const { name, scope, directory } = options;
|
|
const projectDirectory = directory
|
|
? `${directory}/${name}`
|
|
: `libs/${scope}/feature-${name}`;
|
|
|
|
// Generate base library
|
|
await libraryGenerator(tree, {
|
|
name: `feature-${name}`,
|
|
directory: projectDirectory,
|
|
tags: `type:feature,scope:${scope}`,
|
|
style: 'css',
|
|
skipTsConfig: false,
|
|
skipFormat: true,
|
|
unitTestRunner: 'jest',
|
|
linter: 'eslint',
|
|
});
|
|
|
|
// Add custom files
|
|
const projectConfig = readProjectConfiguration(tree, `${scope}-feature-${name}`);
|
|
const projectNames = names(name);
|
|
|
|
generateFiles(
|
|
tree,
|
|
joinPathFragments(__dirname, 'files'),
|
|
projectConfig.sourceRoot,
|
|
{
|
|
...projectNames,
|
|
scope,
|
|
tmpl: '',
|
|
}
|
|
);
|
|
|
|
await formatFiles(tree);
|
|
}
|
|
```
|
|
|
|
### Template 5: CI Configuration with Affected
|
|
|
|
```yaml
|
|
# .github/workflows/ci.yml
|
|
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
env:
|
|
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
|
|
|
jobs:
|
|
main:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
cache: 'npm'
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Derive SHAs for affected commands
|
|
uses: nrwl/nx-set-shas@v4
|
|
|
|
- name: Run affected lint
|
|
run: npx nx affected -t lint --parallel=3
|
|
|
|
- name: Run affected test
|
|
run: npx nx affected -t test --parallel=3 --configuration=ci
|
|
|
|
- name: Run affected build
|
|
run: npx nx affected -t build --parallel=3
|
|
|
|
- name: Run affected e2e
|
|
run: npx nx affected -t e2e --parallel=1
|
|
```
|
|
|
|
### Template 6: Remote Caching Setup
|
|
|
|
```typescript
|
|
// nx.json with Nx Cloud
|
|
{
|
|
"tasksRunnerOptions": {
|
|
"default": {
|
|
"runner": "nx-cloud",
|
|
"options": {
|
|
"cacheableOperations": ["build", "lint", "test", "e2e"],
|
|
"accessToken": "your-nx-cloud-token",
|
|
"parallel": 3,
|
|
"cacheDirectory": ".nx/cache"
|
|
}
|
|
}
|
|
},
|
|
"nxCloudAccessToken": "your-nx-cloud-token"
|
|
}
|
|
|
|
// Self-hosted cache with S3
|
|
{
|
|
"tasksRunnerOptions": {
|
|
"default": {
|
|
"runner": "@nx-aws-cache/nx-aws-cache",
|
|
"options": {
|
|
"cacheableOperations": ["build", "lint", "test"],
|
|
"awsRegion": "us-east-1",
|
|
"awsBucket": "my-nx-cache-bucket",
|
|
"awsProfile": "default"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Common Commands
|
|
|
|
```bash
|
|
# Generate new library
|
|
nx g @nx/react:lib feature-auth --directory=libs/web --tags=type:feature,scope:web
|
|
|
|
# Run affected tests
|
|
nx affected -t test --base=main
|
|
|
|
# View dependency graph
|
|
nx graph
|
|
|
|
# Run specific project
|
|
nx build web --configuration=production
|
|
|
|
# Reset cache
|
|
nx reset
|
|
|
|
# Run migrations
|
|
nx migrate latest
|
|
nx migrate --run-migrations
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### Do's
|
|
- **Use tags consistently** - Enforce with module boundaries
|
|
- **Enable caching early** - Significant CI savings
|
|
- **Keep libs focused** - Single responsibility
|
|
- **Use generators** - Ensure consistency
|
|
- **Document boundaries** - Help new developers
|
|
|
|
### Don'ts
|
|
- **Don't create circular deps** - Graph should be acyclic
|
|
- **Don't skip affected** - Test only what changed
|
|
- **Don't ignore boundaries** - Tech debt accumulates
|
|
- **Don't over-granularize** - Balance lib count
|
|
|
|
## Resources
|
|
|
|
- [Nx Documentation](https://nx.dev/getting-started/intro)
|
|
- [Module Boundaries](https://nx.dev/core-features/enforce-module-boundaries)
|
|
- [Nx Cloud](https://nx.app/)
|