@forinda/kickjs-cli 5.9.1 → 5.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/{agent-docs-CxuUgXF_.mjs → agent-docs-D1k73ywL.mjs} +3 -3
  2. package/dist/{agent-docs-CxuUgXF_.mjs.map → agent-docs-D1k73ywL.mjs.map} +1 -1
  3. package/dist/{builtins-yDCsD2ae.mjs → builtins-DsO7X7g9.mjs} +2 -2
  4. package/dist/cli.mjs +262 -164
  5. package/dist/{config-icjz-b4f.mjs → config-BnCPYsQu.mjs} +3 -3
  6. package/dist/config-BnCPYsQu.mjs.map +1 -0
  7. package/dist/{doctor-BrkA44Gz.mjs → doctor-Brw2ruus.mjs} +126 -30
  8. package/dist/doctor-Brw2ruus.mjs.map +1 -0
  9. package/dist/index.d.mts +11 -5
  10. package/dist/index.d.mts.map +1 -1
  11. package/dist/index.mjs +2 -2
  12. package/dist/{plugin-CdC5d4kw.mjs → plugin-0_cDz8hu.mjs} +3 -3
  13. package/dist/{plugin-CdC5d4kw.mjs.map → plugin-0_cDz8hu.mjs.map} +1 -1
  14. package/dist/{project-docs-LqGoslXZ.mjs → project-docs-Bh20nvAP.mjs} +2 -2
  15. package/dist/{project-docs-LqGoslXZ.mjs.map → project-docs-Bh20nvAP.mjs.map} +1 -1
  16. package/dist/{project-root-CM9jhMiW.mjs → project-root-B7_vpWeX.mjs} +3 -3
  17. package/dist/{project-root-CM9jhMiW.mjs.map → project-root-B7_vpWeX.mjs.map} +1 -1
  18. package/dist/{rolldown-runtime-My-8F9u3.mjs → rolldown-runtime-C3T7GIRR.mjs} +1 -1
  19. package/dist/{run-plugins-B3Ooiczg.mjs → run-plugins-w_kqfVLL.mjs} +14 -12
  20. package/dist/run-plugins-w_kqfVLL.mjs.map +1 -0
  21. package/dist/{typegen-Cd0w7Al-.mjs → typegen-CFtS0A1P.mjs} +5 -5
  22. package/dist/typegen-CFtS0A1P.mjs.map +1 -0
  23. package/dist/{types-Dry-y2AZ.mjs → types-CkZ7XoFJ.mjs} +2 -2
  24. package/dist/{types-Dry-y2AZ.mjs.map → types-CkZ7XoFJ.mjs.map} +1 -1
  25. package/package.json +10 -10
  26. package/dist/config-icjz-b4f.mjs.map +0 -1
  27. package/dist/doctor-BrkA44Gz.mjs.map +0 -1
  28. package/dist/run-plugins-B3Ooiczg.mjs.map +0 -1
  29. package/dist/typegen-Cd0w7Al-.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"doctor-BrkA44Gz.mjs","names":["generateController","generateController","colors","colors"],"sources":["../src/utils/naming.ts","../src/utils/regex.ts","../src/generators/templates/module-index.ts","../src/generators/templates/controller.ts","../src/generators/templates/constants.ts","../src/generators/templates/dtos.ts","../src/generators/templates/use-cases.ts","../src/generators/templates/repository.ts","../src/generators/templates/domain.ts","../src/generators/templates/tests.ts","../src/generators/templates/rest-service.ts","../src/generators/templates/cqrs.ts","../src/generators/templates/drizzle/index.ts","../src/generators/templates/prisma/index.ts","../src/generators/templates/project-app.ts","../src/generators/patterns/minimal.ts","../src/generators/patterns/rest.ts","../src/generators/patterns/cqrs.ts","../src/generators/patterns/ddd.ts","../src/generators/module.ts","../src/generators/adapter.ts","../src/utils/resolve-out-dir.ts","../src/generators/middleware.ts","../src/generators/guard.ts","../src/generators/service.ts","../src/generators/controller.ts","../src/generators/dto.ts","../src/generators/templates/project-config.ts","../src/generators/project.ts","../src/generator-extension/define.ts","../src/generator-extension/context.ts","../src/generator-extension/discover.ts","../src/generator-extension/dispatch.ts","../src/commands/doctor.ts"],"sourcesContent":["import pkg from 'pluralize'\n\n/** Convert a name to PascalCase */\nexport function toPascalCase(name: string): string {\n return name\n .replace(/[-_\\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))\n .replace(/^(.)/, (c) => c.toUpperCase())\n}\n\n/** Convert a name to camelCase */\nexport function toCamelCase(name: string): string {\n const pascal = toPascalCase(name)\n return pascal.charAt(0).toLowerCase() + pascal.slice(1)\n}\n\n/** Convert a name to kebab-case */\nexport function toKebabCase(name: string): string {\n return name\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase()\n}\n\n/**\n * Pluralize a kebab-case name for directory/file names.\n * Uses the `pluralize` npm package for correct English pluralization\n * including irregulars (person → people, status → statuses, child → children).\n */\nexport function pluralize(name: string): string {\n return pkg.plural(name)\n}\n\n/**\n * Pluralize a PascalCase name for class identifiers.\n * Used for `List${pluralPascal}UseCase` to avoid `ListUserssUseCase`.\n */\nexport function pluralizePascal(name: string): string {\n return pkg.plural(name)\n}\n","/**\n * Escape a string for safe interpolation into a `new RegExp(...)`\n * source. Each character that has special meaning inside a regex\n * pattern (`.`, `*`, `+`, `?`, `^`, `$`, `{`, `}`, `(`, `)`, `|`,\n * `[`, `]`, `\\`) is prefixed with a backslash so it's matched\n * literally instead of treated as a metacharacter.\n *\n * Used everywhere the codegen / removal flow builds a regex from\n * adopter-provided identifiers (`pascal`, `plural`, etc). Names\n * coming through `toPascalCase` / `toKebabCase` are already\n * alphanumeric in practice, but escaping defensively keeps the\n * code correct if upstream sanitization ever loosens.\n */\nexport function escapeRegex(input: string): string {\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","import type { RepoType } from '../module'\nimport type { ModuleStyle, TemplateContext } from './types'\n\nconst repoLabelMap: Record<string, string> = {\n inmemory: 'in-memory',\n drizzle: 'Drizzle',\n prisma: 'Prisma',\n}\n\nfunction toPascalRepoType(repo: string): string {\n return (\n repo.charAt(0).toUpperCase() +\n repo.slice(1).replace(/-([a-z])/g, (_, c: string) => c.toUpperCase())\n )\n}\n\nfunction toKebabRepoType(repo: string): string {\n return repo.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()\n}\n\nfunction repoLabel(repo: RepoType): string {\n return repoLabelMap[repo] ?? toPascalRepoType(repo)\n}\n\nfunction repoMaps(pascal: string, kebab: string, repo: RepoType) {\n const repoClassMap: Record<string, string> = {\n inmemory: `InMemory${pascal}Repository`,\n drizzle: `Drizzle${pascal}Repository`,\n prisma: `Prisma${pascal}Repository`,\n }\n const repoFileMap: Record<string, string> = {\n inmemory: `in-memory-${kebab}`,\n drizzle: `drizzle-${kebab}`,\n prisma: `prisma-${kebab}`,\n }\n return {\n repoClass: repoClassMap[repo] ?? `${toPascalRepoType(repo)}${pascal}Repository`,\n repoFile: repoFileMap[repo] ?? `${toKebabRepoType(repo)}-${kebab}`,\n }\n}\n\n/** Resolve the style flag, defaulting to 'define' for new code. */\nfunction resolveStyle(style?: ModuleStyle): ModuleStyle {\n return style ?? 'define'\n}\n\n/** DDD module index — nested folders, use-cases, domain services */\nexport function generateModuleIndex(ctx: TemplateContext & { repo: RepoType }): string {\n const { pascal, kebab, plural = '', repo, style } = ctx\n const { repoClass, repoFile } = repoMaps(pascal, kebab, repo)\n const resolvedStyle = resolveStyle(style)\n\n const header = `/**\n * ${pascal} Module\n *\n * Self-contained feature module following Domain-Driven Design (DDD).\n * Registers dependencies in the DI container and declares HTTP routes.\n *\n * Structure:\n * presentation/ — HTTP controllers (entry points)\n * application/ — Use cases (orchestration) and DTOs (validation)\n * domain/ — Entities, value objects, repository interfaces, domain services\n * infrastructure/ — Repository implementations (currently ${repoLabel(repo)})\n */`\n\n const repoImports = `import { ${pascal.toUpperCase()}_REPOSITORY } from './domain/repositories/${kebab}.repository'\nimport { ${repoClass} } from './infrastructure/repositories/${repoFile}.repository'\nimport { ${pascal}Controller } from './presentation/${kebab}.controller'\n\n// Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container\nimport.meta.glob(\n ['./domain/services/**/*.ts', './application/use-cases/**/*.ts', '!./**/*.test.ts'],\n { eager: true },\n)`\n\n const routesDoc = ` /**\n * Declare HTTP routes for this module. Return value shape:\n *\n * - \\`path\\` — URL prefix for this route set, mounted under\n * \\`/{apiPrefix}/v{version}{path}\\`.\n * - \\`controller\\` — Controller class. Used both for the route\n * handler bindings and OpenAPI spec generation.\n * - \\`version\\` — Optional. Overrides the app-wide API version\n * for this route set only.\n *\n * Return an **array** to mount multiple route sets under the\n * same module (e.g. side-by-side v1 + v2 controllers):\n *\n * return [\n * { path: '/${plural}', version: 1, controller: ${pascal}V1Controller },\n * { path: '/${plural}', version: 2, controller: ${pascal}V2Controller },\n * ]\n */`\n\n if (resolvedStyle === 'class') {\n return `${header}\nimport { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'\n${repoImports}\n\nexport class ${pascal}Module implements AppModule {\n /**\n * Register module dependencies in the DI container.\n * Bind repository interface tokens to their implementations here.\n * Currently wired to ${repoLabel(repo)}. To swap implementations, change the factory target.\n */\n register(container: Container): void {\n container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>\n container.resolve(${repoClass}),\n )\n }\n\n${routesDoc.replace(/^ {4}/gm, ' ').replace(/^ {6}/gm, ' ')}\n routes(): ModuleRoutes {\n return {\n path: '/${plural}',\n controller: ${pascal}Controller,\n }\n }\n}\n`\n }\n\n return `${header}\nimport { defineModule } from '@forinda/kickjs'\n${repoImports}\n\nexport const ${pascal}Module = defineModule({\n name: '${pascal}Module',\n build: () => ({\n /**\n * Register module dependencies in the DI container.\n * Bind repository interface tokens to their implementations here.\n * Currently wired to ${repoLabel(repo)}. To swap implementations, change the factory target.\n */\n register(container) {\n container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>\n container.resolve(${repoClass}),\n )\n },\n\n${routesDoc}\n routes() {\n return {\n path: '/${plural}',\n controller: ${pascal}Controller,\n }\n },\n }),\n})\n`\n}\n\n/** REST module index — flat folder, service + controller, no use-cases */\nexport function generateRestModuleIndex(ctx: TemplateContext & { repo: RepoType }): string {\n const { pascal, kebab, plural = '', repo, style } = ctx\n const { repoClass, repoFile } = repoMaps(pascal, kebab, repo)\n const resolvedStyle = resolveStyle(style)\n\n const header = `/**\n * ${pascal} Module\n *\n * REST module with a flat folder structure.\n * Controller delegates to service, service wraps the repository.\n *\n * Structure:\n * ${kebab}.controller.ts — HTTP routes (CRUD)\n * ${kebab}.service.ts — Business logic\n * ${kebab}.repository.ts — Repository interface\n * ${repoFile}.repository.ts — Repository implementation\n * dtos/ — Request/response schemas\n */`\n\n const repoImports = `import { ${pascal.toUpperCase()}_REPOSITORY } from './${kebab}.repository'\nimport { ${repoClass} } from './${repoFile}.repository'\nimport { ${pascal}Controller } from './${kebab}.controller'\n\n// Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container\nimport.meta.glob(['./**/*.service.ts', './**/*.repository.ts', '!./**/*.test.ts'], { eager: true })`\n\n const routesDoc = ` /**\n * Declare HTTP routes for this module. Return value shape:\n *\n * - \\`path\\` — URL prefix for this route set.\n * - \\`controller\\` — Controller class (also drives OpenAPI).\n * - \\`version\\` — Optional. Overrides the app-wide API version.\n *\n * Return an **array** to mount multiple route sets — admin\n * surfaces, side-by-side v1 + v2 controllers, etc:\n *\n * return [\n * { path: '/${plural}', version: 1, controller: ${pascal}V1Controller },\n * { path: '/${plural}', version: 2, controller: ${pascal}V2Controller },\n * ]\n */`\n\n if (resolvedStyle === 'class') {\n return `${header}\nimport { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'\n${repoImports}\n\nexport class ${pascal}Module implements AppModule {\n register(container: Container): void {\n container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>\n container.resolve(${repoClass}),\n )\n }\n\n${routesDoc.replace(/^ {4}/gm, ' ').replace(/^ {6}/gm, ' ')}\n routes(): ModuleRoutes {\n return {\n path: '/${plural}',\n controller: ${pascal}Controller,\n }\n }\n}\n`\n }\n\n return `${header}\nimport { defineModule } from '@forinda/kickjs'\n${repoImports}\n\nexport const ${pascal}Module = defineModule({\n name: '${pascal}Module',\n build: () => ({\n register(container) {\n container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>\n container.resolve(${repoClass}),\n )\n },\n\n${routesDoc}\n routes() {\n return {\n path: '/${plural}',\n controller: ${pascal}Controller,\n }\n },\n }),\n})\n`\n}\n\n/** Minimal module index — just controller, no service/repo */\nexport function generateMinimalModuleIndex(ctx: TemplateContext): string {\n const { pascal, kebab, plural = '', style } = ctx\n const resolvedStyle = resolveStyle(style)\n\n const routesDoc = ` /**\n * Declare HTTP routes. Return value shape:\n *\n * - \\`path\\` — URL prefix for this route set.\n * - \\`controller\\` — Controller class (also drives OpenAPI).\n * - \\`version\\` — Optional. Overrides the app-wide API version.\n *\n * Return an array to mount multiple route sets:\n *\n * return [\n * { path: '/${plural}', version: 1, controller: ${pascal}V1Controller },\n * { path: '/${plural}', version: 2, controller: ${pascal}V2Controller },\n * ]\n */`\n\n if (resolvedStyle === 'class') {\n return `import { type AppModule, type ModuleRoutes } from '@forinda/kickjs'\nimport { ${pascal}Controller } from './${kebab}.controller'\n\nexport class ${pascal}Module implements AppModule {\n${routesDoc.replace(/^ {4}/gm, ' ').replace(/^ {6}/gm, ' ')}\n routes(): ModuleRoutes {\n return {\n path: '/${plural}',\n controller: ${pascal}Controller,\n }\n }\n}\n`\n }\n\n return `import { defineModule } from '@forinda/kickjs'\nimport { ${pascal}Controller } from './${kebab}.controller'\n\nexport const ${pascal}Module = defineModule({\n name: '${pascal}Module',\n build: () => ({\n${routesDoc}\n routes() {\n return {\n path: '/${plural}',\n controller: ${pascal}Controller,\n }\n },\n }),\n})\n`\n}\n","import type { TemplateContext } from './types'\n\n/** DDD controller — injects use-cases, nested import paths */\nexport function generateController(ctx: TemplateContext): string {\n const { pascal, kebab, plural = '', pluralPascal = '' } = ctx\n return `import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'\nimport { ApiTags } from '@forinda/kickjs-swagger'\nimport { Create${pascal}UseCase } from '../application/use-cases/create-${kebab}.use-case'\nimport { Get${pascal}UseCase } from '../application/use-cases/get-${kebab}.use-case'\nimport { List${pluralPascal}UseCase } from '../application/use-cases/list-${plural}.use-case'\nimport { Update${pascal}UseCase } from '../application/use-cases/update-${kebab}.use-case'\nimport { Delete${pascal}UseCase } from '../application/use-cases/delete-${kebab}.use-case'\nimport { create${pascal}Schema } from '../application/dtos/create-${kebab}.dto'\nimport { update${pascal}Schema } from '../application/dtos/update-${kebab}.dto'\nimport { ${pascal.toUpperCase()}_QUERY_CONFIG } from '../constants'\n\n// Each handler annotates its \\`ctx\\` with \\`Ctx<KickRoutes.${pascal}Controller['<method>']>\\`\n// so \\`ctx.params\\`, \\`ctx.body\\`, and \\`ctx.query\\` are typed end-to-end.\n// The \\`KickRoutes\\` namespace is generated by \\`kick typegen\\` (auto-run on\n// \\`kick dev\\`) — see https://forinda.github.io/kick-js/guide/typegen.\n\n@Controller()\nexport class ${pascal}Controller {\n @Autowired() private readonly create${pascal}UseCase!: Create${pascal}UseCase\n @Autowired() private readonly get${pascal}UseCase!: Get${pascal}UseCase\n @Autowired() private readonly list${pluralPascal}UseCase!: List${pluralPascal}UseCase\n @Autowired() private readonly update${pascal}UseCase!: Update${pascal}UseCase\n @Autowired() private readonly delete${pascal}UseCase!: Delete${pascal}UseCase\n\n @Get('/')\n @ApiTags('${pascal}')\n @ApiQueryParams(${pascal.toUpperCase()}_QUERY_CONFIG)\n async list(ctx: Ctx<KickRoutes.${pascal}Controller['list']>) {\n return ctx.paginate(\n (parsed) => this.list${pluralPascal}UseCase.execute(parsed),\n ${pascal.toUpperCase()}_QUERY_CONFIG,\n )\n }\n\n @Get('/:id')\n @ApiTags('${pascal}')\n async getById(ctx: Ctx<KickRoutes.${pascal}Controller['getById']>) {\n const result = await this.get${pascal}UseCase.execute(ctx.params.id)\n if (!result) return ctx.notFound('${pascal} not found')\n ctx.json(result)\n }\n\n @Post('/', { body: create${pascal}Schema, name: 'Create${pascal}' })\n @ApiTags('${pascal}')\n async create(ctx: Ctx<KickRoutes.${pascal}Controller['create']>) {\n const result = await this.create${pascal}UseCase.execute(ctx.body)\n ctx.created(result)\n }\n\n @Put('/:id', { body: update${pascal}Schema, name: 'Update${pascal}' })\n @ApiTags('${pascal}')\n async update(ctx: Ctx<KickRoutes.${pascal}Controller['update']>) {\n const result = await this.update${pascal}UseCase.execute(ctx.params.id, ctx.body)\n ctx.json(result)\n }\n\n @Delete('/:id')\n @ApiTags('${pascal}')\n async remove(ctx: Ctx<KickRoutes.${pascal}Controller['remove']>) {\n await this.delete${pascal}UseCase.execute(ctx.params.id)\n ctx.noContent()\n }\n}\n`\n}\n\n/** REST controller — injects service directly, flat import paths */\nexport function generateRestController(ctx: TemplateContext): string {\n const { pascal, kebab } = ctx\n const camel = pascal.charAt(0).toLowerCase() + pascal.slice(1)\n return `import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'\nimport { ApiTags } from '@forinda/kickjs-swagger'\nimport { ${pascal}Service } from './${kebab}.service'\nimport { create${pascal}Schema } from './dtos/create-${kebab}.dto'\nimport { update${pascal}Schema } from './dtos/update-${kebab}.dto'\nimport { ${pascal.toUpperCase()}_QUERY_CONFIG } from './${kebab}.constants'\n\n// Each handler annotates its \\`ctx\\` with \\`Ctx<KickRoutes.${pascal}Controller['<method>']>\\`\n// so \\`ctx.params\\`, \\`ctx.body\\`, and \\`ctx.query\\` are typed end-to-end.\n// The \\`KickRoutes\\` namespace is generated by \\`kick typegen\\` (auto-run on\n// \\`kick dev\\`) — see https://forinda.github.io/kick-js/guide/typegen.\n\n@Controller()\nexport class ${pascal}Controller {\n @Autowired() private readonly ${camel}Service!: ${pascal}Service\n\n @Get('/')\n @ApiTags('${pascal}')\n @ApiQueryParams(${pascal.toUpperCase()}_QUERY_CONFIG)\n async list(ctx: Ctx<KickRoutes.${pascal}Controller['list']>) {\n return ctx.paginate(\n (parsed) => this.${camel}Service.findPaginated(parsed),\n ${pascal.toUpperCase()}_QUERY_CONFIG,\n )\n }\n\n @Get('/:id')\n @ApiTags('${pascal}')\n async getById(ctx: Ctx<KickRoutes.${pascal}Controller['getById']>) {\n const result = await this.${camel}Service.findById(ctx.params.id)\n if (!result) return ctx.notFound('${pascal} not found')\n ctx.json(result)\n }\n\n @Post('/', { body: create${pascal}Schema, name: 'Create${pascal}' })\n @ApiTags('${pascal}')\n async create(ctx: Ctx<KickRoutes.${pascal}Controller['create']>) {\n const result = await this.${camel}Service.create(ctx.body)\n ctx.created(result)\n }\n\n @Put('/:id', { body: update${pascal}Schema, name: 'Update${pascal}' })\n @ApiTags('${pascal}')\n async update(ctx: Ctx<KickRoutes.${pascal}Controller['update']>) {\n const result = await this.${camel}Service.update(ctx.params.id, ctx.body)\n ctx.json(result)\n }\n\n @Delete('/:id')\n @ApiTags('${pascal}')\n async remove(ctx: Ctx<KickRoutes.${pascal}Controller['remove']>) {\n await this.${camel}Service.delete(ctx.params.id)\n ctx.noContent()\n }\n}\n`\n}\n","import type { TemplateContext } from './types'\n\nexport function generateConstants(ctx: TemplateContext): string {\n const { pascal } = ctx\n return `import type { QueryParamsConfig } from '@forinda/kickjs'\n\nexport const ${pascal.toUpperCase()}_QUERY_CONFIG: QueryParamsConfig = {\n filterable: ['name'],\n sortable: ['name', 'createdAt'],\n searchable: ['name'],\n}\n`\n}\n","import type { TemplateContext } from './types'\n\nexport function generateCreateDTO(ctx: TemplateContext): string {\n const { pascal } = ctx\n return `import { z } from 'zod'\n\n/**\n * Create ${pascal} DTO — Zod schema for validating POST request bodies.\n * This schema is passed to @Post('/', { body: create${pascal}Schema }) for automatic validation.\n * It also generates OpenAPI request body docs when SwaggerAdapter is used.\n *\n * Add more fields as needed. Supported Zod types:\n * z.string(), z.number(), z.boolean(), z.enum([...]),\n * z.array(), z.object(), .optional(), .default(), .transform()\n */\nexport const create${pascal}Schema = z.object({\n name: z.string().min(1, 'Name is required').max(200),\n})\n\nexport type Create${pascal}DTO = z.infer<typeof create${pascal}Schema>\n`\n}\n\nexport function generateUpdateDTO(ctx: TemplateContext): string {\n const { pascal } = ctx\n return `import { z } from 'zod'\n\nexport const update${pascal}Schema = z.object({\n name: z.string().min(1).max(200).optional(),\n})\n\nexport type Update${pascal}DTO = z.infer<typeof update${pascal}Schema>\n`\n}\n\nexport function generateResponseDTO(ctx: TemplateContext): string {\n const { pascal } = ctx\n return `export interface ${pascal}ResponseDTO {\n id: string\n name: string\n createdAt: string\n updatedAt: string\n}\n`\n}\n","import type { TemplateContext } from './types'\n\nexport function generateUseCases(ctx: TemplateContext): { file: string; content: string }[] {\n const { pascal, kebab, plural = '', pluralPascal = '' } = ctx\n return [\n {\n file: `create-${kebab}.use-case.ts`,\n content: `/**\n * Create ${pascal} Use Case\n *\n * Application layer — orchestrates a single business operation.\n * Use cases are thin: validate input (via DTO), call domain/repo, return response.\n * Keep business rules in the domain service, not here.\n */\nimport { Service, Inject } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { Create${pascal}DTO } from '../dtos/create-${kebab}.dto'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Create${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n return this.repo.create(dto)\n }\n}\n`,\n },\n {\n file: `get-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Get${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.repo.findById(id)\n }\n}\n`,\n },\n {\n file: `list-${plural}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { ParsedQuery } from '@forinda/kickjs'\n\n@Service()\nexport class List${pluralPascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(parsed: ParsedQuery) {\n return this.repo.findPaginated(parsed)\n }\n}\n`,\n },\n {\n file: `update-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { Update${pascal}DTO } from '../dtos/update-${kebab}.dto'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Update${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n return this.repo.update(id, dto)\n }\n}\n`,\n },\n {\n file: `delete-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\n\n@Service()\nexport class Delete${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string): Promise<void> {\n await this.repo.delete(id)\n }\n}\n`,\n },\n ]\n}\n","import type { TemplateContext } from './types'\n\nexport function generateRepositoryInterface(ctx: TemplateContext): string {\n const { pascal, kebab, dtoPrefix = '../../application/dtos', tokenScope = 'app' } = ctx\n return `/**\n * ${pascal} Repository Interface\n *\n * Defines the contract for data access.\n * The interface declares what operations are available;\n * implementations (in-memory, Drizzle, Prisma) fulfill the contract.\n *\n * To swap implementations, change the factory in the module's register() method.\n */\nimport { createToken } from '@forinda/kickjs'\nimport type { ${pascal}ResponseDTO } from '${dtoPrefix}/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '${dtoPrefix}/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '${dtoPrefix}/update-${kebab}.dto'\nimport type { ParsedQuery } from '@forinda/kickjs'\n\nexport interface I${pascal}Repository {\n findById(id: string): Promise<${pascal}ResponseDTO | null>\n findAll(): Promise<${pascal}ResponseDTO[]>\n findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }>\n create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO>\n update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO>\n delete(id: string): Promise<void>\n}\n\n/**\n * Collision-safe DI token bound to \\`I${pascal}Repository\\`.\n * \\`container.resolve(${pascal.toUpperCase()}_REPOSITORY)\\` and\n * \\`@Inject(${pascal.toUpperCase()}_REPOSITORY)\\` both return the typed\n * interface — no manual generic, no \\`any\\` cast.\n *\n * The \\`'${tokenScope}/'\\` prefix matches the project scope so\n * \\`kick-lint\\`'s \\`token-reserved-prefix\\` rule never fires —\n * adopters must NOT use the reserved \\`'kick/'\\` namespace.\n */\nexport const ${pascal.toUpperCase()}_REPOSITORY = createToken<I${pascal}Repository>('${tokenScope}/${pascal}/repository')\n`\n}\n\nexport function generateInMemoryRepository(ctx: TemplateContext): string {\n const {\n pascal,\n kebab,\n repoPrefix = '../../domain/repositories',\n dtoPrefix = '../../application/dtos',\n } = ctx\n return `/**\n * In-Memory ${pascal} Repository\n *\n * Implements the repository interface using a Map.\n * Useful for prototyping and testing. Replace with a database implementation\n * (Drizzle, Prisma, etc.) for production use.\n *\n * @Repository() registers this class in the DI container as a singleton.\n */\nimport { randomUUID } from 'node:crypto'\nimport { Repository, HttpException } from '@forinda/kickjs'\nimport type { ParsedQuery } from '@forinda/kickjs'\nimport type { I${pascal}Repository } from '${repoPrefix}/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '${dtoPrefix}/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '${dtoPrefix}/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '${dtoPrefix}/update-${kebab}.dto'\n\n@Repository()\nexport class InMemory${pascal}Repository implements I${pascal}Repository {\n private store = new Map<string, ${pascal}ResponseDTO>()\n\n async findById(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.store.get(id) ?? null\n }\n\n async findAll(): Promise<${pascal}ResponseDTO[]> {\n return Array.from(this.store.values())\n }\n\n async findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }> {\n const all = Array.from(this.store.values())\n const data = all.slice(parsed.pagination.offset, parsed.pagination.offset + parsed.pagination.limit)\n return { data, total: all.length }\n }\n\n async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const now = new Date().toISOString()\n const entity: ${pascal}ResponseDTO = {\n id: randomUUID(),\n name: dto.name,\n createdAt: now,\n updatedAt: now,\n }\n this.store.set(entity.id, entity)\n return entity\n }\n\n async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const existing = this.store.get(id)\n if (!existing) throw HttpException.notFound('${pascal} not found')\n const updated = { ...existing, ...dto, updatedAt: new Date().toISOString() }\n this.store.set(id, updated)\n return updated\n }\n\n async delete(id: string): Promise<void> {\n if (!this.store.has(id)) throw HttpException.notFound('${pascal} not found')\n this.store.delete(id)\n }\n}\n`\n}\n\nexport function generateCustomRepository(ctx: TemplateContext): string {\n const {\n pascal,\n kebab,\n repoType = '',\n repoPrefix = '../../domain/repositories',\n dtoPrefix = '../../application/dtos',\n } = ctx\n const repoTypePascal =\n repoType.charAt(0).toUpperCase() +\n repoType.slice(1).replace(/-([a-z])/g, (_, c: string) => c.toUpperCase())\n return `/**\n * ${repoTypePascal} ${pascal} Repository\n *\n * Stub implementation for a custom '${repoType}' repository.\n * Implements the repository interface using an in-memory Map as a placeholder.\n *\n * TODO: Replace the in-memory Map with your ${repoType} data-access logic.\n * See I${pascal}Repository for the interface contract.\n *\n * @Repository() registers this class in the DI container as a singleton.\n */\nimport { randomUUID } from 'node:crypto'\nimport { Repository, HttpException } from '@forinda/kickjs'\nimport type { ParsedQuery } from '@forinda/kickjs'\nimport type { I${pascal}Repository } from '${repoPrefix}/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '${dtoPrefix}/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '${dtoPrefix}/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '${dtoPrefix}/update-${kebab}.dto'\n\n@Repository()\nexport class ${repoTypePascal}${pascal}Repository implements I${pascal}Repository {\n // TODO: Replace with your ${repoType} client/connection\n private store = new Map<string, ${pascal}ResponseDTO>()\n\n async findById(id: string): Promise<${pascal}ResponseDTO | null> {\n // TODO: Implement with ${repoType}\n return this.store.get(id) ?? null\n }\n\n async findAll(): Promise<${pascal}ResponseDTO[]> {\n // TODO: Implement with ${repoType}\n return Array.from(this.store.values())\n }\n\n async findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }> {\n // TODO: Implement with ${repoType}\n const all = Array.from(this.store.values())\n const data = all.slice(parsed.pagination.offset, parsed.pagination.offset + parsed.pagination.limit)\n return { data, total: all.length }\n }\n\n async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n // TODO: Implement with ${repoType}\n const now = new Date().toISOString()\n const entity: ${pascal}ResponseDTO = {\n id: randomUUID(),\n name: dto.name,\n createdAt: now,\n updatedAt: now,\n }\n this.store.set(entity.id, entity)\n return entity\n }\n\n async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n // TODO: Implement with ${repoType}\n const existing = this.store.get(id)\n if (!existing) throw HttpException.notFound('${pascal} not found')\n const updated = { ...existing, ...dto, updatedAt: new Date().toISOString() }\n this.store.set(id, updated)\n return updated\n }\n\n async delete(id: string): Promise<void> {\n // TODO: Implement with ${repoType}\n if (!this.store.has(id)) throw HttpException.notFound('${pascal} not found')\n this.store.delete(id)\n }\n}\n`\n}\n","import type { TemplateContext } from './types'\n\nexport function generateDomainService(ctx: TemplateContext): string {\n const { pascal, kebab } = ctx\n return `/**\n * ${pascal} Domain Service\n *\n * Domain layer — contains business rules that don't belong to a single entity.\n * Use this for cross-entity logic, validation rules, and domain invariants.\n * Keep it free of HTTP/framework concerns.\n */\nimport { Service, Inject, HttpException } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../repositories/${kebab}.repository'\n\n@Service()\nexport class ${pascal}DomainService {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async ensureExists(id: string): Promise<void> {\n const entity = await this.repo.findById(id)\n if (!entity) {\n throw HttpException.notFound('${pascal} not found')\n }\n }\n}\n`\n}\n\nexport function generateEntity(ctx: TemplateContext): string {\n const { pascal, kebab } = ctx\n return `/**\n * ${pascal} Entity\n *\n * Domain layer — the core business object.\n * Uses a private constructor with static factory methods (create, reconstitute)\n * to enforce invariants. Properties are accessed via getters to maintain encapsulation.\n *\n * Patterns used:\n * - Private constructor: prevents direct instantiation\n * - create(): factory for new entities (generates ID, sets timestamps)\n * - reconstitute(): factory for rebuilding from persistence (no side effects)\n * - changeName(): mutation method that enforces business rules\n */\nimport { ${pascal}Id } from '../value-objects/${kebab}-id.vo'\n\ninterface ${pascal}Props {\n id: ${pascal}Id\n name: string\n createdAt: Date\n updatedAt: Date\n}\n\nexport class ${pascal} {\n private constructor(private props: ${pascal}Props) {}\n\n static create(params: { name: string }): ${pascal} {\n const now = new Date()\n return new ${pascal}({\n id: ${pascal}Id.create(),\n name: params.name,\n createdAt: now,\n updatedAt: now,\n })\n }\n\n static reconstitute(props: ${pascal}Props): ${pascal} {\n return new ${pascal}(props)\n }\n\n get id(): ${pascal}Id {\n return this.props.id\n }\n get name(): string {\n return this.props.name\n }\n get createdAt(): Date {\n return this.props.createdAt\n }\n get updatedAt(): Date {\n return this.props.updatedAt\n }\n\n changeName(name: string): void {\n if (!name || name.trim().length === 0) {\n throw new Error('Name cannot be empty')\n }\n this.props.name = name.trim()\n this.props.updatedAt = new Date()\n }\n\n toJSON() {\n return {\n id: this.props.id.toString(),\n name: this.props.name,\n createdAt: this.props.createdAt.toISOString(),\n updatedAt: this.props.updatedAt.toISOString(),\n }\n }\n}\n`\n}\n\nexport function generateValueObject(ctx: TemplateContext): string {\n const { pascal } = ctx\n return `/**\n * ${pascal} ID Value Object\n *\n * Domain layer — wraps a primitive ID with type safety and validation.\n * Value objects are immutable and compared by value, not reference.\n *\n * ${pascal}Id.create() — generate a new UUID\n * ${pascal}Id.from(id) — wrap an existing ID string (validates non-empty)\n * id.equals(other) — compare two IDs by value\n */\nimport { randomUUID } from 'node:crypto'\n\nexport class ${pascal}Id {\n private constructor(private readonly value: string) {}\n\n static create(): ${pascal}Id {\n return new ${pascal}Id(randomUUID())\n }\n\n static from(id: string): ${pascal}Id {\n if (!id || id.trim().length === 0) {\n throw new Error('${pascal}Id cannot be empty')\n }\n return new ${pascal}Id(id)\n }\n\n toString(): string {\n return this.value\n }\n\n equals(other: ${pascal}Id): boolean {\n return this.value === other.value\n }\n}\n`\n}\n","import type { TemplateContext } from './types'\n\nexport function generateControllerTest(ctx: TemplateContext): string {\n const { pascal, kebab, plural = '' } = ctx\n return `import { describe, it, expect, beforeEach } from 'vitest'\nimport { Container } from '@forinda/kickjs'\n\ndescribe('${pascal}Controller', () => {\n beforeEach(() => {\n Container.reset()\n })\n\n it('should be defined', () => {\n expect(true).toBe(true)\n })\n\n describe('POST /${plural}', () => {\n it('should create a new ${kebab}', async () => {\n // TODO: Set up test module, call create endpoint, assert 201\n expect(true).toBe(true)\n })\n })\n\n describe('GET /${plural}', () => {\n it('should return paginated ${plural}', async () => {\n // TODO: Set up test module, call list endpoint, assert { data, meta }\n expect(true).toBe(true)\n })\n })\n\n describe('GET /${plural}/:id', () => {\n it('should return a ${kebab} by id', async () => {\n // TODO: Create a ${kebab}, then fetch by id, assert match\n expect(true).toBe(true)\n })\n\n it('should return 404 for non-existent ${kebab}', async () => {\n // TODO: Fetch non-existent id, assert 404\n expect(true).toBe(true)\n })\n })\n\n describe('PUT /${plural}/:id', () => {\n it('should update an existing ${kebab}', async () => {\n // TODO: Create, update, assert changes\n expect(true).toBe(true)\n })\n })\n\n describe('DELETE /${plural}/:id', () => {\n it('should delete a ${kebab}', async () => {\n // TODO: Create, delete, assert gone\n expect(true).toBe(true)\n })\n })\n})\n`\n}\n\nexport function generateRepositoryTest(ctx: TemplateContext): string {\n const {\n pascal,\n kebab,\n plural = '',\n repoPrefix = `../infrastructure/repositories/in-memory-${kebab}.repository`,\n } = ctx\n return `import { describe, it, expect, beforeEach } from 'vitest'\nimport { InMemory${pascal}Repository } from '${repoPrefix}'\n\ndescribe('InMemory${pascal}Repository', () => {\n let repo: InMemory${pascal}Repository\n\n beforeEach(() => {\n repo = new InMemory${pascal}Repository()\n })\n\n it('should create and retrieve a ${kebab}', async () => {\n const created = await repo.create({ name: 'Test ${pascal}' })\n expect(created).toBeDefined()\n expect(created.name).toBe('Test ${pascal}')\n expect(created.id).toBeDefined()\n\n const found = await repo.findById(created.id)\n expect(found).toEqual(created)\n })\n\n it('should return null for non-existent id', async () => {\n const found = await repo.findById('non-existent')\n expect(found).toBeNull()\n })\n\n it('should list all ${plural}', async () => {\n await repo.create({ name: '${pascal} 1' })\n await repo.create({ name: '${pascal} 2' })\n\n const all = await repo.findAll()\n expect(all).toHaveLength(2)\n })\n\n it('should return paginated results', async () => {\n await repo.create({ name: '${pascal} 1' })\n await repo.create({ name: '${pascal} 2' })\n await repo.create({ name: '${pascal} 3' })\n\n const result = await repo.findPaginated({\n filters: [],\n sort: [],\n search: '',\n pagination: { page: 1, limit: 2, offset: 0 },\n })\n\n expect(result.data).toHaveLength(2)\n expect(result.total).toBe(3)\n })\n\n it('should update a ${kebab}', async () => {\n const created = await repo.create({ name: 'Original' })\n const updated = await repo.update(created.id, { name: 'Updated' })\n expect(updated.name).toBe('Updated')\n })\n\n it('should delete a ${kebab}', async () => {\n const created = await repo.create({ name: 'To Delete' })\n await repo.delete(created.id)\n const found = await repo.findById(created.id)\n expect(found).toBeNull()\n })\n})\n`\n}\n","import type { TemplateContext } from './types'\n\n/** REST service — wraps repository with CRUD methods, replaces use-cases for flat pattern */\nexport function generateRestService(ctx: TemplateContext): string {\n const { pascal, kebab } = ctx\n return `import { Service, Inject, HttpException } from '@forinda/kickjs'\nimport type { ParsedQuery } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from './${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from './dtos/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from './dtos/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from './dtos/update-${kebab}.dto'\n\n@Service()\nexport class ${pascal}Service {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async findById(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.repo.findById(id)\n }\n\n async findAll(): Promise<${pascal}ResponseDTO[]> {\n return this.repo.findAll()\n }\n\n async findPaginated(parsed: ParsedQuery) {\n return this.repo.findPaginated(parsed)\n }\n\n async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n return this.repo.create(dto)\n }\n\n async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n return this.repo.update(id, dto)\n }\n\n async delete(id: string): Promise<void> {\n await this.repo.delete(id)\n }\n}\n`\n}\n\n/** REST constants — query config for flat pattern */\nexport function generateRestConstants(ctx: TemplateContext): string {\n const { pascal } = ctx\n return `import type { QueryFieldConfig } from '@forinda/kickjs'\n\nexport const ${pascal.toUpperCase()}_QUERY_CONFIG: QueryFieldConfig = {\n filterable: ['name'],\n sortable: ['name', 'createdAt'],\n searchable: ['name'],\n}\n`\n}\n","import type { TemplateContext } from './types'\n\n/** CQRS module index — commands, queries, events, WebSocket + queue integration */\nexport function generateCqrsModuleIndex(ctx: TemplateContext & { repo: string }): string {\n const { pascal, kebab, plural = '', repo, style } = ctx\n const repoClassMap: Record<string, string> = {\n inmemory: `InMemory${pascal}Repository`,\n drizzle: `Drizzle${pascal}Repository`,\n prisma: `Prisma${pascal}Repository`,\n }\n const repoFileMap: Record<string, string> = {\n inmemory: `in-memory-${kebab}`,\n drizzle: `drizzle-${kebab}`,\n prisma: `prisma-${kebab}`,\n }\n const repoClass = repoClassMap[repo] ?? repoClassMap.inmemory\n const repoFile = repoFileMap[repo] ?? repoFileMap.inmemory\n const resolvedStyle = style ?? 'define'\n\n const header = `/**\n * ${pascal} Module — CQRS Pattern\n *\n * Separates read (queries) and write (commands) operations.\n * Events are emitted after state changes and can be handled via\n * WebSocket broadcasts, queue jobs, or ETL pipelines.\n *\n * Structure:\n * commands/ — Write operations (create, update, delete)\n * queries/ — Read operations (get, list)\n * events/ — Domain events + handlers (WS broadcast, queue dispatch)\n * dtos/ — Request/response schemas\n */`\n\n const repoImports = `import { ${pascal.toUpperCase()}_REPOSITORY } from './${kebab}.repository'\nimport { ${repoClass} } from './${repoFile}.repository'\nimport { ${pascal}Controller } from './${kebab}.controller'\n\n// Eagerly load decorated classes\nimport.meta.glob(\n [\n './commands/**/*.ts',\n './queries/**/*.ts',\n './events/**/*.ts',\n '!./**/*.test.ts',\n ],\n { eager: true },\n)`\n\n const routesDoc = ` /**\n * Declare HTTP routes for this CQRS module. Return value shape:\n *\n * - \\`path\\` — URL prefix for this route set.\n * - \\`controller\\` — Controller class (also drives OpenAPI).\n * - \\`version\\` — Optional. Overrides the app-wide API version.\n *\n * Return an array to mount multiple route sets:\n *\n * return [\n * { path: '/${plural}', version: 1, controller: ${pascal}V1Controller },\n * { path: '/${plural}', version: 2, controller: ${pascal}V2Controller },\n * ]\n */`\n\n if (resolvedStyle === 'class') {\n return `${header}\nimport { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'\n${repoImports}\n\nexport class ${pascal}Module implements AppModule {\n register(container: Container): void {\n container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>\n container.resolve(${repoClass}),\n )\n }\n\n${routesDoc.replace(/^ {4}/gm, ' ').replace(/^ {6}/gm, ' ')}\n routes(): ModuleRoutes {\n return {\n path: '/${plural}',\n controller: ${pascal}Controller,\n }\n }\n}\n`\n }\n\n return `${header}\nimport { defineModule } from '@forinda/kickjs'\n${repoImports}\n\nexport const ${pascal}Module = defineModule({\n name: '${pascal}Module',\n build: () => ({\n register(container) {\n container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>\n container.resolve(${repoClass}),\n )\n },\n\n${routesDoc}\n routes() {\n return {\n path: '/${plural}',\n controller: ${pascal}Controller,\n }\n },\n }),\n})\n`\n}\n\n/** CQRS controller — dispatches to command/query handlers */\nexport function generateCqrsController(ctx: TemplateContext): string {\n const { pascal, kebab, plural = '', pluralPascal = '' } = ctx\n return `import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'\nimport { ApiTags } from '@forinda/kickjs-swagger'\nimport { Create${pascal}Command } from './commands/create-${kebab}.command'\nimport { Update${pascal}Command } from './commands/update-${kebab}.command'\nimport { Delete${pascal}Command } from './commands/delete-${kebab}.command'\nimport { Get${pascal}Query } from './queries/get-${kebab}.query'\nimport { List${pluralPascal}Query } from './queries/list-${plural}.query'\nimport { create${pascal}Schema } from './dtos/create-${kebab}.dto'\nimport { update${pascal}Schema } from './dtos/update-${kebab}.dto'\nimport { ${pascal.toUpperCase()}_QUERY_CONFIG } from './${kebab}.constants'\n\n// Each handler annotates its \\`ctx\\` with \\`Ctx<KickRoutes.${pascal}Controller['<method>']>\\`\n// so \\`ctx.params\\`, \\`ctx.body\\`, and \\`ctx.query\\` are typed end-to-end.\n// The \\`KickRoutes\\` namespace is generated by \\`kick typegen\\` (auto-run on\n// \\`kick dev\\`) — see https://forinda.github.io/kick-js/guide/typegen.\n\n@Controller()\nexport class ${pascal}Controller {\n @Autowired() private readonly create${pascal}Command!: Create${pascal}Command\n @Autowired() private readonly update${pascal}Command!: Update${pascal}Command\n @Autowired() private readonly delete${pascal}Command!: Delete${pascal}Command\n @Autowired() private readonly get${pascal}Query!: Get${pascal}Query\n @Autowired() private readonly list${pluralPascal}Query!: List${pluralPascal}Query\n\n @Get('/')\n @ApiTags('${pascal}')\n @ApiQueryParams(${pascal.toUpperCase()}_QUERY_CONFIG)\n async list(ctx: Ctx<KickRoutes.${pascal}Controller['list']>) {\n return ctx.paginate(\n (parsed) => this.list${pluralPascal}Query.execute(parsed),\n ${pascal.toUpperCase()}_QUERY_CONFIG,\n )\n }\n\n @Get('/:id')\n @ApiTags('${pascal}')\n async getById(ctx: Ctx<KickRoutes.${pascal}Controller['getById']>) {\n const result = await this.get${pascal}Query.execute(ctx.params.id)\n if (!result) return ctx.notFound('${pascal} not found')\n ctx.json(result)\n }\n\n @Post('/', { body: create${pascal}Schema, name: 'Create${pascal}' })\n @ApiTags('${pascal}')\n async create(ctx: Ctx<KickRoutes.${pascal}Controller['create']>) {\n const result = await this.create${pascal}Command.execute(ctx.body)\n ctx.created(result)\n }\n\n @Put('/:id', { body: update${pascal}Schema, name: 'Update${pascal}' })\n @ApiTags('${pascal}')\n async update(ctx: Ctx<KickRoutes.${pascal}Controller['update']>) {\n const result = await this.update${pascal}Command.execute(ctx.params.id, ctx.body)\n ctx.json(result)\n }\n\n @Delete('/:id')\n @ApiTags('${pascal}')\n async remove(ctx: Ctx<KickRoutes.${pascal}Controller['remove']>) {\n await this.delete${pascal}Command.execute(ctx.params.id)\n ctx.noContent()\n }\n}\n`\n}\n\n/** CQRS commands — write operations that emit events */\nexport function generateCqrsCommands(ctx: TemplateContext): { file: string; content: string }[] {\n const { pascal, kebab } = ctx\n return [\n {\n file: `create-${kebab}.command.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../${kebab}.repository'\nimport type { Create${pascal}DTO } from '../dtos/create-${kebab}.dto'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\nimport { ${pascal}Events } from '../events/${kebab}.events'\n\n@Service()\nexport class Create${pascal}Command {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n @Inject(${pascal}Events) private readonly events: ${pascal}Events,\n ) {}\n\n async execute(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const result = await this.repo.create(dto)\n this.events.emit('${kebab}.created', result)\n return result\n }\n}\n`,\n },\n {\n file: `update-${kebab}.command.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../${kebab}.repository'\nimport type { Update${pascal}DTO } from '../dtos/update-${kebab}.dto'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\nimport { ${pascal}Events } from '../events/${kebab}.events'\n\n@Service()\nexport class Update${pascal}Command {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n @Inject(${pascal}Events) private readonly events: ${pascal}Events,\n ) {}\n\n async execute(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const result = await this.repo.update(id, dto)\n this.events.emit('${kebab}.updated', result)\n return result\n }\n}\n`,\n },\n {\n file: `delete-${kebab}.command.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../${kebab}.repository'\nimport { ${pascal}Events } from '../events/${kebab}.events'\n\n@Service()\nexport class Delete${pascal}Command {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n @Inject(${pascal}Events) private readonly events: ${pascal}Events,\n ) {}\n\n async execute(id: string): Promise<void> {\n await this.repo.delete(id)\n this.events.emit('${kebab}.deleted', { id })\n }\n}\n`,\n },\n ]\n}\n\n/** CQRS queries — read operations */\nexport function generateCqrsQueries(ctx: TemplateContext): { file: string; content: string }[] {\n const { pascal, kebab, plural = '', pluralPascal = '' } = ctx\n return [\n {\n file: `get-${kebab}.query.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Get${pascal}Query {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.repo.findById(id)\n }\n}\n`,\n },\n {\n file: `list-${plural}.query.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../${kebab}.repository'\nimport type { ParsedQuery } from '@forinda/kickjs'\n\n@Service()\nexport class List${pluralPascal}Query {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(parsed: ParsedQuery) {\n return this.repo.findPaginated(parsed)\n }\n}\n`,\n },\n ]\n}\n\n/** CQRS events — domain event emitter + handler with WS/queue integration */\nexport function generateCqrsEvents(ctx: TemplateContext): { file: string; content: string }[] {\n const { pascal, kebab } = ctx\n return [\n {\n file: `${kebab}.events.ts`,\n content: `import { Service } from '@forinda/kickjs'\nimport { EventEmitter } from 'node:events'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n/**\n * ${pascal} domain event types.\n *\n * These events are emitted by commands after state changes.\n * Subscribe to them in event handlers for side effects:\n * - WebSocket broadcasts (real-time UI updates)\n * - Queue jobs (async processing, ETL pipelines)\n * - Audit logging\n * - Cache invalidation\n */\nexport interface ${pascal}EventMap {\n '${kebab}.created': ${pascal}ResponseDTO\n '${kebab}.updated': ${pascal}ResponseDTO\n '${kebab}.deleted': { id: string }\n}\n\n@Service()\nexport class ${pascal}Events {\n private emitter = new EventEmitter()\n\n emit<K extends keyof ${pascal}EventMap>(event: K, data: ${pascal}EventMap[K]): void {\n this.emitter.emit(event, data)\n }\n\n on<K extends keyof ${pascal}EventMap>(event: K, handler: (data: ${pascal}EventMap[K]) => void): void {\n this.emitter.on(event, handler)\n }\n\n off<K extends keyof ${pascal}EventMap>(event: K, handler: (data: ${pascal}EventMap[K]) => void): void {\n this.emitter.off(event, handler)\n }\n}\n`,\n },\n {\n file: `on-${kebab}-change.handler.ts`,\n content: `import { Service, Autowired } from '@forinda/kickjs'\nimport { ${pascal}Events } from './${kebab}.events'\n\n/**\n * ${pascal} Change Event Handler\n *\n * Reacts to domain events emitted by commands.\n * Wire up side effects here:\n *\n * 1. WebSocket broadcast — notify connected clients in real-time\n * import { WsGateway } from '@forinda/kickjs-ws'\n * this.ws.broadcast('${kebab}-channel', { event, data })\n *\n * 2. Queue dispatch — offload heavy processing to background workers\n * import { QueueService } from '@forinda/kickjs-queue'\n * this.queue.add('${kebab}-etl', { action: event, payload: data })\n *\n * 3. ETL pipeline — transform and load data to external systems\n * await this.etlPipeline.process(data)\n */\n@Service()\nexport class On${pascal}ChangeHandler {\n @Autowired() private events!: ${pascal}Events\n\n // Uncomment to inject WebSocket and Queue services:\n // @Autowired() private ws!: WsGateway\n // @Autowired() private queue!: QueueService\n\n onInit(): void {\n this.events.on('${kebab}.created', (data) => {\n console.log('[${pascal}] Created:', data.id)\n // TODO: Broadcast via WebSocket\n // this.ws.broadcast('${kebab}-channel', { event: '${kebab}.created', data })\n // TODO: Dispatch to queue for async processing / ETL\n // this.queue.add('${kebab}-etl', { action: 'create', payload: data })\n })\n\n this.events.on('${kebab}.updated', (data) => {\n console.log('[${pascal}] Updated:', data.id)\n // TODO: Broadcast via WebSocket\n // this.ws.broadcast('${kebab}-channel', { event: '${kebab}.updated', data })\n })\n\n this.events.on('${kebab}.deleted', (data) => {\n console.log('[${pascal}] Deleted:', data.id)\n // TODO: Broadcast via WebSocket\n // this.ws.broadcast('${kebab}-channel', { event: '${kebab}.deleted', data })\n })\n }\n}\n`,\n },\n ]\n}\n","import type { TemplateContext } from '../types'\n\nexport function generateDrizzleRepository(ctx: TemplateContext): string {\n const {\n pascal,\n kebab,\n repoPrefix = '../../domain/repositories',\n dtoPrefix = '../../application/dtos',\n } = ctx\n return `/**\n * Drizzle ${pascal} Repository\n *\n * Implements the repository interface using Drizzle ORM.\n * Uses buildFromColumns() with Column objects for type-safe query building.\n *\n * TODO: Update the schema import to match your Drizzle schema file.\n * TODO: Replace DRIZZLE_DB injection token with your actual database token.\n *\n * @Repository() registers this class in the DI container as a singleton.\n */\nimport { eq, ne, gt, gte, lt, lte, ilike, inArray, between, and, or, asc, desc, count, sql } from 'drizzle-orm'\nimport { Repository, HttpException, Inject } from '@forinda/kickjs'\nimport { DRIZZLE_DB, DrizzleQueryAdapter } from '@forinda/kickjs-drizzle'\nimport type { ParsedQuery } from '@forinda/kickjs'\nimport type { I${pascal}Repository } from '${repoPrefix}/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '${dtoPrefix}/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '${dtoPrefix}/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '${dtoPrefix}/update-${kebab}.dto'\nimport { ${pascal.toUpperCase()}_QUERY_CONFIG } from '../../constants'\n\n// TODO: Import your Drizzle schema table — e.g.:\n// import { ${kebab}s } from '@/db/schema'\n\nconst queryAdapter = new DrizzleQueryAdapter({\n eq, ne, gt, gte, lt, lte, ilike, inArray, between, and, or, asc, desc,\n})\n\n@Repository()\nexport class Drizzle${pascal}Repository implements I${pascal}Repository {\n constructor(@Inject(DRIZZLE_DB) private db: any) {}\n\n async findById(id: string): Promise<${pascal}ResponseDTO | null> {\n // TODO: Implement with Drizzle\n // const row = this.db.select().from(${kebab}s).where(eq(${kebab}s.id, id)).get()\n // return row ?? null\n throw new Error('Drizzle ${pascal} repository not yet implemented — update schema imports and queries')\n }\n\n async findAll(): Promise<${pascal}ResponseDTO[]> {\n // TODO: Implement with Drizzle\n // return this.db.select().from(${kebab}s).all()\n throw new Error('Drizzle ${pascal} repository not yet implemented')\n }\n\n async findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }> {\n // TODO: Use buildFromColumns() with your query config for type-safe filtering\n // const query = queryAdapter.buildFromColumns(parsed, ${pascal.toUpperCase()}_QUERY_CONFIG)\n //\n // const data = this.db\n // .select().from(${kebab}s).$dynamic()\n // .where(query.where).orderBy(...query.orderBy)\n // .limit(query.limit).offset(query.offset).all()\n //\n // const totalResult = this.db\n // .select({ count: count() }).from(${kebab}s)\n // .$dynamic().where(query.where).get()\n //\n // return { data, total: totalResult?.count ?? 0 }\n throw new Error('Drizzle ${pascal} repository not yet implemented')\n }\n\n async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n // TODO: Implement with Drizzle\n // return this.db.insert(${kebab}s).values(dto).returning().get()\n throw new Error('Drizzle ${pascal} repository not yet implemented')\n }\n\n async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n // TODO: Implement with Drizzle\n // const row = this.db.update(${kebab}s).set(dto).where(eq(${kebab}s.id, id)).returning().get()\n // if (!row) throw HttpException.notFound('${pascal} not found')\n // return row\n throw new Error('Drizzle ${pascal} repository not yet implemented')\n }\n\n async delete(id: string): Promise<void> {\n // TODO: Implement with Drizzle\n // this.db.delete(${kebab}s).where(eq(${kebab}s.id, id)).run()\n throw new Error('Drizzle ${pascal} repository not yet implemented')\n }\n}\n`\n}\n\nexport function generateDrizzleConstants(ctx: TemplateContext): string {\n const { pascal, kebab } = ctx\n return `import type { DrizzleQueryParamsConfig } from '@forinda/kickjs-drizzle'\n// TODO: Import your schema table and reference actual columns for type safety\n// import { ${kebab}s } from '@/db/schema'\n\nexport const ${pascal.toUpperCase()}_QUERY_CONFIG: DrizzleQueryParamsConfig = {\n columns: {\n // Replace with actual Drizzle Column references for type-safe filtering:\n // name: ${kebab}s.name,\n // status: ${kebab}s.status,\n },\n sortable: {\n // name: ${kebab}s.name,\n // createdAt: ${kebab}s.createdAt,\n },\n searchColumns: [\n // ${kebab}s.name,\n ],\n}\n`\n}\n","import type { TemplateContext } from '../types'\n\nexport function generatePrismaRepository(ctx: TemplateContext): string {\n const {\n pascal,\n kebab,\n repoPrefix = '../../domain/repositories',\n dtoPrefix = '../../application/dtos',\n } = ctx\n const camel = kebab.replace(/-([a-z])/g, (_, c) => c.toUpperCase())\n return `/**\n * Prisma ${pascal} Repository\n *\n * Implements the repository interface using Prisma Client.\n * Requires a PrismaClient instance injected via the DI container.\n *\n * Ensure your Prisma schema has a '${pascal}' model defined.\n *\n * For full Prisma field-level type safety, replace PrismaModelDelegate with your PrismaClient:\n * @Inject(PRISMA_CLIENT) private prisma!: PrismaClient\n *\n * @Repository() registers this class in the DI container as a singleton.\n */\nimport { Repository, HttpException, Inject } from '@forinda/kickjs'\nimport { PRISMA_CLIENT, type PrismaModelDelegate } from '@forinda/kickjs-prisma'\nimport type { ParsedQuery } from '@forinda/kickjs'\nimport type { I${pascal}Repository } from '${repoPrefix}/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '${dtoPrefix}/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '${dtoPrefix}/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '${dtoPrefix}/update-${kebab}.dto'\n\n@Repository()\nexport class Prisma${pascal}Repository implements I${pascal}Repository {\n @Inject(PRISMA_CLIENT) private prisma!: { ${camel}: PrismaModelDelegate }\n\n async findById(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.prisma.${camel}.findUnique({ where: { id } }) as Promise<${pascal}ResponseDTO | null>\n }\n\n async findAll(): Promise<${pascal}ResponseDTO[]> {\n return this.prisma.${camel}.findMany() as Promise<${pascal}ResponseDTO[]>\n }\n\n async findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }> {\n const [data, total] = await Promise.all([\n this.prisma.${camel}.findMany({\n skip: parsed.pagination.offset,\n take: parsed.pagination.limit,\n }) as Promise<${pascal}ResponseDTO[]>,\n this.prisma.${camel}.count(),\n ])\n return { data, total }\n }\n\n async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n return this.prisma.${camel}.create({ data: dto as Record<string, unknown> }) as Promise<${pascal}ResponseDTO>\n }\n\n async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const existing = await this.prisma.${camel}.findUnique({ where: { id } })\n if (!existing) throw HttpException.notFound('${pascal} not found')\n return this.prisma.${camel}.update({ where: { id }, data: dto as Record<string, unknown> }) as Promise<${pascal}ResponseDTO>\n }\n\n async delete(id: string): Promise<void> {\n await this.prisma.${camel}.deleteMany({ where: { id } })\n }\n}\n`\n}\n","type ProjectTemplate = 'rest' | 'ddd' | 'cqrs' | 'minimal'\n\n/**\n * Generate src/index.ts entry file with template-specific bootstrap.\n *\n * All templates export the app for the Vite plugin (dev mode).\n * In production, bootstrap() auto-starts the HTTP server when\n * `globalThis.__kickjs_httpServer` is not set.\n */\nexport function generateEntryFile(\n name: string,\n template: ProjectTemplate,\n version: string,\n packages: string[] = [],\n): string {\n switch (template) {\n case 'cqrs': {\n // Build adapters based on user-selected packages\n const cqrsImports: string[] = []\n const cqrsAdapters: string[] = []\n\n if (packages.includes('devtools')) {\n cqrsImports.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`)\n cqrsAdapters.push(` DevToolsAdapter(),`)\n }\n if (packages.includes('swagger')) {\n cqrsImports.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`)\n cqrsAdapters.push(\n ` SwaggerAdapter({\\n info: { title: '${name}', version: '${version}' },\\n }),`,\n )\n }\n const cqrsImportsBlock = cqrsImports.length ? cqrsImports.join('\\n') + '\\n' : ''\n const cqrsAdaptersBlock = cqrsImports.length\n ? `\\n adapters: [\\n${cqrsAdapters.join('\\n')}\\n // Uncomment for WebSocket support:\\n // WsAdapter(),\\n // Uncomment when Redis is available:\\n // QueueAdapter({\\n // provider: new BullMQProvider({ host: 'localhost', port: 6379 }),\\n // }),\\n ],`\n : `\\n adapters: [\\n // Uncomment for WebSocket support:\\n // WsAdapter(),\\n // Uncomment when Redis is available:\\n // QueueAdapter({\\n // provider: new BullMQProvider({ host: 'localhost', port: 6379 }),\\n // }),\\n ],`\n\n return `import 'reflect-metadata'\n// Side-effect import — registers the extended env schema with kickjs\n// **before** any controller / service / @Value gets resolved. Without\n// this line ConfigService.get('YOUR_KEY') returns undefined because the\n// cached schema would still be the base shape. See guide/configuration.\nimport './config'\nimport { bootstrap } from '@forinda/kickjs'\n// import { WsAdapter } from '@forinda/kickjs-ws'\n// import { QueueAdapter, BullMQProvider } from '@forinda/kickjs-queue'\n${cqrsImportsBlock}import { modules } from './modules'\n\n// Export the app for the Vite plugin (dev mode)\nexport const app = await bootstrap({\n modules,${cqrsAdaptersBlock}\n})\n`\n }\n\n case 'minimal': {\n const imports: string[] = []\n const adapters: string[] = []\n\n if (packages.includes('swagger')) {\n imports.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`)\n adapters.push(` SwaggerAdapter({ info: { title: '${name}', version: '${version}' } }),`)\n }\n if (packages.includes('devtools')) {\n imports.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`)\n adapters.push(` DevToolsAdapter(),`)\n }\n const importsBlock = imports.length ? imports.join('\\n') + '\\n' : ''\n const adaptersBlock = adapters.length ? `,\\n adapters: [\\n${adapters.join('\\n')}\\n ]` : ''\n\n return `import 'reflect-metadata'\n// Side-effect import — registers the extended env schema with kickjs\n// **before** any controller / service / @Value gets resolved. Without\n// this line ConfigService.get('YOUR_KEY') returns undefined because the\n// cached schema would still be the base shape. See guide/configuration.\nimport './config'\nimport { bootstrap } from '@forinda/kickjs'\n${importsBlock}import { modules } from './modules'\n\n// Export the app for the Vite plugin (dev mode)\nexport const app = await bootstrap({ modules${adaptersBlock} })\n`\n }\n\n case 'ddd':\n case 'rest':\n default: {\n // Build adapters based on user-selected packages\n const restImports: string[] = []\n const restAdapters: string[] = []\n\n if (packages.includes('devtools')) {\n restImports.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`)\n restAdapters.push(` DevToolsAdapter(),`)\n }\n if (packages.includes('swagger')) {\n restImports.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`)\n restAdapters.push(\n ` SwaggerAdapter({\\n info: { title: '${name}', version: '${version}' },\\n }),`,\n )\n }\n const restImportsBlock = restImports.length ? restImports.join('\\n') + '\\n' : ''\n const restAdaptersBlock = restAdapters.length\n ? `\\n adapters: [\\n${restAdapters.join('\\n')}\\n ],`\n : ''\n\n return `import 'reflect-metadata'\n// Side-effect import — registers the extended env schema with kickjs\n// **before** any controller / service / @Value gets resolved. Without\n// this line ConfigService.get('YOUR_KEY') returns undefined because the\n// cached schema would still be the base shape. See guide/configuration.\nimport './config'\nimport express from 'express'\nimport {\n bootstrap,\n requestId,\n requestLogger,\n helmet,\n cors,\n} from '@forinda/kickjs'\n${restImportsBlock}import { modules } from './modules'\n\n// Export the app for the Vite plugin (dev mode)\nexport const app = await bootstrap({\n modules,${restAdaptersBlock}\n middleware: [\n helmet(),\n cors({ origin: '*' }),\n requestId(),\n requestLogger(),\n express.json(),\n ],\n})\n`\n }\n }\n}\n\n/** Generate src/modules/index.ts module registry */\nexport function generateModulesIndex(): string {\n return `import { defineModules } from '@forinda/kickjs'\nimport { HelloModule } from './hello/hello.module'\n\n// Remove HelloModule and run: kick g module <name>\n// \\`defineModules()\\` returns a chainable list — \\`kick g module\\` appends\n// \\`.mount(NewModule())\\` to the chain on every generation.\nexport const modules = defineModules().mount(HelloModule())\n`\n}\n\n/**\n * Generate `src/config/index.ts` — the project's typed env schema.\n *\n * Default-exports a `defineEnv(...)` schema so `kick typegen` can\n * infer it into the global `KickEnv` registry, and *also* calls\n * `loadEnv(envSchema)` as a module-load side effect so `ConfigService`\n * and `@Value()` see the extended shape from the very first DI\n * resolution. The companion `src/index.ts` template adds\n * `import './config'` immediately after `reflect-metadata` so the\n * registration runs before `bootstrap()` constructs anything.\n *\n * After typegen runs:\n *\n * @Value('DATABASE_URL') private url!: Env<'DATABASE_URL'>\n * process.env.DATABASE_URL // typed as string\n *\n * Both autocomplete and type-check at compile time.\n */\nexport function generateEnvFile(): string {\n return `import { defineEnv, loadEnv } from '@forinda/kickjs/config'\nimport { z } from 'zod'\n\n/**\n * Project environment schema.\n *\n * Extend the base schema with your application's variables. The\n * default export is the contract \\`kick typegen\\` reads to populate\n * the global \\`KickEnv\\` registry — that's what makes \\`@Value('FOO')\\`\n * autocomplete and \\`process.env.FOO\\` typed.\n *\n * @example\n * DATABASE_URL: z.string().url(),\n * JWT_SECRET: z.string().min(32),\n * REDIS_URL: z.string().url().optional(),\n */\nconst envSchema = defineEnv((base) =>\n base.extend({\n // DATABASE_URL: z.string().url(),\n }),\n)\n\n/**\n * IMPORTANT — side effect: register the schema with kickjs's env cache\n * **at module-load time**. \\`ConfigService\\` and \\`@Value()\\` both consume\n * this cache, and they will fall back to the base schema (or undefined)\n * if no extended schema has been registered before they're resolved.\n *\n * As long as \\`src/index.ts\\` imports this file (\\`import './env'\\`) at the\n * top — before \\`bootstrap()\\` runs — every controller and service in the\n * app sees the typed extended values.\n */\nexport const env = loadEnv(envSchema)\n\nexport default envSchema\n`\n}\n\n/** Generate src/modules/hello/hello.service.ts */\nexport function generateHelloService(): string {\n return `import { Service } from '@forinda/kickjs'\n\n@Service()\nexport class HelloService {\n greet(name: string) {\n return { message: \\`Hello \\${name} from KickJS!\\`, timestamp: new Date().toISOString() }\n }\n\n healthCheck() {\n return { status: 'ok', uptime: process.uptime() }\n }\n}\n`\n}\n\n/** Generate src/modules/hello/hello.controller.ts */\nexport function generateHelloController(): string {\n return `import { Controller, Get, Autowired, type Ctx } from '@forinda/kickjs'\nimport { HelloService } from './hello.service'\n\n// \\`Ctx<KickRoutes.HelloController['<method>']>\\` is generated by\n// \\`kick typegen\\` (auto-run on \\`kick dev\\`). The first run after a fresh\n// scaffold creates \\`.kickjs/types/routes.ts\\` so this file typechecks.\n// See https://forinda.github.io/kick-js/guide/typegen.\n\n@Controller()\nexport class HelloController {\n @Autowired() private readonly helloService!: HelloService\n\n @Get('/')\n index(ctx: Ctx<KickRoutes.HelloController['index']>) {\n ctx.json(this.helloService.greet('World'))\n }\n\n @Get('/health')\n health(ctx: Ctx<KickRoutes.HelloController['health']>) {\n ctx.json(this.helloService.healthCheck())\n }\n}\n`\n}\n\n/** Generate src/modules/hello/hello.module.ts */\nexport function generateHelloModule(): string {\n return `import { defineModule } from '@forinda/kickjs'\nimport { HelloController } from './hello.controller'\n\nexport const HelloModule = defineModule({\n name: 'HelloModule',\n build: () => ({\n // \\`register(container)\\` is optional — only implement it when you need\n // to bind a token to a concrete implementation, e.g.\n // register(container) {\n // container.registerFactory(USER_REPOSITORY, () => container.resolve(InMemoryUserRepository))\n // }\n // The HelloService uses @Service() so the decorator handles registration.\n\n routes() {\n return {\n path: '/hello',\n controller: HelloController,\n }\n },\n }),\n})\n`\n}\n\n/** Generate kick.config.ts CLI configuration */\nexport function generateKickConfig(\n template: ProjectTemplate,\n defaultRepo: string = 'inmemory',\n packageManager: 'pnpm' | 'npm' | 'yarn' | 'bun' = 'pnpm',\n): string {\n const builtinRepos = ['drizzle', 'inmemory', 'prisma']\n const repoValue = builtinRepos.includes(defaultRepo)\n ? `'${defaultRepo}'`\n : `{ name: '${defaultRepo}' }`\n\n return `import { defineConfig } from '@forinda/kickjs-cli'\n\nexport default defineConfig({\n pattern: '${template}',\n // Pinned so \\`kick add\\` and other dep-installing commands always use the\n // project's intended package manager, regardless of which lockfile exists.\n packageManager: '${packageManager}',\n modules: {\n dir: 'src/modules',\n repo: ${repoValue},\n pluralize: true,\n },\n\n // \\`kick typegen\\` populates \\`.kickjs/types/\\` so \\`Ctx<KickRoutes.X['method']>\\`\n // resolves to fully-typed params/body/query. Auto-runs on \\`kick dev\\`.\n // Set \\`schemaValidator: false\\` to skip schema-driven body typing entirely.\n typegen: {\n schemaValidator: 'zod',\n },\n\n commands: [\n {\n name: 'test',\n description: 'Run tests with Vitest',\n steps: 'npx vitest run',\n },\n {\n name: 'format',\n description: 'Format code with Prettier',\n steps: 'npx prettier --write src/',\n },\n {\n name: 'format:check',\n description: 'Check formatting without writing',\n steps: 'npx prettier --check src/',\n },\n {\n name: 'ci:check',\n description: 'Run typecheck + format check',\n steps: ['npx tsc --noEmit', 'npx prettier --check src/'],\n aliases: ['verify'],\n },\n ],\n})\n`\n}\n","import type { ModuleContext } from './types'\nimport { generateMinimalModuleIndex } from '../templates'\n\nexport async function generateMinimalFiles(ctx: ModuleContext): Promise<void> {\n const { pascal, kebab, plural, style, write } = ctx\n\n // Module file (named `<kebab>.module.ts` so Vite's module-discovery plugin picks it up)\n await write(`${kebab}.module.ts`, generateMinimalModuleIndex({ pascal, kebab, plural, style }))\n\n await write(\n `${kebab}.controller.ts`,\n `import { Controller, Get, type Ctx } from '@forinda/kickjs'\n\n// \\`Ctx<KickRoutes.${pascal}Controller['<method>']>\\` is generated by\n// \\`kick typegen\\` (auto-run on \\`kick dev\\`).\n\n@Controller()\nexport class ${pascal}Controller {\n @Get('/')\n async list(ctx: Ctx<KickRoutes.${pascal}Controller['list']>) {\n ctx.json({ message: '${pascal} list' })\n }\n}\n`,\n )\n}\n","import type { ModuleContext } from './types'\nimport { toKebabCase } from '../../utils/naming'\nimport {\n generateRestModuleIndex,\n generateRestController,\n generateRestConstants,\n generateRestService,\n generateCreateDTO,\n generateUpdateDTO,\n generateResponseDTO,\n generateRepositoryInterface,\n generateInMemoryRepository,\n generateCustomRepository,\n generateDrizzleRepository,\n generatePrismaRepository,\n generateControllerTest,\n generateRepositoryTest,\n} from '../templates'\n\nexport async function generateRestFiles(ctx: ModuleContext): Promise<void> {\n const {\n pascal,\n kebab,\n plural,\n pluralPascal,\n repo,\n noTests,\n prismaClientPath,\n tokenScope,\n style,\n write,\n } = ctx\n\n // Module file (named `<kebab>.module.ts` so Vite's module-discovery plugin picks it up)\n await write(`${kebab}.module.ts`, generateRestModuleIndex({ pascal, kebab, plural, repo, style }))\n\n // Constants\n await write(`${kebab}.constants.ts`, generateRestConstants({ pascal, kebab }))\n\n // Controller (injects service)\n await write(\n `${kebab}.controller.ts`,\n generateRestController({ pascal, kebab, plural, pluralPascal }),\n )\n\n // Service (wraps repository)\n await write(`${kebab}.service.ts`, generateRestService({ pascal, kebab }))\n\n // DTOs\n await write(`dtos/create-${kebab}.dto.ts`, generateCreateDTO({ pascal, kebab }))\n await write(`dtos/update-${kebab}.dto.ts`, generateUpdateDTO({ pascal, kebab }))\n await write(`dtos/${kebab}-response.dto.ts`, generateResponseDTO({ pascal, kebab }))\n\n // Repository interface (flat imports)\n await write(\n `${kebab}.repository.ts`,\n generateRepositoryInterface({ pascal, kebab, dtoPrefix: './dtos', tokenScope }),\n )\n\n // Repository implementation (flat imports)\n const builtinRepoFileMap: Record<string, string> = {\n inmemory: `in-memory-${kebab}`,\n drizzle: `drizzle-${kebab}`,\n prisma: `prisma-${kebab}`,\n }\n const builtinRepoGeneratorMap: Record<string, () => string> = {\n inmemory: () =>\n generateInMemoryRepository({ pascal, kebab, repoPrefix: '.', dtoPrefix: './dtos' }),\n drizzle: () =>\n generateDrizzleRepository({ pascal, kebab, repoPrefix: '.', dtoPrefix: './dtos' }),\n prisma: () =>\n generatePrismaRepository({\n pascal,\n kebab,\n repoPrefix: '.',\n dtoPrefix: './dtos',\n prismaClientPath,\n }),\n }\n const repoFile = builtinRepoFileMap[repo] ?? `${toKebabCase(repo)}-${kebab}`\n const repoGenerator =\n builtinRepoGeneratorMap[repo] ??\n (() =>\n generateCustomRepository({\n pascal,\n kebab,\n repoType: repo,\n repoPrefix: '.',\n dtoPrefix: './dtos',\n }))\n await write(`${repoFile}.repository.ts`, repoGenerator())\n\n // Tests\n if (!noTests) {\n // Always generate an in-memory repo for testing — even when using drizzle/prisma\n if (repo !== 'inmemory') {\n await write(\n `in-memory-${kebab}.repository.ts`,\n generateInMemoryRepository({ pascal, kebab, repoPrefix: '.', dtoPrefix: './dtos' }),\n )\n }\n await write(\n `__tests__/${kebab}.controller.test.ts`,\n generateControllerTest({ pascal, kebab, plural }),\n )\n await write(\n `__tests__/${kebab}.repository.test.ts`,\n generateRepositoryTest({\n pascal,\n kebab,\n plural,\n repoPrefix: `../${builtinRepoFileMap.inmemory ?? `in-memory-${kebab}`}.repository`,\n }),\n )\n }\n}\n","import type { ModuleContext } from './types'\nimport { toKebabCase } from '../../utils/naming'\nimport {\n generateCqrsModuleIndex,\n generateRestConstants,\n generateCqrsController,\n generateCreateDTO,\n generateUpdateDTO,\n generateResponseDTO,\n generateCqrsCommands,\n generateCqrsQueries,\n generateCqrsEvents,\n generateRepositoryInterface,\n generateInMemoryRepository,\n generateCustomRepository,\n generateDrizzleRepository,\n generatePrismaRepository,\n generateControllerTest,\n generateRepositoryTest,\n} from '../templates'\n\nexport async function generateCqrsFiles(ctx: ModuleContext): Promise<void> {\n const {\n pascal,\n kebab,\n plural,\n pluralPascal,\n repo,\n noTests,\n prismaClientPath,\n tokenScope,\n style,\n write,\n } = ctx\n\n // Module file (named `<kebab>.module.ts` so Vite's module-discovery plugin picks it up)\n await write(`${kebab}.module.ts`, generateCqrsModuleIndex({ pascal, kebab, plural, repo, style }))\n\n // Constants\n await write(`${kebab}.constants.ts`, generateRestConstants({ pascal, kebab }))\n\n // Controller (dispatches commands/queries)\n await write(\n `${kebab}.controller.ts`,\n generateCqrsController({ pascal, kebab, plural, pluralPascal }),\n )\n\n // DTOs\n await write(`dtos/create-${kebab}.dto.ts`, generateCreateDTO({ pascal, kebab }))\n await write(`dtos/update-${kebab}.dto.ts`, generateUpdateDTO({ pascal, kebab }))\n await write(`dtos/${kebab}-response.dto.ts`, generateResponseDTO({ pascal, kebab }))\n\n // Commands\n const commands = generateCqrsCommands({ pascal, kebab })\n for (const cmd of commands) {\n await write(`commands/${cmd.file}`, cmd.content)\n }\n\n // Queries\n const queries = generateCqrsQueries({ pascal, kebab, plural, pluralPascal })\n for (const q of queries) {\n await write(`queries/${q.file}`, q.content)\n }\n\n // Events\n const events = generateCqrsEvents({ pascal, kebab })\n for (const e of events) {\n await write(`events/${e.file}`, e.content)\n }\n\n // Repository interface (flat imports)\n await write(\n `${kebab}.repository.ts`,\n generateRepositoryInterface({ pascal, kebab, dtoPrefix: './dtos', tokenScope }),\n )\n\n // Repository implementation (flat imports)\n const builtinRepoFileMap: Record<string, string> = {\n inmemory: `in-memory-${kebab}`,\n drizzle: `drizzle-${kebab}`,\n prisma: `prisma-${kebab}`,\n }\n const builtinRepoGeneratorMap: Record<string, () => string> = {\n inmemory: () =>\n generateInMemoryRepository({ pascal, kebab, repoPrefix: '.', dtoPrefix: './dtos' }),\n drizzle: () =>\n generateDrizzleRepository({ pascal, kebab, repoPrefix: '.', dtoPrefix: './dtos' }),\n prisma: () =>\n generatePrismaRepository({\n pascal,\n kebab,\n repoPrefix: '.',\n dtoPrefix: './dtos',\n prismaClientPath,\n }),\n }\n const repoFile = builtinRepoFileMap[repo] ?? `${toKebabCase(repo)}-${kebab}`\n const repoGenerator =\n builtinRepoGeneratorMap[repo] ??\n (() =>\n generateCustomRepository({\n pascal,\n kebab,\n repoType: repo,\n repoPrefix: '.',\n dtoPrefix: './dtos',\n }))\n await write(`${repoFile}.repository.ts`, repoGenerator())\n\n // Tests\n if (!noTests) {\n // Always generate an in-memory repo for testing — even when using drizzle/prisma\n if (repo !== 'inmemory') {\n await write(\n `in-memory-${kebab}.repository.ts`,\n generateInMemoryRepository({ pascal, kebab, repoPrefix: '.', dtoPrefix: './dtos' }),\n )\n }\n await write(\n `__tests__/${kebab}.controller.test.ts`,\n generateControllerTest({ pascal, kebab, plural }),\n )\n await write(\n `__tests__/${kebab}.repository.test.ts`,\n generateRepositoryTest({\n pascal,\n kebab,\n plural,\n repoPrefix: `../${builtinRepoFileMap.inmemory ?? `in-memory-${kebab}`}.repository`,\n }),\n )\n }\n}\n","import type { ModuleContext } from './types'\nimport { toKebabCase } from '../../utils/naming'\nimport {\n generateModuleIndex,\n generateConstants,\n generateDrizzleConstants,\n generateController,\n generateCreateDTO,\n generateUpdateDTO,\n generateResponseDTO,\n generateUseCases,\n generateRepositoryInterface,\n generateDomainService,\n generateInMemoryRepository,\n generateCustomRepository,\n generateDrizzleRepository,\n generatePrismaRepository,\n generateEntity,\n generateValueObject,\n generateControllerTest,\n generateRepositoryTest,\n} from '../templates'\n\nexport async function generateDddFiles(ctx: ModuleContext): Promise<void> {\n const {\n pascal,\n kebab,\n plural,\n pluralPascal,\n repo,\n noEntity,\n noTests,\n prismaClientPath,\n tokenScope,\n style,\n write,\n } = ctx\n\n // Module file (named `<kebab>.module.ts` so Vite's module-discovery plugin picks it up)\n await write(`${kebab}.module.ts`, generateModuleIndex({ pascal, kebab, plural, repo, style }))\n\n // Constants — use Drizzle-specific type-safe config when repo is drizzle\n await write(\n 'constants.ts',\n repo === 'drizzle'\n ? generateDrizzleConstants({ pascal, kebab })\n : generateConstants({ pascal, kebab }),\n )\n\n // Controller (injects use-cases)\n await write(\n `presentation/${kebab}.controller.ts`,\n generateController({ pascal, kebab, plural, pluralPascal }),\n )\n\n // DTOs\n await write(`application/dtos/create-${kebab}.dto.ts`, generateCreateDTO({ pascal, kebab }))\n await write(`application/dtos/update-${kebab}.dto.ts`, generateUpdateDTO({ pascal, kebab }))\n await write(`application/dtos/${kebab}-response.dto.ts`, generateResponseDTO({ pascal, kebab }))\n\n // Use Cases\n const useCases = generateUseCases({ pascal, kebab, plural, pluralPascal })\n for (const uc of useCases) {\n await write(`application/use-cases/${uc.file}`, uc.content)\n }\n\n // Repository Interface\n await write(\n `domain/repositories/${kebab}.repository.ts`,\n generateRepositoryInterface({ pascal, kebab, tokenScope }),\n )\n\n // Domain Service\n await write(\n `domain/services/${kebab}-domain.service.ts`,\n generateDomainService({ pascal, kebab }),\n )\n\n // Repository Implementation\n const builtinRepoFileMap: Record<string, string> = {\n inmemory: `in-memory-${kebab}`,\n drizzle: `drizzle-${kebab}`,\n prisma: `prisma-${kebab}`,\n }\n const builtinRepoGeneratorMap: Record<string, () => string> = {\n inmemory: () => generateInMemoryRepository({ pascal, kebab }),\n drizzle: () => generateDrizzleRepository({ pascal, kebab }),\n prisma: () => generatePrismaRepository({ pascal, kebab, prismaClientPath }),\n }\n const repoFile = builtinRepoFileMap[repo] ?? `${toKebabCase(repo)}-${kebab}`\n const repoGenerator =\n builtinRepoGeneratorMap[repo] ??\n (() => generateCustomRepository({ pascal, kebab, repoType: repo }))\n await write(`infrastructure/repositories/${repoFile}.repository.ts`, repoGenerator())\n\n // Entity & Value Objects\n if (!noEntity) {\n await write(`domain/entities/${kebab}.entity.ts`, generateEntity({ pascal, kebab }))\n await write(`domain/value-objects/${kebab}-id.vo.ts`, generateValueObject({ pascal, kebab }))\n }\n\n // Tests\n if (!noTests) {\n // Always generate an in-memory repo for testing — even when using drizzle/prisma\n if (repo !== 'inmemory') {\n await write(\n `infrastructure/repositories/in-memory-${kebab}.repository.ts`,\n generateInMemoryRepository({ pascal, kebab }),\n )\n }\n await write(\n `__tests__/${kebab}.controller.test.ts`,\n generateControllerTest({ pascal, kebab, plural }),\n )\n await write(\n `__tests__/${kebab}.repository.test.ts`,\n generateRepositoryTest({ pascal, kebab, plural }),\n )\n }\n}\n","import { join } from 'node:path'\nimport { writeFileSafe, fileExists } from '../utils/fs'\nimport { confirm, log } from '../utils/prompts'\nimport { colors } from '../utils/colors'\nimport { toPascalCase, toKebabCase, pluralize, pluralizePascal } from '../utils/naming'\nimport { escapeRegex } from '../utils/regex'\nimport { readFile, writeFile } from 'node:fs/promises'\nimport type { ProjectPattern, RepoTypeConfig } from '../config'\nimport type { ModuleStyle } from './templates/types'\nimport {\n generateMinimalFiles,\n generateRestFiles,\n generateCqrsFiles,\n generateDddFiles,\n} from './patterns'\nimport type { ModuleContext } from './patterns'\n\nexport type BuiltinRepoType = 'drizzle' | 'inmemory' | 'prisma'\nexport type RepoType = BuiltinRepoType | (string & {})\n\n/** Resolve a RepoTypeConfig (from kick.config.ts) into a flat repo type string */\nexport function resolveRepoType(config?: RepoTypeConfig): RepoType {\n if (!config) return 'inmemory'\n if (typeof config === 'string') return config\n return config.name\n}\n\ninterface GenerateModuleOptions {\n name: string\n modulesDir: string\n noEntity?: boolean\n noTests?: boolean\n repo?: RepoType\n minimal?: boolean\n force?: boolean\n pattern?: ProjectPattern\n dryRun?: boolean\n /** When false, skip pluralization — use singular names for folders, routes, and classes */\n pluralize?: boolean\n /** Prisma client import path (default: '@prisma/client', Prisma 7+: '@/generated/prisma/client') */\n prismaClientPath?: string\n /**\n * DI-token scope prefix substituted into emitted `createToken<T>()`\n * literals. Resolved by the orchestrating command from\n * `kick.config.ts > tokenScope` or the project's package.json.\n * Falls back to `'app'` when not set so the generator can be called\n * without a config in tests/fixtures.\n */\n tokenScope?: string\n /**\n * Module declaration style — `'define'` (factory, default) or\n * `'class'` (legacy). Resolved by the orchestrating command from\n * `kick.config.ts > modules.style`.\n */\n style?: ModuleStyle\n}\n\n/**\n * Generate a module — structure depends on the project pattern.\n *\n * Patterns:\n * rest — flat folder: controller + service + DTOs + repo\n * ddd — nested DDD: presentation/ application/ domain/ infrastructure/\n * cqrs — commands, queries, events with WS/queue integration\n * minimal — just controller + module index\n */\nexport async function generateModule(options: GenerateModuleOptions): Promise<string[]> {\n const { name, modulesDir, noEntity, noTests, repo = 'inmemory', force, dryRun } = options\n const shouldPluralize = options.pluralize !== false\n\n let pattern = options.pattern ?? 'ddd'\n if (options.minimal) pattern = 'minimal'\n\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const plural = shouldPluralize ? pluralize(kebab) : kebab\n const pluralPascal = shouldPluralize ? pluralizePascal(pascal) : pascal\n const moduleDir = join(modulesDir, plural)\n\n const files: string[] = []\n let overwriteAll = force ?? false\n\n const write = async (relativePath: string, content: string) => {\n const fullPath = join(moduleDir, relativePath)\n if (dryRun) {\n files.push(fullPath)\n return\n }\n if (!overwriteAll && (await fileExists(fullPath))) {\n const shouldOverwrite = await confirm({\n message: `File exists: ${colors.dim(relativePath)}. Overwrite?`,\n initialValue: false,\n })\n if (!shouldOverwrite) {\n log.warn(`Skipped: ${relativePath}`)\n return\n }\n }\n await writeFileSafe(fullPath, content)\n files.push(fullPath)\n }\n\n const ctx: ModuleContext = {\n kebab,\n pascal,\n plural,\n pluralPascal,\n moduleDir,\n repo,\n noEntity: noEntity ?? false,\n noTests: noTests ?? false,\n prismaClientPath: options.prismaClientPath ?? '@prisma/client',\n tokenScope: options.tokenScope ?? 'app',\n style: options.style ?? 'define',\n write,\n files,\n }\n\n switch (pattern) {\n case 'minimal':\n await generateMinimalFiles(ctx)\n break\n case 'rest':\n await generateRestFiles(ctx)\n break\n case 'cqrs':\n await generateCqrsFiles(ctx)\n break\n case 'ddd':\n default:\n await generateDddFiles(ctx)\n break\n }\n\n // Auto-register in modules index (all patterns need this)\n if (!dryRun) {\n await autoRegisterModule(modulesDir, pascal, plural, kebab, ctx.style)\n }\n\n return files\n}\n\n// ── Auto-register in modules index ──────────────────────────────────────\n\n/**\n * Add a new module to `src/modules/index.ts`. Handles both the\n * fresh-project initial-write path (creates the file with the right\n * shape for the resolved style) and the established-project append\n * path (inserts the import + extends the flat array OR fluent chain\n * via {@link appendModuleEntry}).\n *\n * Exported so `scaffold.ts` can reuse it — both call sites had\n * line-for-line identical bodies before consolidation, including\n * the inline-regex chain-append bug CodeRabbit caught on PR #196.\n * Single source of truth here keeps the orchestrator and scaffold\n * paths from drifting again.\n */\nexport async function autoRegisterModule(\n modulesDir: string,\n pascal: string,\n plural: string,\n kebab: string,\n style: ModuleStyle = 'define',\n): Promise<void> {\n const indexPath = join(modulesDir, 'index.ts')\n const exists = await fileExists(indexPath)\n const importPath = `./${plural}/${kebab}.module`\n // `defineModule` factories are called at the registration site\n // (`${pascal}Module()`); legacy class modules are passed by reference\n // (`${pascal}Module`). Application's loader discriminates class vs\n // instance at boot, so both forms work — `style` only controls what\n // the orchestrator emits.\n const entryToken = style === 'class' ? `${pascal}Module` : `${pascal}Module()`\n\n if (!exists) {\n // For 'class' style we emit the legacy flat-array form because\n // `defineModules` is the factory-form companion. For 'define' (default)\n // we emit the fluent `defineModules().mount(...)` chain so subsequent\n // `kick g module` invocations append cleanly.\n const initialBody =\n style === 'class'\n ? `import type { AppModuleEntry } from '@forinda/kickjs'\nimport { ${pascal}Module } from '${importPath}'\n\nexport const modules: AppModuleEntry[] = [${entryToken}]\n`\n : `import { defineModules } from '@forinda/kickjs'\nimport { ${pascal}Module } from '${importPath}'\n\nexport const modules = defineModules().mount(${entryToken})\n`\n await writeFileSafe(indexPath, initialBody)\n return\n }\n\n let content = await readFile(indexPath, 'utf-8')\n\n // Two independent checks — a stale comment, a doc snippet, or a\n // partially-edited file shouldn't make the gate skip both halves.\n //\n // 1. Import-line check: look for an exact `import { XModule }\n // from '<importPath>'` statement, not just any `XModule`\n // mention. A comment that names XModule doesn't satisfy this.\n // 2. Registry-entry check: look for `XModule` (word-bounded)\n // inside the actual `export const modules` initializer rhs,\n // not anywhere in the file. Recovers a half-edited registry\n // where the import survived but the .mount entry was deleted.\n const importLine = `import { ${pascal}Module } from '${importPath}'`\n const escapedImportPath = escapeRegex(importPath)\n const importPresentRe = new RegExp(\n `^import\\\\s*\\\\{[^}]*\\\\b${escapeRegex(pascal)}Module\\\\b[^}]*\\\\}\\\\s*from\\\\s*['\"]${escapedImportPath}['\"]`,\n 'm',\n )\n if (!importPresentRe.test(content)) {\n // Insert import after last existing import\n const lastImportIdx = content.lastIndexOf('import ')\n if (lastImportIdx !== -1) {\n const lineEnd = content.indexOf('\\n', lastImportIdx)\n content = content.slice(0, lineEnd + 1) + importLine + '\\n' + content.slice(lineEnd + 1)\n } else {\n content = importLine + '\\n' + content\n }\n }\n\n // Independently confirm the registry rhs already names this entry\n // before skipping the append. Scoped to the `export const modules`\n // slice via `findModulesRhsSpan` so an `XModule` mention in an\n // unrelated builder above the registry doesn't mask a missing\n // `.mount(XModule())` further down.\n const span = findModulesRhsSpan(content)\n if (span) {\n const rhsSlice = content.slice(span.rhsStart, span.rhsEnd + 1)\n const entryPresentRe = new RegExp(`\\\\b${escapeRegex(pascal)}Module\\\\b`)\n if (!entryPresentRe.test(rhsSlice)) {\n content = appendModuleEntry(content, entryToken)\n }\n } else {\n // No recognizable registry shape — fall back to the previous\n // best-effort behaviour: try appending; appendModuleEntry returns\n // content unchanged when it can't find a target.\n content = appendModuleEntry(content, entryToken)\n }\n\n await writeFile(indexPath, content, 'utf-8')\n}\n\n/**\n * Append `entryToken` (e.g. `LionModule()` or bare `LionModule`) to\n * the project's modules registry. Handles two shapes:\n *\n * 1. Flat array literal:\n * `export const modules: AppModuleEntry[] = [HelloModule()]`\n * 2. Fluent factory chain:\n * `export const modules = defineModules().mount(HelloModule())`\n *\n * If neither shape is detected, content is returned unchanged — the\n * adopter's registration site is non-standard and they mount the\n * new module themselves.\n *\n * Exported so `scaffold.ts`'s auto-register can reuse the same\n * balanced-paren scanner — duplicating the regex caused\n * `mount(UserModule())` to corrupt on the scaffold path before this\n * was hoisted out.\n */\nexport function appendModuleEntry(content: string, entryToken: string): string {\n const span = findModulesRhsSpan(content)\n if (!span) return content\n\n // Shape 1 — flat array literal at the rhs.\n if (span.shape === 'array') {\n const inside = content.slice(span.rhsStart + 1, span.rhsEnd)\n const trimmed = inside.trim()\n let rewritten: string\n if (!trimmed) {\n rewritten = `[${entryToken}]`\n } else {\n const needsComma = trimmed.endsWith(',') ? '' : ','\n rewritten = `[${inside.trimEnd()}${needsComma} ${entryToken}]`\n }\n return content.slice(0, span.rhsStart) + rewritten + content.slice(span.rhsEnd + 1)\n }\n\n // Shape 2 — `defineModules()` fluent chain at the rhs. Insert\n // `.mount(...)` right at `chainEnd` (the offset just past the\n // last `.mount(...)`'s closing `)` or just past `defineModules()`'s\n // closing `)` when the chain is empty).\n return `${content.slice(0, span.chainEnd)}\\n .mount(${entryToken})${content.slice(span.chainEnd)}`\n}\n\n/**\n * Span returned by {@link findModulesRhsSpan}. Both shapes carry the\n * `rhsStart` (the offset of `[` for arrays or `d` for the chain) and\n * an `end` offset describing where the rhs concludes:\n *\n * - `array`: `rhsEnd` points at the matching `]` (inclusive).\n * - `chain`: `chainEnd` points just past the last `.mount(...)`\n * call (or just past `defineModules()`'s closing `)` when the\n * chain is empty). For consistency with arrays, `rhsEnd =\n * chainEnd - 1` so a `[rhsStart, rhsEnd + 1)` slice covers the\n * whole initializer.\n */\ntype ModulesRhsSpan =\n | { shape: 'array'; rhsStart: number; rhsEnd: number }\n | { shape: 'chain'; rhsStart: number; rhsEnd: number; chainEnd: number }\n\n/**\n * Locate the `export const modules = <rhs>` initializer in `content`\n * and return the rhs's start/end offsets along with which shape it\n * is (flat array vs `defineModules()` chain). Used by both the\n * append path (`appendModuleEntry`) AND the remove path\n * (`stripChainMount` / array-entry rm regex) so neither mutates\n * unrelated text elsewhere in the file.\n *\n * Returns `null` when no `export const modules` declaration is\n * found, or when its rhs doesn't match either supported shape.\n */\nexport function findModulesRhsSpan(content: string): ModulesRhsSpan | null {\n const declMatch = /export\\s+const\\s+modules\\b[^=]*=/.exec(content)\n if (!declMatch) return null\n const eqEnd = declMatch.index + declMatch[0].length\n let rhsStart = eqEnd\n while (rhsStart < content.length && /\\s/.test(content[rhsStart] ?? '')) rhsStart++\n\n if (content[rhsStart] === '[') {\n const close = balancedBracketClose(content, rhsStart)\n if (close === -1) return null\n return { shape: 'array', rhsStart, rhsEnd: close }\n }\n\n if (content.slice(rhsStart, rhsStart + 'defineModules'.length) === 'defineModules') {\n const chainEnd = findChainEnd(content, rhsStart)\n if (chainEnd === -1) return null\n return { shape: 'chain', rhsStart, rhsEnd: chainEnd - 1, chainEnd }\n }\n\n return null\n}\n\n/**\n * Locate the insertion point after the last `.mount(...)` call in a\n * `defineModules()...` chain, or after `defineModules()` itself when\n * the chain is empty. Returns the source-string offset to insert at,\n * or -1 when the chain isn't found.\n *\n * The scanner walks balanced parens so nested factory calls inside\n * `.mount(X())` don't break boundary detection.\n */\nfunction findChainEnd(src: string, fromIdx = 0): number {\n // Match the CALL site, not the import statement. We require an\n // immediately-following `(` (allowing whitespace) to anchor on\n // `defineModules(...)` and skip past `import { defineModules }`.\n // `fromIdx` lets the caller scope the search to the rhs of an\n // `export const modules =` declaration so unrelated calls\n // elsewhere in the file aren't matched.\n const callRegex = /defineModules\\s*\\(/g\n callRegex.lastIndex = fromIdx\n const match = callRegex.exec(src)\n if (!match) return -1\n // `match.index` points at `d`; `(` is at `match.index + match[0].length - 1`.\n let i = match.index + match[0].length - 1\n if (src[i] !== '(') return -1\n // Balance the args of `defineModules(...)`.\n i = balancedClose(src, i)\n if (i === -1) return -1\n i++\n // Now consume zero or more `.mount(...)` calls. After each, `i`\n // points just past its closing `)` so the next iteration sees a\n // potential `.mount(...)` ahead.\n for (;;) {\n let j = i\n while (j < src.length && /\\s/.test(src[j] ?? '')) j++\n if (src[j] !== '.') break\n if (src.slice(j, j + 6) !== '.mount') break\n j += 6\n while (j < src.length && /\\s/.test(src[j] ?? '')) j++\n if (src[j] !== '(') break\n const close = balancedClose(src, j)\n if (close === -1) break\n i = close + 1\n }\n return i\n}\n\n/**\n * Skip past a `//` line comment or `/* … *\\/` block comment that\n * starts at offset `i`. Returns the offset of the first character\n * after the comment, or `i` unchanged when there's no comment at\n * that position. Used by the balanced-paren / bracket scanners so\n * a `]` or `)` inside a comment doesn't terminate the scan early.\n */\nfunction skipComment(src: string, i: number): number {\n const two = src.slice(i, i + 2)\n if (two === '//') {\n i += 2\n while (i < src.length && src[i] !== '\\n') i++\n return i\n }\n if (two === '/*') {\n i += 2\n while (i + 1 < src.length && !(src[i] === '*' && src[i + 1] === '/')) i++\n return i + 2\n }\n return i\n}\n\n/**\n * Given an offset pointing at `[`, return the offset of its matching\n * `]`. Skips brackets inside string literals so `['weird]name']`\n * doesn't break, and inside `//` / `/* *\\/` comments. Returns -1 on\n * imbalance.\n */\nfunction balancedBracketClose(src: string, openIdx: number): number {\n if (src[openIdx] !== '[') return -1\n let depth = 1\n let i = openIdx + 1\n while (i < src.length) {\n const next = src.slice(i, i + 2)\n if (next === '//' || next === '/*') {\n i = skipComment(src, i)\n continue\n }\n const ch = src[i] ?? ''\n if (ch === \"'\" || ch === '\"' || ch === '`') {\n const quote = ch\n i++\n while (i < src.length && src[i] !== quote) {\n if (src[i] === '\\\\') i++\n i++\n }\n if (i < src.length) i++\n continue\n }\n if (ch === '[') depth++\n else if (ch === ']') {\n depth--\n if (depth === 0) return i\n }\n i++\n }\n return -1\n}\n\n/**\n * Given an offset pointing at `(`, return the offset of its matching\n * `)`, or -1 on imbalance. Skips parens inside string literals\n * (single, double, backtick) and inside `//` / `/* *\\/` comments —\n * a `)` in either would terminate the scan early and corrupt the\n * insertion offset.\n */\nfunction balancedClose(src: string, openIdx: number): number {\n if (src[openIdx] !== '(') return -1\n let depth = 1\n let i = openIdx + 1\n while (i < src.length) {\n const next = src.slice(i, i + 2)\n if (next === '//' || next === '/*') {\n i = skipComment(src, i)\n continue\n }\n const ch = src[i] ?? ''\n if (ch === \"'\" || ch === '\"' || ch === '`') {\n // Skip string literal — find matching unescaped quote.\n const quote = ch\n i++\n while (i < src.length && src[i] !== quote) {\n if (src[i] === '\\\\') i++\n i++\n }\n if (i < src.length) i++\n continue\n }\n if (ch === '(') depth++\n else if (ch === ')') {\n depth--\n if (depth === 0) return i\n }\n i++\n }\n return -1\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\n\ninterface GenerateAdapterOptions {\n name: string\n outDir: string\n}\n\n/**\n * Scaffold a `defineAdapter()` factory under `src/adapters/<name>.adapter.ts`.\n *\n * v4 dropped the `class implements AppAdapter` pattern in favour of the\n * `defineAdapter()` factory (architecture.md §21.3.4). The generated\n * template uses the new factory shape so adopters get a working\n * adapter with all four lifecycle hooks (beforeMount, beforeStart,\n * afterStart, shutdown), a typed config object with defaults, and the\n * factory's call / `.scoped()` / `.async()` surfaces — without\n * writing a single class.\n */\nexport async function generateAdapter(options: GenerateAdapterOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.adapter.ts`)\n await writeFileSafe(\n filePath,\n `import {\n defineAdapter,\n type AdapterContext,\n type AdapterMiddleware,\n type ContributorRegistrations,\n type Constructor,\n} from '@forinda/kickjs'\n\n/**\n * Configuration for the ${pascal} adapter.\n *\n * Adapters typically take a small config object so callers can tune\n * behaviour at bootstrap time. Keep the shape narrow — anything\n * derived from the environment should be read inside the build\n * function via getEnv(), not forced onto the caller.\n */\nexport interface ${pascal}AdapterConfig {\n // Add your adapter configuration here, e.g.:\n // enabled?: boolean\n // apiKey?: string\n}\n\n/**\n * ${pascal} adapter — built via \\`defineAdapter()\\` so callers get the\n * factory's call / \\`.scoped()\\` / \\`.async()\\` surfaces for free.\n *\n * Hooks into the Application lifecycle to add middleware, routes,\n * Context Contributors, or external service connections.\n *\n * Every lifecycle hook below is OPTIONAL. The scaffold emits all of\n * them so adopters can browse what's available and delete what they\n * don't need — \\`build()\\` returning \\`{}\\` is also valid for an adapter\n * that only contributes config defaults.\n *\n * @example\n * \\`\\`\\`ts\n * import { bootstrap } from '@forinda/kickjs'\n * import { ${pascal}Adapter } from './adapters/${kebab}.adapter'\n *\n * bootstrap({\n * modules,\n * adapters: [${pascal}Adapter({ /* config overrides *\\\\/ })],\n * })\n * \\`\\`\\`\n */\nexport const ${pascal}Adapter = defineAdapter<${pascal}AdapterConfig>({\n name: '${pascal}Adapter',\n defaults: {\n // Default config values go here. The adopter's overrides shallow-merge\n // on top of these before \\`build()\\` runs.\n },\n build: (_config, { name: _name }) => {\n // Closures inside \\`build()\\` are how each adapter instance owns its\n // own state (database client, Map, timer handle, …). The same\n // \\`_config\\` is visible to every hook below.\n\n return {\n /**\n * Express middleware entries the Application mounts at named phases.\n *\n * \\`phase\\` controls where each handler sits in the pipeline:\n * 'beforeGlobal' | 'afterGlobal' | 'beforeRoutes' | 'afterRoutes'.\n *\n * \\`path\\` (optional) scopes the entry to a path prefix.\n *\n * Delete this hook entirely if you don't add middleware.\n */\n middleware(): AdapterMiddleware[] {\n return [\n // Example: add a custom header to all responses\n // {\n // phase: 'beforeGlobal',\n // handler: (_req, res, next) => {\n // res.setHeader('X-${pascal}', 'true')\n // next()\n // },\n // },\n // Example: scope a rate limiter to one path prefix\n // {\n // phase: 'beforeRoutes',\n // path: '/api/v1/auth',\n // handler: rateLimit({ max: 10 }),\n // },\n ]\n },\n\n /**\n * Runs BEFORE global middleware. Mount routes that should bypass the\n * middleware stack — health checks, docs UI, static assets, OAuth\n * callbacks. Anything you want reachable even if a global middleware\n * later in the chain rejects requests.\n *\n * Delete this hook if you have no early routes.\n */\n beforeMount(_ctx: AdapterContext): void {\n // Example:\n // _ctx.app.get('/${kebab}/status', (_req, res) => res.json({ status: 'ok' }))\n },\n\n /**\n * Fires once per controller class as the router mounts. Use this to\n * collect route metadata for OpenAPI specs, dependency graphs, route\n * inventories, devtools dashboards.\n *\n * Delete this hook unless your adapter introspects the route registry.\n */\n onRouteMount(_controllerClass: Constructor, _mountPath: string): void {\n // Example (Swagger-style): collect routes for the spec.\n // openApiSpec.addController(_controllerClass, _mountPath)\n },\n\n /**\n * Runs AFTER modules + routes are wired, BEFORE the server starts.\n * Right place for late-stage DI registrations or final config validation.\n *\n * Delete this hook if there's nothing to wire post-modules.\n */\n beforeStart(_ctx: AdapterContext): void {\n // Example: _ctx.container.registerInstance(MY_TOKEN, new MyService(_config))\n },\n\n /**\n * Runs AFTER the HTTP server is listening. The raw \\`http.Server\\` is\n * available on \\`ctx.server\\` — attach upgrade handlers (Socket.IO,\n * gRPC, GraphQL subscriptions), warm caches, log a banner.\n *\n * Delete this hook if you don't need the running server reference.\n */\n afterStart(_ctx: AdapterContext): void {\n // Example: const io = new Server(_ctx.server)\n },\n\n /**\n * Returns Context Contributors to merge into every route's pipeline\n * at the \\`'adapter'\\` precedence level. Per-route handlers can\n * override the value at the method / class / module level.\n *\n * Delete this hook unless your adapter ships typed per-request values\n * (auth user, tenant, locale, feature flags, geo, etc).\n */\n contributors(): ContributorRegistrations {\n return [\n // Example:\n // import { defineHttpContextDecorator } from '@forinda/kickjs'\n // declare module '@forinda/kickjs' { interface ContextMeta { ${kebab}: { id: string } } }\n // const Load${pascal} = defineHttpContextDecorator({\n // key: '${kebab}',\n // resolve: (ctx) => ({ id: ctx.req.headers['x-${kebab}-id'] as string }),\n // })\n // return [Load${pascal}.registration]\n ]\n },\n\n /**\n * Runs on graceful shutdown (SIGINT/SIGTERM). Clean up long-lived\n * resources the adapter owns: close connections, flush buffers,\n * cancel timers. The framework runs every adapter's \\`shutdown\\`\n * concurrently via \\`Promise.allSettled\\` — one failure won't block\n * sibling adapters.\n *\n * Delete this hook if your adapter holds no resources.\n */\n async shutdown(): Promise<void> {\n // Example: await this.pool.end()\n // Example: clearInterval(this.heartbeatTimer)\n },\n }\n },\n})\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { resolve, join } from 'node:path'\nimport { pluralize, toKebabCase } from './naming'\nimport type { ProjectPattern } from '../config'\n\n/**\n * DDD folder mapping — nested layered architecture.\n */\nconst DDD_FOLDER_MAP: Record<string, string> = {\n controller: 'presentation',\n service: 'domain/services',\n dto: 'application/dtos',\n guard: 'presentation/guards',\n middleware: 'middleware',\n}\n\n/**\n * Flat folder mapping — REST and minimal patterns.\n * Files live at the module root or in minimal subdirectories.\n */\nconst FLAT_FOLDER_MAP: Record<string, string> = {\n controller: '',\n service: '',\n dto: 'dtos',\n guard: 'guards',\n middleware: 'middleware',\n}\n\n/**\n * CQRS folder mapping — commands, queries, events.\n */\nconst CQRS_FOLDER_MAP: Record<string, string> = {\n controller: '',\n service: '',\n dto: 'dtos',\n guard: 'guards',\n middleware: 'middleware',\n command: 'commands',\n query: 'queries',\n event: 'events',\n}\n\nexport interface ResolveOutDirOptions {\n /** The artifact type (controller, service, dto, guard, middleware) */\n type: string\n /** Explicit -o / --out dir from CLI flag (takes highest priority) */\n outDir?: string\n /** Module name from --module flag */\n moduleName?: string\n /** Modules directory (from config or default) */\n modulesDir?: string\n /** Standalone default directory when no --module is used (e.g. 'src/controllers') */\n defaultDir: string\n /** Project pattern — determines folder structure inside modules */\n pattern?: ProjectPattern\n /** Whether to pluralize the module folder name (default: true) */\n shouldPluralize?: boolean\n}\n\n/**\n * Resolve the output directory for a generator artifact.\n *\n * Priority:\n * 1. Explicit --out flag (always wins)\n * 2. --module flag → maps into module's folder (DDD or flat based on pattern)\n * 3. Standalone default directory\n */\nexport function resolveOutDir(options: ResolveOutDirOptions): string {\n const {\n type,\n outDir,\n moduleName,\n modulesDir = 'src/modules',\n defaultDir,\n pattern = 'ddd',\n shouldPluralize = true,\n } = options\n\n // Explicit --out always wins\n if (outDir) return resolve(outDir)\n\n // Module-scoped: place inside the module's folder\n if (moduleName) {\n const folderMap =\n pattern === 'ddd' ? DDD_FOLDER_MAP : pattern === 'cqrs' ? CQRS_FOLDER_MAP : FLAT_FOLDER_MAP\n const kebab = toKebabCase(moduleName)\n const folder = shouldPluralize ? pluralize(kebab) : kebab\n const subfolder = folderMap[type] ?? ''\n const base = join(modulesDir, folder)\n return resolve(subfolder ? join(base, subfolder) : base)\n }\n\n // Standalone default\n return resolve(defaultDir)\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\nimport { resolveOutDir } from '../utils/resolve-out-dir'\nimport type { ProjectPattern } from '../config'\n\ninterface GenerateMiddlewareOptions {\n name: string\n outDir?: string\n moduleName?: string\n modulesDir?: string\n pattern?: ProjectPattern\n pluralize?: boolean\n}\n\nexport async function generateMiddleware(options: GenerateMiddlewareOptions): Promise<string[]> {\n const { name, moduleName, modulesDir, pattern } = options\n const outDir = resolveOutDir({\n type: 'middleware',\n outDir: options.outDir,\n moduleName,\n modulesDir,\n defaultDir: 'src/middleware',\n pattern,\n shouldPluralize: options.pluralize ?? true,\n })\n const kebab = toKebabCase(name)\n const camel = toCamelCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.middleware.ts`)\n await writeFileSafe(\n filePath,\n `import type { Request, Response, NextFunction } from 'express'\n\nexport interface ${toPascalCase(name)}Options {\n // Add configuration options here. The factory below closes over the\n // resolved options object; pass them at the call site —\n // \\`${camel}({ foo: 'bar' })\\` — and the closure preserves them across\n // every request.\n}\n\n/**\n * ${toPascalCase(name)} middleware.\n *\n * Usage in bootstrap (fires on every request):\n * middleware: [${camel}()]\n *\n * Usage with adapter — phase controls *when* the handler runs:\n *\n * middleware() {\n * return [{ handler: ${camel}(), phase: 'afterGlobal' }]\n * }\n *\n * Phase semantics (see \\`MiddlewarePhase\\` JSDoc for the full contract):\n * - 'beforeGlobal' / 'afterGlobal' / 'beforeRoutes' — fire on every\n * request, before module routes run.\n * - 'afterRoutes' — fires ONLY when no route matched (404 fall-through)\n * OR a route handler called \\`next()\\` without ending the response.\n * Controllers that call \\`ctx.json(…)\\` end the chain and skip this\n * phase. For per-response work (logging, metrics) attach to\n * \\`res.on('finish', …)\\` from an earlier-phase middleware instead.\n *\n * Optional path scope — string, RegExp, or array of either:\n * middleware() {\n * return [{\n * handler: ${camel}({ region: 'eu' }),\n * phase: 'afterGlobal',\n * path: ['/api', /^\\\\/admin/],\n * }]\n * }\n *\n * Usage with @Middleware decorator:\n * @Middleware(${camel}())\n */\nexport function ${camel}(options: ${toPascalCase(name)}Options = {}) {\n return (req: Request, res: Response, next: NextFunction) => {\n // Implement your middleware logic here. \\`options\\` is captured by\n // closure — log or read it anywhere in this handler body.\n void options\n next()\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\nimport { resolveOutDir } from '../utils/resolve-out-dir'\nimport type { ProjectPattern } from '../config'\n\ninterface GenerateGuardOptions {\n name: string\n outDir?: string\n moduleName?: string\n modulesDir?: string\n pattern?: ProjectPattern\n pluralize?: boolean\n}\n\nexport async function generateGuard(options: GenerateGuardOptions): Promise<string[]> {\n const { name, moduleName, modulesDir, pattern } = options\n const outDir = resolveOutDir({\n type: 'guard',\n outDir: options.outDir,\n moduleName,\n modulesDir,\n defaultDir: 'src/guards',\n pattern,\n shouldPluralize: options.pluralize ?? true,\n })\n const kebab = toKebabCase(name)\n const camel = toCamelCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.guard.ts`)\n await writeFileSafe(\n filePath,\n `import { Container, HttpException } from '@forinda/kickjs'\nimport type { RequestContext } from '@forinda/kickjs'\n\n/**\n * ${pascal} guard.\n *\n * Guards protect routes by checking conditions before the handler runs.\n * Return early with an error response to block access.\n *\n * Usage:\n * @Middleware(${camel}Guard)\n * @Get('/protected')\n * async handler(ctx: RequestContext) { ... }\n */\nexport async function ${camel}Guard(ctx: RequestContext, next: () => void): Promise<void> {\n // Example: check for an authorization header\n const header = ctx.headers.authorization\n if (!header?.startsWith('Bearer ')) {\n ctx.res.status(401).json({ message: 'Missing or invalid authorization header' })\n return\n }\n\n const token = header.slice(7)\n\n try {\n // Verify the token using a service from the DI container\n // const container = Container.getInstance()\n // const authService = container.resolve(AuthService)\n // const payload = authService.verifyToken(token)\n // ctx.set('auth', payload)\n\n next()\n } catch {\n ctx.res.status(401).json({ message: 'Invalid or expired token' })\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\nimport { resolveOutDir } from '../utils/resolve-out-dir'\nimport type { ProjectPattern } from '../config'\n\ninterface GenerateServiceOptions {\n name: string\n outDir?: string\n moduleName?: string\n modulesDir?: string\n pattern?: ProjectPattern\n pluralize?: boolean\n}\n\nexport async function generateService(options: GenerateServiceOptions): Promise<string[]> {\n const { name, moduleName, modulesDir, pattern } = options\n const outDir = resolveOutDir({\n type: 'service',\n outDir: options.outDir,\n moduleName,\n modulesDir,\n defaultDir: 'src/services',\n pattern,\n shouldPluralize: options.pluralize ?? true,\n })\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.service.ts`)\n await writeFileSafe(\n filePath,\n `import { Service } from '@forinda/kickjs'\n\n@Service()\nexport class ${pascal}Service {\n // Inject dependencies via constructor\n // constructor(\n // @Inject(MY_REPO) private readonly repo: IMyRepository,\n // ) {}\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\nimport { resolveOutDir } from '../utils/resolve-out-dir'\nimport type { ProjectPattern } from '../config'\n\ninterface GenerateControllerOptions {\n name: string\n outDir?: string\n moduleName?: string\n modulesDir?: string\n pattern?: ProjectPattern\n pluralize?: boolean\n}\n\nexport async function generateController(options: GenerateControllerOptions): Promise<string[]> {\n const { name, moduleName, modulesDir, pattern } = options\n const outDir = resolveOutDir({\n type: 'controller',\n outDir: options.outDir,\n moduleName,\n modulesDir,\n defaultDir: 'src/controllers',\n pattern,\n shouldPluralize: options.pluralize ?? true,\n })\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.controller.ts`)\n await writeFileSafe(\n filePath,\n `import { Controller, Get, Post, type Ctx } from '@forinda/kickjs'\n\n// \\`Ctx<KickRoutes.${pascal}Controller['<method>']>\\` is generated by\n// \\`kick typegen\\` (auto-run on \\`kick dev\\`). After the first run, your IDE\n// will autocomplete \\`ctx.params\\`, \\`ctx.body\\`, and \\`ctx.query\\`.\n// See https://forinda.github.io/kick-js/guide/typegen for details.\n\n@Controller()\nexport class ${pascal}Controller {\n // @Autowired() private readonly myService!: MyService\n\n @Get('/')\n async list(ctx: Ctx<KickRoutes.${pascal}Controller['list']>) {\n ctx.json({ message: '${pascal} list' })\n }\n\n @Post('/')\n async create(ctx: Ctx<KickRoutes.${pascal}Controller['create']>) {\n ctx.created({ message: '${pascal} created', data: ctx.body })\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\nimport { resolveOutDir } from '../utils/resolve-out-dir'\nimport type { ProjectPattern } from '../config'\n\ninterface GenerateDtoOptions {\n name: string\n outDir?: string\n moduleName?: string\n modulesDir?: string\n pattern?: ProjectPattern\n pluralize?: boolean\n}\n\nexport async function generateDto(options: GenerateDtoOptions): Promise<string[]> {\n const { name, moduleName, modulesDir, pattern } = options\n const outDir = resolveOutDir({\n type: 'dto',\n outDir: options.outDir,\n moduleName,\n modulesDir,\n defaultDir: 'src/dtos',\n pattern,\n shouldPluralize: options.pluralize ?? true,\n })\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const camel = toCamelCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.dto.ts`)\n await writeFileSafe(\n filePath,\n `import { z } from 'zod'\n\nexport const ${camel}Schema = z.object({\n // Define your schema fields here\n name: z.string().min(1).max(200),\n})\n\nexport type ${pascal}DTO = z.infer<typeof ${camel}Schema>\n`,\n )\n files.push(filePath)\n\n return files\n}\n","type ProjectTemplate = 'rest' | 'ddd' | 'cqrs' | 'minimal'\n\n/** Map of optional package names to their npm package identifiers */\nconst PACKAGE_DEPS: Record<string, string> = {\n swagger: '@forinda/kickjs-swagger',\n ws: '@forinda/kickjs-ws',\n queue: '@forinda/kickjs-queue',\n devtools: '@forinda/kickjs-devtools',\n}\n\n/**\n * Map of package name → semver range string (`^x.y.z`). Resolved\n * from `npm view <name> version` upstream so per-package independent\n * versioning is honoured at scaffold time. Every sibling\n * `@forinda/kickjs-*` package we might add to the new project must\n * appear here; missing keys throw during package.json generation\n * (loud failure beats silently shipping `^undefined`).\n */\nexport type SiblingVersions = Record<string, string>\n\nfunction take(versions: SiblingVersions, name: string): string {\n const v = versions[name]\n if (!v) {\n throw new Error(\n `generatePackageJson: missing resolved version for ${name}. ` +\n `Add it to SIBLING_PACKAGES in generators/project.ts.`,\n )\n }\n return v\n}\n\n/** Generate package.json with template-aware dependencies */\nexport function generatePackageJson(\n name: string,\n template: ProjectTemplate,\n versions: SiblingVersions,\n packages: string[] = [],\n): string {\n const baseDeps: Record<string, string> = {\n '@forinda/kickjs': take(versions, '@forinda/kickjs'),\n // `dotenv` is an optional peer of @forinda/kickjs — scaffolded apps\n // get it pre-installed so `.env` files Just Work. Apps that load\n // env from the shell or a secret manager can drop this safely.\n dotenv: '^17.3.1',\n express: '^5.1.0',\n 'reflect-metadata': '^0.2.2',\n zod: '^4.3.6',\n }\n\n // Add user-selected optional packages — each looked up against\n // the resolved version map so they're independently up-to-date.\n for (const pkg of packages) {\n const dep = PACKAGE_DEPS[pkg]\n if (dep && !baseDeps[dep]) {\n baseDeps[dep] = take(versions, dep)\n }\n }\n\n return JSON.stringify(\n {\n name,\n // Project starts at 0.0.0 — adopters bump as they ship. Tying\n // the project version to the CLI version (the previous\n // behaviour) made every scaffolded app `5.4.0` on day one,\n // which broke npm publishing for adopters trying their first\n // release.\n version: '0.0.0',\n type: 'module',\n scripts: {\n dev: 'vite',\n 'dev:debug': 'kick dev:debug',\n build: 'kick build',\n start: 'kick start',\n test: 'vitest run',\n 'test:watch': 'vitest',\n typecheck: 'tsc --noEmit',\n typegen: 'kick typegen',\n lint: 'eslint src/',\n format: 'prettier --write src/',\n },\n dependencies: baseDeps,\n devDependencies: {\n '@forinda/kickjs-cli': take(versions, '@forinda/kickjs-cli'),\n '@forinda/kickjs-vite': take(versions, '@forinda/kickjs-vite'),\n '@swc/core': '^1.15.21',\n '@types/express': '^5.0.6',\n '@types/node': '^25.0.0',\n 'unplugin-swc': '^1.5.9',\n vite: '^8.0.3',\n vitest: '^4.1.2',\n typescript: '^6.0.3',\n prettier: '^3.8.1',\n },\n },\n null,\n 2,\n )\n}\n\n/**\n * Generate vite.config.ts with the KickJS Vite plugin.\n *\n * The plugin handles:\n * - SSR environment setup for backend Node.js code\n * - Virtual module generation (virtual:kickjs/app)\n * - Module auto-discovery (scans *.module.ts files)\n * - HMR with selective container invalidation\n * - Express mounting via configureServer() post-hook\n * - httpServer piping to adapters (WsAdapter, Socket.IO, etc.)\n */\nexport function generateViteConfig(): string {\n return `import { defineConfig } from 'vite'\nimport { resolve } from 'node:path'\nimport swc from 'unplugin-swc'\nimport { kickjsVitePlugin, envWatchPlugin } from '@forinda/kickjs-vite'\n\nexport default defineConfig({\n oxc: false,\n plugins: [\n swc.vite(),\n kickjsVitePlugin({ entry: 'src/index.ts' }),\n // Watches .env files and triggers a full reload on change so the\n // dev server picks up env tweaks without a manual restart.\n envWatchPlugin(),\n ],\n resolve: {\n alias: {\n '@': resolve(__dirname, 'src'),\n },\n },\n build: {\n target: 'node20',\n ssr: true,\n outDir: 'dist',\n sourcemap: true,\n rollupOptions: {\n input: resolve(__dirname, 'src/index.ts'),\n output: { format: 'esm' },\n },\n },\n})\n`\n}\n\n/** Generate tsconfig.json with decorator support */\nexport function generateTsConfig(): string {\n return JSON.stringify(\n {\n compilerOptions: {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n lib: ['ES2022'],\n types: ['node', 'vite/client'],\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n sourceMap: true,\n declaration: true,\n experimentalDecorators: true,\n emitDecoratorMetadata: true,\n outDir: 'dist',\n // rootDir omitted so .kickjs/types/*.d.ts can sit outside src/\n paths: { '@/*': ['./src/*'] },\n },\n // .kickjs/types is generated by `kick typegen` and refreshed\n // automatically on `kick dev`. Including it here makes\n // `container.resolve()` and module discovery type-safe.\n // Both .d.ts and .ts are matched: registry/services/modules are\n // declarations, but routes.ts holds resolvable imports from your\n // controllers' Zod schemas (TS silently degrades inline `import('...')`\n // inside `.d.ts` files under `moduleResolution: 'bundler'`).\n include: ['src', '.kickjs/types/**/*.d.ts', '.kickjs/types/**/*.ts'],\n },\n null,\n 2,\n )\n}\n\n/** Generate .prettierrc with project formatting rules */\nexport function generatePrettierConfig(): string {\n return JSON.stringify(\n {\n semi: false,\n singleQuote: true,\n trailingComma: 'all',\n printWidth: 100,\n tabWidth: 2,\n },\n null,\n 2,\n )\n}\n\n/** Generate .editorconfig for consistent editor settings */\nexport function generateEditorConfig(): string {\n return `# https://editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.md]\ntrim_trailing_whitespace = false\n`\n}\n\n/** Generate .gitignore with common Node.js patterns */\nexport function generateGitIgnore(): string {\n return `node_modules/\ndist/\n.env\ncoverage/\n.DS_Store\n*.tsbuildinfo\n.kickjs/\n`\n}\n\n/** Generate .gitattributes for consistent line endings */\nexport function generateGitAttributes(): string {\n return `# Auto-detect text files and normalise line endings to LF\n* text=auto eol=lf\n\n# Explicitly mark generated / binary files\n*.png binary\n*.jpg binary\n*.jpeg binary\n*.gif binary\n*.ico binary\n*.woff binary\n*.woff2 binary\n*.ttf binary\n*.eot binary\n\n# Lock files — treat as generated\npnpm-lock.yaml -diff linguist-generated\nyarn.lock -diff linguist-generated\npackage-lock.json -diff linguist-generated\n`\n}\n\n/** Generate .env file with default environment variables */\nexport function generateEnv(): string {\n return `PORT=3000\nNODE_ENV=development\n`\n}\n\n/** Generate .env.example file as a template */\nexport function generateEnvExample(): string {\n return `PORT=3000\nNODE_ENV=development\n`\n}\n\n/** Generate vitest.config.ts for test configuration */\nexport function generateVitestConfig(): string {\n return `import { defineConfig } from 'vitest/config'\nimport swc from 'unplugin-swc'\n\nexport default defineConfig({\n plugins: [swc.vite()],\n test: {\n globals: true,\n environment: 'node',\n include: ['src/**/*.test.ts'],\n },\n})\n`\n}\n","import { join, dirname } from 'node:path'\nimport { execFileSync, execSync } from 'node:child_process'\nimport { readFileSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { writeFileSafe } from '../utils/fs'\nimport {\n generatePackageJson,\n generateViteConfig,\n generateTsConfig,\n generatePrettierConfig,\n generateEditorConfig,\n generateGitIgnore,\n generateGitAttributes,\n generateEnv,\n generateEnvExample,\n generateVitestConfig,\n} from './templates/project-config'\nimport {\n generateEntryFile,\n generateEnvFile,\n generateModulesIndex,\n generateKickConfig,\n generateHelloService,\n generateHelloController,\n generateHelloModule,\n} from './templates/project-app'\nimport { generateReadme } from './templates/project-docs'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst cliPkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'))\nconst CLI_VERSION_FALLBACK = `^${cliPkg.version}`\n\n/**\n * Sibling `@forinda/kickjs-*` packages whose versions are resolved\n * independently when scaffolding a new project. Each entry is queried\n * via `npm view <name> version`; failure falls back to the CLI's own\n * version (`CLI_VERSION_FALLBACK`).\n *\n * Per-package independent versioning landed with changesets — before\n * that, every sibling shipped in lockstep with the CLI so a single\n * pin was correct. Now `@forinda/kickjs@5.5.0` may pair with\n * `@forinda/kickjs-cli@5.4.2` and `@forinda/kickjs-swagger@5.3.1`;\n * pinning them all to the CLI's version under-installs adopters.\n */\nconst SIBLING_PACKAGES = [\n '@forinda/kickjs',\n '@forinda/kickjs-cli',\n '@forinda/kickjs-vite',\n '@forinda/kickjs-swagger',\n '@forinda/kickjs-ws',\n '@forinda/kickjs-queue',\n '@forinda/kickjs-devtools',\n '@forinda/kickjs-testing',\n] as const\n\n/**\n * Resolve the latest published version of every sibling package via\n * `npm view <name> version` (via execFileSync — no shell, no\n * injection vector). Each query has a short timeout; failures fall\n * back to the CLI's own version with a `^` prefix so the scaffold\n * stays usable offline.\n */\nasync function resolveSiblingVersions(): Promise<Record<string, string>> {\n const results = await Promise.all(\n SIBLING_PACKAGES.map(async (name) => {\n try {\n const out = execFileSync('npm', ['view', name, 'version'], {\n encoding: 'utf-8',\n timeout: 5000,\n stdio: ['ignore', 'pipe', 'ignore'],\n })\n .toString()\n .trim()\n if (out && /^\\d+\\.\\d+\\.\\d+/.test(out)) {\n return [name, `^${out}`] as const\n }\n } catch {\n // Network failure / package not yet published / npm\n // unavailable. Fall back to CLI version below.\n }\n return [name, CLI_VERSION_FALLBACK] as const\n }),\n )\n return Object.fromEntries(results)\n}\n\ntype ProjectTemplate = 'rest' | 'ddd' | 'cqrs' | 'minimal'\n\ninterface InitProjectOptions {\n name: string\n directory: string\n packageManager?: 'pnpm' | 'npm' | 'yarn' | 'bun'\n initGit?: boolean\n installDeps?: boolean\n template?: ProjectTemplate\n defaultRepo?: string\n packages?: string[]\n}\n\n/** Scaffold a new KickJS project */\nexport async function initProject(options: InitProjectOptions): Promise<void> {\n const {\n name,\n directory,\n packageManager = 'pnpm',\n template = 'rest',\n defaultRepo = 'inmemory',\n packages = [],\n } = options\n const dir = directory\n\n const log = (msg: string) => console.log(` ${msg}`)\n\n console.log(`\\n Creating KickJS project: ${name}\\n`)\n\n // Resolve published version of every sibling kickjs package in\n // parallel. Per-package independent versioning means\n // `@forinda/kickjs@5.5.0` may pair with `@forinda/kickjs-cli@5.4.2`\n // and `@forinda/kickjs-swagger@5.3.1`; pinning every dep to the\n // CLI's own version under-installs adopters whenever a sibling\n // bumps independently. `npm view` fallback keeps the scaffold\n // working offline.\n log('Resolving package versions...')\n const versions = await resolveSiblingVersions()\n\n // ── package.json — template-aware deps ────────────────────────────\n await writeFileSafe(\n join(dir, 'package.json'),\n generatePackageJson(name, template, versions, packages),\n )\n\n // ── vite.config.ts — enables HMR + SWC for decorators ──────────────\n await writeFileSafe(join(dir, 'vite.config.ts'), generateViteConfig())\n\n // ── tsconfig.json ───────────────────────────────────────────────────\n await writeFileSafe(join(dir, 'tsconfig.json'), generateTsConfig())\n\n // ── .prettierrc ─────────────────────────────────────────────────────\n await writeFileSafe(join(dir, '.prettierrc'), generatePrettierConfig())\n\n // ── .editorconfig ─────────────────────────────────────────────────────\n await writeFileSafe(join(dir, '.editorconfig'), generateEditorConfig())\n\n // ── .gitignore ──────────────────────────────────────────────────────\n await writeFileSafe(join(dir, '.gitignore'), generateGitIgnore())\n\n // ── .gitattributes ────────────────────────────────────────────────────\n await writeFileSafe(join(dir, '.gitattributes'), generateGitAttributes())\n\n // ── .env ────────────────────────────────────────────────────────────\n await writeFileSafe(join(dir, '.env'), generateEnv())\n\n await writeFileSafe(join(dir, '.env.example'), generateEnvExample())\n\n // ── src/config/index.ts — typed env schema (read by `kick typegen`) ─\n // Lives under `src/config/` so the framework's \"config\" concept has a\n // single, conventional home. Old projects with `src/env.ts` still\n // work — `detectEnvFile()` searches both locations.\n await writeFileSafe(join(dir, 'src/config/index.ts'), generateEnvFile())\n\n // ── src/index.ts — template-aware entry point ─────────────────────\n await writeFileSafe(\n join(dir, 'src/index.ts'),\n generateEntryFile(name, template, cliPkg.version, packages),\n )\n\n // ── src/modules/index.ts ────────────────────────────────────────────\n await writeFileSafe(join(dir, 'src/modules/index.ts'), generateModulesIndex())\n\n // ── src/modules/hello/ — sample module ─────────────────────────────\n await writeFileSafe(join(dir, 'src/modules/hello/hello.service.ts'), generateHelloService())\n await writeFileSafe(join(dir, 'src/modules/hello/hello.controller.ts'), generateHelloController())\n await writeFileSafe(join(dir, 'src/modules/hello/hello.module.ts'), generateHelloModule())\n\n // ── kick.config.ts — CLI configuration ─────────────────────────────\n await writeFileSafe(\n join(dir, 'kick.config.ts'),\n generateKickConfig(template, defaultRepo, packageManager),\n )\n\n // ── vitest.config.ts ────────────────────────────────────────────────\n await writeFileSafe(join(dir, 'vitest.config.ts'), generateVitestConfig())\n\n // ── README.md ────────────────────────────────────────────────────────\n await writeFileSafe(join(dir, 'README.md'), generateReadme(name, template, packageManager))\n\n // ── Agent docs ──────────────────────────────────────────────────────\n // Delegate to `generateAgentDocs()` so `kick new` emits the same\n // `.agents/` subfolder layout as `kick g agents -f`. Otherwise the\n // two paths drifted: kick new was writing the legacy flat layout\n // (root-level AGENTS.md + kickjs-skills.md) while kick g agents\n // emits the per-skill SKILL.md format under .agents/. `force: true`\n // because the project directory is fresh — no overwrite prompts\n // make sense during init.\n const { generateAgentDocs } = await import('./agent-docs')\n await generateAgentDocs({\n outDir: dir,\n name,\n pm: packageManager,\n template,\n only: 'all',\n force: true,\n })\n\n // ── Install Dependencies ────────────────────────────────────────────\n // Install BEFORE git init so the lockfile is included in the first commit.\n if (options.installDeps) {\n console.log(`\\n Installing dependencies with ${packageManager}...\\n`)\n try {\n execSync(`${packageManager} install`, { cwd: dir, stdio: 'inherit' })\n console.log('\\n Dependencies installed successfully!')\n } catch {\n console.log(`\\n Warning: ${packageManager} install failed. Run it manually.`)\n }\n }\n\n // ── Initial typegen ────────────────────────────────────────────────\n // Run typegen once so the freshly-scaffolded HelloController's\n // `Ctx<KickRoutes.HelloController['index']>` references resolve in\n // the user's editor immediately. Failures are non-fatal.\n try {\n const { runTypegen } = await import('../typegen')\n await runTypegen({ cwd: dir, allowDuplicates: true, silent: true })\n } catch {\n // First-run typegen errors are non-fatal — `kick dev` will retry.\n }\n\n // ── Git Init ─────────────────────────────────────────────────────────\n // Runs after install + typegen so lockfile and generated types are\n // included in the initial commit.\n if (options.initGit) {\n try {\n execSync('git init', { cwd: dir, stdio: 'pipe' })\n execSync('git branch -M main', { cwd: dir, stdio: 'pipe' })\n execSync('git add -A', { cwd: dir, stdio: 'pipe' })\n execSync('git commit -m \"chore: initial commit from kick new\"', {\n cwd: dir,\n stdio: 'pipe',\n })\n log('Git repository initialized')\n } catch {\n log('Warning: git init failed (git may not be installed)')\n }\n }\n\n console.log('\\n Project scaffolded successfully!')\n console.log()\n\n const needsCd = dir !== process.cwd()\n log('Next steps:')\n if (needsCd) log(` cd ${name}`)\n if (!options.installDeps) log(` ${packageManager} install`)\n\n const genHint: Record<string, string> = {\n rest: 'kick g module user',\n ddd: 'kick g module user --repo drizzle',\n cqrs: 'kick g module user --pattern cqrs',\n minimal: '# add your routes to src/index.ts',\n }\n log(` ${genHint[template] ?? genHint.rest}`)\n log(' kick dev')\n log('')\n log('Commands:')\n log(' kick dev Start dev server with Vite HMR')\n log(' kick build Production build via Vite')\n log(' kick start Run production build')\n log('')\n log('Generators:')\n log(' kick g module <name> Full DDD module (controller, DTOs, use-cases, repo)')\n log(' kick g scaffold <n> <f..> CRUD module from field definitions')\n log(' kick g controller <name> Standalone controller')\n log(' kick g service <name> @Service() class')\n log(' kick g middleware <name> Express middleware')\n log(' kick g guard <name> Route guard (auth, roles, etc.)')\n log(' kick g adapter <name> AppAdapter with lifecycle hooks')\n log(' kick g dto <name> Zod DTO schema')\n if (template === 'cqrs') log(' kick g job <name> Queue job processor')\n log(' kick g config Generate kick.config.ts')\n log('')\n log('Add packages:')\n log(' kick add <pkg> Install a KickJS package + peers')\n log(' kick add --list Show all available packages')\n log('')\n log('Available: auth, swagger, drizzle, prisma, ws, queue, devtools, mcp, testing')\n log('')\n}\n","/**\n * Plugin generator API per architecture.md §21.2.3 — lets first-party\n * AND third-party packages ship `kick g <name>` scaffolders the same\n * way the framework's built-in generators do.\n *\n * Plugins declare a discovery file in their `package.json`:\n *\n * ```json\n * {\n * \"name\": \"@my-org/kickjs-cqrs\",\n * \"kickjs\": { \"generators\": \"./dist/generators.js\" }\n * }\n * ```\n *\n * The discovery file exports a typed manifest:\n *\n * ```ts\n * import { defineGenerator } from '@forinda/kickjs-cli'\n *\n * export default [\n * defineGenerator({\n * name: 'command',\n * description: 'Generate a CQRS command + handler',\n * args: [{ name: 'name', required: true }],\n * files: (ctx) => [\n * {\n * path: `src/modules/${ctx.kebab}/commands/create-${ctx.kebab}.command.ts`,\n * content: `// generated command for ${ctx.pascal}`,\n * },\n * ],\n * }),\n * ]\n * ```\n *\n * `kick g command Order` then dispatches against the registered\n * generator and writes the returned files relative to `cwd`.\n */\n\n/**\n * Resolved naming variants + project context handed to a generator's\n * `files()` factory. Keys mirror `TemplateContext` so first-party\n * generators that rely on the same shape can be migrated to plugin\n * generators without rewriting their templates.\n */\nexport interface GeneratorContext {\n /** Raw name passed on the command line (`kick g drizzle-typegen UserPost` → `'UserPost'`). */\n name: string\n /** PascalCase form (`UserPost`). */\n pascal: string\n /** kebab-case form (`user-post`). */\n kebab: string\n /** camelCase form (`userPost`). */\n camel: string\n /** snake_case form (`user_post`). */\n snake: string\n /** Pluralized PascalCase (`UserPosts`) — present when the project enables `pluralize`. */\n pluralPascal?: string\n /** Pluralized kebab-case (`user-posts`). */\n pluralKebab?: string\n /** Pluralized camelCase (`userPosts`). */\n pluralCamel?: string\n /** Modules directory from `kick.config.ts` (default `'src/modules'`). */\n modulesDir: string\n /**\n * Working directory the CLI was invoked from. May be a nested subdirectory\n * if the adopter ran `kick g ...` from `src/modules/foo/` — for \"write\n * files relative to the project\" use {@link projectRoot} instead.\n */\n cwd: string\n /**\n * Resolved project root — the nearest ancestor directory of {@link cwd}\n * containing `kick.config.{ts,js,mjs,json}` (or `package.json` as a\n * fallback). Always set; falls back to `cwd` when no marker is found.\n *\n * Use this when writing files that must land at a known location\n * regardless of where the adopter typed the command. `kick g ...` from\n * inside `src/modules/foo/` will still see `projectRoot` pointing at\n * the directory that owns `kick.config.ts`.\n */\n projectRoot: string\n /** Positional arguments passed AFTER the name (e.g. `kick g command Order extra1 extra2` → `['extra1', 'extra2']`). */\n args: string[]\n /** Flag values from the command line — booleans for switches, strings for `--key value`. */\n flags: Record<string, string | boolean>\n}\n\n/** A single output file the generator wants written. */\nexport interface GeneratorFile {\n /**\n * Output path. Relative paths resolve against `ctx.cwd`; absolute\n * paths are used as-is. Parent directories are created automatically.\n */\n path: string\n /** File contents written verbatim (UTF-8). */\n content: string\n}\n\n/** CLI argument descriptor surfaced in `kick g --list` help. */\nexport interface GeneratorArg {\n name: string\n required?: boolean\n description?: string\n}\n\n/** CLI flag descriptor — boolean unless `takesValue: true`. */\nexport interface GeneratorFlag {\n name: string\n alias?: string\n description?: string\n takesValue?: boolean\n}\n\n/**\n * Spec returned by {@link defineGenerator}. Plugin discovery files\n * export `GeneratorSpec[]` as their default export.\n */\nexport interface GeneratorSpec {\n /**\n * Dispatch name — `kick g <name>` looks for an exact match against\n * this string after the built-in generators are checked.\n */\n name: string\n /** Description shown in `kick g --list` and `--help`. */\n description: string\n /** Optional argument descriptors — informational, surfaced in help output. */\n args?: readonly GeneratorArg[]\n /** Optional flag descriptors — informational, surfaced in help output. */\n flags?: readonly GeneratorFlag[]\n /** Build the output files for one invocation. May return a Promise. */\n files(ctx: GeneratorContext): GeneratorFile[] | Promise<GeneratorFile[]>\n}\n\n/**\n * Identity factory — returns the spec verbatim. Exists for type\n * inference and forward-compatibility (future fields can be added with\n * defaults).\n *\n * @example\n * ```ts\n * import { defineGenerator } from '@forinda/kickjs-cli'\n *\n * export default [\n * defineGenerator({\n * name: 'command',\n * description: 'Generate a CQRS command + handler',\n * files: (ctx) => [\n * {\n * path: `src/modules/${ctx.kebab}/commands/${ctx.kebab}.command.ts`,\n * content: `// command for ${ctx.pascal}`,\n * },\n * ],\n * }),\n * ]\n * ```\n */\nexport function defineGenerator(spec: GeneratorSpec): GeneratorSpec {\n return spec\n}\n","import { pathToFileURL } from 'node:url'\nimport { resolve } from 'node:path'\nimport { toPascalCase, toCamelCase, toKebabCase, pluralize } from '../utils/naming'\nimport { findProjectRoot } from '../utils/project-root'\nimport type { GeneratorContext } from './define'\n\n/** Convert any string to snake_case (`UserPost` / `user-post` → `user_post`). */\nfunction toSnakeCase(name: string): string {\n return toKebabCase(name).replace(/-/g, '_')\n}\n\n/**\n * Build a {@link GeneratorContext} from the raw name + invocation\n * arguments. Centralises the case-transformation logic so every plugin\n * generator sees the same shape regardless of how the name was typed\n * on the command line (`Post` vs `post` vs `user_post`).\n */\nexport function buildGeneratorContext(input: {\n name: string\n args?: string[]\n flags?: Record<string, string | boolean>\n modulesDir?: string\n cwd?: string\n /**\n * Resolved project root. When omitted, derived from {@link cwd} via\n * `findProjectRoot()` so adopter-facing call sites stay zero-config.\n * Callers that already know the root (e.g. `cli.ts` after one upfront\n * resolution) should pass it through to avoid redundant fs walks.\n */\n projectRoot?: string\n pluralize?: boolean\n}): GeneratorContext {\n const cwd = input.cwd ?? process.cwd()\n const projectRoot = input.projectRoot ?? findProjectRoot(cwd)\n const usePlural = input.pluralize ?? true\n\n const pascal = toPascalCase(input.name)\n const camel = toCamelCase(input.name)\n const kebab = toKebabCase(input.name)\n const snake = toSnakeCase(input.name)\n\n const ctx: GeneratorContext = {\n name: input.name,\n pascal,\n camel,\n kebab,\n snake,\n modulesDir: input.modulesDir ?? 'src/modules',\n cwd,\n projectRoot,\n args: input.args ?? [],\n flags: input.flags ?? {},\n }\n\n if (usePlural) {\n const pluralKebab = pluralize(kebab)\n ctx.pluralKebab = pluralKebab\n ctx.pluralPascal = toPascalCase(pluralKebab)\n ctx.pluralCamel = toCamelCase(pluralKebab)\n }\n\n return ctx\n}\n\n/** Resolve a generator output path against the context's cwd. */\nexport function resolveGeneratorPath(ctx: GeneratorContext, path: string): string {\n return resolve(ctx.cwd, path)\n}\n\n/**\n * Dynamic-import a generator manifest file. Wraps `pathToFileURL` so\n * callers don't have to think about Windows/Unix path quirks.\n */\nexport async function importManifest(absPath: string): Promise<unknown> {\n return import(pathToFileURL(absPath).href)\n}\n","import { readFile } from 'node:fs/promises'\nimport { resolve, dirname } from 'node:path'\nimport { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport type { GeneratorSpec } from './define'\nimport { importManifest } from './context'\n\n/**\n * One row in the discovered registry. `source` is the npm package name\n * the generator came from — surfaced in error messages so adopters can\n * see which plugin owns a given generator.\n */\nexport interface DiscoveredGenerator {\n source: string\n spec: GeneratorSpec\n}\n\n/**\n * Plugin discovery result, kept around even when no generators were\n * registered so callers can distinguish \"no plugins installed\" from\n * \"no plugins matched the requested name.\"\n */\nexport interface DiscoveryResult {\n generators: DiscoveredGenerator[]\n /** Packages whose `kickjs.generators` was loaded successfully. */\n loaded: string[]\n /**\n * Packages we tried to load but failed — typically a missing entry\n * file or a default export that wasn't an array of GeneratorSpec.\n */\n failed: Array<{ source: string; reason: string }>\n}\n\n/** Shape of `package.json` we care about during discovery. */\ninterface PluginPackage {\n name?: string\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n peerDependencies?: Record<string, string>\n kickjs?: {\n generators?: string\n }\n keywords?: string[]\n}\n\n/**\n * Discover generator manifests shipped by every kickjs plugin in the\n * project's direct deps. Spec rationale: walking the\n * `node_modules/@scope/kickjs-name/` tree is one option, but reading\n * the project's own `package.json` and resolving each dep through\n * Node's module resolver gives:\n *\n * 1. Predictable scoping — only deps the project actually declared\n * get scanned, no surprises from transitive packages\n * 2. pnpm `.pnpm` store compatibility — `createRequire().resolve()`\n * handles the symlinked layout correctly\n * 3. Clear error attribution — the source package name is always\n * known before the import happens\n *\n * The walk is shallow (direct deps only). Transitive plugins that want\n * to expose generators must be re-exported by a direct dep.\n *\n * Caches per-cwd inside one CLI invocation so a single `kick g` call\n * does the disk + import work exactly once even when multiple\n * generators dispatch through the same registry.\n */\nconst cache = new Map<string, Promise<DiscoveryResult>>()\n\nexport async function discoverPluginGenerators(cwd: string): Promise<DiscoveryResult> {\n const cached = cache.get(cwd)\n if (cached) return cached\n const promise = doDiscover(cwd)\n cache.set(cwd, promise)\n return promise\n}\n\n/** Reset the cache — used by tests so repeated runs see fresh fixtures. */\nexport function resetGeneratorDiscoveryCache(): void {\n cache.clear()\n}\n\nasync function doDiscover(cwd: string): Promise<DiscoveryResult> {\n const projectPkgPath = resolve(cwd, 'package.json')\n if (!existsSync(projectPkgPath)) {\n return { generators: [], loaded: [], failed: [] }\n }\n\n const projectPkg = JSON.parse(await readFile(projectPkgPath, 'utf-8')) as PluginPackage\n const depNames = collectDepNames(projectPkg)\n\n const require = createRequire(resolve(cwd, 'package.json'))\n\n const generators: DiscoveredGenerator[] = []\n const loaded: string[] = []\n const failed: Array<{ source: string; reason: string }> = []\n\n for (const depName of depNames) {\n let depPkgPath: string\n try {\n depPkgPath = require.resolve(`${depName}/package.json`)\n } catch {\n // Dep declared but not installed (or doesn't expose package.json\n // via its export map). Skip silently — this is `kick g`, not\n // `pnpm install`.\n continue\n }\n\n let depPkg: PluginPackage\n try {\n depPkg = JSON.parse(await readFile(depPkgPath, 'utf-8')) as PluginPackage\n } catch (err) {\n failed.push({ source: depName, reason: `failed to parse package.json: ${err}` })\n continue\n }\n\n if (!depPkg.kickjs?.generators) continue\n\n const entryRel = depPkg.kickjs.generators\n const entryAbs = resolve(dirname(depPkgPath), entryRel)\n if (!existsSync(entryAbs)) {\n failed.push({\n source: depName,\n reason: `kickjs.generators points to missing file: ${entryRel}`,\n })\n continue\n }\n\n let mod: unknown\n try {\n mod = await importManifest(entryAbs)\n } catch (err) {\n failed.push({ source: depName, reason: `failed to import manifest: ${err}` })\n continue\n }\n\n const manifest = (mod as { default?: unknown }).default\n if (!Array.isArray(manifest)) {\n failed.push({\n source: depName,\n reason: `manifest's default export is not an array of GeneratorSpec`,\n })\n continue\n }\n\n for (const entry of manifest) {\n if (!isGeneratorSpec(entry)) {\n failed.push({\n source: depName,\n reason: `manifest entry is not a valid GeneratorSpec (missing name/files)`,\n })\n continue\n }\n generators.push({ source: depName, spec: entry })\n }\n loaded.push(depName)\n }\n\n return { generators, loaded, failed }\n}\n\nfunction collectDepNames(pkg: PluginPackage): string[] {\n const set = new Set<string>()\n for (const block of [pkg.dependencies, pkg.devDependencies, pkg.peerDependencies]) {\n if (!block) continue\n for (const name of Object.keys(block)) set.add(name)\n }\n return Array.from(set)\n}\n\nfunction isGeneratorSpec(entry: unknown): entry is GeneratorSpec {\n if (!entry || typeof entry !== 'object') return false\n const e = entry as Record<string, unknown>\n return typeof e.name === 'string' && typeof e.files === 'function'\n}\n","import { writeFileSafe } from '../utils/fs'\nimport { buildGeneratorContext, resolveGeneratorPath } from './context'\nimport {\n discoverPluginGenerators,\n type DiscoveredGenerator,\n type DiscoveryResult,\n} from './discover'\nimport type { GeneratorSpec } from './define'\n\nexport interface DispatchInput {\n /** The generator name typed on the CLI (`kick g command Order` → `'command'`). */\n generatorName: string\n /** The first positional argument after the generator name. */\n itemName: string\n /** Remaining positional arguments. */\n args?: string[]\n /** Parsed flag map (booleans + values). */\n flags?: Record<string, string | boolean>\n /** Project context — usually `process.cwd()`. */\n cwd?: string\n /**\n * Resolved project root. When omitted, `buildGeneratorContext` derives\n * it from `cwd` via `findProjectRoot()`. Pass through when the caller\n * has already resolved it (the CLI entry point does this once at\n * startup) to avoid re-walking the filesystem on every generator call.\n */\n projectRoot?: string\n /** Modules dir from `kick.config.ts`. */\n modulesDir?: string\n /** Whether the project enables auto-pluralization. */\n pluralize?: boolean\n}\n\nexport interface DispatchResult {\n /** Absolute paths of the files that were written. */\n files: string[]\n /** Which plugin owns the generator we ran. */\n source: string\n}\n\n/**\n * Look up a plugin generator by name and run it. Returns `null` when\n * no plugin generator matches — callers can then fall through to the\n * built-in dispatch (module / scaffold / etc.).\n *\n * Resolution order:\n * 1. `additional` — generators sourced from `KickCliPlugin.generators`\n * via `kick.config.ts > plugins[]`. Authoritative; the canonical\n * path going forward.\n * 2. `package.json > kickjs.generators` discovery — first-match-wins\n * in dependency declaration order. Deprecated path retained for\n * packages that haven't migrated yet.\n *\n * Adopters with conflicts should rename the generator on their side or\n * pin one of the plugins to a different version.\n */\nexport async function tryDispatchPluginGenerator(\n input: DispatchInput,\n additional: readonly DiscoveredGenerator[] = [],\n): Promise<DispatchResult | null> {\n const cwd = input.cwd ?? process.cwd()\n\n const fromConfig = additional.find((g) => g.spec.name === input.generatorName)\n if (fromConfig) {\n return runGenerator(fromConfig.spec, fromConfig.source, input, cwd)\n }\n\n const discovery = await discoverPluginGenerators(cwd)\n const match = findGenerator(discovery, input.generatorName)\n if (!match) return null\n\n return runGenerator(match.spec, match.source, input, cwd)\n}\n\n/**\n * Public helper for `kick g --list` — returns every plugin generator\n * the CLI knows about, merging config-supplied entries on top of the\n * package.json discovery result. Config entries always come first.\n */\nexport async function listPluginGenerators(\n cwd: string,\n additional: readonly DiscoveredGenerator[] = [],\n): Promise<DiscoveryResult> {\n const discovered = await discoverPluginGenerators(cwd)\n const configNames = new Set(additional.map((g) => g.spec.name))\n const filteredDiscovered = discovered.generators.filter((g) => !configNames.has(g.spec.name))\n return {\n generators: [...additional, ...filteredDiscovered],\n loaded: discovered.loaded,\n failed: discovered.failed,\n }\n}\n\nfunction findGenerator(discovery: DiscoveryResult, name: string): DiscoveredGenerator | undefined {\n return discovery.generators.find((g) => g.spec.name === name)\n}\n\n/**\n * Invoke a {@link GeneratorSpec.files} factory with a fully-populated\n * {@link GeneratorContext}, write every returned file under the context's\n * cwd, and return the absolute paths along with the source plugin name.\n *\n * Threads `input.projectRoot` through to `buildGeneratorContext` so\n * callers that already resolved the project root (the CLI entry does\n * this once at startup) avoid a redundant filesystem walk. When omitted,\n * `buildGeneratorContext` derives `projectRoot` from `cwd` via\n * `findProjectRoot()` — keeping ad-hoc callers zero-config.\n */\nasync function runGenerator(\n spec: GeneratorSpec,\n source: string,\n input: DispatchInput,\n cwd: string,\n): Promise<DispatchResult> {\n const ctx = buildGeneratorContext({\n name: input.itemName,\n args: input.args,\n flags: input.flags,\n modulesDir: input.modulesDir,\n pluralize: input.pluralize,\n cwd,\n projectRoot: input.projectRoot,\n })\n\n const files = await spec.files(ctx)\n const written: string[] = []\n\n for (const file of files) {\n const absPath = resolveGeneratorPath(ctx, file.path)\n await writeFileSafe(absPath, file.content)\n written.push(absPath)\n }\n\n return { files: written, source }\n}\n","import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'\nimport { dirname, join, relative, resolve } from 'node:path'\nimport type { Command } from 'commander'\nimport { loadKickConfig, type KickConfig } from '../config'\nimport { colors } from '../utils/colors'\nimport { intro, outro, log } from '../utils/prompts'\n\n/**\n * `kick doctor` — pre-flight checks for a KickJS project's dev\n * environment. Detects common misconfigs before they bite, with an\n * actionable fix hint for each problem.\n *\n * Sibling command to `kick check --deploy`, which scans for production\n * readiness (JWT, CORS, helmet, etc.). Doctor is the dev-setup\n * counterpart — \"is my environment correctly wired?\"\n *\n * Extending: adopters can ship their own checks by exporting a\n * `doctor.checks` array from `kick.config.ts`. Each {@link DoctorCheck}\n * receives the same {@link DoctorContext} the built-ins use and\n * returns one or more {@link DoctorResult}s. The framework stays\n * ORM-agnostic — Prisma / Drizzle / Mongoose-specific checks belong\n * in their respective adapters or in adopter config, not in core.\n */\n\n// ── Public types ──────────────────────────────────────────────────────\n\nexport interface DoctorContext {\n cwd: string\n pkg: any | null\n tsconfig: any | null\n}\n\nexport interface DoctorResult {\n /** Short label for the check (printed first). */\n name: string\n status: 'pass' | 'warn' | 'fail'\n /** Optional extra context after the label (e.g. resolved version). */\n message?: string\n /** Multi-line actionable fix shown when status is `warn` or `fail`. */\n fix?: string\n}\n\nexport type DoctorCheck = (\n ctx: DoctorContext,\n) => DoctorResult | DoctorResult[] | null | Promise<DoctorResult | DoctorResult[] | null>\n\n/**\n * Shape of a doctor extension — the `doctor` block on `KickConfig`,\n * also the publishable unit that plugins and shared modules use to\n * ship a bundle of related checks.\n */\nexport interface DoctorExtension {\n /** Extra checks merged after the built-ins. */\n checks?: DoctorCheck[]\n}\n\n/**\n * Identity helper for adopters / plugins authoring a doctor extension.\n *\n * Provides type inference + autocomplete on the `checks` array without\n * requiring an explicit `: DoctorExtension` annotation. Mirrors the\n * `defineConfig` pattern.\n *\n * @example\n * ```ts\n * // doctor-checks/prisma.ts (shared across projects, or shipped as a\n * // standalone package)\n * import { defineDoctorExtension } from '@forinda/kickjs-cli'\n * import { existsSync } from 'node:fs'\n * import { join } from 'node:path'\n *\n * export const prismaDoctor = defineDoctorExtension({\n * checks: [\n * (ctx) => {\n * if (!existsSync(join(ctx.cwd, 'prisma/schema.prisma'))) return null\n * const generated = join(ctx.cwd, 'node_modules/@prisma/client/default.js')\n * return existsSync(generated)\n * ? { name: 'Prisma client generated', status: 'pass' }\n * : { name: 'Prisma client generated', status: 'fail', fix: 'pnpm exec prisma generate' }\n * },\n * ],\n * })\n *\n * // kick.config.ts\n * import { defineConfig } from '@forinda/kickjs-cli'\n * import { prismaDoctor } from './doctor-checks/prisma'\n *\n * export default defineConfig({ doctor: prismaDoctor })\n * ```\n */\nexport function defineDoctorExtension(ext: DoctorExtension): DoctorExtension {\n return ext\n}\n\n/**\n * Identity helper for a single doctor check. Pairs with\n * `defineDoctorExtension` when assembling an extension from separate\n * per-check files, and gives the same type-inference win for one-offs.\n *\n * @example\n * ```ts\n * import { defineDoctorCheck } from '@forinda/kickjs-cli'\n *\n * export const checkJwtSecretLength = defineDoctorCheck((ctx) => {\n * const v = process.env.JWT_SECRET\n * if (!v || v.length < 32) {\n * return {\n * name: 'JWT_SECRET ≥ 32 chars',\n * status: 'warn',\n * fix: 'Generate a strong secret: openssl rand -hex 32',\n * }\n * }\n * return { name: 'JWT_SECRET ≥ 32 chars', status: 'pass' }\n * })\n * ```\n */\nexport function defineDoctorCheck(check: DoctorCheck): DoctorCheck {\n return check\n}\n\n// ── File helpers ──────────────────────────────────────────────────────\n\nfunction safeReadJson(filepath: string): any | null {\n try {\n return JSON.parse(readFileSync(filepath, 'utf-8'))\n } catch {\n return null\n }\n}\n\nfunction safeRead(filepath: string): string | null {\n try {\n return readFileSync(filepath, 'utf-8')\n } catch {\n return null\n }\n}\n\n/**\n * Read tsconfig.json with `extends` followed one level. Most adopters\n * use a single config; for those that extend kickjs-cli's preset, we\n * pick up the inherited `compilerOptions`. Avoids pulling in `tsc` just\n * to resolve the chain.\n */\nfunction loadTsConfig(cwd: string): any | null {\n const tsconfigPath = join(cwd, 'tsconfig.json')\n const raw = safeRead(tsconfigPath)\n if (!raw) return null\n // tsconfig files often contain comments; strip them before parsing.\n const stripped = raw.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '').replace(/\\/\\/.*$/gm, '')\n let cfg: any\n try {\n cfg = JSON.parse(stripped)\n } catch {\n return null\n }\n if (typeof cfg?.extends === 'string') {\n const extendsPath = resolveExtends(cwd, cfg.extends)\n if (extendsPath) {\n const parent = safeReadJson(extendsPath) ?? {}\n cfg.compilerOptions = {\n ...parent.compilerOptions,\n ...cfg.compilerOptions,\n }\n }\n }\n return cfg\n}\n\nfunction resolveExtends(cwd: string, ext: string): string | null {\n if (ext.startsWith('.')) {\n const resolved = resolve(cwd, ext)\n return existsSync(resolved) ? resolved : null\n }\n const mod = join(cwd, 'node_modules', ext)\n if (existsSync(mod)) return mod\n return null\n}\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n// ── Individual checks ─────────────────────────────────────────────────\n\nconst MIN_NODE_MAJOR = 20\n\nexport function checkNodeVersion(): DoctorResult {\n const v = process.version\n const major = Number.parseInt(v.replace(/^v/, '').split('.')[0]!, 10)\n if (Number.isNaN(major) || major < MIN_NODE_MAJOR) {\n return {\n name: 'Node version',\n status: 'fail',\n message: v,\n fix: `KickJS requires Node ${MIN_NODE_MAJOR} or newer.\\nInstall a supported version via nvm / fnm / volta.`,\n }\n }\n return { name: 'Node version', status: 'pass', message: v }\n}\n\nexport function checkKickJsInstalled(ctx: DoctorContext): DoctorResult {\n if (!ctx.pkg) {\n return { name: '@forinda/kickjs installed', status: 'warn', message: 'no package.json' }\n }\n const deps = {\n ...ctx.pkg.dependencies,\n ...ctx.pkg.peerDependencies,\n }\n if (!deps['@forinda/kickjs']) {\n return {\n name: '@forinda/kickjs installed',\n status: 'fail',\n fix: 'This directory does not look like a KickJS project — `@forinda/kickjs` is not in your package.json. Run `kick doctor` from the project root, or scaffold a fresh project with `kick new <name>`.',\n }\n }\n return { name: '@forinda/kickjs installed', status: 'pass', message: deps['@forinda/kickjs'] }\n}\n\nexport function checkExpressInstalled(ctx: DoctorContext): DoctorResult | null {\n if (!ctx.pkg) return null\n const deps = {\n ...ctx.pkg.dependencies,\n ...ctx.pkg.peerDependencies,\n }\n if (deps['@forinda/kickjs'] && !deps.express) {\n return {\n name: 'express installed',\n status: 'fail',\n fix: '`@forinda/kickjs` declares `express` as a required peer dependency, but your package.json does not include it. Install: pnpm add express',\n }\n }\n return deps.express ? { name: 'express installed', status: 'pass', message: deps.express } : null\n}\n\nexport function checkReflectMetadata(ctx: DoctorContext): DoctorResult {\n if (!ctx.pkg)\n return { name: 'reflect-metadata installed', status: 'warn', message: 'no package.json' }\n const deps = {\n ...ctx.pkg.dependencies,\n ...ctx.pkg.peerDependencies,\n ...ctx.pkg.devDependencies,\n }\n if (!deps['reflect-metadata']) {\n return {\n name: 'reflect-metadata installed',\n status: 'fail',\n fix: `KickJS decorators require the reflect-metadata polyfill.\\nInstall it: pnpm add reflect-metadata\\nThen import it at the top of src/index.ts:\\n\\n import 'reflect-metadata'\\n // ... rest of bootstrap`,\n }\n }\n return { name: 'reflect-metadata installed', status: 'pass', message: deps['reflect-metadata'] }\n}\n\nexport function checkDecoratorTsConfig(ctx: DoctorContext): DoctorResult[] {\n if (!ctx.tsconfig) {\n return [\n {\n name: 'tsconfig.json present',\n status: 'fail',\n fix: 'Create a tsconfig.json with `experimentalDecorators: true` and `emitDecoratorMetadata: true`. `kick new` scaffolds one automatically.',\n },\n ]\n }\n const co = ctx.tsconfig.compilerOptions ?? {}\n const results: DoctorResult[] = []\n results.push(\n co.experimentalDecorators === true\n ? { name: 'tsconfig: experimentalDecorators', status: 'pass' }\n : {\n name: 'tsconfig: experimentalDecorators',\n status: 'fail',\n fix: 'Add `\"experimentalDecorators\": true` to compilerOptions in tsconfig.json. Without it, @Service / @Controller / @Get etc. don\\'t register any metadata at compile time.',\n },\n )\n results.push(\n co.emitDecoratorMetadata === true\n ? { name: 'tsconfig: emitDecoratorMetadata', status: 'pass' }\n : {\n name: 'tsconfig: emitDecoratorMetadata',\n status: 'fail',\n fix: 'Add `\"emitDecoratorMetadata\": true` to compilerOptions in tsconfig.json. The DI container uses this metadata for constructor-parameter injection.',\n },\n )\n return results\n}\n\n/**\n * The canonical \"you forgot to wire env\" footgun: an env-init file\n * (e.g. `src/env.ts`, `src/config/env.ts`, `src/config/index.ts`)\n * calls `loadEnv(envSchema)` as a side effect, but the app entry\n * (`src/index.ts` / `src/main.ts`) doesn't import it. Result:\n * `ConfigService.get('X')` returns undefined while `@Value('X')`\n * works via process.env fallback, so adopters get half-broken config\n * without seeing an error.\n *\n * Detection walks the common env-file locations, identifies the ones\n * that actually call `loadEnv(`, then verifies the app entry imports\n * one of them (via relative path or `@/` alias) BEFORE `bootstrap(`.\n */\nexport function checkEnvWiring(ctx: DoctorContext): DoctorResult | null {\n const envCandidates = [\n 'src/env.ts',\n 'src/env/index.ts',\n 'src/config/env.ts',\n 'src/config/index.ts',\n ]\n const envFiles = envCandidates\n .map((p) => join(ctx.cwd, p))\n .filter((p) => existsSync(p))\n .filter((p) => /\\bloadEnv\\s*\\(/.test(safeRead(p) ?? ''))\n\n if (envFiles.length === 0) return null\n\n const indexPath = ['src/index.ts', 'src/main.ts']\n .map((p) => join(ctx.cwd, p))\n .find((p) => existsSync(p))\n if (!indexPath) {\n return {\n name: 'env wiring',\n status: 'warn',\n message: 'env-init file exists but no src/index.ts or src/main.ts found',\n }\n }\n const indexContent = safeRead(indexPath) ?? ''\n const indexDir = dirname(indexPath)\n\n // Build the set of import specifiers that would wire any of these\n // env files — relative paths (`./env`, `./config/env`, …) and the\n // `@/` alias variant (`@/env`, `@/config/env`, …). For each file we\n // accept both the with-`/index` suffix and the bare-directory form,\n // since both resolve to the same module under TypeScript.\n const importSpecs: string[] = []\n for (const envFile of envFiles) {\n const relPath = relative(indexDir, envFile).replace(/\\\\/g, '/').replace(/\\.ts$/, '')\n const relImport = relPath.startsWith('.') ? relPath : './' + relPath\n const relImportNoIndex = relImport.replace(/\\/index$/, '')\n importSpecs.push(relImport, relImportNoIndex)\n\n // `@/` is the standard alias the scaffold and most adopters use;\n // it points at `src/`. Derive the @/-prefixed path from the file's\n // location under src/.\n const srcRel = envFile.replace(/\\\\/g, '/').match(/\\/src\\/(.+?)(?:\\.ts)?$/)\n if (srcRel) {\n const aliasImport = '@/' + srcRel[1]\n const aliasNoIndex = aliasImport.replace(/\\/index$/, '')\n importSpecs.push(aliasImport, aliasNoIndex)\n }\n }\n\n let earliestImportIdx = -1\n for (const spec of new Set(importSpecs)) {\n const re = new RegExp(`^import\\\\s+(?:.*?from\\\\s+)?['\"]${escapeRegExp(spec)}['\"]`, 'm')\n const m = indexContent.match(re)\n if (m && m.index !== undefined) {\n if (earliestImportIdx === -1 || m.index < earliestImportIdx) {\n earliestImportIdx = m.index\n }\n }\n }\n\n const bootstrapIdx = indexContent.search(/\\bbootstrap\\s*\\(/)\n const sample = envFiles.map((p) => relative(ctx.cwd, p).replace(/\\\\/g, '/')).join(', ')\n\n if (earliestImportIdx === -1) {\n return {\n name: 'env wiring',\n status: 'fail',\n message: sample,\n fix: `An env-init file (${sample}) calls \\`loadEnv(...)\\` but \\`${relative(ctx.cwd, indexPath).replace(/\\\\/g, '/')}\\` doesn't import it.\\nWithout this, ConfigService.get('X') returns undefined while @Value('X') works via process.env fallback — a half-broken config you won't notice until something is missing.\\n\\nFix: add a side-effect import at the top of ${relative(ctx.cwd, indexPath).replace(/\\\\/g, '/')} (above bootstrap()), pointing at one of the detected files. For example:\\n\\n import './env'\\n // or\\n import './config'\\n // or, with the @/ alias:\\n import '@/config/env'`,\n }\n }\n if (bootstrapIdx !== -1 && earliestImportIdx > bootstrapIdx) {\n return {\n name: 'env wiring',\n status: 'warn',\n message: 'env-init imported AFTER bootstrap() — should be before',\n fix: `Move the env import above the bootstrap() call so the schema runs before any service reads from ConfigService.`,\n }\n }\n return { name: 'env wiring', status: 'pass' }\n}\n\n/**\n * Find the newest file mtime under a directory tree, recursively.\n * Returns `0` when the tree is empty or unreadable. Walks at most\n * {@link MAX_TYPEGEN_FILES} entries to bound the cost on large\n * generated trees.\n *\n * Necessary because the directory's own `mtimeMs` does not update\n * when existing files inside are rewritten — `kick typegen` can run\n * successfully and the directory stat still looks stale. The newest\n * file mtime is the honest measure.\n */\nfunction newestFileMtime(dir: string, budget = MAX_TYPEGEN_FILES): number {\n let newest = 0\n let visited = 0\n const stack = [dir]\n while (stack.length > 0 && visited < budget) {\n const current = stack.pop()!\n let entries\n try {\n entries = readdirSync(current, { withFileTypes: true })\n } catch {\n continue\n }\n for (const entry of entries) {\n if (visited >= budget) break\n visited++\n const full = join(current, entry.name)\n if (entry.isDirectory()) {\n stack.push(full)\n continue\n }\n try {\n const m = statSync(full).mtimeMs\n if (m > newest) newest = m\n } catch {\n // skip unreadable files\n }\n }\n }\n return newest\n}\n\nconst MAX_TYPEGEN_FILES = 2000\n\nexport function checkTypegenFreshness(ctx: DoctorContext): DoctorResult | null {\n const typegenDir = join(ctx.cwd, '.kickjs', 'types')\n if (!existsSync(typegenDir)) return null\n const newest = newestFileMtime(typegenDir)\n if (newest === 0) return null\n const ageMs = Date.now() - newest\n const ageMin = Math.floor(ageMs / 60_000)\n if (ageMin > 60) {\n return {\n name: 'typegen freshness',\n status: 'warn',\n message: `last updated ${ageMin} minutes ago`,\n fix: 'Re-run `kick typegen` (or `kick dev`, which runs it on every reload) so generated types match the current code.',\n }\n }\n return {\n name: 'typegen freshness',\n status: 'pass',\n message: ageMin === 0 ? 'just now' : `${ageMin}m ago`,\n }\n}\n\n// ── Runner ────────────────────────────────────────────────────────────\n\nconst BUILT_IN_CHECKS: DoctorCheck[] = [\n () => checkNodeVersion(),\n checkKickJsInstalled,\n checkExpressInstalled,\n checkReflectMetadata,\n checkDecoratorTsConfig,\n checkEnvWiring,\n checkTypegenFreshness,\n]\n\nexport async function runChecks(\n cwd: string,\n options: { extraChecks?: DoctorCheck[] } = {},\n): Promise<DoctorResult[]> {\n const ctx: DoctorContext = {\n cwd,\n pkg: safeReadJson(join(cwd, 'package.json')),\n tsconfig: loadTsConfig(cwd),\n }\n const checks = [...BUILT_IN_CHECKS, ...(options.extraChecks ?? [])]\n const out: DoctorResult[] = []\n for (const check of checks) {\n // Per-check try/catch so one buggy extension can't abort the whole\n // report. A throwing check produces a synthetic `fail` result with\n // the error message; the loop continues to the next check.\n let r: DoctorResult | DoctorResult[] | null\n try {\n r = await check(ctx)\n } catch (err) {\n out.push({\n name: check.name || 'doctor check',\n status: 'fail',\n message: err instanceof Error ? err.message : String(err),\n })\n continue\n }\n if (r == null) continue\n if (Array.isArray(r)) out.push(...r)\n else out.push(r)\n }\n return out\n}\n\n// ── Output ────────────────────────────────────────────────────────────\n\nfunction statusTag(status: DoctorResult['status']): string {\n switch (status) {\n case 'pass':\n return colors.green('✔')\n case 'warn':\n return colors.yellow('⚠')\n case 'fail':\n return colors.red('✖')\n }\n}\n\nfunction formatResult(r: DoctorResult): string {\n const tag = statusTag(r.status)\n const tail = r.message ? ` ${colors.dim(`(${r.message})`)}` : ''\n return `${tag} ${r.name}${tail}`\n}\n\nfunction formatFix(fix: string): string {\n return fix\n .split('\\n')\n .map((line) => ` ${colors.dim('→')} ${line}`)\n .join('\\n')\n}\n\n// ── Command registration ──────────────────────────────────────────────\n\nfunction extractDoctorChecks(config: KickConfig | null): DoctorCheck[] {\n return config?.doctor?.checks ?? []\n}\n\nexport function registerDoctorCommand(program: Command): void {\n program\n .command('doctor')\n .description('Pre-flight checks for your KickJS project (dev environment health)')\n .action(async () => {\n const cwd = process.cwd()\n const config = await loadKickConfig(cwd)\n const extraChecks = extractDoctorChecks(config)\n\n intro('KickJS Doctor')\n\n const results = await runChecks(cwd, { extraChecks })\n\n for (const r of results) {\n log.message(formatResult(r))\n if (r.fix && r.status !== 'pass') {\n log.message(formatFix(r.fix))\n }\n }\n\n const passed = results.filter((r) => r.status === 'pass').length\n const warns = results.filter((r) => r.status === 'warn').length\n const fails = results.filter((r) => r.status === 'fail').length\n\n const passText = colors.green(`${passed} passed`)\n const warnText =\n warns > 0 ? colors.yellow(`${warns} warning${warns === 1 ? '' : 's'}`) : `${warns} warnings`\n const failText =\n fails > 0 ? colors.red(`${fails} error${fails === 1 ? '' : 's'}`) : `${fails} errors`\n const summary = [passText, warnText, failText].join(', ')\n\n if (fails > 0) {\n outro(`${summary} — fix the errors above before running the app`)\n process.exit(1)\n } else if (warns > 0) {\n outro(`${summary} — review the warnings`)\n } else {\n outro(colors.green(`${summary} — your environment looks good`))\n }\n })\n}\n"],"mappings":"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"}