一键导入
agent-orchestration
Node.js automation scripts, AST-based code generation, and CLI tools for scaffolding Clean Architecture boilerplate from Prisma schemas.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Node.js automation scripts, AST-based code generation, and CLI tools for scaffolding Clean Architecture boilerplate from Prisma schemas.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Review TypeScript DDD clean architecture code for correctness bugs, layer violations, and simplification opportunities. Trigger when the user says "review this", "check this code", "code review", "what's wrong with this", "is this correct DDD?", "does this follow clean architecture?", or when changes touch domain, application, or infrastructure layers.
Security review for TypeScript DDD code — OWASP-mapped checks for injection, auth/authz, data exposure, and misconfiguration. Trigger when the user says "security review", "check for vulnerabilities", "is this secure?", "review auth code", or when changes touch authentication, authorization, input handling, external APIs, file uploads, or user-controlled data.
Review and simplify TypeScript DDD code — remove unnecessary complexity, eliminate duplication, reduce premature abstraction. Trigger when the user says "simplify this", "too much boilerplate", "over-engineered", "clean this up", "refactor this", or when code has grown too abstract or introduces patterns before they prove value.
Design and implement the CQRS (Command Query Responsibility Segregation) pattern in a TypeScript DDD clean architecture project. Trigger when the user says "implement CQRS", "add a command", "add a query", "create a use case", "add a command handler", "build the application layer", "set up the command bus", or when the user needs to add a new feature and is asking how to wire up the application layer. Also trigger when distinguishing between write operations (commands) and read operations (queries) in any context.
Design and implement the Repository pattern in a TypeScript DDD clean architecture project — define repository interfaces in the domain layer and implementations in the infrastructure layer. Trigger when the user says "create a repository", "implement persistence", "add database access", "wire up TypeORM/Prisma/Drizzle", "implement the repository interface", "add Unit of Work", "how do I persist this aggregate", or when connecting domain aggregates to any data store. Also trigger when reviewing persistence code that may be violating the dependency rule.
Design and implement CI/CD pipelines for a TypeScript DDD clean architecture project — GitHub Actions, GitLab CI, Docker builds, environment promotion, and secrets management. Trigger when the user says "set up CI", "add a pipeline", "automate tests", "write a GitHub Actions workflow", "configure deployment", "add Docker support", "set up CD", "automate the build", or when the project needs automated quality gates before merge. Also trigger when the user asks about environment promotion (dev → staging → prod) or secrets management strategy.
| name | agent-orchestration |
| description | Node.js automation scripts, AST-based code generation, and CLI tools for scaffolding Clean Architecture boilerplate from Prisma schemas. |
Tooling and Developer Experience (DevEx) expert. Builds local Node.js / Bash scripts that automate the repetitive, mechanical parts of Clean Architecture scaffolding.
ts-morph to read, transform, or update existing TypeScript source files@prisma/internals or ts-morph) and scaffold DDD layers automatically.ts-morph) to read and modify TypeScript files precisely, rather than fragile string manipulation.ts-ddd-clean-architecture skill.# Place scripts in a dedicated directory
mkdir scripts
# Install tooling as dev dependencies
npm install -D ts-morph @prisma/internals zx handlebars
scripts/
├── scaffold-feature.ts # Main entry: reads Prisma model → generates all layers
├── generators/
│ ├── entity.generator.ts
│ ├── repository-port.generator.ts
│ ├── use-case.generator.ts
│ └── controller.generator.ts
└── templates/
├── entity.hbs
├── repository-port.hbs
└── use-case.hbs
Add a script to package.json:
{
"scripts": {
"scaffold": "npx ts-node scripts/scaffold-feature.ts"
}
}
// scripts/read-prisma-schema.ts
import { getDMMF } from '@prisma/internals';
import { readFileSync } from 'fs';
async function getPrismaModels() {
const schema = readFileSync('prisma/schema.prisma', 'utf-8');
const dmmf = await getDMMF({ datamodel: schema });
return dmmf.datamodel.models.map((model) => ({
name: model.name, // e.g. "User"
fields: model.fields.map((f) => ({
name: f.name, // e.g. "email"
type: f.type, // e.g. "String"
isRequired: f.isRequired,
isId: f.isId,
})),
}));
}
export { getPrismaModels };
// scripts/generators/entity.generator.ts
import Handlebars from 'handlebars';
import { writeFileSync, mkdirSync } from 'fs';
import { join } from 'path';
const ENTITY_TEMPLATE = `
import { DomainEvent } from '../events/domain-event.interface';
export class {{PascalName}} {
private readonly domainEvents: DomainEvent[] = [];
private constructor(
private readonly id: {{PascalName}}Id,
{{#each fields}}
private {{camelName}}: {{tsType}},
{{/each}}
) {}
static create(id: {{PascalName}}Id, {{constructorParams}}): {{PascalName}} {
const entity = new {{PascalName}}(id, {{constructorArgs}});
// TODO: push domain event if needed
return entity;
}
pullDomainEvents(): DomainEvent[] {
const events = [...this.domainEvents];
this.domainEvents.length = 0;
return events;
}
getId(): {{PascalName}}Id { return this.id; }
}
`.trim();
interface EntityTemplateData {
PascalName: string;
fields: Array<{ camelName: string; tsType: string }>;
constructorParams: string;
constructorArgs: string;
}
export function generateEntity(data: EntityTemplateData, outputDir: string): void {
const template = Handlebars.compile(ENTITY_TEMPLATE);
const content = template(data);
const filePath = join(outputDir, `${data.PascalName.toLowerCase()}.entity.ts`);
mkdirSync(outputDir, { recursive: true });
writeFileSync(filePath, content, 'utf-8');
console.log(`✔ Generated: ${filePath}`);
}
// scripts/wire-use-case.ts
// Automatically adds a new Use Case binding to src/main/container.ts
import { Project } from 'ts-morph';
interface WireOptions {
useCaseName: string; // e.g. "DeleteUserUseCase"
repoVariable: string; // e.g. "userRepo"
publisherVariable: string; // e.g. "publisher"
}
export function wireUseCase({ useCaseName, repoVariable, publisherVariable }: WireOptions): void {
const project = new Project({ tsConfigFilePath: 'tsconfig.json' });
const containerFile = project.getSourceFileOrThrow('src/main/container.ts');
// 1. Add the import if it doesn't exist
const importPath = `../application/use-cases/${toKebabCase(useCaseName)}.use-case`;
const hasImport = containerFile.getImportDeclarations()
.some((d) => d.getModuleSpecifierValue() === importPath);
if (!hasImport) {
containerFile.addImportDeclaration({
namedImports: [useCaseName],
moduleSpecifier: importPath,
});
}
// 2. Append the instantiation at the bottom of the file
const camelName = useCaseName.charAt(0).toLowerCase() + useCaseName.slice(1);
containerFile.addStatements(
`export const ${camelName} = new ${useCaseName}(${repoVariable}, ${publisherVariable});`,
);
containerFile.saveSync();
console.log(`✔ Wired ${useCaseName} into container.ts`);
}
function toKebabCase(str: string): string {
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
// scripts/scaffold-feature.ts
import { getPrismaModels } from './read-prisma-schema';
import { generateEntity } from './generators/entity.generator';
import { generateRepositoryPort } from './generators/repository-port.generator';
import { generateUseCase } from './generators/use-case.generator';
const [,, modelName] = process.argv;
if (!modelName) {
console.error('Usage: npm run scaffold <ModelName>');
process.exit(1);
}
async function main() {
const models = await getPrismaModels();
const model = models.find((m) => m.name === modelName);
if (!model) {
console.error(`Model "${modelName}" not found in schema.prisma`);
process.exit(1);
}
const fields = model.fields
.filter((f) => !f.isId)
.map((f) => ({ camelName: f.name, tsType: prismaTypeToTs(f.type) }));
console.log(`\nScaffolding feature for: ${modelName}`);
generateEntity({ PascalName: modelName, fields, constructorParams: '', constructorArgs: '' },
`src/domain/entities`);
generateRepositoryPort({ PascalName: modelName }, `src/application/ports`);
generateUseCase({ PascalName: modelName }, `src/application/use-cases`);
console.log('\n✅ Done. Review generated files and fill in the business logic.');
}
function prismaTypeToTs(prismaType: string): string {
const map: Record<string, string> = {
String: 'string', Int: 'number', Float: 'number',
Boolean: 'boolean', DateTime: 'Date', Json: 'Record<string, unknown>',
};
return map[prismaType] ?? prismaType;
}
main().catch(console.error);
When asked to build an automation script:
@prisma/internals for Prisma schema)ts-morph to update container.ts)package.json under "scripts"| ❌ Never Do | ✅ Instead |
|---|---|
| String-replace existing TypeScript files | Use ts-morph AST transformations |
| Hardcode file paths inside generator functions | Pass outputDir as a parameter |
| Generate files that violate Clean Architecture layers | Follow the workflow in ts-ddd-clean-architecture skill |
Run generators in src/ directly | Keep scripts in scripts/ directory; never in src/ |
| Commit generated boilerplate blindly | Review and fill in business logic before committing |