@forinda/kickjs-cli 5.3.2 → 5.4.0

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 (28) hide show
  1. package/dist/{builtins-CnQ6lxcV.mjs → builtins-DBzZkJey.mjs} +151 -90
  2. package/dist/builtins-DBzZkJey.mjs.map +1 -0
  3. package/dist/{builtins-BxGfcEP6.mjs → builtins-K-nRJcJG.mjs} +409 -214
  4. package/dist/cli.mjs +2 -2
  5. package/dist/config-CCNnXhar.mjs +11 -0
  6. package/dist/config-CQZ6Hppr.mjs +12 -0
  7. package/dist/config-CQZ6Hppr.mjs.map +1 -0
  8. package/dist/{generator-extension-BNgcYlom.mjs → generator-extension-Bn2aH7kY.mjs} +228 -94
  9. package/dist/generator-extension-Bn2aH7kY.mjs.map +1 -0
  10. package/dist/index.d.mts +45 -0
  11. package/dist/index.d.mts.map +1 -1
  12. package/dist/index.mjs +2 -2
  13. package/dist/{plugin-B56zClEX.mjs → plugin-D0C4ISZA.mjs} +2 -2
  14. package/dist/{plugin-Ccvf-gn6.mjs → plugin-DasN_2Zr.mjs} +3 -3
  15. package/dist/{plugin-Ccvf-gn6.mjs.map → plugin-DasN_2Zr.mjs.map} +1 -1
  16. package/dist/{rolldown-runtime-CP9PNXAB.mjs → rolldown-runtime-BTpMa50s.mjs} +1 -1
  17. package/dist/{run-plugins-sjeIm8hS.mjs → run-plugins-Dk7KBKON.mjs} +2 -2
  18. package/dist/{typegen-CYA1y8NJ.mjs → typegen-Bl9kUVNL.mjs} +4 -4
  19. package/dist/{typegen-CYA1y8NJ.mjs.map → typegen-Bl9kUVNL.mjs.map} +1 -1
  20. package/dist/{typegen-CFW1vIgv.mjs → typegen-DDQJNnUl.mjs} +3 -3
  21. package/dist/{types-2ICiQzlQ.mjs → types-kAfWJgh0.mjs} +2 -2
  22. package/dist/{types-2ICiQzlQ.mjs.map → types-kAfWJgh0.mjs.map} +1 -1
  23. package/package.json +2 -2
  24. package/dist/builtins-CnQ6lxcV.mjs.map +0 -1
  25. package/dist/config-B5g_GsV2.mjs +0 -12
  26. package/dist/config-B5g_GsV2.mjs.map +0 -1
  27. package/dist/config-DE9Vo6LN.mjs +0 -11
  28. package/dist/generator-extension-BNgcYlom.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs-cli v5.3.2
2
+ * @forinda/kickjs-cli v5.4.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{t as e}from"./rolldown-runtime-CP9PNXAB.mjs";import{a as t,i as n,r,t as i}from"./config-DE9Vo6LN.mjs";import{n as a,r as o}from"./plugin-B56zClEX.mjs";import{a as s,i as c,o as l,r as u,s as d,t as f}from"./typegen-CFW1vIgv.mjs";import{createRequire as p}from"node:module";import{cpSync as m,existsSync as h,mkdirSync as g,readFileSync as _,readdirSync as ee,rmSync as te,statSync as ne,writeFileSync as re}from"node:fs";import v,{basename as ie,dirname as y,extname as ae,isAbsolute as oe,join as b,relative as x,resolve as S,sep as se}from"node:path";import{fileURLToPath as ce,pathToFileURL as C}from"node:url";import{execSync as w,fork as le,spawn as ue,spawnSync as de}from"node:child_process";import{access as fe,mkdir as pe,readFile as T,rm as me,writeFile as E}from"node:fs/promises";import*as D from"@clack/prompts";import O from"picocolors";import he from"pluralize";import{glob as ge}from"glob";import{groupAssetKeys as _e}from"@forinda/kickjs";import{arch as ve,platform as ye,release as be}from"node:os";import{generate as xe,migrateDown as Se,migrateLatest as Ce,migrateRollback as we,migrateStatus as Te,migrateUp as Ee,renderSchemaSource as De,resolveDbConfig as Oe}from"@forinda/kickjs-db";function ke(e,t,n){w(e,{cwd:t,stdio:`inherit`,env:n?{...process.env,...n}:process.env})}function Ae(e,t,n){let r=de(process.execPath,[e],{cwd:n,stdio:`inherit`,env:{...process.env,...t}});r.status!==0&&process.exit(r.status??1)}let je=!1;function k(e){je=e}const Me=new Set([`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.json`,`.md`]);async function A(e,t){je||(await pe(y(e),{recursive:!0}),await E(e,t,`utf-8`),Me.has(ae(e))&&await Pe(e,t).catch(()=>{}))}let j;async function Ne(e){if(j!==void 0)return j;try{j=await import(p(b(e,`package.json`)).resolve(`oxfmt`))}catch{j=null}return j}async function Pe(e,t){let n=await Ne(process.cwd());if(!n)return;let r=await Fe(e);if(r===null)return;let i=await n.format(e,t,r);i.code!==t&&await E(e,i.code,`utf-8`)}const M=new Map;async function Fe(e){let t=y(e),n=t;if(M.has(n))return M.get(n);for(;;){let e=b(t,`.oxfmtrc.json`);if(h(e))try{let t=await T(e,`utf-8`),r=JSON.parse(t);return delete r.$schema,delete r.ignorePatterns,M.set(n,r),r}catch{return M.set(n,null),null}let r=y(t);if(r===t)return M.set(n,null),null;t=r}}async function N(e){try{return await fe(e),!0}catch{return!1}}const Ie={auth:`@forinda/kickjs-auth`,swagger:`@forinda/kickjs-swagger`,ws:`@forinda/kickjs-ws`,queue:`@forinda/kickjs-queue`,devtools:`@forinda/kickjs-devtools`};function Le(e,t,n,r=[]){let i={"@forinda/kickjs":n,dotenv:`^17.3.1`,express:`^5.1.0`,"reflect-metadata":`^0.2.2`,zod:`^4.3.6`,pino:`^10.3.1`,"pino-pretty":`^13.1.3`};for(let e of r){let t=Ie[e];t&&!i[t]&&(i[t]=n)}return JSON.stringify({name:e,version:n.replace(`^`,``),type:`module`,scripts:{dev:`vite`,"dev:debug":`kick dev:debug`,build:`kick build`,start:`kick start`,test:`vitest run`,"test:watch":`vitest`,typecheck:`tsc --noEmit`,typegen:`kick typegen`,lint:`eslint src/`,format:`prettier --write src/`},dependencies:i,devDependencies:{"@forinda/kickjs-cli":n,"@forinda/kickjs-vite":n,"@swc/core":`^1.15.21`,"@types/express":`^5.0.6`,"@types/node":`^25.0.0`,"unplugin-swc":`^1.5.9`,vite:`^8.0.3`,vitest:`^4.1.2`,typescript:`^6.0.3`,prettier:`^3.8.1`}},null,2)}function Re(){return`import { defineConfig } from 'vite'
11
+ import{t as e}from"./rolldown-runtime-BTpMa50s.mjs";import{a as t,i as n,r,t as i}from"./config-CCNnXhar.mjs";import{n as a,r as o}from"./plugin-D0C4ISZA.mjs";import{a as s,i as c,o as l,r as u,s as d,t as f}from"./typegen-DDQJNnUl.mjs";import{createRequire as p}from"node:module";import{cpSync as m,existsSync as h,mkdirSync as g,readFileSync as _,readdirSync as ee,rmSync as v,statSync as te,writeFileSync as ne}from"node:fs";import y,{basename as re,dirname as b,extname as ie,isAbsolute as ae,join as x,relative as S,resolve as C,sep as oe}from"node:path";import{fileURLToPath as se,pathToFileURL as w}from"node:url";import{execSync as T,fork as ce,spawn as le,spawnSync as ue}from"node:child_process";import{access as de,copyFile as fe,mkdir as pe,readFile as E,readdir as me,rm as he,stat as ge,writeFile as D}from"node:fs/promises";import*as O from"@clack/prompts";import k from"picocolors";import _e from"pluralize";import{glob as ve}from"glob";import{groupAssetKeys as ye}from"@forinda/kickjs";import{arch as be,platform as xe,release as Se}from"node:os";import{generate as Ce,migrateDown as we,migrateLatest as Te,migrateRollback as Ee,migrateStatus as De,migrateUp as Oe,renderSchemaSource as ke,resolveDbConfig as Ae}from"@forinda/kickjs-db";function je(e,t,n){T(e,{cwd:t,stdio:`inherit`,env:n?{...process.env,...n}:process.env})}function Me(e,t,n){let r=ue(process.execPath,[e],{cwd:n,stdio:`inherit`,env:{...process.env,...t}});r.status!==0&&process.exit(r.status??1)}let Ne=!1;function A(e){Ne=e}const Pe=new Set([`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.json`,`.md`]);async function j(e,t){Ne||(await pe(b(e),{recursive:!0}),await D(e,t,`utf-8`),Pe.has(ie(e))&&await Ie(e,t).catch(()=>{}))}let M;async function Fe(e){if(M!==void 0)return M;try{M=await import(p(x(e,`package.json`)).resolve(`oxfmt`))}catch{M=null}return M}async function Ie(e,t){let n=await Fe(process.cwd());if(!n)return;let r=await Le(e);if(r===null)return;let i=await n.format(e,t,r);i.code!==t&&await D(e,i.code,`utf-8`)}const N=new Map;async function Le(e){let t=b(e),n=t;if(N.has(n))return N.get(n);for(;;){let e=x(t,`.oxfmtrc.json`);if(h(e))try{let t=await E(e,`utf-8`),r=JSON.parse(t);return delete r.$schema,delete r.ignorePatterns,N.set(n,r),r}catch{return N.set(n,null),null}let r=b(t);if(r===t)return N.set(n,null),null;t=r}}async function Re(e){try{return await de(e),!0}catch{return!1}}const ze={auth:`@forinda/kickjs-auth`,swagger:`@forinda/kickjs-swagger`,ws:`@forinda/kickjs-ws`,queue:`@forinda/kickjs-queue`,devtools:`@forinda/kickjs-devtools`};function Be(e,t,n,r=[]){let i={"@forinda/kickjs":n,dotenv:`^17.3.1`,express:`^5.1.0`,"reflect-metadata":`^0.2.2`,zod:`^4.3.6`,pino:`^10.3.1`,"pino-pretty":`^13.1.3`};for(let e of r){let t=ze[e];t&&!i[t]&&(i[t]=n)}return JSON.stringify({name:e,version:n.replace(`^`,``),type:`module`,scripts:{dev:`vite`,"dev:debug":`kick dev:debug`,build:`kick build`,start:`kick start`,test:`vitest run`,"test:watch":`vitest`,typecheck:`tsc --noEmit`,typegen:`kick typegen`,lint:`eslint src/`,format:`prettier --write src/`},dependencies:i,devDependencies:{"@forinda/kickjs-cli":n,"@forinda/kickjs-vite":n,"@swc/core":`^1.15.21`,"@types/express":`^5.0.6`,"@types/node":`^25.0.0`,"unplugin-swc":`^1.5.9`,vite:`^8.0.3`,vitest:`^4.1.2`,typescript:`^6.0.3`,prettier:`^3.8.1`}},null,2)}function Ve(){return`import { defineConfig } from 'vite'
12
12
  import { resolve } from 'node:path'
13
13
  import swc from 'unplugin-swc'
14
14
  import { kickjsVitePlugin, envWatchPlugin } from '@forinda/kickjs-vite'
@@ -43,7 +43,7 @@ export default defineConfig({
43
43
  },
44
44
  },
45
45
  })
46
- `}function ze(){return JSON.stringify({compilerOptions:{target:`ES2022`,module:`ESNext`,moduleResolution:`bundler`,lib:[`ES2022`],types:[`node`,`vite/client`],strict:!0,esModuleInterop:!0,skipLibCheck:!0,sourceMap:!0,declaration:!0,experimentalDecorators:!0,emitDecoratorMetadata:!0,outDir:`dist`,paths:{"@/*":[`./src/*`]}},include:[`src`,`.kickjs/types/**/*.d.ts`,`.kickjs/types/**/*.ts`]},null,2)}function Be(){return JSON.stringify({semi:!1,singleQuote:!0,trailingComma:`all`,printWidth:100,tabWidth:2},null,2)}function Ve(){return`# https://editorconfig.org
46
+ `}function He(){return JSON.stringify({compilerOptions:{target:`ES2022`,module:`ESNext`,moduleResolution:`bundler`,lib:[`ES2022`],types:[`node`,`vite/client`],strict:!0,esModuleInterop:!0,skipLibCheck:!0,sourceMap:!0,declaration:!0,experimentalDecorators:!0,emitDecoratorMetadata:!0,outDir:`dist`,paths:{"@/*":[`./src/*`]}},include:[`src`,`.kickjs/types/**/*.d.ts`,`.kickjs/types/**/*.ts`]},null,2)}function Ue(){return JSON.stringify({semi:!1,singleQuote:!0,trailingComma:`all`,printWidth:100,tabWidth:2},null,2)}function We(){return`# https://editorconfig.org
47
47
  root = true
48
48
 
49
49
  [*]
@@ -56,14 +56,14 @@ insert_final_newline = true
56
56
 
57
57
  [*.md]
58
58
  trim_trailing_whitespace = false
59
- `}function He(){return`node_modules/
59
+ `}function Ge(){return`node_modules/
60
60
  dist/
61
61
  .env
62
62
  coverage/
63
63
  .DS_Store
64
64
  *.tsbuildinfo
65
65
  .kickjs/
66
- `}function Ue(){return`# Auto-detect text files and normalise line endings to LF
66
+ `}function Ke(){return`# Auto-detect text files and normalise line endings to LF
67
67
  * text=auto eol=lf
68
68
 
69
69
  # Explicitly mark generated / binary files
@@ -81,11 +81,11 @@ coverage/
81
81
  pnpm-lock.yaml -diff linguist-generated
82
82
  yarn.lock -diff linguist-generated
83
83
  package-lock.json -diff linguist-generated
84
- `}function We(){return`PORT=3000
84
+ `}function qe(){return`PORT=3000
85
85
  NODE_ENV=development
86
- `}function Ge(){return`PORT=3000
86
+ `}function Je(){return`PORT=3000
87
87
  NODE_ENV=development
88
- `}function Ke(){return`import { defineConfig } from 'vitest/config'
88
+ `}function Ye(){return`import { defineConfig } from 'vitest/config'
89
89
  import swc from 'unplugin-swc'
90
90
 
91
91
  export default defineConfig({
@@ -96,7 +96,7 @@ export default defineConfig({
96
96
  include: ['src/**/*.test.ts'],
97
97
  },
98
98
  })
99
- `}function qe(e,t,n,r=[]){switch(t){case`cqrs`:{let t=[],i=[];return r.includes(`devtools`)&&(t.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`),i.push(` DevToolsAdapter(),`)),r.includes(`swagger`)&&(t.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`),i.push(` SwaggerAdapter({\n info: { title: '${e}', version: '${n}' },\n }),`)),`import 'reflect-metadata'
99
+ `}function Xe(e,t,n,r=[]){switch(t){case`cqrs`:{let t=[],i=[];return r.includes(`devtools`)&&(t.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`),i.push(` DevToolsAdapter(),`)),r.includes(`swagger`)&&(t.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`),i.push(` SwaggerAdapter({\n info: { title: '${e}', version: '${n}' },\n }),`)),`import 'reflect-metadata'
100
100
  // Side-effect import — registers the extended env schema with kickjs
101
101
  // **before** any controller / service / @Value gets resolved. Without
102
102
  // this line ConfigService.get('YOUR_KEY') returns undefined because the
@@ -166,12 +166,14 @@ export const app = await bootstrap({
166
166
  express.json(),
167
167
  ],
168
168
  })
169
- `}}}function Je(){return`import type { AppModuleClass } from '@forinda/kickjs'
169
+ `}}}function Ze(){return`import type { AppModuleEntry } from '@forinda/kickjs'
170
170
  import { HelloModule } from './hello/hello.module'
171
171
 
172
172
  // Remove HelloModule and run: kick g module <name>
173
- export const modules: AppModuleClass[] = [HelloModule]
174
- `}function Ye(){return`import { defineEnv, loadEnv } from '@forinda/kickjs/config'
173
+ // Modules built with \`defineModule\` are called as factories — the
174
+ // invocation produces the AppModule instance bootstrap registers.
175
+ export const modules: AppModuleEntry[] = [HelloModule()]
176
+ `}function Qe(){return`import { defineEnv, loadEnv } from '@forinda/kickjs/config'
175
177
  import { z } from 'zod'
176
178
 
177
179
  /**
@@ -206,7 +208,7 @@ const envSchema = defineEnv((base) =>
206
208
  export const env = loadEnv(envSchema)
207
209
 
208
210
  export default envSchema
209
- `}function Xe(){return`import { Service } from '@forinda/kickjs'
211
+ `}function $e(){return`import { Service } from '@forinda/kickjs'
210
212
 
211
213
  @Service()
212
214
  export class HelloService {
@@ -218,7 +220,7 @@ export class HelloService {
218
220
  return { status: 'ok', uptime: process.uptime() }
219
221
  }
220
222
  }
221
- `}function Ze(){return`import { Controller, Get, Autowired, type Ctx } from '@forinda/kickjs'
223
+ `}function et(){return`import { Controller, Get, Autowired, type Ctx } from '@forinda/kickjs'
222
224
  import { HelloService } from './hello.service'
223
225
 
224
226
  // \`Ctx<KickRoutes.HelloController['<method>']>\` is generated by
@@ -240,26 +242,28 @@ export class HelloController {
240
242
  ctx.json(this.helloService.healthCheck())
241
243
  }
242
244
  }
243
- `}function Qe(){return`import { type AppModule, type ModuleRoutes, buildRoutes } from '@forinda/kickjs'
245
+ `}function tt(){return`import { defineModule } from '@forinda/kickjs'
244
246
  import { HelloController } from './hello.controller'
245
247
 
246
- export class HelloModule implements AppModule {
247
- // \`register(container)\` is optional — only implement it when you need
248
- // to bind a token to a concrete implementation, e.g.
249
- // register(container) {
250
- // container.registerFactory(USER_REPOSITORY, () => container.resolve(InMemoryUserRepository))
251
- // }
252
- // The HelloService uses @Service() so the decorator handles registration.
253
-
254
- routes(): ModuleRoutes {
255
- return {
256
- path: '/hello',
257
- router: buildRoutes(HelloController),
258
- controller: HelloController,
259
- }
260
- }
261
- }
262
- `}function $e(e,t=`inmemory`,n=`pnpm`){return`import { defineConfig } from '@forinda/kickjs-cli'
248
+ export const HelloModule = defineModule({
249
+ name: 'HelloModule',
250
+ build: () => ({
251
+ // \`register(container)\` is optional — only implement it when you need
252
+ // to bind a token to a concrete implementation, e.g.
253
+ // register(container) {
254
+ // container.registerFactory(USER_REPOSITORY, () => container.resolve(InMemoryUserRepository))
255
+ // }
256
+ // The HelloService uses @Service() so the decorator handles registration.
257
+
258
+ routes() {
259
+ return {
260
+ path: '/hello',
261
+ controller: HelloController,
262
+ }
263
+ },
264
+ }),
265
+ })
266
+ `}function nt(e,t=`inmemory`,n=`pnpm`){return`import { defineConfig } from '@forinda/kickjs-cli'
263
267
 
264
268
  export default defineConfig({
265
269
  pattern: '${e}',
@@ -303,7 +307,7 @@ export default defineConfig({
303
307
  },
304
308
  ],
305
309
  })
306
- `}function et(e,t,n){let r={rest:`REST API`,ddd:`Domain-Driven Design`,cqrs:`CQRS + Event-Driven`,minimal:`Minimal`},i=[`@forinda/kickjs`,`@forinda/kickjs-vite`];return t!==`minimal`&&i.push(`@forinda/kickjs-swagger`,`@forinda/kickjs-devtools`),t===`cqrs`&&i.push(`@forinda/kickjs-queue`,`@forinda/kickjs-ws`),`# ${e}
310
+ `}function rt(e,t,n){let r={rest:`REST API`,ddd:`Domain-Driven Design`,cqrs:`CQRS + Event-Driven`,minimal:`Minimal`},i=[`@forinda/kickjs`,`@forinda/kickjs-vite`];return t!==`minimal`&&i.push(`@forinda/kickjs-swagger`,`@forinda/kickjs-devtools`),t===`cqrs`&&i.push(`@forinda/kickjs-queue`,`@forinda/kickjs-ws`),`# ${e}
307
311
 
308
312
  A **${r[t]??`REST API`}** built with [KickJS](https://forinda.github.io/kick-js/) — a decorator-driven Node.js framework on Express 5 and TypeScript.
309
313
 
@@ -366,7 +370,7 @@ Copy \`.env.example\` to \`.env\` and configure:
366
370
 
367
371
  - [KickJS Documentation](https://forinda.github.io/kick-js/)
368
372
  - [CLI Reference](https://forinda.github.io/kick-js/api/cli.html)
369
- `}function tt(e,t,n){return`# CLAUDE.md — ${e}
373
+ `}function it(e,t,n){return`# CLAUDE.md — ${e}
370
374
 
371
375
  **Read \`./AGENTS.md\` first.** It is the canonical, multi-agent
372
376
  reference for this project (Claude, Copilot, Codex, Gemini, etc.) —
@@ -432,7 +436,7 @@ When generating or modifying code in this project, stay aligned with the v4 conv
432
436
  - **Refresh these files**: \`kick g agents -f\` regenerates \`AGENTS.md\` + \`CLAUDE.md\` from the latest CLI templates. Hand-edited content is overwritten — keep customisation in \`AGENTS.local.md\`.
433
437
 
434
438
  For everything else (controllers, services, modules, RequestContext API, generators, CLI commands, package additions, env wiring, troubleshooting) → \`AGENTS.md\`.
435
- `}function nt(e,t,n){return`# AGENTS.md — AI Agent Guide for ${e}
439
+ `}function at(e,t,n){return`# AGENTS.md — AI Agent Guide for ${e}
436
440
 
437
441
  This guide is the **canonical, multi-agent reference** for this KickJS
438
442
  application — Claude, Copilot, Codex, Gemini, etc. all read it first.
@@ -537,7 +541,7 @@ mistakes:
537
541
 
538
542
  \`\`\`ts
539
543
  // src/modules/index.ts
540
- export const modules: AppModuleClass[] = [HelloModule, UsersModule, ...]
544
+ export const modules: AppModuleEntry[] = [HelloModule(), UsersModule(), ...]
541
545
 
542
546
  // src/middleware/index.ts
543
547
  export const middleware = [helmet(), cors(), requestId(), ...]
@@ -601,7 +605,7 @@ ${t===`ddd`?`\`\`\`
601
605
  ├── <name>.repository.ts # Data access (@Repository)
602
606
  ├── <name>.dto.ts # Request/response schemas (Zod)
603
607
  ├── <name>.entity.ts # Domain entity (optional)
604
- └── <name>.module.ts # Module definition (implements AppModule)
608
+ └── <name>.module.ts # Module definition (defineModule factory)
605
609
  \`\`\`
606
610
  `:t===`cqrs`?`\`\`\`
607
611
  <name>/
@@ -615,14 +619,14 @@ ${t===`ddd`?`\`\`\`
615
619
  │ └── <name>-created.event.ts
616
620
  ├── <name>.controller.ts # HTTP routes
617
621
  ├── <name>.repository.ts # Data access
618
- └── <name>.module.ts # Module definition (implements AppModule)
622
+ └── <name>.module.ts # Module definition (defineModule factory)
619
623
  \`\`\`
620
624
  `:t===`rest`?`\`\`\`
621
625
  <name>/
622
626
  ├── <name>.controller.ts # HTTP routes (@Controller)
623
627
  ├── <name>.service.ts # Business logic (@Service)
624
628
  ├── <name>.dto.ts # Request/response schemas (Zod)
625
- └── <name>.module.ts # Module definition (implements AppModule)
629
+ └── <name>.module.ts # Module definition (defineModule factory)
626
630
  \`\`\`
627
631
  `:"```\nsrc/\n├── index.ts # Add routes here\n└── ... # Custom structure\n```\n"}
628
632
 
@@ -653,8 +657,8 @@ If not using generators:
653
657
  - [ ] Create \`src/modules/<name>/<name>.controller.ts\`
654
658
  - [ ] Add \`@Controller()\` decorator
655
659
  - [ ] Add route handlers with \`@Get()\`, \`@Post()\`, etc.
656
- - [ ] Create module file implementing \`AppModule\` with \`routes()\` returning \`{ path, router: buildRoutes(Controller), controller }\`
657
- - [ ] Register module in \`src/modules/index.ts\` (\`AppModuleClass[]\` array)
660
+ - [ ] Create module file with \`defineModule({ name, build: () => ({ routes() { return { path, controller } } }) })\` (the framework derives the Express router from the controller)
661
+ - [ ] Register module in \`src/modules/index.ts\` (\`AppModuleEntry[]\` array — call the factory at the registration site: \`[MyModule()]\`)
658
662
  - [ ] Test with \`kick dev\`
659
663
 
660
664
  ### Manual Service
@@ -935,7 +939,7 @@ ${t===`cqrs`?`### Background Jobs
935
939
  - [Decorators Guide](https://forinda.github.io/kick-js/guide/decorators.html)
936
940
  - [DI System](https://forinda.github.io/kick-js/guide/dependency-injection.html)
937
941
  - [Testing](https://forinda.github.io/kick-js/api/testing.html)
938
- `}function rt(e,t,n){return`# kickjs-skills.md — Task Skills for AI Agents (${e})
942
+ `}function ot(e,t,n){return`# kickjs-skills.md — Task Skills for AI Agents (${e})
939
943
 
940
944
  This file is the agent-facing **skills index** for KickJS work in this
941
945
  repo. Each block below is a short, rigid workflow keyed to a specific
@@ -1182,16 +1186,16 @@ description: Patterns to refuse outright when the user asks for them — they br
1182
1186
  - [Decorators](https://forinda.github.io/kick-js/guide/decorators.html)
1183
1187
  - [Context Decorators](https://forinda.github.io/kick-js/guide/context-decorators.html)
1184
1188
  - [Testing](https://forinda.github.io/kick-js/api/testing.html)
1185
- `}const it=y(ce(import.meta.url)),at=JSON.parse(_(b(it,`..`,`package.json`),`utf-8`)),ot=`^${at.version}`;async function st(e){let{name:t,directory:n,packageManager:r=`pnpm`,template:i=`rest`,defaultRepo:a=`inmemory`,packages:o=[]}=e,s=n,c=e=>console.log(` ${e}`);if(console.log(`\n Creating KickJS project: ${t}\n`),await A(b(s,`package.json`),Le(t,i,ot,o)),await A(b(s,`vite.config.ts`),Re()),await A(b(s,`tsconfig.json`),ze()),await A(b(s,`.prettierrc`),Be()),await A(b(s,`.editorconfig`),Ve()),await A(b(s,`.gitignore`),He()),await A(b(s,`.gitattributes`),Ue()),await A(b(s,`.env`),We()),await A(b(s,`.env.example`),Ge()),await A(b(s,`src/config/index.ts`),Ye()),await A(b(s,`src/index.ts`),qe(t,i,at.version,o)),await A(b(s,`src/modules/index.ts`),Je()),await A(b(s,`src/modules/hello/hello.service.ts`),Xe()),await A(b(s,`src/modules/hello/hello.controller.ts`),Ze()),await A(b(s,`src/modules/hello/hello.module.ts`),Qe()),await A(b(s,`kick.config.ts`),$e(i,a,r)),await A(b(s,`vitest.config.ts`),Ke()),await A(b(s,`README.md`),et(t,i,r)),await A(b(s,`CLAUDE.md`),tt(t,i,r)),await A(b(s,`AGENTS.md`),nt(t,i,r)),await A(b(s,`kickjs-skills.md`),rt(t,i,r)),e.installDeps){console.log(`\n Installing dependencies with ${r}...\n`);try{w(`${r} install`,{cwd:s,stdio:`inherit`}),console.log(`
1186
- Dependencies installed successfully!`)}catch{console.log(`\n Warning: ${r} install failed. Run it manually.`)}}try{let{runTypegen:e}=await import(`./typegen-CFW1vIgv.mjs`).then(e=>e.n);await e({cwd:s,allowDuplicates:!0,silent:!0})}catch{}if(e.initGit)try{w(`git init`,{cwd:s,stdio:`pipe`}),w(`git branch -M main`,{cwd:s,stdio:`pipe`}),w(`git add -A`,{cwd:s,stdio:`pipe`}),w(`git commit -m "chore: initial commit from kick new"`,{cwd:s,stdio:`pipe`}),c(`Git repository initialized`)}catch{c(`Warning: git init failed (git may not be installed)`)}console.log(`
1187
- Project scaffolded successfully!`),console.log();let l=s!==process.cwd();c(`Next steps:`),l&&c(` cd ${t}`),e.installDeps||c(` ${r} install`);let u={rest:`kick g module user`,ddd:`kick g module user --repo drizzle`,cqrs:`kick g module user --pattern cqrs`,minimal:`# add your routes to src/index.ts`};c(` ${u[i]??u.rest}`),c(` kick dev`),c(``),c(`Commands:`),c(` kick dev Start dev server with Vite HMR`),c(` kick build Production build via Vite`),c(` kick start Run production build`),c(``),c(`Generators:`),c(` kick g module <name> Full DDD module (controller, DTOs, use-cases, repo)`),c(` kick g scaffold <n> <f..> CRUD module from field definitions`),c(` kick g controller <name> Standalone controller`),c(` kick g service <name> @Service() class`),c(` kick g middleware <name> Express middleware`),c(` kick g guard <name> Route guard (auth, roles, etc.)`),c(` kick g adapter <name> AppAdapter with lifecycle hooks`),c(` kick g dto <name> Zod DTO schema`),i===`cqrs`&&c(` kick g job <name> Queue job processor`),c(` kick g config Generate kick.config.ts`),c(``),c(`Add packages:`),c(` kick add <pkg> Install a KickJS package + peers`),c(` kick add --list Show all available packages`),c(``),c(`Available: auth, swagger, drizzle, prisma, ws, queue, devtools, mcp, testing`),c(``)}const ct={GET:O.green,POST:O.cyan,PUT:O.yellow,PATCH:O.magenta,DELETE:O.red};function lt(e){return(ct[e]??O.dim)(e.padEnd(7))}function ut(e){let t=`[${e}]`.padEnd(10);switch(e){case`CRITICAL`:return O.red(t);case`WARNING`:return O.yellow(t);case`INFO`:return O.blue(O.dim(t));default:return t}}O.green(`✓`),O.red(`✖`),O.yellow(`⚠`),O.blue(`ℹ`);function dt(e){D.intro(O.bgCyan(O.black(` ${e} `)))}function P(e){D.outro(e)}function ft(e){D.isCancel(e)&&(D.cancel(`Operation cancelled.`),process.exit(0))}async function pt(e){let t=await D.text(e);return ft(t),t}async function mt(e){let t=await D.select(e);return ft(t),t}async function ht(e){let t=await D.multiselect(e);return ft(t),t}async function F(e){let t=await D.confirm(e);return ft(t),t}function gt(){return D.spinner()}const I=D.log,_t={kickjs:{pkg:`@forinda/kickjs`,peers:[`express`],description:`Unified framework: DI, decorators, routing, middleware`,core:!0},vite:{pkg:`@forinda/kickjs-vite`,peers:[`vite`],description:`Vite plugin: dev server, HMR, module discovery`,dev:!0,core:!0},cli:{pkg:`@forinda/kickjs-cli`,peers:[],description:`CLI tool and code generators`,dev:!0,core:!0},swagger:{pkg:`@forinda/kickjs-swagger`,peers:[],description:`OpenAPI spec + Swagger UI + ReDoc`},db:{pkg:`@forinda/kickjs-db`,peers:[],description:`kick/db core — schema DSL, migrations, KickDbClient, customType`},"db-pg":{pkg:`@forinda/kickjs-db-pg`,peers:[`pg`],description:`kick/db PostgreSQL dialect + adapter (pgDialect, pgAdapter)`},drizzle:{pkg:`@forinda/kickjs-drizzle`,peers:[`drizzle-orm`],description:`Drizzle ORM adapter + query builder`},prisma:{pkg:`@forinda/kickjs-prisma`,peers:[`@prisma/client`],description:`Prisma adapter + query builder`},ws:{pkg:`@forinda/kickjs-ws`,peers:[`socket.io`],description:`WebSocket with @WsController decorators`},devtools:{pkg:`@forinda/kickjs-devtools`,peers:[],description:`Development dashboard — routes, DI, metrics, health`,dev:!0},auth:{pkg:`@forinda/kickjs-auth`,peers:[`jsonwebtoken`],description:`Authentication — JWT, API key, and custom strategies`},queue:{pkg:`@forinda/kickjs-queue`,peers:[],description:`Queue adapter (BullMQ/RabbitMQ/Kafka)`},"queue:bullmq":{pkg:`@forinda/kickjs-queue`,peers:[`bullmq`,`ioredis`],description:`Queue with BullMQ + Redis`},"queue:rabbitmq":{pkg:`@forinda/kickjs-queue`,peers:[`amqplib`],description:`Queue with RabbitMQ`},"queue:kafka":{pkg:`@forinda/kickjs-queue`,peers:[`kafkajs`],description:`Queue with Kafka`},mcp:{pkg:`@forinda/kickjs-mcp`,peers:[`@modelcontextprotocol/sdk`],description:`Model Context Protocol server — expose @Controller endpoints as AI tools`},testing:{pkg:`@forinda/kickjs-testing`,peers:[],description:`Test utilities and TestModule builder`,dev:!0}};function L(e,t=process.cwd()){let n=t;for(;;){if(h(S(n,e)))return n;let t=y(n);if(t===n)return null;n=t}}function vt(){return L(`pnpm-lock.yaml`)?`pnpm`:L(`yarn.lock`)?`yarn`:L(`bun.lockb`)||L(`bun.lock`)?`bun`:L(`package-lock.json`)?`npm`:null}function yt(){let e=process.cwd();for(;e;){let t=S(e,`package.json`);if(h(t))try{let e=JSON.parse(_(t,`utf-8`)).packageManager;if(typeof e==`string`){let t=e.split(`@`)[0];if(i.includes(t))return t}}catch{}let n=y(e);if(n===e)return null;e=n}return null}async function bt(e){if(e&&i.includes(e))return{pm:e,source:`flag`};let t=await r(process.cwd());if(t?.packageManager&&i.includes(t.packageManager))return{pm:t.packageManager,source:`config`};let n=yt();if(n)return{pm:n,source:`package.json`};let a=vt();return a?{pm:a,source:`lockfile`}:{pm:`npm`,source:`default`}}async function xt(e){let{pm:t}=await bt(e);return t}function St(e=!1){let t=Object.entries(_t),n=Math.max(...t.map(([e])=>e.length)),r=t.filter(([,e])=>e.core),i=t.filter(([,e])=>!e.core),a=([e,t])=>{let r=e.padEnd(n+2),i=t.peers.length?` (+ ${t.peers.join(`, `)})`:``;return` ${r} ${t.description}${i}`};console.log(`
1189
+ `}const st=b(se(import.meta.url)),ct=JSON.parse(_(x(st,`..`,`package.json`),`utf-8`)),lt=`^${ct.version}`;async function ut(e){let{name:t,directory:n,packageManager:r=`pnpm`,template:i=`rest`,defaultRepo:a=`inmemory`,packages:o=[]}=e,s=n,c=e=>console.log(` ${e}`);if(console.log(`\n Creating KickJS project: ${t}\n`),await j(x(s,`package.json`),Be(t,i,lt,o)),await j(x(s,`vite.config.ts`),Ve()),await j(x(s,`tsconfig.json`),He()),await j(x(s,`.prettierrc`),Ue()),await j(x(s,`.editorconfig`),We()),await j(x(s,`.gitignore`),Ge()),await j(x(s,`.gitattributes`),Ke()),await j(x(s,`.env`),qe()),await j(x(s,`.env.example`),Je()),await j(x(s,`src/config/index.ts`),Qe()),await j(x(s,`src/index.ts`),Xe(t,i,ct.version,o)),await j(x(s,`src/modules/index.ts`),Ze()),await j(x(s,`src/modules/hello/hello.service.ts`),$e()),await j(x(s,`src/modules/hello/hello.controller.ts`),et()),await j(x(s,`src/modules/hello/hello.module.ts`),tt()),await j(x(s,`kick.config.ts`),nt(i,a,r)),await j(x(s,`vitest.config.ts`),Ye()),await j(x(s,`README.md`),rt(t,i,r)),await j(x(s,`CLAUDE.md`),it(t,i,r)),await j(x(s,`AGENTS.md`),at(t,i,r)),await j(x(s,`kickjs-skills.md`),ot(t,i,r)),e.installDeps){console.log(`\n Installing dependencies with ${r}...\n`);try{T(`${r} install`,{cwd:s,stdio:`inherit`}),console.log(`
1190
+ Dependencies installed successfully!`)}catch{console.log(`\n Warning: ${r} install failed. Run it manually.`)}}try{let{runTypegen:e}=await import(`./typegen-DDQJNnUl.mjs`).then(e=>e.n);await e({cwd:s,allowDuplicates:!0,silent:!0})}catch{}if(e.initGit)try{T(`git init`,{cwd:s,stdio:`pipe`}),T(`git branch -M main`,{cwd:s,stdio:`pipe`}),T(`git add -A`,{cwd:s,stdio:`pipe`}),T(`git commit -m "chore: initial commit from kick new"`,{cwd:s,stdio:`pipe`}),c(`Git repository initialized`)}catch{c(`Warning: git init failed (git may not be installed)`)}console.log(`
1191
+ Project scaffolded successfully!`),console.log();let l=s!==process.cwd();c(`Next steps:`),l&&c(` cd ${t}`),e.installDeps||c(` ${r} install`);let u={rest:`kick g module user`,ddd:`kick g module user --repo drizzle`,cqrs:`kick g module user --pattern cqrs`,minimal:`# add your routes to src/index.ts`};c(` ${u[i]??u.rest}`),c(` kick dev`),c(``),c(`Commands:`),c(` kick dev Start dev server with Vite HMR`),c(` kick build Production build via Vite`),c(` kick start Run production build`),c(``),c(`Generators:`),c(` kick g module <name> Full DDD module (controller, DTOs, use-cases, repo)`),c(` kick g scaffold <n> <f..> CRUD module from field definitions`),c(` kick g controller <name> Standalone controller`),c(` kick g service <name> @Service() class`),c(` kick g middleware <name> Express middleware`),c(` kick g guard <name> Route guard (auth, roles, etc.)`),c(` kick g adapter <name> AppAdapter with lifecycle hooks`),c(` kick g dto <name> Zod DTO schema`),i===`cqrs`&&c(` kick g job <name> Queue job processor`),c(` kick g config Generate kick.config.ts`),c(``),c(`Add packages:`),c(` kick add <pkg> Install a KickJS package + peers`),c(` kick add --list Show all available packages`),c(``),c(`Available: auth, swagger, drizzle, prisma, ws, queue, devtools, mcp, testing`),c(``)}const dt={GET:k.green,POST:k.cyan,PUT:k.yellow,PATCH:k.magenta,DELETE:k.red};function ft(e){return(dt[e]??k.dim)(e.padEnd(7))}function pt(e){let t=`[${e}]`.padEnd(10);switch(e){case`CRITICAL`:return k.red(t);case`WARNING`:return k.yellow(t);case`INFO`:return k.blue(k.dim(t));default:return t}}k.green(`✓`),k.red(`✖`),k.yellow(`⚠`),k.blue(`ℹ`);function mt(e){O.intro(k.bgCyan(k.black(` ${e} `)))}function P(e){O.outro(e)}function ht(e){O.isCancel(e)&&(O.cancel(`Operation cancelled.`),process.exit(0))}async function gt(e){let t=await O.text(e);return ht(t),t}async function _t(e){let t=await O.select(e);return ht(t),t}async function vt(e){let t=await O.multiselect(e);return ht(t),t}async function F(e){let t=await O.confirm(e);return ht(t),t}function yt(){return O.spinner()}const I=O.log,bt={kickjs:{pkg:`@forinda/kickjs`,peers:[`express`],description:`Unified framework: DI, decorators, routing, middleware`,core:!0},vite:{pkg:`@forinda/kickjs-vite`,peers:[`vite`],description:`Vite plugin: dev server, HMR, module discovery`,dev:!0,core:!0},cli:{pkg:`@forinda/kickjs-cli`,peers:[],description:`CLI tool and code generators`,dev:!0,core:!0},swagger:{pkg:`@forinda/kickjs-swagger`,peers:[],description:`OpenAPI spec + Swagger UI + ReDoc`},db:{pkg:`@forinda/kickjs-db`,peers:[],description:`kick/db core — schema DSL, migrations, KickDbClient, customType`},"db-pg":{pkg:`@forinda/kickjs-db-pg`,peers:[`pg`],description:`kick/db PostgreSQL dialect + adapter (pgDialect, pgAdapter)`},drizzle:{pkg:`@forinda/kickjs-drizzle`,peers:[`drizzle-orm`],description:`Drizzle ORM adapter + query builder`},prisma:{pkg:`@forinda/kickjs-prisma`,peers:[`@prisma/client`],description:`Prisma adapter + query builder`},ws:{pkg:`@forinda/kickjs-ws`,peers:[`socket.io`],description:`WebSocket with @WsController decorators`},devtools:{pkg:`@forinda/kickjs-devtools`,peers:[],description:`Development dashboard — routes, DI, metrics, health`,dev:!0},auth:{pkg:`@forinda/kickjs-auth`,peers:[`jsonwebtoken`],description:`Authentication — JWT, API key, and custom strategies`},queue:{pkg:`@forinda/kickjs-queue`,peers:[],description:`Queue adapter (BullMQ/RabbitMQ/Kafka)`},"queue:bullmq":{pkg:`@forinda/kickjs-queue`,peers:[`bullmq`,`ioredis`],description:`Queue with BullMQ + Redis`},"queue:rabbitmq":{pkg:`@forinda/kickjs-queue`,peers:[`amqplib`],description:`Queue with RabbitMQ`},"queue:kafka":{pkg:`@forinda/kickjs-queue`,peers:[`kafkajs`],description:`Queue with Kafka`},mcp:{pkg:`@forinda/kickjs-mcp`,peers:[`@modelcontextprotocol/sdk`],description:`Model Context Protocol server — expose @Controller endpoints as AI tools`},testing:{pkg:`@forinda/kickjs-testing`,peers:[],description:`Test utilities and TestModule builder`,dev:!0}};function L(e,t=process.cwd()){let n=t;for(;;){if(h(C(n,e)))return n;let t=b(n);if(t===n)return null;n=t}}function xt(){return L(`pnpm-lock.yaml`)?`pnpm`:L(`yarn.lock`)?`yarn`:L(`bun.lockb`)||L(`bun.lock`)?`bun`:L(`package-lock.json`)?`npm`:null}function St(){let e=process.cwd();for(;e;){let t=C(e,`package.json`);if(h(t))try{let e=JSON.parse(_(t,`utf-8`)).packageManager;if(typeof e==`string`){let t=e.split(`@`)[0];if(i.includes(t))return t}}catch{}let n=b(e);if(n===e)return null;e=n}return null}async function Ct(e){if(e&&i.includes(e))return{pm:e,source:`flag`};let t=await r(process.cwd());if(t?.packageManager&&i.includes(t.packageManager))return{pm:t.packageManager,source:`config`};let n=St();if(n)return{pm:n,source:`package.json`};let a=xt();return a?{pm:a,source:`lockfile`}:{pm:`npm`,source:`default`}}async function wt(e){let{pm:t}=await Ct(e);return t}function Tt(e=!1){let t=Object.entries(bt),n=Math.max(...t.map(([e])=>e.length)),r=t.filter(([,e])=>e.core),i=t.filter(([,e])=>!e.core),a=([e,t])=>{let r=e.padEnd(n+2),i=t.peers.length?` (+ ${t.peers.join(`, `)})`:``;return` ${r} ${t.description}${i}`};console.log(`
1188
1192
  Core packages (always installed by \`kick new\`):
1189
1193
  `);for(let e of r)console.log(a(e));if(e){console.log(`
1190
1194
  Optional packages (add as needed):
1191
1195
  `);for(let e of i)console.log(a(e))}else console.log(`\n Plus ${i.length} optional packages (auth, swagger, db, queue, …).`),console.log(" Run `kick add --list --all` for the full catalog.");console.log(`
1192
- Usage: kick add auth drizzle swagger`),console.log(` kick add queue:bullmq`),console.log()}function Ct(e){e.command(`list`).alias(`ls`).description(`List KickJS packages (core only; pair with --all for the full catalog)`).option(`--all`,`Include the full optional catalog`).action(e=>{St(!!e.all)})}function wt(e){e.command(`add [packages...]`).description(`Add KickJS packages with their required dependencies`).option(`--pm <manager>`,`Package manager override`).option(`-D, --dev`,`Install as dev dependency`).option(`--list`,`List packages (core only by default; pair with --all)`).option(`--all`,`When listing, include the full optional catalog`).action(async(e,t)=>{if(t.list||e.length===0){St(!!t.all);return}let{pm:n,source:r}=await bt(t.pm);console.log(`\n Using ${n} (resolved from ${r})`);let i=t.dev,a=new Set,o=new Set,s=[];for(let t of e){let e=_t[t];if(!e){s.push(t);continue}let n=i||e.dev?o:a;n.add(e.pkg);for(let t of e.peers)n.add(t)}if(!(s.length>0&&(console.log(`\n Unknown packages: ${s.join(`, `)}`),console.log(` Run "kick add --list" to see available packages.
1193
- `),a.size===0&&o.size===0))){if(a.size>0){let e=Array.from(a),t=`${n} add ${e.join(` `)}`;console.log(`\n Installing ${e.length} dependency(ies):`);for(let t of e)console.log(` + ${t}`);console.log();try{w(t,{stdio:`inherit`})}catch{console.log(`\n Installation failed. Run manually:\n ${t}\n`)}}if(o.size>0){let e=Array.from(o),t=`${n} add -D ${e.join(` `)}`;console.log(`\n Installing ${e.length} dev dependency(ies):`);for(let t of e)console.log(` + ${t} (dev)`);console.log();try{w(t,{stdio:`inherit`})}catch{console.log(`\n Installation failed. Run manually:\n ${t}\n`)}}console.log(` Done!
1194
- `)}})}const Tt=[{value:`auth`,label:`Auth`,hint:`JWT, OAuth, API keys`},{value:`swagger`,label:`Swagger`,hint:`OpenAPI docs`},{value:`ws`,label:`WebSocket`,hint:`rooms, heartbeat`},{value:`queue`,label:`Queue`,hint:`BullMQ/RabbitMQ/Kafka`},{value:`devtools`,label:`DevTools`,hint:`debug dashboard`}];function Et(e){e.command(`new [name]`).alias(`init`).description(`Create a new KickJS project (use "." for current directory)`).option(`-d, --directory <dir>`,`Target directory (defaults to project name)`).option(`--pm <manager>`,`Package manager: pnpm | npm | yarn | bun`).option(`--git`,`Initialize git repository`).option(`--no-git`,`Skip git initialization`).option(`--install`,`Install dependencies after scaffolding`).option(`--no-install`,`Skip dependency installation`).option(`-f, --force`,`Remove existing files without prompting`).option(`-t, --template <type>`,`Project template: rest | ddd | cqrs | minimal`).option(`-r, --repo <type>`,`Default repository: prisma | drizzle | inmemory | custom`).option(`--packages <packages>`,`Comma-separated packages to include (e.g. auth,swagger,ws,queue)`).option(`-y, --yes`,`Pick safe defaults for every prompt (template=minimal, repo=inmemory, no extras, git+install on)`).option(`--non-interactive`,`alias for --yes`).action(async(e,t)=>{dt(`KickJS — Create a new project`);let n=!!(t.yes||t.nonInteractive);e||=n?`my-api`:await pt({message:`Project name`,placeholder:`my-api`,defaultValue:`my-api`});let r;if(e===`.`?(r=S(`.`),e=ie(r)):r=S(t.directory||e),h(r)){let i=ee(r);if(i.length>0){if(t.force)I.warn(`Clearing existing files in ${r}`);else if(n){I.warn(`Directory "${e}" is not empty. Pass --force to clear it.`),P(`Aborted.`);return}else{I.warn(`Directory "${e}" is not empty:`);let t=i.slice(0,5);for(let e of t)I.message(` - ${e}`);if(i.length>5&&I.message(` ... and ${i.length-5} more`),!await F({message:O.red(`Remove all existing files and proceed?`),initialValue:!1})){P(`Aborted.`);return}}for(let e of i)te(S(r,e),{recursive:!0,force:!0})}}let i=t.template;i||=n?`minimal`:await mt({message:`Project template`,options:[{value:`rest`,label:`REST API`,hint:`Express + Swagger`},{value:`ddd`,label:`DDD`,hint:`Domain-Driven Design modules`},{value:`cqrs`,label:`CQRS`,hint:`Commands, Queries, Events + WS/Queue`},{value:`minimal`,label:`Minimal`,hint:`bare Express`}]});let a=t.pm;a||=n?await xt(void 0):await mt({message:`Package manager`,options:[{value:`pnpm`,label:`pnpm`},{value:`npm`,label:`npm`},{value:`yarn`,label:`yarn`},{value:`bun`,label:`bun`}]});let o=t.repo;o||(n?o=`inmemory`:(o=await mt({message:`Default repository/ORM`,options:[{value:`prisma`,label:`Prisma`},{value:`drizzle`,label:`Drizzle`},{value:`inmemory`,label:`In-Memory`},{value:`custom`,label:`Custom`,hint:`specify later`}]}),o===`custom`&&(o=await pt({message:`Custom repository name`,defaultValue:`custom`}))));let s;if(t.packages!==void 0){let e=t.packages.trim().toLowerCase();s=e===``||e===`none`||e===`false`?[]:t.packages.split(`,`).map(e=>e.trim()).filter(Boolean)}else s=n?[]:await ht({message:`Select packages to include`,options:[...Tt],required:!1});let c;c=t.git===void 0?n?!0:await F({message:`Initialize git repository?`,initialValue:!0}):t.git;let l;l=t.install===void 0?n?!0:await F({message:`Install dependencies?`,initialValue:!0}):t.install,await st({name:e,directory:r,packageManager:a,initGit:c,installDeps:l,template:i,defaultRepo:o,packages:s}),P(`Done! Next steps: ${O.cyan(`cd ${e} && ${a} dev`)}`)})}function R(e){return e.replace(/[-_\s]+(.)?/g,(e,t)=>t?t.toUpperCase():``).replace(/^(.)/,e=>e.toUpperCase())}function z(e){let t=R(e);return t.charAt(0).toLowerCase()+t.slice(1)}function B(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()}function V(e){return he.plural(e)}function Dt(e){return he.plural(e)}function Ot(e){return B(e).replace(/-/g,`_`)}function kt(e){let t=e.cwd??process.cwd(),n=e.pluralize??!0,r=R(e.name),i=z(e.name),a=B(e.name),o=Ot(e.name),s={name:e.name,pascal:r,camel:i,kebab:a,snake:o,modulesDir:e.modulesDir??`src/modules`,cwd:t,args:e.args??[],flags:e.flags??{}};if(n){let e=V(a);s.pluralKebab=e,s.pluralPascal=R(e),s.pluralCamel=z(e)}return s}function At(e,t){return S(e.cwd,t)}async function jt(e){return import(C(e).href)}const Mt=new Map;async function Nt(e){let t=Mt.get(e);if(t)return t;let n=Pt(e);return Mt.set(e,n),n}async function Pt(e){let t=S(e,`package.json`);if(!h(t))return{generators:[],loaded:[],failed:[]};let n=Ft(JSON.parse(await T(t,`utf-8`))),r=p(S(e,`package.json`)),i=[],a=[],o=[];for(let e of n){let t;try{t=r.resolve(`${e}/package.json`)}catch{continue}let n;try{n=JSON.parse(await T(t,`utf-8`))}catch(t){o.push({source:e,reason:`failed to parse package.json: ${t}`});continue}if(!n.kickjs?.generators)continue;let s=n.kickjs.generators,c=S(y(t),s);if(!h(c)){o.push({source:e,reason:`kickjs.generators points to missing file: ${s}`});continue}let l;try{l=await jt(c)}catch(t){o.push({source:e,reason:`failed to import manifest: ${t}`});continue}let u=l.default;if(!Array.isArray(u)){o.push({source:e,reason:`manifest's default export is not an array of GeneratorSpec`});continue}for(let t of u){if(!It(t)){o.push({source:e,reason:`manifest entry is not a valid GeneratorSpec (missing name/files)`});continue}i.push({source:e,spec:t})}a.push(e)}return{generators:i,loaded:a,failed:o}}function Ft(e){let t=new Set;for(let n of[e.dependencies,e.devDependencies,e.peerDependencies])if(n)for(let e of Object.keys(n))t.add(e);return Array.from(t)}function It(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.name==`string`&&typeof t.files==`function`}async function Lt(e,t=[]){let n=e.cwd??process.cwd(),r=t.find(t=>t.spec.name===e.generatorName);if(r)return Bt(r.spec,r.source,e,n);let i=zt(await Nt(n),e.generatorName);return i?Bt(i.spec,i.source,e,n):null}async function Rt(e,t=[]){let n=await Nt(e),r=new Set(t.map(e=>e.spec.name)),i=n.generators.filter(e=>!r.has(e.spec.name));return{generators:[...t,...i],loaded:n.loaded,failed:n.failed}}function zt(e,t){return e.generators.find(e=>e.spec.name===t)}async function Bt(e,t,n,r){let i=kt({name:n.itemName,args:n.args,flags:n.flags,modulesDir:n.modulesDir,pluralize:n.pluralize,cwd:r}),a=await e.files(i),o=[];for(let e of a){let t=At(i,e.path);await A(t,e.content),o.push(t)}return{files:o,source:t}}const Vt={inmemory:`in-memory`,drizzle:`Drizzle`,prisma:`Prisma`};function Ht(e){return e.charAt(0).toUpperCase()+e.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase())}function Ut(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}function Wt(e){return Vt[e]??Ht(e)}function Gt(e,t,n){let r={inmemory:`InMemory${e}Repository`,drizzle:`Drizzle${e}Repository`,prisma:`Prisma${e}Repository`},i={inmemory:`in-memory-${t}`,drizzle:`drizzle-${t}`,prisma:`prisma-${t}`};return{repoClass:r[n]??`${Ht(n)}${e}Repository`,repoFile:i[n]??`${Ut(n)}-${t}`}}function Kt(e){let{pascal:t,kebab:n,plural:r=``,repo:i}=e,{repoClass:a,repoFile:o}=Gt(t,n,i);return`/**
1196
+ Usage: kick add auth drizzle swagger`),console.log(` kick add queue:bullmq`),console.log()}function Et(e){e.command(`list`).alias(`ls`).description(`List KickJS packages (core only; pair with --all for the full catalog)`).option(`--all`,`Include the full optional catalog`).action(e=>{Tt(!!e.all)})}function Dt(e){e.command(`add [packages...]`).description(`Add KickJS packages with their required dependencies`).option(`--pm <manager>`,`Package manager override`).option(`-D, --dev`,`Install as dev dependency`).option(`--list`,`List packages (core only by default; pair with --all)`).option(`--all`,`When listing, include the full optional catalog`).action(async(e,t)=>{if(t.list||e.length===0){Tt(!!t.all);return}let{pm:n,source:r}=await Ct(t.pm);console.log(`\n Using ${n} (resolved from ${r})`);let i=t.dev,a=new Set,o=new Set,s=[];for(let t of e){let e=bt[t];if(!e){s.push(t);continue}let n=i||e.dev?o:a;n.add(e.pkg);for(let t of e.peers)n.add(t)}if(!(s.length>0&&(console.log(`\n Unknown packages: ${s.join(`, `)}`),console.log(` Run "kick add --list" to see available packages.
1197
+ `),a.size===0&&o.size===0))){if(a.size>0){let e=Array.from(a),t=`${n} add ${e.join(` `)}`;console.log(`\n Installing ${e.length} dependency(ies):`);for(let t of e)console.log(` + ${t}`);console.log();try{T(t,{stdio:`inherit`})}catch{console.log(`\n Installation failed. Run manually:\n ${t}\n`)}}if(o.size>0){let e=Array.from(o),t=`${n} add -D ${e.join(` `)}`;console.log(`\n Installing ${e.length} dev dependency(ies):`);for(let t of e)console.log(` + ${t} (dev)`);console.log();try{T(t,{stdio:`inherit`})}catch{console.log(`\n Installation failed. Run manually:\n ${t}\n`)}}console.log(` Done!
1198
+ `)}})}const Ot=[{value:`auth`,label:`Auth`,hint:`JWT, OAuth, API keys`},{value:`swagger`,label:`Swagger`,hint:`OpenAPI docs`},{value:`ws`,label:`WebSocket`,hint:`rooms, heartbeat`},{value:`queue`,label:`Queue`,hint:`BullMQ/RabbitMQ/Kafka`},{value:`devtools`,label:`DevTools`,hint:`debug dashboard`}];function kt(e){e.command(`new [name]`).alias(`init`).description(`Create a new KickJS project (use "." for current directory)`).option(`-d, --directory <dir>`,`Target directory (defaults to project name)`).option(`--pm <manager>`,`Package manager: pnpm | npm | yarn | bun`).option(`--git`,`Initialize git repository`).option(`--no-git`,`Skip git initialization`).option(`--install`,`Install dependencies after scaffolding`).option(`--no-install`,`Skip dependency installation`).option(`-f, --force`,`Remove existing files without prompting`).option(`-t, --template <type>`,`Project template: rest | ddd | cqrs | minimal`).option(`-r, --repo <type>`,`Default repository: prisma | drizzle | inmemory | custom`).option(`--packages <packages>`,`Comma-separated packages to include (e.g. auth,swagger,ws,queue)`).option(`-y, --yes`,`Pick safe defaults for every prompt (template=minimal, repo=inmemory, no extras, git+install on)`).option(`--non-interactive`,`alias for --yes`).action(async(e,t)=>{mt(`KickJS — Create a new project`);let n=!!(t.yes||t.nonInteractive);e||=n?`my-api`:await gt({message:`Project name`,placeholder:`my-api`,defaultValue:`my-api`});let r;if(e===`.`?(r=C(`.`),e=re(r)):r=C(t.directory||e),h(r)){let i=ee(r);if(i.length>0){if(t.force)I.warn(`Clearing existing files in ${r}`);else if(n){I.warn(`Directory "${e}" is not empty. Pass --force to clear it.`),P(`Aborted.`);return}else{I.warn(`Directory "${e}" is not empty:`);let t=i.slice(0,5);for(let e of t)I.message(` - ${e}`);if(i.length>5&&I.message(` ... and ${i.length-5} more`),!await F({message:k.red(`Remove all existing files and proceed?`),initialValue:!1})){P(`Aborted.`);return}}for(let e of i)v(C(r,e),{recursive:!0,force:!0})}}let i=t.template;i||=n?`minimal`:await _t({message:`Project template`,options:[{value:`rest`,label:`REST API`,hint:`Express + Swagger`},{value:`ddd`,label:`DDD`,hint:`Domain-Driven Design modules`},{value:`cqrs`,label:`CQRS`,hint:`Commands, Queries, Events + WS/Queue`},{value:`minimal`,label:`Minimal`,hint:`bare Express`}]});let a=t.pm;a||=n?await wt(void 0):await _t({message:`Package manager`,options:[{value:`pnpm`,label:`pnpm`},{value:`npm`,label:`npm`},{value:`yarn`,label:`yarn`},{value:`bun`,label:`bun`}]});let o=t.repo;o||(n?o=`inmemory`:(o=await _t({message:`Default repository/ORM`,options:[{value:`prisma`,label:`Prisma`},{value:`drizzle`,label:`Drizzle`},{value:`inmemory`,label:`In-Memory`},{value:`custom`,label:`Custom`,hint:`specify later`}]}),o===`custom`&&(o=await gt({message:`Custom repository name`,defaultValue:`custom`}))));let s;if(t.packages!==void 0){let e=t.packages.trim().toLowerCase();s=e===``||e===`none`||e===`false`?[]:t.packages.split(`,`).map(e=>e.trim()).filter(Boolean)}else s=n?[]:await vt({message:`Select packages to include`,options:[...Ot],required:!1});let c;c=t.git===void 0?n?!0:await F({message:`Initialize git repository?`,initialValue:!0}):t.git;let l;l=t.install===void 0?n?!0:await F({message:`Install dependencies?`,initialValue:!0}):t.install,await ut({name:e,directory:r,packageManager:a,initGit:c,installDeps:l,template:i,defaultRepo:o,packages:s}),P(`Done! Next steps: ${k.cyan(`cd ${e} && ${a} dev`)}`)})}function R(e){return e.replace(/[-_\s]+(.)?/g,(e,t)=>t?t.toUpperCase():``).replace(/^(.)/,e=>e.toUpperCase())}function z(e){let t=R(e);return t.charAt(0).toLowerCase()+t.slice(1)}function B(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()}function V(e){return _e.plural(e)}function At(e){return _e.plural(e)}function jt(e){return B(e).replace(/-/g,`_`)}function Mt(e){let t=e.cwd??process.cwd(),n=e.pluralize??!0,r=R(e.name),i=z(e.name),a=B(e.name),o=jt(e.name),s={name:e.name,pascal:r,camel:i,kebab:a,snake:o,modulesDir:e.modulesDir??`src/modules`,cwd:t,args:e.args??[],flags:e.flags??{}};if(n){let e=V(a);s.pluralKebab=e,s.pluralPascal=R(e),s.pluralCamel=z(e)}return s}function Nt(e,t){return C(e.cwd,t)}async function Pt(e){return import(w(e).href)}const Ft=new Map;async function It(e){let t=Ft.get(e);if(t)return t;let n=Lt(e);return Ft.set(e,n),n}async function Lt(e){let t=C(e,`package.json`);if(!h(t))return{generators:[],loaded:[],failed:[]};let n=Rt(JSON.parse(await E(t,`utf-8`))),r=p(C(e,`package.json`)),i=[],a=[],o=[];for(let e of n){let t;try{t=r.resolve(`${e}/package.json`)}catch{continue}let n;try{n=JSON.parse(await E(t,`utf-8`))}catch(t){o.push({source:e,reason:`failed to parse package.json: ${t}`});continue}if(!n.kickjs?.generators)continue;let s=n.kickjs.generators,c=C(b(t),s);if(!h(c)){o.push({source:e,reason:`kickjs.generators points to missing file: ${s}`});continue}let l;try{l=await Pt(c)}catch(t){o.push({source:e,reason:`failed to import manifest: ${t}`});continue}let u=l.default;if(!Array.isArray(u)){o.push({source:e,reason:`manifest's default export is not an array of GeneratorSpec`});continue}for(let t of u){if(!zt(t)){o.push({source:e,reason:`manifest entry is not a valid GeneratorSpec (missing name/files)`});continue}i.push({source:e,spec:t})}a.push(e)}return{generators:i,loaded:a,failed:o}}function Rt(e){let t=new Set;for(let n of[e.dependencies,e.devDependencies,e.peerDependencies])if(n)for(let e of Object.keys(n))t.add(e);return Array.from(t)}function zt(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.name==`string`&&typeof t.files==`function`}async function Bt(e,t=[]){let n=e.cwd??process.cwd(),r=t.find(t=>t.spec.name===e.generatorName);if(r)return Ut(r.spec,r.source,e,n);let i=Ht(await It(n),e.generatorName);return i?Ut(i.spec,i.source,e,n):null}async function Vt(e,t=[]){let n=await It(e),r=new Set(t.map(e=>e.spec.name)),i=n.generators.filter(e=>!r.has(e.spec.name));return{generators:[...t,...i],loaded:n.loaded,failed:n.failed}}function Ht(e,t){return e.generators.find(e=>e.spec.name===t)}async function Ut(e,t,n,r){let i=Mt({name:n.itemName,args:n.args,flags:n.flags,modulesDir:n.modulesDir,pluralize:n.pluralize,cwd:r}),a=await e.files(i),o=[];for(let e of a){let t=Nt(i,e.path);await j(t,e.content),o.push(t)}return{files:o,source:t}}const Wt={inmemory:`in-memory`,drizzle:`Drizzle`,prisma:`Prisma`};function Gt(e){return e.charAt(0).toUpperCase()+e.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase())}function Kt(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}function qt(e){return Wt[e]??Gt(e)}function Jt(e,t,n){let r={inmemory:`InMemory${e}Repository`,drizzle:`Drizzle${e}Repository`,prisma:`Prisma${e}Repository`},i={inmemory:`in-memory-${t}`,drizzle:`drizzle-${t}`,prisma:`prisma-${t}`};return{repoClass:r[n]??`${Gt(n)}${e}Repository`,repoFile:i[n]??`${Kt(n)}-${t}`}}function Yt(e){return e??`define`}function Xt(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,{repoClass:o,repoFile:s}=Jt(t,n,i),c=Yt(a),l=`/**
1195
1199
  * ${t} Module
1196
1200
  *
1197
1201
  * Self-contained feature module following Domain-Driven Design (DDD).
@@ -1201,46 +1205,84 @@ description: Patterns to refuse outright when the user asks for them — they br
1201
1205
  * presentation/ — HTTP controllers (entry points)
1202
1206
  * application/ — Use cases (orchestration) and DTOs (validation)
1203
1207
  * domain/ — Entities, value objects, repository interfaces, domain services
1204
- * infrastructure/ — Repository implementations (currently ${Wt(i)})
1205
- */
1206
- import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
1207
- import { buildRoutes } from '@forinda/kickjs'
1208
- import { ${t.toUpperCase()}_REPOSITORY } from './domain/repositories/${n}.repository'
1209
- import { ${a} } from './infrastructure/repositories/${o}.repository'
1208
+ * infrastructure/ — Repository implementations (currently ${qt(i)})
1209
+ */`,u=`import { ${t.toUpperCase()}_REPOSITORY } from './domain/repositories/${n}.repository'
1210
+ import { ${o} } from './infrastructure/repositories/${s}.repository'
1210
1211
  import { ${t}Controller } from './presentation/${n}.controller'
1211
1212
 
1212
1213
  // Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container
1213
1214
  import.meta.glob(
1214
1215
  ['./domain/services/**/*.ts', './application/use-cases/**/*.ts', '!./**/*.test.ts'],
1215
1216
  { eager: true },
1216
- )
1217
+ )`,d=` /**
1218
+ * Declare HTTP routes for this module.
1219
+ *
1220
+ * The path is prefixed with the global apiPrefix and version
1221
+ * (e.g. /api/v1/${r}). The framework derives the Express
1222
+ * Router from the controller via \`buildRoutes()\` and uses the
1223
+ * same controller for OpenAPI spec generation via SwaggerAdapter.
1224
+ *
1225
+ * Return an **array** to mount multiple route sets under the
1226
+ * same module (e.g. side-by-side v1 + v2 controllers). Each
1227
+ * entry can override the API version with a \`version\` field —
1228
+ * the mount path becomes \`/{apiPrefix}/v{version}{path}\`:
1229
+ *
1230
+ * return [
1231
+ * { path: '/${r}', version: 1, controller: ${t}V1Controller },
1232
+ * { path: '/${r}', version: 2, controller: ${t}V2Controller },
1233
+ * ]
1234
+ */`;return c===`class`?`${l}
1235
+ import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
1236
+ ${u}
1217
1237
 
1218
1238
  export class ${t}Module implements AppModule {
1219
1239
  /**
1220
1240
  * Register module dependencies in the DI container.
1221
1241
  * Bind repository interface tokens to their implementations here.
1222
- * Currently wired to ${Wt(i)}. To swap implementations, change the factory target.
1242
+ * Currently wired to ${qt(i)}. To swap implementations, change the factory target.
1223
1243
  */
1224
1244
  register(container: Container): void {
1225
1245
  container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
1226
- container.resolve(${a}),
1246
+ container.resolve(${o}),
1227
1247
  )
1228
1248
  }
1229
1249
 
1230
- /**
1231
- * Declare HTTP routes for this module.
1232
- * The path is prefixed with the global apiPrefix and version (e.g. /api/v1/${r}).
1233
- * Passing 'controller' enables automatic OpenAPI spec generation via SwaggerAdapter.
1234
- */
1250
+ ${d.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
1235
1251
  routes(): ModuleRoutes {
1236
1252
  return {
1237
1253
  path: '/${r}',
1238
- router: buildRoutes(${t}Controller),
1239
1254
  controller: ${t}Controller,
1240
1255
  }
1241
1256
  }
1242
1257
  }
1243
- `}function qt(e){let{pascal:t,kebab:n,plural:r=``,repo:i}=e,{repoClass:a,repoFile:o}=Gt(t,n,i);return`/**
1258
+ `:`${l}
1259
+ import { defineModule } from '@forinda/kickjs'
1260
+ ${u}
1261
+
1262
+ export const ${t}Module = defineModule({
1263
+ name: '${t}Module',
1264
+ build: () => ({
1265
+ /**
1266
+ * Register module dependencies in the DI container.
1267
+ * Bind repository interface tokens to their implementations here.
1268
+ * Currently wired to ${qt(i)}. To swap implementations, change the factory target.
1269
+ */
1270
+ register(container) {
1271
+ container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
1272
+ container.resolve(${o}),
1273
+ )
1274
+ },
1275
+
1276
+ ${d}
1277
+ routes() {
1278
+ return {
1279
+ path: '/${r}',
1280
+ controller: ${t}Controller,
1281
+ }
1282
+ },
1283
+ }),
1284
+ })
1285
+ `}function Zt(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,{repoClass:o,repoFile:s}=Jt(t,n,i),c=Yt(a),l=`/**
1244
1286
  * ${t} Module
1245
1287
  *
1246
1288
  * REST module with a flat folder structure.
@@ -1250,47 +1292,107 @@ export class ${t}Module implements AppModule {
1250
1292
  * ${n}.controller.ts — HTTP routes (CRUD)
1251
1293
  * ${n}.service.ts — Business logic
1252
1294
  * ${n}.repository.ts — Repository interface
1253
- * ${o}.repository.ts — Repository implementation
1295
+ * ${s}.repository.ts — Repository implementation
1254
1296
  * dtos/ — Request/response schemas
1255
- */
1256
- import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
1257
- import { buildRoutes } from '@forinda/kickjs'
1258
- import { ${t.toUpperCase()}_REPOSITORY } from './${n}.repository'
1259
- import { ${a} } from './${o}.repository'
1297
+ */`,u=`import { ${t.toUpperCase()}_REPOSITORY } from './${n}.repository'
1298
+ import { ${o} } from './${s}.repository'
1260
1299
  import { ${t}Controller } from './${n}.controller'
1261
1300
 
1262
1301
  // Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container
1263
- import.meta.glob(['./**/*.service.ts', './**/*.repository.ts', '!./**/*.test.ts'], { eager: true })
1302
+ import.meta.glob(['./**/*.service.ts', './**/*.repository.ts', '!./**/*.test.ts'], { eager: true })`,d=` /**
1303
+ * Declare HTTP routes for this module.
1304
+ *
1305
+ * Pass \`controller\` and the framework derives the Express
1306
+ * Router via \`buildRoutes()\` and uses the same controller for
1307
+ * OpenAPI spec generation through SwaggerAdapter.
1308
+ *
1309
+ * Return an **array** to mount multiple route sets under the
1310
+ * same module (side-by-side v1 + v2 controllers, admin surfaces).
1311
+ * Each entry can override the API version with a \`version\` field:
1312
+ *
1313
+ * return [
1314
+ * { path: '/${r}', version: 1, controller: ${t}V1Controller },
1315
+ * { path: '/${r}', version: 2, controller: ${t}V2Controller },
1316
+ * ]
1317
+ */`;return c===`class`?`${l}
1318
+ import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
1319
+ ${u}
1264
1320
 
1265
1321
  export class ${t}Module implements AppModule {
1266
1322
  register(container: Container): void {
1267
1323
  container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
1268
- container.resolve(${a}),
1324
+ container.resolve(${o}),
1269
1325
  )
1270
1326
  }
1271
1327
 
1328
+ ${d.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
1272
1329
  routes(): ModuleRoutes {
1273
1330
  return {
1274
1331
  path: '/${r}',
1275
- router: buildRoutes(${t}Controller),
1276
1332
  controller: ${t}Controller,
1277
1333
  }
1278
1334
  }
1279
1335
  }
1280
- `}function Jt(e){let{pascal:t,kebab:n,plural:r=``}=e;return`import { type AppModule, type ModuleRoutes } from '@forinda/kickjs'
1281
- import { buildRoutes } from '@forinda/kickjs'
1336
+ `:`${l}
1337
+ import { defineModule } from '@forinda/kickjs'
1338
+ ${u}
1339
+
1340
+ export const ${t}Module = defineModule({
1341
+ name: '${t}Module',
1342
+ build: () => ({
1343
+ register(container) {
1344
+ container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
1345
+ container.resolve(${o}),
1346
+ )
1347
+ },
1348
+
1349
+ ${d}
1350
+ routes() {
1351
+ return {
1352
+ path: '/${r}',
1353
+ controller: ${t}Controller,
1354
+ }
1355
+ },
1356
+ }),
1357
+ })
1358
+ `}function Qt(e){let{pascal:t,kebab:n,plural:r=``,style:i}=e,a=Yt(i),o=` /**
1359
+ * Pass \`controller\` and the framework derives the Express
1360
+ * Router via \`buildRoutes()\`. Return an array to mount multiple
1361
+ * route sets — each entry can override the API version with a
1362
+ * \`version\` field:
1363
+ *
1364
+ * return [
1365
+ * { path: '/${r}', version: 1, controller: ${t}V1Controller },
1366
+ * { path: '/${r}', version: 2, controller: ${t}V2Controller },
1367
+ * ]
1368
+ */`;return a===`class`?`import { type AppModule, type ModuleRoutes } from '@forinda/kickjs'
1282
1369
  import { ${t}Controller } from './${n}.controller'
1283
1370
 
1284
1371
  export class ${t}Module implements AppModule {
1372
+ ${o.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
1285
1373
  routes(): ModuleRoutes {
1286
1374
  return {
1287
1375
  path: '/${r}',
1288
- router: buildRoutes(${t}Controller),
1289
1376
  controller: ${t}Controller,
1290
1377
  }
1291
1378
  }
1292
1379
  }
1293
- `}function Yt(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
1380
+ `:`import { defineModule } from '@forinda/kickjs'
1381
+ import { ${t}Controller } from './${n}.controller'
1382
+
1383
+ export const ${t}Module = defineModule({
1384
+ name: '${t}Module',
1385
+ build: () => ({
1386
+ ${o}
1387
+ routes() {
1388
+ return {
1389
+ path: '/${r}',
1390
+ controller: ${t}Controller,
1391
+ }
1392
+ },
1393
+ }),
1394
+ })
1395
+ `}function $t(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
1294
1396
  import { ApiTags } from '@forinda/kickjs-swagger'
1295
1397
  import { Create${t}UseCase } from '../application/use-cases/create-${n}.use-case'
1296
1398
  import { Get${t}UseCase } from '../application/use-cases/get-${n}.use-case'
@@ -1353,7 +1455,7 @@ export class ${t}Controller {
1353
1455
  ctx.noContent()
1354
1456
  }
1355
1457
  }
1356
- `}function Xt(e){let{pascal:t,kebab:n}=e,r=t.charAt(0).toLowerCase()+t.slice(1);return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
1458
+ `}function en(e){let{pascal:t,kebab:n}=e,r=t.charAt(0).toLowerCase()+t.slice(1);return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
1357
1459
  import { ApiTags } from '@forinda/kickjs-swagger'
1358
1460
  import { ${t}Service } from './${n}.service'
1359
1461
  import { create${t}Schema } from './dtos/create-${n}.dto'
@@ -1408,14 +1510,14 @@ export class ${t}Controller {
1408
1510
  ctx.noContent()
1409
1511
  }
1410
1512
  }
1411
- `}function Zt(e){let{pascal:t}=e;return`import type { QueryParamsConfig } from '@forinda/kickjs'
1513
+ `}function tn(e){let{pascal:t}=e;return`import type { QueryParamsConfig } from '@forinda/kickjs'
1412
1514
 
1413
1515
  export const ${t.toUpperCase()}_QUERY_CONFIG: QueryParamsConfig = {
1414
1516
  filterable: ['name'],
1415
1517
  sortable: ['name', 'createdAt'],
1416
1518
  searchable: ['name'],
1417
1519
  }
1418
- `}function Qt(e){let{pascal:t}=e;return`import { z } from 'zod'
1520
+ `}function nn(e){let{pascal:t}=e;return`import { z } from 'zod'
1419
1521
 
1420
1522
  /**
1421
1523
  * Create ${t} DTO — Zod schema for validating POST request bodies.
@@ -1431,20 +1533,20 @@ export const create${t}Schema = z.object({
1431
1533
  })
1432
1534
 
1433
1535
  export type Create${t}DTO = z.infer<typeof create${t}Schema>
1434
- `}function $t(e){let{pascal:t}=e;return`import { z } from 'zod'
1536
+ `}function rn(e){let{pascal:t}=e;return`import { z } from 'zod'
1435
1537
 
1436
1538
  export const update${t}Schema = z.object({
1437
1539
  name: z.string().min(1).max(200).optional(),
1438
1540
  })
1439
1541
 
1440
1542
  export type Update${t}DTO = z.infer<typeof update${t}Schema>
1441
- `}function en(e){let{pascal:t}=e;return`export interface ${t}ResponseDTO {
1543
+ `}function an(e){let{pascal:t}=e;return`export interface ${t}ResponseDTO {
1442
1544
  id: string
1443
1545
  name: string
1444
1546
  createdAt: string
1445
1547
  updatedAt: string
1446
1548
  }
1447
- `}function tn(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return[{file:`create-${n}.use-case.ts`,content:`/**
1549
+ `}function on(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return[{file:`create-${n}.use-case.ts`,content:`/**
1448
1550
  * Create ${t} Use Case
1449
1551
  *
1450
1552
  * Application layer — orchestrates a single business operation.
@@ -1522,7 +1624,7 @@ export class Delete${t}UseCase {
1522
1624
  await this.repo.delete(id)
1523
1625
  }
1524
1626
  }
1525
- `}]}function nn(e){let{pascal:t,kebab:n,dtoPrefix:r=`../../application/dtos`,tokenScope:i=`app`}=e;return`/**
1627
+ `}]}function sn(e){let{pascal:t,kebab:n,dtoPrefix:r=`../../application/dtos`,tokenScope:i=`app`}=e;return`/**
1526
1628
  * ${t} Repository Interface
1527
1629
  *
1528
1630
  * Defines the contract for data access.
@@ -1617,7 +1719,7 @@ export class InMemory${t}Repository implements I${t}Repository {
1617
1719
  this.store.delete(id)
1618
1720
  }
1619
1721
  }
1620
- `}function rn(e){let{pascal:t,kebab:n,repoType:r=``,repoPrefix:i=`../../domain/repositories`,dtoPrefix:a=`../../application/dtos`}=e,o=r.charAt(0).toUpperCase()+r.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase());return`/**
1722
+ `}function cn(e){let{pascal:t,kebab:n,repoType:r=``,repoPrefix:i=`../../domain/repositories`,dtoPrefix:a=`../../application/dtos`}=e,o=r.charAt(0).toUpperCase()+r.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase());return`/**
1621
1723
  * ${o} ${t} Repository
1622
1724
  *
1623
1725
  * Stub implementation for a custom '${r}' repository.
@@ -1686,7 +1788,7 @@ export class ${o}${t}Repository implements I${t}Repository {
1686
1788
  this.store.delete(id)
1687
1789
  }
1688
1790
  }
1689
- `}function an(e){let{pascal:t,kebab:n}=e;return`/**
1791
+ `}function ln(e){let{pascal:t,kebab:n}=e;return`/**
1690
1792
  * ${t} Domain Service
1691
1793
  *
1692
1794
  * Domain layer — contains business rules that don't belong to a single entity.
@@ -1709,7 +1811,7 @@ export class ${t}DomainService {
1709
1811
  }
1710
1812
  }
1711
1813
  }
1712
- `}function on(e){let{pascal:t,kebab:n}=e;return`/**
1814
+ `}function un(e){let{pascal:t,kebab:n}=e;return`/**
1713
1815
  * ${t} Entity
1714
1816
  *
1715
1817
  * Domain layer — the core business object.
@@ -1778,7 +1880,7 @@ export class ${t} {
1778
1880
  }
1779
1881
  }
1780
1882
  }
1781
- `}function sn(e){let{pascal:t}=e;return`/**
1883
+ `}function dn(e){let{pascal:t}=e;return`/**
1782
1884
  * ${t} ID Value Object
1783
1885
  *
1784
1886
  * Domain layer — wraps a primitive ID with type safety and validation.
@@ -1812,7 +1914,7 @@ export class ${t}Id {
1812
1914
  return this.value === other.value
1813
1915
  }
1814
1916
  }
1815
- `}function cn(e){let{pascal:t,kebab:n,plural:r=``}=e;return`import { describe, it, expect, beforeEach } from 'vitest'
1917
+ `}function fn(e){let{pascal:t,kebab:n,plural:r=``}=e;return`import { describe, it, expect, beforeEach } from 'vitest'
1816
1918
  import { Container } from '@forinda/kickjs'
1817
1919
 
1818
1920
  describe('${t}Controller', () => {
@@ -1864,7 +1966,7 @@ describe('${t}Controller', () => {
1864
1966
  })
1865
1967
  })
1866
1968
  })
1867
- `}function ln(e){let{pascal:t,kebab:n,plural:r=``,repoPrefix:i=`../infrastructure/repositories/in-memory-${n}.repository`}=e;return`import { describe, it, expect, beforeEach } from 'vitest'
1969
+ `}function pn(e){let{pascal:t,kebab:n,plural:r=``,repoPrefix:i=`../infrastructure/repositories/in-memory-${n}.repository`}=e;return`import { describe, it, expect, beforeEach } from 'vitest'
1868
1970
  import { InMemory${t}Repository } from '${i}'
1869
1971
 
1870
1972
  describe('InMemory${t}Repository', () => {
@@ -1926,7 +2028,7 @@ describe('InMemory${t}Repository', () => {
1926
2028
  expect(found).toBeNull()
1927
2029
  })
1928
2030
  })
1929
- `}function un(e){let{pascal:t,kebab:n}=e;return`import { Service, Inject, HttpException } from '@forinda/kickjs'
2031
+ `}function mn(e){let{pascal:t,kebab:n}=e;return`import { Service, Inject, HttpException } from '@forinda/kickjs'
1930
2032
  import type { ParsedQuery } from '@forinda/kickjs'
1931
2033
  import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from './${n}.repository'
1932
2034
  import type { ${t}ResponseDTO } from './dtos/${n}-response.dto'
@@ -1963,14 +2065,14 @@ export class ${t}Service {
1963
2065
  await this.repo.delete(id)
1964
2066
  }
1965
2067
  }
1966
- `}function dn(e){let{pascal:t}=e;return`import type { QueryFieldConfig } from '@forinda/kickjs'
2068
+ `}function hn(e){let{pascal:t}=e;return`import type { QueryFieldConfig } from '@forinda/kickjs'
1967
2069
 
1968
2070
  export const ${t.toUpperCase()}_QUERY_CONFIG: QueryFieldConfig = {
1969
2071
  filterable: ['name'],
1970
2072
  sortable: ['name', 'createdAt'],
1971
2073
  searchable: ['name'],
1972
2074
  }
1973
- `}function fn(e){let{pascal:t,kebab:n,plural:r=``,repo:i}=e,a={inmemory:`InMemory${t}Repository`,drizzle:`Drizzle${t}Repository`,prisma:`Prisma${t}Repository`},o={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},s=a[i]??a.inmemory,c=o[i]??o.inmemory;return`/**
2075
+ `}function gn(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,o={inmemory:`InMemory${t}Repository`,drizzle:`Drizzle${t}Repository`,prisma:`Prisma${t}Repository`},s={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},c=o[i]??o.inmemory,l=s[i]??s.inmemory,u=a??`define`,d=`/**
1974
2076
  * ${t} Module — CQRS Pattern
1975
2077
  *
1976
2078
  * Separates read (queries) and write (commands) operations.
@@ -1982,11 +2084,8 @@ export const ${t.toUpperCase()}_QUERY_CONFIG: QueryFieldConfig = {
1982
2084
  * queries/ — Read operations (get, list)
1983
2085
  * events/ — Domain events + handlers (WS broadcast, queue dispatch)
1984
2086
  * dtos/ — Request/response schemas
1985
- */
1986
- import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
1987
- import { buildRoutes } from '@forinda/kickjs'
1988
- import { ${t.toUpperCase()}_REPOSITORY } from './${n}.repository'
1989
- import { ${s} } from './${c}.repository'
2087
+ */`,f=`import { ${t.toUpperCase()}_REPOSITORY } from './${n}.repository'
2088
+ import { ${c} } from './${l}.repository'
1990
2089
  import { ${t}Controller } from './${n}.controller'
1991
2090
 
1992
2091
  // Eagerly load decorated classes
@@ -1998,24 +2097,59 @@ import.meta.glob(
1998
2097
  '!./**/*.test.ts',
1999
2098
  ],
2000
2099
  { eager: true },
2001
- )
2100
+ )`,p=` /**
2101
+ * Declare HTTP routes. Pass \`controller\` and the framework
2102
+ * derives the Express Router via \`buildRoutes()\` and uses the
2103
+ * same controller for OpenAPI spec generation. Return an array
2104
+ * to mount multiple route sets — each entry can override the API
2105
+ * version with a \`version\` field:
2106
+ *
2107
+ * return [
2108
+ * { path: '/${r}', version: 1, controller: ${t}V1Controller },
2109
+ * { path: '/${r}', version: 2, controller: ${t}V2Controller },
2110
+ * ]
2111
+ */`;return u===`class`?`${d}
2112
+ import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
2113
+ ${f}
2002
2114
 
2003
2115
  export class ${t}Module implements AppModule {
2004
2116
  register(container: Container): void {
2005
2117
  container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
2006
- container.resolve(${s}),
2118
+ container.resolve(${c}),
2007
2119
  )
2008
2120
  }
2009
2121
 
2122
+ ${p.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
2010
2123
  routes(): ModuleRoutes {
2011
2124
  return {
2012
2125
  path: '/${r}',
2013
- router: buildRoutes(${t}Controller),
2014
2126
  controller: ${t}Controller,
2015
2127
  }
2016
2128
  }
2017
2129
  }
2018
- `}function pn(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
2130
+ `:`${d}
2131
+ import { defineModule } from '@forinda/kickjs'
2132
+ ${f}
2133
+
2134
+ export const ${t}Module = defineModule({
2135
+ name: '${t}Module',
2136
+ build: () => ({
2137
+ register(container) {
2138
+ container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
2139
+ container.resolve(${c}),
2140
+ )
2141
+ },
2142
+
2143
+ ${p}
2144
+ routes() {
2145
+ return {
2146
+ path: '/${r}',
2147
+ controller: ${t}Controller,
2148
+ }
2149
+ },
2150
+ }),
2151
+ })
2152
+ `}function _n(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
2019
2153
  import { ApiTags } from '@forinda/kickjs-swagger'
2020
2154
  import { Create${t}Command } from './commands/create-${n}.command'
2021
2155
  import { Update${t}Command } from './commands/update-${n}.command'
@@ -2078,7 +2212,7 @@ export class ${t}Controller {
2078
2212
  ctx.noContent()
2079
2213
  }
2080
2214
  }
2081
- `}function mn(e){let{pascal:t,kebab:n}=e;return[{file:`create-${n}.command.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
2215
+ `}function vn(e){let{pascal:t,kebab:n}=e;return[{file:`create-${n}.command.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
2082
2216
  import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../${n}.repository'
2083
2217
  import type { Create${t}DTO } from '../dtos/create-${n}.dto'
2084
2218
  import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
@@ -2132,7 +2266,7 @@ export class Delete${t}Command {
2132
2266
  this.events.emit('${n}.deleted', { id })
2133
2267
  }
2134
2268
  }
2135
- `}]}function hn(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return[{file:`get-${n}.query.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
2269
+ `}]}function yn(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return[{file:`get-${n}.query.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
2136
2270
  import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../${n}.repository'
2137
2271
  import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
2138
2272
 
@@ -2160,7 +2294,7 @@ export class List${i}Query {
2160
2294
  return this.repo.findPaginated(parsed)
2161
2295
  }
2162
2296
  }
2163
- `}]}function gn(e){let{pascal:t,kebab:n}=e;return[{file:`${n}.events.ts`,content:`import { Service } from '@forinda/kickjs'
2297
+ `}]}function bn(e){let{pascal:t,kebab:n}=e;return[{file:`${n}.events.ts`,content:`import { Service } from '@forinda/kickjs'
2164
2298
  import { EventEmitter } from 'node:events'
2165
2299
  import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
2166
2300
 
@@ -2246,7 +2380,7 @@ export class On${t}ChangeHandler {
2246
2380
  })
2247
2381
  }
2248
2382
  }
2249
- `}]}function _n(e){let{pascal:t,kebab:n,repoPrefix:r=`../../domain/repositories`,dtoPrefix:i=`../../application/dtos`}=e;return`/**
2383
+ `}]}function xn(e){let{pascal:t,kebab:n,repoPrefix:r=`../../domain/repositories`,dtoPrefix:i=`../../application/dtos`}=e;return`/**
2250
2384
  * Drizzle ${t} Repository
2251
2385
  *
2252
2386
  * Implements the repository interface using Drizzle ORM.
@@ -2328,7 +2462,7 @@ export class Drizzle${t}Repository implements I${t}Repository {
2328
2462
  throw new Error('Drizzle ${t} repository not yet implemented')
2329
2463
  }
2330
2464
  }
2331
- `}function vn(e){let{pascal:t,kebab:n}=e;return`import type { DrizzleQueryParamsConfig } from '@forinda/kickjs-drizzle'
2465
+ `}function Sn(e){let{pascal:t,kebab:n}=e;return`import type { DrizzleQueryParamsConfig } from '@forinda/kickjs-drizzle'
2332
2466
  // TODO: Import your schema table and reference actual columns for type safety
2333
2467
  // import { ${n}s } from '@/db/schema'
2334
2468
 
@@ -2346,7 +2480,7 @@ export const ${t.toUpperCase()}_QUERY_CONFIG: DrizzleQueryParamsConfig = {
2346
2480
  // ${n}s.name,
2347
2481
  ],
2348
2482
  }
2349
- `}function yn(e){let{pascal:t,kebab:n,repoPrefix:r=`../../domain/repositories`,dtoPrefix:i=`../../application/dtos`}=e,a=n.replace(/-([a-z])/g,(e,t)=>t.toUpperCase());return`/**
2483
+ `}function Cn(e){let{pascal:t,kebab:n,repoPrefix:r=`../../domain/repositories`,dtoPrefix:i=`../../application/dtos`}=e,a=n.replace(/-([a-z])/g,(e,t)=>t.toUpperCase());return`/**
2350
2484
  * Prisma ${t} Repository
2351
2485
  *
2352
2486
  * Implements the repository interface using Prisma Client.
@@ -2404,7 +2538,7 @@ export class Prisma${t}Repository implements I${t}Repository {
2404
2538
  await this.prisma.${a}.deleteMany({ where: { id } })
2405
2539
  }
2406
2540
  }
2407
- `}async function bn(e){let{pascal:t,kebab:n,plural:r,write:i}=e;await i(`${n}.module.ts`,Jt({pascal:t,kebab:n,plural:r})),await i(`${n}.controller.ts`,`import { Controller, Get, type Ctx } from '@forinda/kickjs'
2541
+ `}async function wn(e){let{pascal:t,kebab:n,plural:r,style:i,write:a}=e;await a(`${n}.module.ts`,Qt({pascal:t,kebab:n,plural:r,style:i})),await a(`${n}.controller.ts`,`import { Controller, Get, type Ctx } from '@forinda/kickjs'
2408
2542
 
2409
2543
  // \`Ctx<KickRoutes.${t}Controller['<method>']>\` is generated by
2410
2544
  // \`kick typegen\` (auto-run on \`kick dev\`).
@@ -2416,14 +2550,14 @@ export class ${t}Controller {
2416
2550
  ctx.json({ message: '${t} list' })
2417
2551
  }
2418
2552
  }
2419
- `)}async function xn(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noTests:o,prismaClientPath:s,tokenScope:c,write:l}=e;await l(`${n}.module.ts`,qt({pascal:t,kebab:n,plural:r,repo:a})),await l(`${n}.constants.ts`,dn({pascal:t,kebab:n})),await l(`${n}.controller.ts`,Xt({pascal:t,kebab:n,plural:r,pluralPascal:i})),await l(`${n}.service.ts`,un({pascal:t,kebab:n})),await l(`dtos/create-${n}.dto.ts`,Qt({pascal:t,kebab:n})),await l(`dtos/update-${n}.dto.ts`,$t({pascal:t,kebab:n})),await l(`dtos/${n}-response.dto.ts`,en({pascal:t,kebab:n})),await l(`${n}.repository.ts`,nn({pascal:t,kebab:n,dtoPrefix:`./dtos`,tokenScope:c}));let u={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},d={inmemory:()=>H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),drizzle:()=>_n({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),prisma:()=>yn({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`,prismaClientPath:s})},f=u[a]??`${B(a)}-${n}`,p=d[a]??(()=>rn({pascal:t,kebab:n,repoType:a,repoPrefix:`.`,dtoPrefix:`./dtos`}));await l(`${f}.repository.ts`,p()),o||(a!==`inmemory`&&await l(`in-memory-${n}.repository.ts`,H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`})),await l(`__tests__/${n}.controller.test.ts`,cn({pascal:t,kebab:n,plural:r})),await l(`__tests__/${n}.repository.test.ts`,ln({pascal:t,kebab:n,plural:r,repoPrefix:`../${u.inmemory??`in-memory-${n}`}.repository`})))}async function Sn(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noTests:o,prismaClientPath:s,tokenScope:c,write:l}=e;await l(`${n}.module.ts`,fn({pascal:t,kebab:n,plural:r,repo:a})),await l(`${n}.constants.ts`,dn({pascal:t,kebab:n})),await l(`${n}.controller.ts`,pn({pascal:t,kebab:n,plural:r,pluralPascal:i})),await l(`dtos/create-${n}.dto.ts`,Qt({pascal:t,kebab:n})),await l(`dtos/update-${n}.dto.ts`,$t({pascal:t,kebab:n})),await l(`dtos/${n}-response.dto.ts`,en({pascal:t,kebab:n}));let u=mn({pascal:t,kebab:n});for(let e of u)await l(`commands/${e.file}`,e.content);let d=hn({pascal:t,kebab:n,plural:r,pluralPascal:i});for(let e of d)await l(`queries/${e.file}`,e.content);let f=gn({pascal:t,kebab:n});for(let e of f)await l(`events/${e.file}`,e.content);await l(`${n}.repository.ts`,nn({pascal:t,kebab:n,dtoPrefix:`./dtos`,tokenScope:c}));let p={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},m={inmemory:()=>H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),drizzle:()=>_n({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),prisma:()=>yn({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`,prismaClientPath:s})},h=p[a]??`${B(a)}-${n}`,g=m[a]??(()=>rn({pascal:t,kebab:n,repoType:a,repoPrefix:`.`,dtoPrefix:`./dtos`}));await l(`${h}.repository.ts`,g()),o||(a!==`inmemory`&&await l(`in-memory-${n}.repository.ts`,H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`})),await l(`__tests__/${n}.controller.test.ts`,cn({pascal:t,kebab:n,plural:r})),await l(`__tests__/${n}.repository.test.ts`,ln({pascal:t,kebab:n,plural:r,repoPrefix:`../${p.inmemory??`in-memory-${n}`}.repository`})))}async function Cn(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noEntity:o,noTests:s,prismaClientPath:c,tokenScope:l,write:u}=e;await u(`${n}.module.ts`,Kt({pascal:t,kebab:n,plural:r,repo:a})),await u(`constants.ts`,a===`drizzle`?vn({pascal:t,kebab:n}):Zt({pascal:t,kebab:n})),await u(`presentation/${n}.controller.ts`,Yt({pascal:t,kebab:n,plural:r,pluralPascal:i})),await u(`application/dtos/create-${n}.dto.ts`,Qt({pascal:t,kebab:n})),await u(`application/dtos/update-${n}.dto.ts`,$t({pascal:t,kebab:n})),await u(`application/dtos/${n}-response.dto.ts`,en({pascal:t,kebab:n}));let d=tn({pascal:t,kebab:n,plural:r,pluralPascal:i});for(let e of d)await u(`application/use-cases/${e.file}`,e.content);await u(`domain/repositories/${n}.repository.ts`,nn({pascal:t,kebab:n,tokenScope:l})),await u(`domain/services/${n}-domain.service.ts`,an({pascal:t,kebab:n}));let f={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},p={inmemory:()=>H({pascal:t,kebab:n}),drizzle:()=>_n({pascal:t,kebab:n}),prisma:()=>yn({pascal:t,kebab:n,prismaClientPath:c})},m=f[a]??`${B(a)}-${n}`,h=p[a]??(()=>rn({pascal:t,kebab:n,repoType:a}));await u(`infrastructure/repositories/${m}.repository.ts`,h()),o||(await u(`domain/entities/${n}.entity.ts`,on({pascal:t,kebab:n})),await u(`domain/value-objects/${n}-id.vo.ts`,sn({pascal:t,kebab:n}))),s||(a!==`inmemory`&&await u(`infrastructure/repositories/in-memory-${n}.repository.ts`,H({pascal:t,kebab:n})),await u(`__tests__/${n}.controller.test.ts`,cn({pascal:t,kebab:n,plural:r})),await u(`__tests__/${n}.repository.test.ts`,ln({pascal:t,kebab:n,plural:r})))}function wn(e){return e?typeof e==`string`?e:e.name:`inmemory`}async function Tn(e){let{name:t,modulesDir:n,noEntity:r,noTests:i,repo:a=`inmemory`,force:o,dryRun:s}=e,c=e.pluralize!==!1,l=e.pattern??`ddd`;e.minimal&&(l=`minimal`);let u=B(t),d=R(t),f=c?V(u):u,p=c?Dt(d):d,m=b(n,f),h=[],g=o??!1,_={kebab:u,pascal:d,plural:f,pluralPascal:p,moduleDir:m,repo:a,noEntity:r??!1,noTests:i??!1,prismaClientPath:e.prismaClientPath??`@prisma/client`,tokenScope:e.tokenScope??`app`,write:async(e,t)=>{let n=b(m,e);if(s){h.push(n);return}if(!g&&await N(n)&&!await F({message:`File exists: ${O.dim(e)}. Overwrite?`,initialValue:!1})){I.warn(`Skipped: ${e}`);return}await A(n,t),h.push(n)},files:h};switch(l){case`minimal`:await bn(_);break;case`rest`:await xn(_);break;case`cqrs`:await Sn(_);break;default:await Cn(_);break}return s||await En(n,d,f,u),h}async function En(e,t,n,r){let i=b(e,`index.ts`),a=await N(i),o=`./${n}/${r}.module`;if(!a){await A(i,`import type { AppModuleClass } from '@forinda/kickjs'
2420
- import { ${t}Module } from '${o}'
2553
+ `)}async function Tn(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noTests:o,prismaClientPath:s,tokenScope:c,style:l,write:u}=e;await u(`${n}.module.ts`,Zt({pascal:t,kebab:n,plural:r,repo:a,style:l})),await u(`${n}.constants.ts`,hn({pascal:t,kebab:n})),await u(`${n}.controller.ts`,en({pascal:t,kebab:n,plural:r,pluralPascal:i})),await u(`${n}.service.ts`,mn({pascal:t,kebab:n})),await u(`dtos/create-${n}.dto.ts`,nn({pascal:t,kebab:n})),await u(`dtos/update-${n}.dto.ts`,rn({pascal:t,kebab:n})),await u(`dtos/${n}-response.dto.ts`,an({pascal:t,kebab:n})),await u(`${n}.repository.ts`,sn({pascal:t,kebab:n,dtoPrefix:`./dtos`,tokenScope:c}));let d={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},f={inmemory:()=>H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),drizzle:()=>xn({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),prisma:()=>Cn({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`,prismaClientPath:s})},p=d[a]??`${B(a)}-${n}`,m=f[a]??(()=>cn({pascal:t,kebab:n,repoType:a,repoPrefix:`.`,dtoPrefix:`./dtos`}));await u(`${p}.repository.ts`,m()),o||(a!==`inmemory`&&await u(`in-memory-${n}.repository.ts`,H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`})),await u(`__tests__/${n}.controller.test.ts`,fn({pascal:t,kebab:n,plural:r})),await u(`__tests__/${n}.repository.test.ts`,pn({pascal:t,kebab:n,plural:r,repoPrefix:`../${d.inmemory??`in-memory-${n}`}.repository`})))}async function En(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noTests:o,prismaClientPath:s,tokenScope:c,style:l,write:u}=e;await u(`${n}.module.ts`,gn({pascal:t,kebab:n,plural:r,repo:a,style:l})),await u(`${n}.constants.ts`,hn({pascal:t,kebab:n})),await u(`${n}.controller.ts`,_n({pascal:t,kebab:n,plural:r,pluralPascal:i})),await u(`dtos/create-${n}.dto.ts`,nn({pascal:t,kebab:n})),await u(`dtos/update-${n}.dto.ts`,rn({pascal:t,kebab:n})),await u(`dtos/${n}-response.dto.ts`,an({pascal:t,kebab:n}));let d=vn({pascal:t,kebab:n});for(let e of d)await u(`commands/${e.file}`,e.content);let f=yn({pascal:t,kebab:n,plural:r,pluralPascal:i});for(let e of f)await u(`queries/${e.file}`,e.content);let p=bn({pascal:t,kebab:n});for(let e of p)await u(`events/${e.file}`,e.content);await u(`${n}.repository.ts`,sn({pascal:t,kebab:n,dtoPrefix:`./dtos`,tokenScope:c}));let m={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},h={inmemory:()=>H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),drizzle:()=>xn({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),prisma:()=>Cn({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`,prismaClientPath:s})},g=m[a]??`${B(a)}-${n}`,_=h[a]??(()=>cn({pascal:t,kebab:n,repoType:a,repoPrefix:`.`,dtoPrefix:`./dtos`}));await u(`${g}.repository.ts`,_()),o||(a!==`inmemory`&&await u(`in-memory-${n}.repository.ts`,H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`})),await u(`__tests__/${n}.controller.test.ts`,fn({pascal:t,kebab:n,plural:r})),await u(`__tests__/${n}.repository.test.ts`,pn({pascal:t,kebab:n,plural:r,repoPrefix:`../${m.inmemory??`in-memory-${n}`}.repository`})))}async function Dn(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noEntity:o,noTests:s,prismaClientPath:c,tokenScope:l,style:u,write:d}=e;await d(`${n}.module.ts`,Xt({pascal:t,kebab:n,plural:r,repo:a,style:u})),await d(`constants.ts`,a===`drizzle`?Sn({pascal:t,kebab:n}):tn({pascal:t,kebab:n})),await d(`presentation/${n}.controller.ts`,$t({pascal:t,kebab:n,plural:r,pluralPascal:i})),await d(`application/dtos/create-${n}.dto.ts`,nn({pascal:t,kebab:n})),await d(`application/dtos/update-${n}.dto.ts`,rn({pascal:t,kebab:n})),await d(`application/dtos/${n}-response.dto.ts`,an({pascal:t,kebab:n}));let f=on({pascal:t,kebab:n,plural:r,pluralPascal:i});for(let e of f)await d(`application/use-cases/${e.file}`,e.content);await d(`domain/repositories/${n}.repository.ts`,sn({pascal:t,kebab:n,tokenScope:l})),await d(`domain/services/${n}-domain.service.ts`,ln({pascal:t,kebab:n}));let p={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},m={inmemory:()=>H({pascal:t,kebab:n}),drizzle:()=>xn({pascal:t,kebab:n}),prisma:()=>Cn({pascal:t,kebab:n,prismaClientPath:c})},h=p[a]??`${B(a)}-${n}`,g=m[a]??(()=>cn({pascal:t,kebab:n,repoType:a}));await d(`infrastructure/repositories/${h}.repository.ts`,g()),o||(await d(`domain/entities/${n}.entity.ts`,un({pascal:t,kebab:n})),await d(`domain/value-objects/${n}-id.vo.ts`,dn({pascal:t,kebab:n}))),s||(a!==`inmemory`&&await d(`infrastructure/repositories/in-memory-${n}.repository.ts`,H({pascal:t,kebab:n})),await d(`__tests__/${n}.controller.test.ts`,fn({pascal:t,kebab:n,plural:r})),await d(`__tests__/${n}.repository.test.ts`,pn({pascal:t,kebab:n,plural:r})))}function On(e){return e?typeof e==`string`?e:e.name:`inmemory`}async function kn(e){let{name:t,modulesDir:n,noEntity:r,noTests:i,repo:a=`inmemory`,force:o,dryRun:s}=e,c=e.pluralize!==!1,l=e.pattern??`ddd`;e.minimal&&(l=`minimal`);let u=B(t),d=R(t),f=c?V(u):u,p=c?At(d):d,m=x(n,f),h=[],g=o??!1,_={kebab:u,pascal:d,plural:f,pluralPascal:p,moduleDir:m,repo:a,noEntity:r??!1,noTests:i??!1,prismaClientPath:e.prismaClientPath??`@prisma/client`,tokenScope:e.tokenScope??`app`,style:e.style??`define`,write:async(e,t)=>{let n=x(m,e);if(s){h.push(n);return}if(!g&&await Re(n)&&!await F({message:`File exists: ${k.dim(e)}. Overwrite?`,initialValue:!1})){I.warn(`Skipped: ${e}`);return}await j(n,t),h.push(n)},files:h};switch(l){case`minimal`:await wn(_);break;case`rest`:await Tn(_);break;case`cqrs`:await En(_);break;default:await Dn(_);break}return s||await An(n,d,f,u,_.style),h}async function An(e,t,n,r,i=`define`){let a=x(e,`index.ts`),o=await Re(a),s=`./${n}/${r}.module`,c=i===`class`?`${t}Module`:`${t}Module()`;if(!o){await j(a,`import type { AppModuleEntry } from '@forinda/kickjs'
2554
+ import { ${t}Module } from '${s}'
2421
2555
 
2422
- export const modules: AppModuleClass[] = [${t}Module]
2423
- `);return}let s=await T(i,`utf-8`),c=`import { ${t}Module } from '${o}'`;if(!s.includes(`${t}Module`)){let e=s.lastIndexOf(`import `);if(e!==-1){let t=s.indexOf(`
2424
- `,e);s=s.slice(0,t+1)+c+`
2425
- `+s.slice(t+1)}else s=c+`
2426
- `+s;s=s.replace(/(=\s*\[)([\s\S]*?)(])/,(e,n,r,i)=>{let a=r.trim();if(!a)return`${n}${t}Module${i}`;let o=a.endsWith(`,`)?``:`,`;return`${n}${r.trimEnd()}${o} ${t}Module${i}`})}await E(i,s,`utf-8`)}async function Dn(e){let{name:t,outDir:n}=e,r=B(t),i=R(t),a=[],o=b(n,`${r}.adapter.ts`);return await A(o,`import {
2556
+ export const modules: AppModuleEntry[] = [${c}]
2557
+ `);return}let l=await E(a,`utf-8`),u=`import { ${t}Module } from '${s}'`;if(!l.includes(`${t}Module`)){let e=l.lastIndexOf(`import `);if(e!==-1){let t=l.indexOf(`
2558
+ `,e);l=l.slice(0,t+1)+u+`
2559
+ `+l.slice(t+1)}else l=u+`
2560
+ `+l;l=l.replace(/(=\s*\[)([\s\S]*?)(])/,(e,t,n,r)=>{let i=n.trim();if(!i)return`${t}${c}${r}`;let a=i.endsWith(`,`)?``:`,`;return`${t}${n.trimEnd()}${a} ${c}${r}`})}await D(a,l,`utf-8`)}async function jn(e){let{name:t,outDir:n}=e,r=B(t),i=R(t),a=[],o=x(n,`${r}.adapter.ts`);return await j(o,`import {
2427
2561
  defineAdapter,
2428
2562
  type AdapterContext,
2429
2563
  type AdapterMiddleware,
@@ -2592,10 +2726,10 @@ export const ${i}Adapter = defineAdapter<${i}AdapterConfig>({
2592
2726
  }
2593
2727
  },
2594
2728
  })
2595
- `),a.push(o),a}async function On(e){let{name:t,outDir:n}=e,r=B(t),i=R(t),a=[],o=b(n,`${r}.plugin.ts`);return await A(o,`import {
2729
+ `),a.push(o),a}async function Mn(e){let{name:t,outDir:n}=e,r=B(t),i=R(t),a=[],o=x(n,`${r}.plugin.ts`);return await j(o,`import {
2596
2730
  definePlugin,
2597
2731
  type AppAdapter,
2598
- type AppModuleClass,
2732
+ type AppModuleEntry,
2599
2733
  type Container,
2600
2734
  type ContributorRegistrations,
2601
2735
  } from '@forinda/kickjs'
@@ -2661,13 +2795,17 @@ export const ${i}Plugin = definePlugin<${i}PluginConfig>({
2661
2795
  },
2662
2796
 
2663
2797
  /**
2664
- * Return module classes this plugin contributes to the app.
2665
- * These load before user modules, so plugin controllers and
2666
- * services are available for user code to \`@Autowired\`.
2798
+ * Return modules this plugin contributes to the app. These load
2799
+ * before user modules, so plugin controllers and services are
2800
+ * available for user code to \`@Autowired\`.
2801
+ *
2802
+ * Accepts both \`defineModule\`-style instances (call the factory:
2803
+ * \`ExampleModule()\`) and legacy \`class … implements AppModule\`
2804
+ * constructors.
2667
2805
  */
2668
- modules(): AppModuleClass[] {
2806
+ modules(): AppModuleEntry[] {
2669
2807
  return [
2670
- // ExampleModule,
2808
+ // ExampleModule(),
2671
2809
  ]
2672
2810
  },
2673
2811
 
@@ -2732,7 +2870,7 @@ export const ${i}Plugin = definePlugin<${i}PluginConfig>({
2732
2870
  },
2733
2871
  }),
2734
2872
  })
2735
- `),a.push(o),a}const kn={controller:`presentation`,service:`domain/services`,dto:`application/dtos`,guard:`presentation/guards`,middleware:`middleware`},An={controller:``,service:``,dto:`dtos`,guard:`guards`,middleware:`middleware`},jn={controller:``,service:``,dto:`dtos`,guard:`guards`,middleware:`middleware`,command:`commands`,query:`queries`,event:`events`};function U(e){let{type:t,outDir:n,moduleName:r,modulesDir:i=`src/modules`,defaultDir:a,pattern:o=`ddd`,shouldPluralize:s=!0}=e;if(n)return S(n);if(r){let e=o===`ddd`?kn:o===`cqrs`?jn:An,n=B(r),a=s?V(n):n,c=e[t]??``,l=b(i,a);return S(c?b(l,c):l)}return S(a)}async function Mn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`middleware`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/middleware`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=z(t),c=[],l=b(a,`${o}.middleware.ts`);return await A(l,`import type { Request, Response, NextFunction } from 'express'
2873
+ `),a.push(o),a}const Nn={controller:`presentation`,service:`domain/services`,dto:`application/dtos`,guard:`presentation/guards`,middleware:`middleware`},Pn={controller:``,service:``,dto:`dtos`,guard:`guards`,middleware:`middleware`},Fn={controller:``,service:``,dto:`dtos`,guard:`guards`,middleware:`middleware`,command:`commands`,query:`queries`,event:`events`};function U(e){let{type:t,outDir:n,moduleName:r,modulesDir:i=`src/modules`,defaultDir:a,pattern:o=`ddd`,shouldPluralize:s=!0}=e;if(n)return C(n);if(r){let e=o===`ddd`?Nn:o===`cqrs`?Fn:Pn,n=B(r),a=s?V(n):n,c=e[t]??``,l=x(i,a);return C(c?x(l,c):l)}return C(a)}async function In(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`middleware`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/middleware`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=z(t),c=[],l=x(a,`${o}.middleware.ts`);return await j(l,`import type { Request, Response, NextFunction } from 'express'
2736
2874
 
2737
2875
  export interface ${R(t)}Options {
2738
2876
  // Add configuration options here
@@ -2756,7 +2894,7 @@ export function ${s}(options: ${R(t)}Options = {}) {
2756
2894
  next()
2757
2895
  }
2758
2896
  }
2759
- `),c.push(l),c}async function Nn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`guard`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/guards`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=z(t),c=R(t),l=[],u=b(a,`${o}.guard.ts`);return await A(u,`import { Container, HttpException } from '@forinda/kickjs'
2897
+ `),c.push(l),c}async function Ln(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`guard`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/guards`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=z(t),c=R(t),l=[],u=x(a,`${o}.guard.ts`);return await j(u,`import { Container, HttpException } from '@forinda/kickjs'
2760
2898
  import type { RequestContext } from '@forinda/kickjs'
2761
2899
 
2762
2900
  /**
@@ -2792,7 +2930,7 @@ export async function ${s}Guard(ctx: RequestContext, next: () => void): Promise<
2792
2930
  ctx.res.status(401).json({ message: 'Invalid or expired token' })
2793
2931
  }
2794
2932
  }
2795
- `),l.push(u),l}async function Pn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`service`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/services`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=R(t),c=[],l=b(a,`${o}.service.ts`);return await A(l,`import { Service } from '@forinda/kickjs'
2933
+ `),l.push(u),l}async function Rn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`service`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/services`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=R(t),c=[],l=x(a,`${o}.service.ts`);return await j(l,`import { Service } from '@forinda/kickjs'
2796
2934
 
2797
2935
  @Service()
2798
2936
  export class ${s}Service {
@@ -2801,7 +2939,7 @@ export class ${s}Service {
2801
2939
  // @Inject(MY_REPO) private readonly repo: IMyRepository,
2802
2940
  // ) {}
2803
2941
  }
2804
- `),c.push(l),c}async function Fn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`controller`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/controllers`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=R(t),c=[],l=b(a,`${o}.controller.ts`);return await A(l,`import { Controller, Get, Post, type Ctx } from '@forinda/kickjs'
2942
+ `),c.push(l),c}async function zn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`controller`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/controllers`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=R(t),c=[],l=x(a,`${o}.controller.ts`);return await j(l,`import { Controller, Get, Post, type Ctx } from '@forinda/kickjs'
2805
2943
 
2806
2944
  // \`Ctx<KickRoutes.${s}Controller['<method>']>\` is generated by
2807
2945
  // \`kick typegen\` (auto-run on \`kick dev\`). After the first run, your IDE
@@ -2822,7 +2960,7 @@ export class ${s}Controller {
2822
2960
  ctx.created({ message: '${s} created', data: ctx.body })
2823
2961
  }
2824
2962
  }
2825
- `),c.push(l),c}async function In(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`dto`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/dtos`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=R(t),c=z(t),l=[],u=b(a,`${o}.dto.ts`);return await A(u,`import { z } from 'zod'
2963
+ `),c.push(l),c}async function Bn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`dto`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/dtos`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=R(t),c=z(t),l=[],u=x(a,`${o}.dto.ts`);return await j(u,`import { z } from 'zod'
2826
2964
 
2827
2965
  export const ${c}Schema = z.object({
2828
2966
  // Define your schema fields here
@@ -2830,8 +2968,8 @@ export const ${c}Schema = z.object({
2830
2968
  })
2831
2969
 
2832
2970
  export type ${s}DTO = z.infer<typeof ${c}Schema>
2833
- `),l.push(u),l}async function Ln(e){let t=b(e.outDir,`kick.config.ts`),n=e.modulesDir??`src/modules`,r=e.defaultRepo??`inmemory`;return h(t)&&!e.force&&!await F({message:`kick.config.ts already exists. Overwrite?`,initialValue:!1})?(console.log(`
2834
- Skipped — existing kick.config.ts preserved.`),[]):(await A(t,`import { defineConfig } from '@forinda/kickjs-cli'
2971
+ `),l.push(u),l}async function Vn(e){let t=x(e.outDir,`kick.config.ts`),n=e.modulesDir??`src/modules`,r=e.defaultRepo??`inmemory`;return h(t)&&!e.force&&!await F({message:`kick.config.ts already exists. Overwrite?`,initialValue:!1})?(console.log(`
2972
+ Skipped — existing kick.config.ts preserved.`),[]):(await j(t,`import { defineConfig } from '@forinda/kickjs-cli'
2835
2973
 
2836
2974
  export default defineConfig({
2837
2975
  modules: {
@@ -2868,7 +3006,18 @@ export default defineConfig({
2868
3006
  },
2869
3007
  ],
2870
3008
  })
2871
- `),[t])}const Rn=new Set([`rest`,`ddd`,`cqrs`,`minimal`]);function zn(e,t){if(t)return t;try{let t=JSON.parse(_(b(e,`package.json`),`utf-8`));if(t.name)return t.name.replace(/^@[^/]+\//,``)}catch{}return e.split(`/`).findLast(Boolean)??`app`}function Bn(e,t){if(t)return t;try{let t=JSON.parse(_(b(e,`package.json`),`utf-8`));if(t.packageManager)return t.packageManager.split(`@`)[0]}catch{}return`pnpm`}async function Vn(e,t){if(t)return t;try{let t=(await r(e))?.pattern;if(t&&Rn.has(t))return t}catch{}return`ddd`}async function Hn(e){let t=e.only??`all`,n=zn(e.outDir,e.name),r=Bn(e.outDir,e.pm),i=await Vn(e.outDir,e.template),a=t===`agents`||t===`both`||t===`all`,o=t===`claude`||t===`both`||t===`all`,s=t===`skills`||t===`all`,c=[];a&&c.push({file:b(e.outDir,`AGENTS.md`),render:()=>nt(n,i,r)}),o&&c.push({file:b(e.outDir,`CLAUDE.md`),render:()=>tt(n,i,r)}),s&&c.push({file:b(e.outDir,`kickjs-skills.md`),render:()=>rt(n,i,r)});let l=[];for(let{file:t,render:n}of c){if(h(t)&&!e.force&&!await F({message:`${t.replace(e.outDir+`/`,``)} already exists. Overwrite?`,initialValue:!1})){console.log(` Skipped — existing ${t.replace(e.outDir+`/`,``)} preserved.`);continue}await A(t,n()),l.push(t)}return l}async function Un(e={}){let t=e.strategy??`jwt`,n=e.outDir??`src/modules/auth`,r=b(n,`dto`),i=[],a=b(n,`auth.module.ts`);await A(a,`import { Module } from '@forinda/kickjs'
3009
+ `),[t])}const Hn=new Set([`rest`,`ddd`,`cqrs`,`minimal`]);function Un(e,t){if(t)return t;try{let t=JSON.parse(_(x(e,`package.json`),`utf-8`));if(t.name)return t.name.replace(/^@[^/]+\//,``)}catch{}return e.split(`/`).findLast(Boolean)??`app`}function Wn(e,t){if(t)return t;try{let t=JSON.parse(_(x(e,`package.json`),`utf-8`));if(t.packageManager)return t.packageManager.split(`@`)[0]}catch{}return`pnpm`}async function Gn(e,t){if(t)return t;try{let t=(await r(e))?.pattern;if(t&&Hn.has(t))return t}catch{}return`ddd`}async function Kn(e){let t=e.only??`all`,n=Un(e.outDir,e.name),r=Wn(e.outDir,e.pm),i=await Gn(e.outDir,e.template),a=t===`agents`||t===`both`||t===`all`,o=t===`claude`||t===`both`||t===`all`,s=t===`skills`||t===`all`,c=[];a&&c.push({file:x(e.outDir,`AGENTS.md`),render:()=>at(n,i,r)}),o&&c.push({file:x(e.outDir,`CLAUDE.md`),render:()=>it(n,i,r)}),s&&c.push({file:x(e.outDir,`kickjs-skills.md`),render:()=>ot(n,i,r)});let l=[];for(let{file:t,render:n}of c){if(h(t)&&!e.force&&!await F({message:`${t.replace(e.outDir+`/`,``)} already exists. Overwrite?`,initialValue:!1})){console.log(` Skipped — existing ${t.replace(e.outDir+`/`,``)} preserved.`);continue}await j(t,n()),l.push(t)}return l}function qn(e,t){if(e[t]!==`{`)return-1;let n=1;for(let r=t+1;r<e.length;r++){let t=e[r];if(t===`{`)n++;else if(t===`}`&&(n--,n===0))return r}return-1}function W(e,t){let n=t.exec(e);if(!n)return null;let r=n.index+n[0].length-1,i=qn(e,r);return i===-1?null:e.slice(r+1,i)}function G(e,t,n){let r=` `.repeat(n);return e.split(`
3010
+ `).map(e=>{if(e.trim()===``)return e;let n=RegExp(`^ {0,${t}}`);return r+e.replace(n,``)}).join(`
3011
+ `)}function Jn(e){return e.replaceAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*'@forinda\/kickjs'/g,(e,t)=>{let n=t.split(`,`).map(e=>e.trim()).filter(e=>e&&e!==`Container`&&e!==`type Container`&&e!==`type AppModule`&&e!==`AppModule`&&e!==`type ModuleRoutes`&&e!==`ModuleRoutes`);return n.includes(`defineModule`)||n.push(`defineModule`),`import { ${n.join(`, `)} } from '@forinda/kickjs'`})}function Yn(e,t){return e.replaceAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*'@forinda\/kickjs'/g,(e,n)=>{let r=n.split(`,`).map(e=>e.trim()).filter(e=>e&&e!==`defineModule`);return t.container&&!r.includes(`Container`)&&r.push(`Container`),t.appModule&&!r.some(e=>e===`AppModule`||e===`type AppModule`)&&r.push(`type AppModule`),t.moduleRoutes&&!r.some(e=>e===`ModuleRoutes`||e===`type ModuleRoutes`)&&r.push(`type ModuleRoutes`),t.contributorRegistrations&&!r.some(e=>e===`ContributorRegistrations`||e===`type ContributorRegistrations`)&&r.push(`type ContributorRegistrations`),`import { ${r.join(`, `)} } from '@forinda/kickjs'`})}function Xn(e){if(/\bdefineModule\s*\(/.test(e))return{migrated:null,reason:`already in target form`};let t=[...e.matchAll(/export\s+class\s+(\w+Module)\s+implements\s+AppModule\s*\{/g)];if(t.length===0)return{migrated:null,reason:`no class form detected`};if(t.length>1)return{migrated:null,reason:`multiple module classes in one file — migrate manually`};let n=t[0],r=n[1],i=n.index+n[0].length-1,a=qn(e,i);if(a===-1)return{migrated:null,reason:`unbalanced class braces`};let o=e.slice(i+1,a),s=e.slice(0,n.index),c=e.slice(a+1),l=W(o,/register\s*\(([^)]*)\)\s*:\s*void\s*\{/),u=W(o,/contributors\s*\(\s*\)\s*:\s*ContributorRegistrations\s*\{/),d=W(o,/routes\s*\(\s*\)\s*:\s*[A-Za-z|[\]\s]+\{/);if(!d)return{migrated:null,reason:`routes() method missing or signature unrecognized`};let f=Jn(s),p=``;return l&&(p+=` register(container) {${G(l,4,6)} },\n\n`),u&&(p+=` contributors() {${G(u,4,6)} },\n\n`),p+=` routes() {${G(d,4,6)} },`,{migrated:`${f}${`export const ${r} = defineModule({
3012
+ name: '${r}',
3013
+ build: () => ({
3014
+ ${p}
3015
+ }),
3016
+ })`}${c}`}}function Zn(e){if(/export\s+class\s+\w+Module\s+implements\s+AppModule\s*\{/.test(e))return{migrated:null,reason:`already in target form`};let t=[...e.matchAll(/export\s+const\s+(\w+Module)\s*=\s*defineModule\s*\(\s*\{/g)];if(t.length===0)return{migrated:null,reason:`no defineModule form detected`};if(t.length>1)return{migrated:null,reason:`multiple defineModule blocks in one file — migrate manually`};let n=t[0],r=n[1],i=n.index+n[0].length-1,a=qn(e,i);if(a===-1)return{migrated:null,reason:`unbalanced defineModule braces`};let o=e.indexOf(`)`,a);if(o===-1)return{migrated:null,reason:`unbalanced defineModule call parens`};let s=e.slice(i+1,a),c=e.slice(0,n.index),l=o+1;for(;l<e.length&&(e[l]===`
3017
+ `||e[l]===`\r`);)l++;let u=e.slice(l),d=/build\s*:\s*\([^)]*\)\s*=>\s*\(\s*\{/g.exec(s);if(!d)return{migrated:null,reason:`build: () => ({...}) not found in defineModule`};let f=d.index+d[0].length-1,p=qn(s,f);if(p===-1)return{migrated:null,reason:`unbalanced build() braces`};let m=s.slice(f+1,p),h=W(m,/register\s*\(([^)]*)\)\s*\{/),g=W(m,/contributors\s*\(\s*\)\s*\{/),_=W(m,/routes\s*\(\s*\)\s*\{/);if(!_)return{migrated:null,reason:`routes() method missing inside build()`};let ee=Yn(c,{container:h!==null,appModule:!0,moduleRoutes:!0,contributorRegistrations:g!==null}),v=``;return h!==null&&(v+=` register(container: Container): void {${G(h,6,4)} }\n\n`),g!==null&&(v+=` contributors(): ContributorRegistrations {${G(g,6,4)} }\n\n`),v+=` routes(): ModuleRoutes {${G(_,6,4)} }`,{migrated:`${ee}${`export class ${r} implements AppModule {
3018
+ ${v}
3019
+ }
3020
+ `}${u}`}}function Qn(e,t){return t===`class`?Zn(e):Xn(e)}function $n(e,t){let n=e,r=!1;if(t===`define`){/\bAppModuleClass\b/.test(n)&&(n=n.replaceAll(/\bAppModuleClass\b/g,`AppModuleEntry`),r=!0);let e=/(=\s*\[)([\s\S]*?)(])/,t=e.exec(n);if(t){let i=t[1],a=t[3],o=t[2],s=o.replaceAll(/(\b\w+Module)(?![(.])/g,`$1()`);s!==o&&(n=n.replace(e,`${i}${s}${a}`),r=!0)}}else{/\bAppModuleEntry\b/.test(n)&&(n=n.replaceAll(/\bAppModuleEntry\b/g,`AppModuleClass`),r=!0);let e=/(=\s*\[)([\s\S]*?)(])/,t=e.exec(n);if(t){let i=t[1],a=t[3],o=t[2],s=o.replaceAll(/(\b\w+Module)\s*\(\s*\)/g,`$1`);s!==o&&(n=n.replace(e,`${i}${s}${a}`),r=!0)}}return r?{migrated:n}:{migrated:null,reason:`no changes needed`}}async function er(e){let t=[];return await n(C(e),0),t;async function n(e,r){let i;try{i=await me(e)}catch{return}for(let a of i){if(a===`node_modules`||a===`dist`||a===`.kickjs`)continue;let i=x(e,a),o;try{o=await ge(i)}catch{continue}o.isDirectory()?await n(i,r+1):(a.endsWith(`.module.ts`)||a===`index.ts`&&r===1)&&t.push(i)}}}async function tr(e,t){let n=0;return await r(e,t),n;async function r(e,t){let i;try{i=await me(e)}catch{return}await pe(t,{recursive:!0});for(let a of i){if(a===`node_modules`||a===`dist`||a===`.kickjs`)continue;let i=x(e,a),o=x(t,a),s;try{s=await ge(i)}catch{continue}s.isDirectory()?await r(i,o):(await fe(i,o),n++)}}}function nr(e){return x(e,`.kickjs`,`codemod-backups`,`${new Date().toISOString().replaceAll(/[:.]/g,`-`)}-modules`)}async function rr(e,t){let{dryRun:n=!1,cwd:r=process.cwd(),target:i}=t,a=t.backup??!n,o=await er(e),s=await E(x(e,`index.ts`),`utf-8`).then(()=>!0,()=>!1),c=null;a&&(o.length>0||s)&&(c=nr(r),await tr(e,c));let l=[];for(let e of o){let t=Qn(await E(e,`utf-8`),i);if(t.migrated==null){l.push({path:e,status:`skipped`,reason:t.reason});continue}n||await D(e,t.migrated,`utf-8`),l.push({path:e,status:`migrated`})}let u=x(e,`index.ts`),d=null;try{d=await E(u,`utf-8`)}catch{return{target:i,files:l,indexStatus:`not-found`,indexPath:u,backupDir:c}}let f=$n(d,i);return f.migrated==null?{target:i,files:l,indexStatus:`skipped`,indexPath:u,indexReason:f.reason,backupDir:c}:(n||await D(u,f.migrated,`utf-8`),{target:i,files:l,indexStatus:`migrated`,indexPath:u,backupDir:c})}async function ir(e,t){let n=await er(e),r=[],i=t===`define`?/export\s+class\s+\w+Module\s+implements\s+AppModule\s*\{/:/export\s+const\s+\w+Module\s*=\s*defineModule\s*\(/;for(let e of n){let t=await E(e,`utf-8`);i.test(t)&&r.push(e)}return r}async function ar(e={}){let t=e.strategy??`jwt`,n=e.outDir??`src/modules/auth`,r=x(n,`dto`),i=[],a=x(n,`auth.module.ts`);await j(a,`import { Module } from '@forinda/kickjs'
2872
3021
  import { AuthController } from './auth.controller'
2873
3022
  import { AuthService } from './auth.service'
2874
3023
 
@@ -2877,7 +3026,7 @@ import { AuthService } from './auth.service'
2877
3026
  services: [AuthService],
2878
3027
  })
2879
3028
  export class AuthModule {}
2880
- `),i.push(a);let o=b(n,`auth.controller.ts`);await A(o,t===`jwt`?Wn():Kn()),i.push(o);let s=b(n,`auth.service.ts`);await A(s,t===`jwt`?Gn():qn()),i.push(s);let c=b(r,`register.dto.ts`);await A(c,`import { z } from 'zod'
3029
+ `),i.push(a);let o=x(n,`auth.controller.ts`);await j(o,t===`jwt`?or():cr()),i.push(o);let s=x(n,`auth.service.ts`);await j(s,t===`jwt`?sr():lr()),i.push(s);let c=x(r,`register.dto.ts`);await j(c,`import { z } from 'zod'
2881
3030
 
2882
3031
  export const RegisterDto = z.object({
2883
3032
  email: z.string().email(),
@@ -2886,7 +3035,7 @@ export const RegisterDto = z.object({
2886
3035
  })
2887
3036
 
2888
3037
  export type RegisterInput = z.infer<typeof RegisterDto>
2889
- `),i.push(c);let l=b(r,`login.dto.ts`);await A(l,`import { z } from 'zod'
3038
+ `),i.push(c);let l=x(r,`login.dto.ts`);await j(l,`import { z } from 'zod'
2890
3039
 
2891
3040
  export const LoginDto = z.object({
2892
3041
  email: z.string().email(),
@@ -2894,7 +3043,7 @@ export const LoginDto = z.object({
2894
3043
  })
2895
3044
 
2896
3045
  export type LoginInput = z.infer<typeof LoginDto>
2897
- `),i.push(l);let u=b(n,`auth.test.ts`);if(await A(u,`import { describe, it, expect } from 'vitest'
3046
+ `),i.push(l);let u=x(n,`auth.test.ts`);if(await j(u,`import { describe, it, expect } from 'vitest'
2898
3047
 
2899
3048
  describe('Auth Module', () => {
2900
3049
  it.todo('POST /register — creates a new user')
@@ -2903,7 +3052,7 @@ describe('Auth Module', () => {
2903
3052
  it.todo('POST /logout — invalidates session/token')
2904
3053
  it.todo('GET /me — returns authenticated user')
2905
3054
  })
2906
- `),i.push(u),e.roleGuards!==!1){let e=b(n,`auth.guard.ts`);await A(e,`import { Roles } from '@forinda/kickjs-auth'
3055
+ `),i.push(u),e.roleGuards!==!1){let e=x(n,`auth.guard.ts`);await j(e,`import { Roles } from '@forinda/kickjs-auth'
2907
3056
 
2908
3057
  /**
2909
3058
  * Role-based access guard.
@@ -2914,7 +3063,7 @@ describe('Auth Module', () => {
2914
3063
  */
2915
3064
  export const AdminOnly = Roles('admin')
2916
3065
  export const ManagerOnly = Roles('manager')
2917
- `),i.push(e)}return i}function Wn(){return`import { Controller, Post, Get } from '@forinda/kickjs'
3066
+ `),i.push(e)}return i}function or(){return`import { Controller, Post, Get } from '@forinda/kickjs'
2918
3067
  import { Authenticated, Public } from '@forinda/kickjs-auth'
2919
3068
  import type { RequestContext } from '@forinda/kickjs'
2920
3069
  import { Autowired } from '@forinda/kickjs'
@@ -2950,7 +3099,7 @@ export class AuthController {
2950
3099
  return ctx.json({ user: ctx.user })
2951
3100
  }
2952
3101
  }
2953
- `}function Gn(){return`import { Service, Autowired } from '@forinda/kickjs'
3102
+ `}function sr(){return`import { Service, Autowired } from '@forinda/kickjs'
2954
3103
  import { PasswordService } from '@forinda/kickjs-auth'
2955
3104
  import type { RegisterInput } from './dto/register.dto'
2956
3105
  import type { LoginInput } from './dto/login.dto'
@@ -2989,7 +3138,7 @@ export class AuthService {
2989
3138
  return { user: { id: user.id, email: user.email, name: user.name } }
2990
3139
  }
2991
3140
  }
2992
- `}function Kn(){return`import { Controller, Post, Get } from '@forinda/kickjs'
3141
+ `}function cr(){return`import { Controller, Post, Get } from '@forinda/kickjs'
2993
3142
  import { Authenticated, Public } from '@forinda/kickjs-auth'
2994
3143
  import { sessionLogin, sessionLogout } from '@forinda/kickjs-auth'
2995
3144
  import type { RequestContext } from '@forinda/kickjs'
@@ -3028,7 +3177,7 @@ export class AuthController {
3028
3177
  return ctx.json({ user: ctx.user })
3029
3178
  }
3030
3179
  }
3031
- `}function qn(){return`import { Service, Autowired } from '@forinda/kickjs'
3180
+ `}function lr(){return`import { Service, Autowired } from '@forinda/kickjs'
3032
3181
  import { PasswordService } from '@forinda/kickjs-auth'
3033
3182
  import type { RegisterInput } from './dto/register.dto'
3034
3183
  import type { LoginInput } from './dto/login.dto'
@@ -3065,7 +3214,7 @@ export class AuthService {
3065
3214
  return { id: user.id, email: user.email, name: user.name }
3066
3215
  }
3067
3216
  }
3068
- `}async function Jn(e){let{name:t,outDir:n}=e,r=R(t),i=B(t),a=z(t),o=e.queue??`${i}-queue`,s=[];return await(async(e,t)=>{let r=b(n,e);await A(r,t),s.push(r)})(`${i}.job.ts`,`import { Inject } from '@forinda/kickjs'
3217
+ `}async function ur(e){let{name:t,outDir:n}=e,r=R(t),i=B(t),a=z(t),o=e.queue??`${i}-queue`,s=[];return await(async(e,t)=>{let r=x(n,e);await j(r,t),s.push(r)})(`${i}.job.ts`,`import { Inject } from '@forinda/kickjs'
3069
3218
  import { Job, Process, QUEUE_MANAGER, type QueueService } from '@forinda/kickjs-queue'
3070
3219
 
3071
3220
  /**
@@ -3098,7 +3247,7 @@ export class ${r}Job {
3098
3247
  // Handle high-priority variant of this job
3099
3248
  }
3100
3249
  }
3101
- `),s}const Yn={string:{ts:`string`,zod:`z.string()`},text:{ts:`string`,zod:`z.string()`},number:{ts:`number`,zod:`z.number()`},int:{ts:`number`,zod:`z.number().int()`},float:{ts:`number`,zod:`z.number()`},boolean:{ts:`boolean`,zod:`z.boolean()`},date:{ts:`string`,zod:`z.string().datetime()`},email:{ts:`string`,zod:`z.string().email()`},url:{ts:`string`,zod:`z.string().url()`},uuid:{ts:`string`,zod:`z.string().uuid()`},json:{ts:`any`,zod:`z.any()`}};function Xn(e){return e.map(e=>{let t=e.indexOf(`:`);if(t===-1)throw Error(`Invalid field: "${e}". Use format: name:type (e.g. title:string)`);let n=e.slice(0,t),r=e.slice(t+1);if(!n||!r)throw Error(`Invalid field: "${e}". Use format: name:type (e.g. title:string)`);let i=!1;r.endsWith(`:optional`)&&(r=r.slice(0,-9),i=!0),n.endsWith(`?`)&&(n=n.slice(0,-1),i=!0),r.endsWith(`?`)&&(r=r.slice(0,-1),i=!0);let a=r;if(a.startsWith(`enum:`)){let e=a.slice(5).split(`,`);return{name:n,type:`enum`,tsType:e.map(e=>`'${e}'`).join(` | `),zodType:`z.enum([${e.map(e=>`'${e}'`).join(`, `)}])`,optional:i}}let o=Yn[a];if(!o){let e=[...Object.keys(Yn),`enum:a,b,c`].join(`, `);throw Error(`Unknown field type: "${a}". Valid types: ${e}`)}return{name:n,type:a,tsType:o.ts,zodType:o.zod,optional:i}})}async function Zn(e){let{name:t,fields:n,modulesDir:r,noEntity:i,noTests:a,repo:o=`inmemory`,tokenScope:s=`app`}=e,c=e.pluralize!==!1,l=B(t),u=R(t);z(t);let d=c?V(l):l,f=c?Dt(u):u,p=b(r,d),m=[],h=async(e,t)=>{let n=b(p,e);await A(n,t),m.push(n)};await h(`${l}.module.ts`,ar(u,l,d)),await h(`constants.ts`,tr(u,n)),await h(`presentation/${l}.controller.ts`,or(u,l,d,f)),await h(`application/dtos/create-${l}.dto.ts`,Qn(u,n)),await h(`application/dtos/update-${l}.dto.ts`,$n(u,n)),await h(`application/dtos/${l}-response.dto.ts`,er(u,n));let g=lr(u,l,d,f);for(let e of g)await h(`application/use-cases/${e.file}`,e.content);return await h(`domain/repositories/${l}.repository.ts`,sr(u,l,s)),await h(`domain/services/${l}-domain.service.ts`,cr(u,l)),o===`inmemory`&&await h(`infrastructure/repositories/in-memory-${l}.repository.ts`,nr(u,l,n)),i||(await h(`domain/entities/${l}.entity.ts`,rr(u,l,n)),await h(`domain/value-objects/${l}-id.vo.ts`,ir(u))),await ur(r,u,d,l),m}function Qn(e,t){return`import { z } from 'zod'
3250
+ `),s}const dr={string:{ts:`string`,zod:`z.string()`},text:{ts:`string`,zod:`z.string()`},number:{ts:`number`,zod:`z.number()`},int:{ts:`number`,zod:`z.number().int()`},float:{ts:`number`,zod:`z.number()`},boolean:{ts:`boolean`,zod:`z.boolean()`},date:{ts:`string`,zod:`z.string().datetime()`},email:{ts:`string`,zod:`z.string().email()`},url:{ts:`string`,zod:`z.string().url()`},uuid:{ts:`string`,zod:`z.string().uuid()`},json:{ts:`any`,zod:`z.any()`}};function fr(e){return e.map(e=>{let t=e.indexOf(`:`);if(t===-1)throw Error(`Invalid field: "${e}". Use format: name:type (e.g. title:string)`);let n=e.slice(0,t),r=e.slice(t+1);if(!n||!r)throw Error(`Invalid field: "${e}". Use format: name:type (e.g. title:string)`);let i=!1;r.endsWith(`:optional`)&&(r=r.slice(0,-9),i=!0),n.endsWith(`?`)&&(n=n.slice(0,-1),i=!0),r.endsWith(`?`)&&(r=r.slice(0,-1),i=!0);let a=r;if(a.startsWith(`enum:`)){let e=a.slice(5).split(`,`);return{name:n,type:`enum`,tsType:e.map(e=>`'${e}'`).join(` | `),zodType:`z.enum([${e.map(e=>`'${e}'`).join(`, `)}])`,optional:i}}let o=dr[a];if(!o){let e=[...Object.keys(dr),`enum:a,b,c`].join(`, `);throw Error(`Unknown field type: "${a}". Valid types: ${e}`)}return{name:n,type:a,tsType:o.ts,zodType:o.zod,optional:i}})}async function pr(e){let{name:t,fields:n,modulesDir:r,noEntity:i,noTests:a,repo:o=`inmemory`,tokenScope:s=`app`,style:c=`define`}=e,l=e.pluralize!==!1,u=B(t),d=R(t);z(t);let f=l?V(u):u,p=l?At(d):d,m=x(r,f),h=[],g=async(e,t)=>{let n=x(m,e);await j(n,t),h.push(n)};await g(`${u}.module.ts`,xr(d,u,f,c)),await g(`constants.ts`,_r(d,n)),await g(`presentation/${u}.controller.ts`,Sr(d,u,f,p)),await g(`application/dtos/create-${u}.dto.ts`,mr(d,n)),await g(`application/dtos/update-${u}.dto.ts`,hr(d,n)),await g(`application/dtos/${u}-response.dto.ts`,gr(d,n));let _=Tr(d,u,f,p);for(let e of _)await g(`application/use-cases/${e.file}`,e.content);return await g(`domain/repositories/${u}.repository.ts`,Cr(d,u,s)),await g(`domain/services/${u}-domain.service.ts`,wr(d,u)),o===`inmemory`&&await g(`infrastructure/repositories/in-memory-${u}.repository.ts`,vr(d,u,n)),i||(await g(`domain/entities/${u}.entity.ts`,yr(d,u,n)),await g(`domain/value-objects/${u}-id.vo.ts`,br(d))),await Er(r,d,f,u,c),h}function mr(e,t){return`import { z } from 'zod'
3102
3251
 
3103
3252
  export const create${e}Schema = z.object({
3104
3253
  ${t.map(e=>{let t=e.zodType;return` ${e.name}: ${t}${e.optional?`.optional()`:``},`}).join(`
@@ -3106,7 +3255,7 @@ ${t.map(e=>{let t=e.zodType;return` ${e.name}: ${t}${e.optional?`.optional()`:`
3106
3255
  })
3107
3256
 
3108
3257
  export type Create${e}DTO = z.infer<typeof create${e}Schema>
3109
- `}function $n(e,t){return`import { z } from 'zod'
3258
+ `}function hr(e,t){return`import { z } from 'zod'
3110
3259
 
3111
3260
  export const update${e}Schema = z.object({
3112
3261
  ${t.map(e=>` ${e.name}: ${e.zodType}.optional(),`).join(`
@@ -3114,21 +3263,21 @@ ${t.map(e=>` ${e.name}: ${e.zodType}.optional(),`).join(`
3114
3263
  })
3115
3264
 
3116
3265
  export type Update${e}DTO = z.infer<typeof update${e}Schema>
3117
- `}function er(e,t){return`export interface ${e}ResponseDTO {
3266
+ `}function gr(e,t){return`export interface ${e}ResponseDTO {
3118
3267
  id: string
3119
3268
  ${t.map(e=>` ${e.name}${e.optional?`?`:``}: ${e.tsType}`).join(`
3120
3269
  `)}
3121
3270
  createdAt: string
3122
3271
  updatedAt: string
3123
3272
  }
3124
- `}function tr(e,t){let n=t.filter(e=>e.tsType===`string`).map(e=>`'${e.name}'`);t.filter(e=>e.tsType===`number`).map(e=>`'${e.name}'`);let r=t.map(e=>`'${e.name}'`),i=[...r].join(`, `),a=[...r,`'createdAt'`,`'updatedAt'`].join(`, `),o=n.length>0?n.join(`, `):`'name'`;return`import type { ApiQueryParamsConfig } from '@forinda/kickjs'
3273
+ `}function _r(e,t){let n=t.filter(e=>e.tsType===`string`).map(e=>`'${e.name}'`);t.filter(e=>e.tsType===`number`).map(e=>`'${e.name}'`);let r=t.map(e=>`'${e.name}'`),i=[...r].join(`, `),a=[...r,`'createdAt'`,`'updatedAt'`].join(`, `),o=n.length>0?n.join(`, `):`'name'`;return`import type { ApiQueryParamsConfig } from '@forinda/kickjs'
3125
3274
 
3126
3275
  export const ${e.toUpperCase()}_QUERY_CONFIG: ApiQueryParamsConfig = {
3127
3276
  filterable: [${i}],
3128
3277
  sortable: [${a}],
3129
3278
  searchable: [${o}],
3130
3279
  }
3131
- `}function nr(e,t,n){return`import { randomUUID } from 'node:crypto'
3280
+ `}function vr(e,t,n){return`import { randomUUID } from 'node:crypto'
3132
3281
  import { Repository, HttpException } from '@forinda/kickjs'
3133
3282
  import type { ParsedQuery } from '@forinda/kickjs'
3134
3283
  import type { I${e}Repository } from '../../domain/repositories/${t}.repository'
@@ -3180,7 +3329,7 @@ ${n.map(e=>` ${e.name}: dto.${e.name},`).join(`
3180
3329
  this.store.delete(id)
3181
3330
  }
3182
3331
  }
3183
- `}function rr(e,t,n){return`import { ${e}Id } from '../value-objects/${t}-id.vo'
3332
+ `}function yr(e,t,n){return`import { ${e}Id } from '../value-objects/${t}-id.vo'
3184
3333
 
3185
3334
  interface ${e}Props {
3186
3335
  id: ${e}Id
@@ -3226,7 +3375,7 @@ ${n.map(e=>` ${e.name}: this.props.${e.name},`).join(`
3226
3375
  }
3227
3376
  }
3228
3377
  }
3229
- `}function ir(e){return`import { randomUUID } from 'node:crypto'
3378
+ `}function br(e){return`import { randomUUID } from 'node:crypto'
3230
3379
 
3231
3380
  export class ${e}Id {
3232
3381
  private constructor(private readonly value: string) {}
@@ -3241,8 +3390,7 @@ export class ${e}Id {
3241
3390
  toString(): string { return this.value }
3242
3391
  equals(other: ${e}Id): boolean { return this.value === other.value }
3243
3392
  }
3244
- `}function ar(e,t,n){return`import { type AppModule, type ModuleRoutes, Container, buildRoutes } from '@forinda/kickjs'
3245
- import { ${e}Controller } from './presentation/${t}.controller'
3393
+ `}function xr(e,t,n,r=`define`){let i=`import { ${e}Controller } from './presentation/${t}.controller'
3246
3394
  import { ${e.toUpperCase()}_REPOSITORY } from './domain/repositories/${t}.repository'
3247
3395
  import { InMemory${e}Repository } from './infrastructure/repositories/in-memory-${t}.repository'
3248
3396
 
@@ -3251,7 +3399,18 @@ import { InMemory${e}Repository } from './infrastructure/repositories/in-memory-
3251
3399
  import.meta.glob(
3252
3400
  ['./domain/services/**/*.ts', './application/use-cases/**/*.ts', '!./**/*.test.ts'],
3253
3401
  { eager: true },
3254
- )
3402
+ )`,a=` /**
3403
+ * Declare HTTP routes. Pass \`controller\` and the framework
3404
+ * derives the Express Router via \`buildRoutes()\`. Return an array
3405
+ * to mount multiple route sets — each entry can override the API
3406
+ * version with a \`version\` field:
3407
+ *
3408
+ * return [
3409
+ * { path: '/${n}', version: 1, controller: ${e}V1Controller },
3410
+ * { path: '/${n}', version: 2, controller: ${e}V2Controller },
3411
+ * ]
3412
+ */`;return r===`class`?`import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
3413
+ ${i}
3255
3414
 
3256
3415
  export class ${e}Module implements AppModule {
3257
3416
  /**
@@ -3266,15 +3425,42 @@ export class ${e}Module implements AppModule {
3266
3425
  )
3267
3426
  }
3268
3427
 
3428
+ ${a.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
3269
3429
  routes(): ModuleRoutes {
3270
3430
  return {
3271
3431
  path: '/${n}',
3272
- router: buildRoutes(${e}Controller),
3273
3432
  controller: ${e}Controller,
3274
3433
  }
3275
3434
  }
3276
3435
  }
3277
- `}function or(e,t,n,r){return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
3436
+ `:`import { defineModule } from '@forinda/kickjs'
3437
+ ${i}
3438
+
3439
+ export const ${e}Module = defineModule({
3440
+ name: '${e}Module',
3441
+ build: () => ({
3442
+ /**
3443
+ * Bind the repository token to its concrete implementation.
3444
+ * Decorator-managed classes (@Service, @Controller, @Repository) are
3445
+ * registered automatically — only token-to-impl bindings need to live here.
3446
+ */
3447
+ register(container) {
3448
+ container.registerFactory(
3449
+ ${e.toUpperCase()}_REPOSITORY,
3450
+ () => container.resolve(InMemory${e}Repository),
3451
+ )
3452
+ },
3453
+
3454
+ ${a}
3455
+ routes() {
3456
+ return {
3457
+ path: '/${n}',
3458
+ controller: ${e}Controller,
3459
+ }
3460
+ },
3461
+ }),
3462
+ })
3463
+ `}function Sr(e,t,n,r){return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
3278
3464
  import { ApiTags } from '@forinda/kickjs-swagger'
3279
3465
  import { Create${e}UseCase } from '../application/use-cases/create-${t}.use-case'
3280
3466
  import { Get${e}UseCase } from '../application/use-cases/get-${t}.use-case'
@@ -3337,7 +3523,7 @@ export class ${e}Controller {
3337
3523
  ctx.noContent()
3338
3524
  }
3339
3525
  }
3340
- `}function sr(e,t,n){return`import { createToken } from '@forinda/kickjs'
3526
+ `}function Cr(e,t,n){return`import { createToken } from '@forinda/kickjs'
3341
3527
  import type { ${e}ResponseDTO } from '../../application/dtos/${t}-response.dto'
3342
3528
  import type { Create${e}DTO } from '../../application/dtos/create-${t}.dto'
3343
3529
  import type { Update${e}DTO } from '../../application/dtos/update-${t}.dto'
@@ -3363,7 +3549,7 @@ export interface I${e}Repository {
3363
3549
  * adopters must NOT use the reserved \`'kick/'\` namespace.
3364
3550
  */
3365
3551
  export const ${e.toUpperCase()}_REPOSITORY = createToken<I${e}Repository>('${n}/${e}/repository')
3366
- `}function cr(e,t){return`import { Service, Inject, HttpException } from '@forinda/kickjs'
3552
+ `}function wr(e,t){return`import { Service, Inject, HttpException } from '@forinda/kickjs'
3367
3553
  import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../repositories/${t}.repository'
3368
3554
 
3369
3555
  @Service()
@@ -3377,7 +3563,7 @@ export class ${e}DomainService {
3377
3563
  if (!entity) throw HttpException.notFound('${e} not found')
3378
3564
  }
3379
3565
  }
3380
- `}function lr(e,t,n,r){return[{file:`create-${t}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
3566
+ `}function Tr(e,t,n,r){return[{file:`create-${t}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
3381
3567
  import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
3382
3568
  import type { Create${e}DTO } from '../dtos/create-${t}.dto'
3383
3569
 
@@ -3420,10 +3606,10 @@ export class Delete${e}UseCase {
3420
3606
  constructor(@Inject(${e.toUpperCase()}_REPOSITORY) private repo: I${e}Repository) {}
3421
3607
  async execute(id: string) { return this.repo.delete(id) }
3422
3608
  }
3423
- `}]}async function ur(e,t,n,r){let i=b(e,`index.ts`),a=await N(i),o=`./${n}/${r}.module`;if(!a){await A(i,`import type { AppModuleClass } from '@forinda/kickjs'\nimport { ${t}Module } from '${o}'\n\nexport const modules: AppModuleClass[] = [${t}Module]\n`);return}let s=await T(i,`utf-8`),c=`import { ${t}Module } from '${o}'`;if(!s.includes(`${t}Module`)){let e=s.lastIndexOf(`import `);if(e!==-1){let t=s.indexOf(`
3424
- `,e);s=s.slice(0,t+1)+c+`
3425
- `+s.slice(t+1)}else s=c+`
3426
- `+s;s=s.replace(/(=\s*\[)([\s\S]*?)(])/,(e,n,r,i)=>{let a=r.trim();if(!a)return`${n}${t}Module${i}`;let o=a.endsWith(`,`)?``:`,`;return`${n}${r.trimEnd()}${o} ${t}Module${i}`})}await E(i,s,`utf-8`)}async function dr(e){let{name:t,moduleName:n,modulesDir:r}=e,i=e.pluralize??!0,a=B(t),o=R(t),s=[],c;if(e.outDir)c=S(e.outDir);else if(n){let e=B(n),t=i?V(e):e;c=S(b(r??`src/modules`,t,`__tests__`))}else c=S(`src/__tests__`);let l=b(c,`${a}.test.ts`);return await A(l,`import { describe, it, expect, beforeEach } from 'vitest'
3609
+ `}]}async function Er(e,t,n,r,i=`define`){let a=x(e,`index.ts`),o=await Re(a),s=`./${n}/${r}.module`,c=i===`class`?`${t}Module`:`${t}Module()`;if(!o){await j(a,`import type { AppModuleEntry } from '@forinda/kickjs'\nimport { ${t}Module } from '${s}'\n\nexport const modules: AppModuleEntry[] = [${c}]\n`);return}let l=await E(a,`utf-8`),u=`import { ${t}Module } from '${s}'`;if(!l.includes(`${t}Module`)){let e=l.lastIndexOf(`import `);if(e!==-1){let t=l.indexOf(`
3610
+ `,e);l=l.slice(0,t+1)+u+`
3611
+ `+l.slice(t+1)}else l=u+`
3612
+ `+l;l=l.replace(/(=\s*\[)([\s\S]*?)(])/,(e,t,n,r)=>{let i=n.trim();if(!i)return`${t}${c}${r}`;let a=i.endsWith(`,`)?``:`,`;return`${t}${n.trimEnd()}${a} ${c}${r}`})}await D(a,l,`utf-8`)}async function Dr(e){let{name:t,moduleName:n,modulesDir:r}=e,i=e.pluralize??!0,a=B(t),o=R(t),s=[],c;if(e.outDir)c=C(e.outDir);else if(n){let e=B(n),t=i?V(e):e;c=C(x(r??`src/modules`,t,`__tests__`))}else c=C(`src/__tests__`);let l=x(c,`${a}.test.ts`);return await j(l,`import { describe, it, expect, beforeEach } from 'vitest'
3427
3613
  import { Container } from '@forinda/kickjs'
3428
3614
 
3429
3615
  describe('${o}', () => {
@@ -3446,59 +3632,59 @@ describe('${o}', () => {
3446
3632
  expect(true).toBe(true)
3447
3633
  })
3448
3634
  })
3449
- `),s.push(l),s}const fr=[`agents`,`claude`,`skills`,`both`,`all`];function W(e){return e.parent?.opts()?.dryRun??!1}function G(e,t=!1){let n=process.cwd();console.log(`\n ${t?`Would generate`:`Generated`} ${e.length} file${e.length===1?``:`s`}:`);for(let t of e)console.log(` ${t.replace(n+`/`,``)}`);t&&console.log(`
3450
- (dry run — no files were written)`),console.log()}async function pr(e){if(!e)try{let e=await r(process.cwd());await f({cwd:process.cwd(),allowDuplicates:!0,silent:!0,schemaValidator:e?.typegen?.schemaValidator??`zod`,envFile:e?.typegen?.envFile,srcDir:e?.typegen?.srcDir,outDir:e?.typegen?.outDir})}catch{}}const mr=[{name:`module <name>`,description:`Full DDD module (controller, DTOs, use-cases, repo)`},{name:`scaffold <name> <fields...>`,description:`CRUD module from field definitions`},{name:`controller <name>`,description:`@Controller() class [-m module]`},{name:`service <name>`,description:`@Service() singleton [-m module]`},{name:`middleware <name>`,description:`Express middleware function [-m module]`},{name:`guard <name>`,description:`Route guard (auth, roles, etc.) [-m module]`},{name:`dto <name>`,description:`Zod DTO schema [-m module]`},{name:`adapter <name>`,description:`AppAdapter with lifecycle hooks (app-level only)`},{name:`test <name>`,description:`Vitest test scaffold [-m module]`},{name:`job <name>`,description:`Queue @Job processor`},{name:`config`,description:`Generate kick.config.ts`},{name:`agents`,description:`Regenerate AGENTS.md + CLAUDE.md + kickjs-skills.md from upstream templates`}];async function hr(){console.log(`
3635
+ `),s.push(l),s}const Or=[`agents`,`claude`,`skills`,`both`,`all`];function K(e){return e.parent?.opts()?.dryRun??!1}function q(e,t=!1){let n=process.cwd();console.log(`\n ${t?`Would generate`:`Generated`} ${e.length} file${e.length===1?``:`s`}:`);for(let t of e)console.log(` ${t.replace(n+`/`,``)}`);t&&console.log(`
3636
+ (dry run — no files were written)`),console.log()}async function kr(e){if(!e)try{let e=await r(process.cwd());await f({cwd:process.cwd(),allowDuplicates:!0,silent:!0,schemaValidator:e?.typegen?.schemaValidator??`zod`,envFile:e?.typegen?.envFile,srcDir:e?.typegen?.srcDir,outDir:e?.typegen?.outDir})}catch{}}const Ar=[{name:`module <name>`,description:`Full DDD module (controller, DTOs, use-cases, repo)`},{name:`scaffold <name> <fields...>`,description:`CRUD module from field definitions`},{name:`controller <name>`,description:`@Controller() class [-m module]`},{name:`service <name>`,description:`@Service() singleton [-m module]`},{name:`middleware <name>`,description:`Express middleware function [-m module]`},{name:`guard <name>`,description:`Route guard (auth, roles, etc.) [-m module]`},{name:`dto <name>`,description:`Zod DTO schema [-m module]`},{name:`adapter <name>`,description:`AppAdapter with lifecycle hooks (app-level only)`},{name:`test <name>`,description:`Vitest test scaffold [-m module]`},{name:`job <name>`,description:`Queue @Job processor`},{name:`config`,description:`Generate kick.config.ts`},{name:`agents`,description:`Regenerate AGENTS.md + CLAUDE.md + kickjs-skills.md from upstream templates`}];async function jr(){console.log(`
3451
3637
  Built-in generators:
3452
- `);let e=Math.max(...mr.map(e=>e.name.length));for(let t of mr)console.log(` kick g ${t.name.padEnd(e+2)} ${t.description}`);let t=await r(process.cwd()),n=a(t?.plugins??[],t?.commands??[]),i=await Rt(process.cwd(),n.generators);if(i.generators.length>0){console.log(`
3638
+ `);let e=Math.max(...Ar.map(e=>e.name.length));for(let t of Ar)console.log(` kick g ${t.name.padEnd(e+2)} ${t.description}`);let t=await r(process.cwd()),n=a(t?.plugins??[],t?.commands??[]),i=await Vt(process.cwd(),n.generators);if(i.generators.length>0){console.log(`
3453
3639
  Plugin generators:
3454
3640
  `);let e=Math.max(...i.generators.map(e=>`${e.spec.name} <name>`.length));for(let{source:t,spec:n}of i.generators){let r=`${n.name} <name>`;console.log(` kick g ${r.padEnd(e+2)} ${n.description} [${t}]`)}}if(i.failed.length>0){console.log(`
3455
3641
  Failed to load:
3456
- `);for(let{source:e,reason:t}of i.failed)console.log(` ${e} — ${t}`)}console.log()}async function gr(e,i,a){let o=await r(process.cwd()),s=n(o),c=i.modulesDir??s.dir??`src/modules`,l=i.repo??wn(s.repo),u=i.pattern??o?.pattern??`ddd`,d=i.pluralize===!1?!1:s.pluralize??!0,f=t(o,process.cwd()),p=[];for(let t of e){let e=await Tn({name:t,modulesDir:S(c),noEntity:i.entity===!1,noTests:i.tests===!1,repo:l,minimal:i.minimal,force:i.force,pattern:u,dryRun:a,pluralize:d,prismaClientPath:s.prismaClientPath,tokenScope:f});p.push(...e)}G(p,a),await pr(a)}function _r(e){let i=e.command(`generate [names...]`).alias(`g`).description("Generate code scaffolds — bare form `kick g <name>` is shorthand for `kick g module <name>`").option(`--list`,`List all available generators`).option(`--dry-run`,`Preview files that would be generated without writing them`).option(`--no-entity`,`Skip entity and value object generation (module shortcut)`).option(`--no-tests`,`Skip test file generation (module shortcut)`).option(`--repo <type>`,`Repository implementation: inmemory | drizzle | prisma`).option(`--pattern <pattern>`,`Override project pattern: rest | ddd | cqrs | minimal`).option(`--minimal`,`Shorthand for --pattern minimal`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular names (skip auto-pluralization)`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t,n)=>{if(t.list){await hr();return}if(!e||e.length===0){i.help();return}let o=W(n);if(k(o),e.length>=2){let[n,i,...s]=e,c=await r(process.cwd()),l=a(c?.plugins??[],c?.commands??[]),u=await Lt({generatorName:n,itemName:i,args:s,flags:t,cwd:process.cwd()},l.generators);if(u){G(u.files,o);return}}await gr(e,t,o)});i.command(`module <names...>`).description(`Generate one or more modules (e.g. kick g module user task project)`).option(`--no-entity`,`Skip entity and value object generation`).option(`--no-tests`,`Skip test file generation`).option(`--repo <type>`,`Repository implementation: inmemory | drizzle | prisma`).option(`--pattern <pattern>`,`Override project pattern: rest | ddd | cqrs | minimal`).option(`--minimal`,`Shorthand for --pattern minimal`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular names (skip auto-pluralization)`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t,n)=>{let r=W(n);k(r),await gr(e,t,r)}),i.command(`adapter <name>`).description(`Generate an AppAdapter with lifecycle hooks and middleware support`).option(`-o, --out <dir>`,`Output directory`,`src/adapters`).action(async(e,t,n)=>{let r=W(n);k(r),G(await Dn({name:e,outDir:S(t.out)}),r)}),i.command(`plugin <name>`).description(`Generate a KickPlugin with DI, modules, adapters, middleware, and lifecycle hooks`).option(`-o, --out <dir>`,`Output directory`,`src/plugins`).action(async(e,t,n)=>{let r=W(n);k(r),G(await On({name:e,outDir:S(t.out)}),r)}),i.command(`middleware <name>`).description(`Generate an Express middleware function
3457
- Use -m to scope it to a module: kick g middleware auth -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=W(i);k(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;G(await Mn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`guard <name>`).description(`Generate a route guard (auth, roles, etc.)
3458
- Use -m to scope it to a module: kick g guard admin -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=W(i);k(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;G(await Nn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`service <name>`).description(`Generate a @Service() class
3459
- Use -m to scope it to a module: kick g service payment -m orders`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=W(i);k(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;G(await Pn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`controller <name>`).description(`Generate a @Controller() class with basic routes
3460
- Use -m to scope it to a module: kick g controller auth -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=W(i);k(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;G(await Fn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a),await pr(a)}),i.command(`dto <name>`).description(`Generate a Zod DTO schema
3461
- Use -m to scope it to a module: kick g dto create-user -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=W(i);k(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;G(await In({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`test <name>`).description(`Generate a Vitest test scaffold
3462
- Use -m to scope it to a module: kick g test user-service -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module's __tests__/ folder`).action(async(e,t,i)=>{let a=W(i);k(a);let o=n(await r(process.cwd())),s=o.dir??`src/modules`;G(await dr({name:e,outDir:t.out,moduleName:t.module,modulesDir:s,pluralize:o.pluralize??!0}),a)}),i.command(`job <name>`).description(`Generate a @Job queue processor with @Process handlers`).option(`-o, --out <dir>`,`Output directory`,`src/jobs`).option(`-q, --queue <name>`,`Queue name (default: <name>-queue)`).action(async(e,t,n)=>{let r=W(n);k(r),G(await Jn({name:e,outDir:S(t.out),queue:t.queue}),r)}),i.command(`scaffold <name> [fields...]`).description(`Generate a full CRUD module from field definitions
3642
+ `);for(let{source:e,reason:t}of i.failed)console.log(` ${e} — ${t}`)}console.log()}async function Mr(e,i,a){let o=await r(process.cwd()),s=n(o),c=i.modulesDir??s.dir??`src/modules`,l=i.repo??On(s.repo),u=i.pattern??o?.pattern??`ddd`,d=i.pluralize===!1?!1:s.pluralize??!0,f=t(o,process.cwd()),p=s.style??`define`;if(!a&&p===`define`){let e=await ir(C(c),`define`);if(e.length>0){console.error(`\n ${k.red(`Error:`)} ${e.length} module file(s) still use the legacy \`class … implements AppModule\` shape.\n ${k.dim(`Project setting:`)} modules.style: 'define' (default)\n\n ${k.bold(`Files needing migration:`)}`);for(let t of e.slice(0,5))console.error(` - ${t}`);e.length>5&&console.error(` … and ${e.length-5} more`),console.error(`\n ${k.bold(`Pick one:`)}\n 1. Migrate everything to defineModule:\n ${k.dim(`$`)} kick codemod modules --experimental --apply\n 2. Keep the class form — pin it in kick.config.ts:\n ${k.dim(`// kick.config.ts`)}\n ${k.dim(`export default defineConfig({ modules: { style: 'class' } })`)}\n`),process.exit(1)}}let m=[];for(let t of e){let e=await kn({name:t,modulesDir:C(c),noEntity:i.entity===!1,noTests:i.tests===!1,repo:l,minimal:i.minimal,force:i.force,pattern:u,dryRun:a,pluralize:d,prismaClientPath:s.prismaClientPath,tokenScope:f,style:s.style});m.push(...e)}q(m,a),await kr(a)}function Nr(e){let i=e.command(`generate [names...]`).alias(`g`).description("Generate code scaffolds — bare form `kick g <name>` is shorthand for `kick g module <name>`").option(`--list`,`List all available generators`).option(`--dry-run`,`Preview files that would be generated without writing them`).option(`--no-entity`,`Skip entity and value object generation (module shortcut)`).option(`--no-tests`,`Skip test file generation (module shortcut)`).option(`--repo <type>`,`Repository implementation: inmemory | drizzle | prisma`).option(`--pattern <pattern>`,`Override project pattern: rest | ddd | cqrs | minimal`).option(`--minimal`,`Shorthand for --pattern minimal`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular names (skip auto-pluralization)`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t,n)=>{if(t.list){await jr();return}if(!e||e.length===0){i.help();return}let o=K(n);if(A(o),e.length>=2){let[n,i,...s]=e,c=await r(process.cwd()),l=a(c?.plugins??[],c?.commands??[]),u=await Bt({generatorName:n,itemName:i,args:s,flags:t,cwd:process.cwd()},l.generators);if(u){q(u.files,o);return}}await Mr(e,t,o)});i.command(`module <names...>`).description(`Generate one or more modules (e.g. kick g module user task project)`).option(`--no-entity`,`Skip entity and value object generation`).option(`--no-tests`,`Skip test file generation`).option(`--repo <type>`,`Repository implementation: inmemory | drizzle | prisma`).option(`--pattern <pattern>`,`Override project pattern: rest | ddd | cqrs | minimal`).option(`--minimal`,`Shorthand for --pattern minimal`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular names (skip auto-pluralization)`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t,n)=>{let r=K(n);A(r),await Mr(e,t,r)}),i.command(`adapter <name>`).description(`Generate an AppAdapter with lifecycle hooks and middleware support`).option(`-o, --out <dir>`,`Output directory`,`src/adapters`).action(async(e,t,n)=>{let r=K(n);A(r),q(await jn({name:e,outDir:C(t.out)}),r)}),i.command(`plugin <name>`).description(`Generate a KickPlugin with DI, modules, adapters, middleware, and lifecycle hooks`).option(`-o, --out <dir>`,`Output directory`,`src/plugins`).action(async(e,t,n)=>{let r=K(n);A(r),q(await Mn({name:e,outDir:C(t.out)}),r)}),i.command(`middleware <name>`).description(`Generate an Express middleware function
3643
+ Use -m to scope it to a module: kick g middleware auth -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=K(i);A(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;q(await In({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`guard <name>`).description(`Generate a route guard (auth, roles, etc.)
3644
+ Use -m to scope it to a module: kick g guard admin -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=K(i);A(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;q(await Ln({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`service <name>`).description(`Generate a @Service() class
3645
+ Use -m to scope it to a module: kick g service payment -m orders`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=K(i);A(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;q(await Rn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`controller <name>`).description(`Generate a @Controller() class with basic routes
3646
+ Use -m to scope it to a module: kick g controller auth -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=K(i);A(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;q(await zn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a),await kr(a)}),i.command(`dto <name>`).description(`Generate a Zod DTO schema
3647
+ Use -m to scope it to a module: kick g dto create-user -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=K(i);A(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;q(await Bn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`test <name>`).description(`Generate a Vitest test scaffold
3648
+ Use -m to scope it to a module: kick g test user-service -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module's __tests__/ folder`).action(async(e,t,i)=>{let a=K(i);A(a);let o=n(await r(process.cwd())),s=o.dir??`src/modules`;q(await Dr({name:e,outDir:t.out,moduleName:t.module,modulesDir:s,pluralize:o.pluralize??!0}),a)}),i.command(`job <name>`).description(`Generate a @Job queue processor with @Process handlers`).option(`-o, --out <dir>`,`Output directory`,`src/jobs`).option(`-q, --queue <name>`,`Queue name (default: <name>-queue)`).action(async(e,t,n)=>{let r=K(n);A(r),q(await ur({name:e,outDir:C(t.out),queue:t.queue}),r)}),i.command(`scaffold <name> [fields...]`).description(`Generate a full CRUD module from field definitions
3463
3649
  Example: kick g scaffold Post title:string body:text:optional published:boolean:optional
3464
3650
  Types: string, text, number, int, float, boolean, date, email, url, uuid, json, enum:a,b,c
3465
3651
  Optional: append :optional (shell-safe): description:text:optional
3466
- or use ? with quoting: "description:text?" or "description?:text"`).option(`--no-entity`,`Skip entity and value object generation`).option(`--no-tests`,`Skip test file generation`).option(`--no-pluralize`,`Use singular names (skip auto-pluralization)`).option(`--modules-dir <dir>`,`Modules directory`).action(async(e,i,a,o)=>{let s=W(o);k(s),i.length===0&&(console.error(`
3652
+ or use ? with quoting: "description:text?" or "description?:text"`).option(`--no-entity`,`Skip entity and value object generation`).option(`--no-tests`,`Skip test file generation`).option(`--no-pluralize`,`Use singular names (skip auto-pluralization)`).option(`--modules-dir <dir>`,`Modules directory`).action(async(e,i,a,o)=>{let s=K(o);A(s),i.length===0&&(console.error(`
3467
3653
  Error: At least one field is required.
3468
3654
  Usage: kick g scaffold <name> <field:type> [field:type...]
3469
3655
  Example: kick g scaffold Post title:string body:text:optional published:boolean:optional
3470
3656
  Optional: append :optional (shell-safe, no quoting needed)
3471
- `),process.exit(1));let c=await r(process.cwd()),l=n(c),u=a.modulesDir??l.dir??`src/modules`,d=Xn(i),f=t(c,process.cwd()),p=await Zn({name:e,fields:d,modulesDir:S(u),noEntity:a.entity===!1,noTests:a.tests===!1,pluralize:a.pluralize===!1?!1:l.pluralize??!0,tokenScope:f});console.log(`\n Scaffolded ${e} with ${d.length} field(s):`);for(let e of d)console.log(` ${e.name}: ${e.type}${e.optional?` (optional)`:``}`);G(p,s),await pr(s)}),i.command(`auth-scaffold`).description(`Generate a complete auth module (register, login, logout, password hashing)
3472
- Includes controller, service, DTOs, and test stubs.`).option(`-s, --strategy <type>`,`Auth strategy: jwt | session`).option(`--role-guards`,`Generate role-based guards (default: true)`).option(`--no-role-guards`,`Skip role-based guard generation`).option(`-o, --out <dir>`,`Output directory`,`src/modules/auth`).action(async(e,t)=>{let n=W(t);k(n);let r=e.strategy;r||=await mt({message:`Auth strategy`,options:[{value:`jwt`,label:`JWT`,hint:`stateless token-based auth`},{value:`session`,label:`Session`,hint:`server-side session with cookies`}]});let i=e.roleGuards;i===void 0&&(i=await F({message:`Generate role-based guards?`,initialValue:!0})),G(await Un({strategy:r,outDir:e.out,roleGuards:i}),n)}),i.command(`config`).description(`Generate a kick.config.ts at the project root`).option(`--modules-dir <dir>`,`Modules directory path`,`src/modules`).option(`--repo <type>`,`Default repository type: inmemory | drizzle | prisma`,`inmemory`).option(`-f, --force`,`Overwrite existing kick.config.ts without prompting`).action(async(e,t)=>{let n=W(t);k(n),G(await Ln({outDir:S(`.`),modulesDir:e.modulesDir,defaultRepo:e.repo,force:e.force}),n)}),i.command(`agents`).alias(`agent-docs`).alias(`ai-docs`).description(`Regenerate AGENTS.md + CLAUDE.md + kickjs-skills.md (sync after framework upgrades)`).option(`--only <which>`,`Limit scope: agents | claude | skills | both (agents+claude) | all (default: all)`,`all`).option(`--name <name>`,`Project name (defaults to package.json name)`).option(`--pm <pm>`,`Package manager (defaults to package.json packageManager)`).option(`--template <template>`,`Template: rest | ddd | cqrs | minimal`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t)=>{let n=W(t);k(n);let r=e.only??`all`;if(!fr.includes(r)){console.error(` Invalid --only value: ${r}. Expected: ${fr.join(` | `)}`),process.exitCode=1;return}G(await Hn({outDir:S(`.`),only:r,name:e.name,pm:e.pm,template:e.template,force:e.force}),n)})}async function vr(e){let t=v.resolve(e.cwd,`.kickjs/types`);await pe(t,{recursive:!0});let n=new Map,r=e.scan??d,i={cwd:e.cwd,config:e.config,async importTs(e){return await import(C(e).href)},async writeFile(t,n){let r=v.resolve(e.cwd,t);await pe(v.dirname(r),{recursive:!0}),await E(r,n,`utf8`)},getScanResult:e=>{let t=yr(e),i=n.get(t);return i||(i=r(e),n.set(t,i)),i},log:console},a=[];for(let n of e.plugins){let r=await n.generate(i);if(r===null){a.push({id:n.id,status:`skipped`});continue}let o=n.outExtension??`.d.ts`,s=v.join(t,`${n.id.replace(/\//g,`__`)}${o}`),c=`/* AUTO-GENERATED by kick typegen — do not edit. Plugin: ${n.id} */\n\n`+r+`
3473
- `,l=``;if(h(s)&&(l=await T(s,`utf8`)),l===c){a.push({id:n.id,status:`unchanged`,outFile:s});continue}if(e.check)throw Error(`kick typegen --check: drift detected for ${n.id} (${s})`);await E(s,c,`utf8`),a.push({id:n.id,status:`written`,outFile:s})}return a}function yr(e){let t=(e.extensions??[]).slice().toSorted().join(`,`),n=(e.exclude??[]).slice().toSorted().join(`,`);return[`root=${e.root}`,`cwd=${e.cwd}`,`extensions=${t}`,`exclude=${n}`,`envFile=${e.envFile??``}`].join(`|`)}function br(e,t){let n=new Set(t),r=[],i=[],a=new Set;for(let t of e)n.has(t.id)?(i.push(t),a.add(t.id)):r.push(t);return{enabled:r,skipped:i,unknown:[...n].filter(e=>!a.has(e))}}var xr=e({applyDisableFilter:()=>br,runAllPluginTypegens:()=>Sr});async function Sr(e){let{enabled:t,skipped:n,unknown:r}=br(a([...Zi,...e.config?.plugins??[]],e.config?.commands??[]).typegens,e.config?.typegen?.disable??[]);if(!e.silent&&n.length>0)for(let e of n)console.log(` ${e.id}: disabled (typegen.disable)`);if(!e.silent&&r.length>0&&console.warn(` kick typegen: disable list references unknown id(s): ${r.map(e=>`'${e}'`).join(`, `)}. Run \`kick typegen --list\` to see registered ids.`),t.length===0)return[];try{let n=await vr({cwd:e.cwd,config:e.config??{},plugins:t,check:e.check});if(!e.silent)for(let e of n)console.log(` ${e.id}: ${e.status}`);return n}catch(t){if(!e.silent){let e=t instanceof Error?t.message:String(t);console.warn(` kick typegen plugins: skipped (${e})`)}return[]}}async function Cr(e,t){let{cwd:n,silent:r=!1}=t,i=t.distDir??e?.build?.outDir??`dist`,a=e?.assetMap;if(!a||Object.keys(a).length===0)return null;let o=r?()=>{}:console.log,s=S(n,i);g(s,{recursive:!0});let c=[],l={};for(let[e,t]of Object.entries(a)){let r=await wr(e,t,n,s);c.push(r.entrySummary),Object.assign(l,r.manifestSlice),o(` ✓ ${e}: ${r.entrySummary.filesCopied} file(s) → ${r.entrySummary.dest}`)}let u={version:1,entries:l},d=b(s,`.kickjs-assets.json`);return re(d,JSON.stringify(u,null,2)+`
3474
- `,`utf-8`),o(` ✓ wrote manifest → ${x(n,d)} (${Object.keys(l).length} entries)`),{manifestPath:d,entries:c,manifest:u}}async function wr(e,t,n,r){let i=S(n,t.src),a=t.dest?S(n,t.dest):b(r,e);if(Er(a,n))return console.warn(` ⚠ assetMap.${e}.dest ('${t.dest}') resolves outside the project root — skipping copy`),{entrySummary:{namespace:e,src:t.src,dest:x(n,a),filesCopied:0},manifestSlice:{}};if(!h(i)||!Dr(i))return{entrySummary:{namespace:e,src:t.src,dest:x(n,a),filesCopied:0},manifestSlice:{}};let o=await ge(t.glob??`**/*`,{cwd:i,nodir:!0,dot:!1,posix:!0});g(a,{recursive:!0});let s={},{pairs:c,collisionGroupsResolved:l}=_e(e,[...o].toSorted(),{strategy:t.keys??`auto`});for(let{rel:e,key:t}of c){let n=b(i,e),o=b(a,e);g(y(o),{recursive:!0}),m(n,o),s[t]=Tr(r,o)}return l>0&&console.log(` ℹ assetMap.${e}: auto-resolved ${l} basename collision(s) by keeping extensions (set 'keys: "strip"' to opt back into legacy last-write-wins behaviour, or 'keys: "with-extension"' to keep all keys verbose).`),{entrySummary:{namespace:e,src:t.src,dest:x(n,a),filesCopied:o.length},manifestSlice:s}}function Tr(e,t){return x(e,t).split(/[\\/]/).filter(Boolean).join(`/`)}function Er(e,t){let n=x(t,e);return n===``?!1:n.startsWith(`..`)||oe(n)}function Dr(e){try{return ne(e).isDirectory()}catch{return!1}}function Or(e){if(typeof e==`boolean`)return e;let t=process.env.KICKJS_WATCH_POLLING;return t===`1`||t===`true`}async function kr(e,t,n={}){t&&(process.env.PORT=t);let i=Or(n.polling),a=process.cwd(),o=await r(a),s=o?.typegen?.schemaValidator??`zod`,c=o?.typegen?.envFile;try{await f({cwd:a,allowDuplicates:!0,schemaValidator:s,envFile:c,srcDir:o?.typegen?.srcDir,outDir:o?.typegen?.outDir,assetMap:o?.assetMap,runPlugins:!1})}catch(e){console.warn(` kick typegen: skipped (${e?.message??e})`)}await Sr({cwd:a,config:o});let{createRequire:l}=await import(`node:module`),{createServer:u}=await import(C(l(S(`package.json`)).resolve(`vite`)).href),d=await u({configFile:S(`vite.config.ts`),server:{port:t?parseInt(t,10):void 0,...i?{watch:{usePolling:!0,interval:100}}:{}}}),p=o?.assetMap?Object.values(o.assetMap).map(e=>e?.src).filter(e=>typeof e==`string`&&e.length>0).map(e=>S(a,e)):[],m=e=>p.some(t=>e===t||e.startsWith(`${t}/`)),h=null,g=e=>{if(e.includes(`.kickjs`)||e.endsWith(`.d.ts`))return;let t=/\.(ts|tsx|mts|cts)$/.test(e),n=m(e);!t&&!n||(h&&clearTimeout(h),h=setTimeout(()=>{f({cwd:a,silent:!0,allowDuplicates:!0,schemaValidator:s,envFile:c,srcDir:o?.typegen?.srcDir,outDir:o?.typegen?.outDir,assetMap:o?.assetMap,runPlugins:!1}).catch(()=>{}),Sr({cwd:a,config:o,silent:!0}).catch(()=>{})},100))};d.watcher.on(`add`,g),d.watcher.on(`unlink`,g),d.watcher.on(`change`,g),p.length>0&&d.watcher.add(p),await d.listen(),d.printUrls(),console.log(`
3657
+ `),process.exit(1));let c=await r(process.cwd()),l=n(c),u=a.modulesDir??l.dir??`src/modules`,d=fr(i),f=t(c,process.cwd()),p=c?.pattern??`ddd`;p!==`ddd`&&(console.error(`\n Error: 'kick g scaffold' currently only supports the DDD pattern.\n Detected project pattern: '${p}'.\n Workarounds:\n - Run 'kick g module ${e}' for the ${p} layout (no fields), then add fields manually.\n - Override the pattern for this scaffold by setting kick.config.ts pattern: 'ddd'.\n`),process.exit(1));let m=await pr({name:e,fields:d,modulesDir:C(u),noEntity:a.entity===!1,noTests:a.tests===!1,pluralize:a.pluralize===!1?!1:l.pluralize??!0,tokenScope:f,style:l.style});console.log(`\n Scaffolded ${e} with ${d.length} field(s):`);for(let e of d)console.log(` ${e.name}: ${e.type}${e.optional?` (optional)`:``}`);q(m,s),await kr(s)}),i.command(`auth-scaffold`).description(`Generate a complete auth module (register, login, logout, password hashing)
3658
+ Includes controller, service, DTOs, and test stubs.`).option(`-s, --strategy <type>`,`Auth strategy: jwt | session`).option(`--role-guards`,`Generate role-based guards (default: true)`).option(`--no-role-guards`,`Skip role-based guard generation`).option(`-o, --out <dir>`,`Output directory`,`src/modules/auth`).action(async(e,t)=>{let n=K(t);A(n);let r=e.strategy;r||=await _t({message:`Auth strategy`,options:[{value:`jwt`,label:`JWT`,hint:`stateless token-based auth`},{value:`session`,label:`Session`,hint:`server-side session with cookies`}]});let i=e.roleGuards;i===void 0&&(i=await F({message:`Generate role-based guards?`,initialValue:!0})),q(await ar({strategy:r,outDir:e.out,roleGuards:i}),n)}),i.command(`config`).description(`Generate a kick.config.ts at the project root`).option(`--modules-dir <dir>`,`Modules directory path`,`src/modules`).option(`--repo <type>`,`Default repository type: inmemory | drizzle | prisma`,`inmemory`).option(`-f, --force`,`Overwrite existing kick.config.ts without prompting`).action(async(e,t)=>{let n=K(t);A(n),q(await Vn({outDir:C(`.`),modulesDir:e.modulesDir,defaultRepo:e.repo,force:e.force}),n)}),i.command(`agents`).alias(`agent-docs`).alias(`ai-docs`).description(`Regenerate AGENTS.md + CLAUDE.md + kickjs-skills.md (sync after framework upgrades)`).option(`--only <which>`,`Limit scope: agents | claude | skills | both (agents+claude) | all (default: all)`,`all`).option(`--name <name>`,`Project name (defaults to package.json name)`).option(`--pm <pm>`,`Package manager (defaults to package.json packageManager)`).option(`--template <template>`,`Template: rest | ddd | cqrs | minimal`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t)=>{let n=K(t);A(n);let r=e.only??`all`;if(!Or.includes(r)){console.error(` Invalid --only value: ${r}. Expected: ${Or.join(` | `)}`),process.exitCode=1;return}q(await Kn({outDir:C(`.`),only:r,name:e.name,pm:e.pm,template:e.template,force:e.force}),n)})}async function Pr(e){let t=y.resolve(e.cwd,`.kickjs/types`);await pe(t,{recursive:!0});let n=new Map,r=e.scan??d,i={cwd:e.cwd,config:e.config,async importTs(e){return await import(w(e).href)},async writeFile(t,n){let r=y.resolve(e.cwd,t);await pe(y.dirname(r),{recursive:!0}),await D(r,n,`utf8`)},getScanResult:e=>{let t=Fr(e),i=n.get(t);return i||(i=r(e),n.set(t,i)),i},log:console},a=[];for(let n of e.plugins){let r=await n.generate(i);if(r===null){a.push({id:n.id,status:`skipped`});continue}let o=n.outExtension??`.d.ts`,s=y.join(t,`${n.id.replace(/\//g,`__`)}${o}`),c=`/* AUTO-GENERATED by kick typegen — do not edit. Plugin: ${n.id} */\n\n`+r+`
3659
+ `,l=``;if(h(s)&&(l=await E(s,`utf8`)),l===c){a.push({id:n.id,status:`unchanged`,outFile:s});continue}if(e.check)throw Error(`kick typegen --check: drift detected for ${n.id} (${s})`);await D(s,c,`utf8`),a.push({id:n.id,status:`written`,outFile:s})}return a}function Fr(e){let t=(e.extensions??[]).slice().toSorted().join(`,`),n=(e.exclude??[]).slice().toSorted().join(`,`);return[`root=${e.root}`,`cwd=${e.cwd}`,`extensions=${t}`,`exclude=${n}`,`envFile=${e.envFile??``}`].join(`|`)}function Ir(e,t){let n=new Set(t),r=[],i=[],a=new Set;for(let t of e)n.has(t.id)?(i.push(t),a.add(t.id)):r.push(t);return{enabled:r,skipped:i,unknown:[...n].filter(e=>!a.has(e))}}var Lr=e({applyDisableFilter:()=>Ir,runAllPluginTypegens:()=>Rr});async function Rr(e){let{enabled:t,skipped:n,unknown:r}=Ir(a([..._a,...e.config?.plugins??[]],e.config?.commands??[]).typegens,e.config?.typegen?.disable??[]);if(!e.silent&&n.length>0)for(let e of n)console.log(` ${e.id}: disabled (typegen.disable)`);if(!e.silent&&r.length>0&&console.warn(` kick typegen: disable list references unknown id(s): ${r.map(e=>`'${e}'`).join(`, `)}. Run \`kick typegen --list\` to see registered ids.`),t.length===0)return[];try{let n=await Pr({cwd:e.cwd,config:e.config??{},plugins:t,check:e.check});if(!e.silent)for(let e of n)console.log(` ${e.id}: ${e.status}`);return n}catch(t){if(!e.silent){let e=t instanceof Error?t.message:String(t);console.warn(` kick typegen plugins: skipped (${e})`)}return[]}}async function zr(e,t){let{cwd:n,silent:r=!1}=t,i=t.distDir??e?.build?.outDir??`dist`,a=e?.assetMap;if(!a||Object.keys(a).length===0)return null;let o=r?()=>{}:console.log,s=C(n,i);g(s,{recursive:!0});let c=[],l={};for(let[e,t]of Object.entries(a)){let r=await Br(e,t,n,s);c.push(r.entrySummary),Object.assign(l,r.manifestSlice),o(` ✓ ${e}: ${r.entrySummary.filesCopied} file(s) → ${r.entrySummary.dest}`)}let u={version:1,entries:l},d=x(s,`.kickjs-assets.json`);return ne(d,JSON.stringify(u,null,2)+`
3660
+ `,`utf-8`),o(` ✓ wrote manifest → ${S(n,d)} (${Object.keys(l).length} entries)`),{manifestPath:d,entries:c,manifest:u}}async function Br(e,t,n,r){let i=C(n,t.src),a=t.dest?C(n,t.dest):x(r,e);if(Hr(a,n))return console.warn(` ⚠ assetMap.${e}.dest ('${t.dest}') resolves outside the project root — skipping copy`),{entrySummary:{namespace:e,src:t.src,dest:S(n,a),filesCopied:0},manifestSlice:{}};if(!h(i)||!Ur(i))return{entrySummary:{namespace:e,src:t.src,dest:S(n,a),filesCopied:0},manifestSlice:{}};let o=await ve(t.glob??`**/*`,{cwd:i,nodir:!0,dot:!1,posix:!0});g(a,{recursive:!0});let s={},{pairs:c,collisionGroupsResolved:l}=ye(e,[...o].toSorted(),{strategy:t.keys??`auto`});for(let{rel:e,key:t}of c){let n=x(i,e),o=x(a,e);g(b(o),{recursive:!0}),m(n,o),s[t]=Vr(r,o)}return l>0&&console.log(` ℹ assetMap.${e}: auto-resolved ${l} basename collision(s) by keeping extensions (set 'keys: "strip"' to opt back into legacy last-write-wins behaviour, or 'keys: "with-extension"' to keep all keys verbose).`),{entrySummary:{namespace:e,src:t.src,dest:S(n,a),filesCopied:o.length},manifestSlice:s}}function Vr(e,t){return S(e,t).split(/[\\/]/).filter(Boolean).join(`/`)}function Hr(e,t){let n=S(t,e);return n===``?!1:n.startsWith(`..`)||ae(n)}function Ur(e){try{return te(e).isDirectory()}catch{return!1}}function Wr(e){if(typeof e==`boolean`)return e;let t=process.env.KICKJS_WATCH_POLLING;return t===`1`||t===`true`}async function Gr(e,t,n={}){t&&(process.env.PORT=t);let i=Wr(n.polling),a=process.cwd(),o=await r(a),s=o?.typegen?.schemaValidator??`zod`,c=o?.typegen?.envFile;try{await f({cwd:a,allowDuplicates:!0,schemaValidator:s,envFile:c,srcDir:o?.typegen?.srcDir,outDir:o?.typegen?.outDir,assetMap:o?.assetMap,runPlugins:!1})}catch(e){console.warn(` kick typegen: skipped (${e?.message??e})`)}await Rr({cwd:a,config:o});let{createRequire:l}=await import(`node:module`),{createServer:u}=await import(w(l(C(`package.json`)).resolve(`vite`)).href),d=await u({configFile:C(`vite.config.ts`),server:{port:t?parseInt(t,10):void 0,...i?{watch:{usePolling:!0,interval:100}}:{}}}),p=o?.assetMap?Object.values(o.assetMap).map(e=>e?.src).filter(e=>typeof e==`string`&&e.length>0).map(e=>C(a,e)):[],m=e=>p.some(t=>e===t||e.startsWith(`${t}/`)),h=null,g=e=>{if(e.includes(`.kickjs`)||e.endsWith(`.d.ts`))return;let t=/\.(ts|tsx|mts|cts)$/.test(e),n=m(e);!t&&!n||(h&&clearTimeout(h),h=setTimeout(()=>{f({cwd:a,silent:!0,allowDuplicates:!0,schemaValidator:s,envFile:c,srcDir:o?.typegen?.srcDir,outDir:o?.typegen?.outDir,assetMap:o?.assetMap,runPlugins:!1}).catch(()=>{}),Rr({cwd:a,config:o,silent:!0}).catch(()=>{})},100))};d.watcher.on(`add`,g),d.watcher.on(`unlink`,g),d.watcher.on(`change`,g),p.length>0&&d.watcher.add(p),await d.listen(),d.printUrls(),console.log(`
3475
3661
  KickJS dev server running (Vite + @forinda/kickjs-vite)
3476
- `);let _=async()=>{h&&clearTimeout(h),await d.close(),process.exit(0)};process.on(`SIGINT`,_),process.on(`SIGTERM`,_)}function Ar(e){e.command(`dev`).description(`Start development server with Vite HMR (zero-downtime reload)`).option(`-e, --entry <file>`,`Entry file`,`src/index.ts`).option(`-p, --port <port>`,`Port number`).option(`--polling`,`Force chokidar to poll for file changes (Docker / WSL / NFS / older kernels)`).action(async e=>{try{await kr(e.entry,e.port,{polling:e.polling})}catch(e){e.code===`ERR_MODULE_NOT_FOUND`&&e.message?.includes(`vite`)?console.error(`
3662
+ `);let _=async()=>{h&&clearTimeout(h),await d.close(),process.exit(0)};process.on(`SIGINT`,_),process.on(`SIGTERM`,_)}function Kr(e){e.command(`dev`).description(`Start development server with Vite HMR (zero-downtime reload)`).option(`-e, --entry <file>`,`Entry file`,`src/index.ts`).option(`-p, --port <port>`,`Port number`).option(`--polling`,`Force chokidar to poll for file changes (Docker / WSL / NFS / older kernels)`).action(async e=>{try{await Gr(e.entry,e.port,{polling:e.polling})}catch(e){e.code===`ERR_MODULE_NOT_FOUND`&&e.message?.includes(`vite`)?console.error(`
3477
3663
  Error: vite is not installed.
3478
3664
  Run: pnpm add -D vite unplugin-swc
3479
3665
  `):console.error(`
3480
3666
  Dev server failed:`,e.message??e),process.exit(1)}}),e.command(`build`).description(`Build for production via Vite`).action(async()=>{console.log(`
3481
3667
  Building for production...
3482
- `);let{createRequire:e}=await import(`node:module`),{build:t}=await import(C(e(S(`package.json`)).resolve(`vite`)).href);await t({configFile:S(`vite.config.ts`)});let n=await r(process.cwd()),i=n?.copyDirs??[];if(i.length>0){console.log(`
3483
- Copying directories to dist...`);for(let e of i){let t=typeof e==`string`?e:e.src,n=typeof e==`string`?b(`dist`,e):e.dest??b(`dist`,t),r=S(t),i=S(n);if(!h(r)){console.log(` ⚠ Skipped ${t} (not found)`);continue}g(i,{recursive:!0}),m(r,i,{recursive:!0}),console.log(` ✓ ${t} → ${n}`)}}if(n?.assetMap&&Object.keys(n.assetMap).length>0){console.log(`
3484
- Building asset map...`);try{await Cr(n,{cwd:process.cwd()})}catch(e){console.error(` ✗ asset build failed: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}console.log(`
3668
+ `);let{createRequire:e}=await import(`node:module`),{build:t}=await import(w(e(C(`package.json`)).resolve(`vite`)).href);await t({configFile:C(`vite.config.ts`)});let n=await r(process.cwd()),i=n?.copyDirs??[];if(i.length>0){console.log(`
3669
+ Copying directories to dist...`);for(let e of i){let t=typeof e==`string`?e:e.src,n=typeof e==`string`?x(`dist`,e):e.dest??x(`dist`,t),r=C(t),i=C(n);if(!h(r)){console.log(` ⚠ Skipped ${t} (not found)`);continue}g(i,{recursive:!0}),m(r,i,{recursive:!0}),console.log(` ✓ ${t} → ${n}`)}}if(n?.assetMap&&Object.keys(n.assetMap).length>0){console.log(`
3670
+ Building asset map...`);try{await zr(n,{cwd:process.cwd()})}catch(e){console.error(` ✗ asset build failed: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}console.log(`
3485
3671
  Build complete.
3486
3672
  `)}),e.command(`build:assets`).description(`Rebuild the .kickjs-assets.json manifest under the configured outDir (no JS rebuild)`).action(async()=>{let e=await r(process.cwd());if(!e?.assetMap||Object.keys(e.assetMap).length===0){console.log(` No assetMap entries — nothing to build.`);return}console.log(`
3487
- Building asset map...`);try{await Cr(e,{cwd:process.cwd()}),console.log(`
3673
+ Building asset map...`);try{await zr(e,{cwd:process.cwd()}),console.log(`
3488
3674
  Asset build complete.
3489
- `)}catch(e){console.error(` ✗ ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}),e.command(`start`).description(`Start production server`).option(`-e, --entry <file>`,`Entry file`,`dist/index.js`).option(`-p, --port <port>`,`Port number`).action(e=>{let t={NODE_ENV:`production`};e.port&&(t.PORT=String(e.port)),Ae(e.entry,t)}),e.command(`dev:debug`).description(`Start dev server with Node.js inspector attached`).option(`-e, --entry <file>`,`Entry file`,`src/index.ts`).option(`-p, --port <port>`,`Port number`).option(`--inspect-port <port>`,`Inspector port`,`9229`).action(async e=>{let t=e.inspectPort??`9229`;process.env.NODE_OPTIONS=`--inspect=0.0.0.0:${t}`,console.log(` Debugger: ws://0.0.0.0:${t}`);try{await kr(e.entry,e.port)}catch(e){console.error(`
3490
- Dev server (debug) failed:`,e.message??e),process.exit(1)}})}function jr(e){e.command(`info`).description(`Print system and framework info`).action(()=>{console.log(`
3675
+ `)}catch(e){console.error(` ✗ ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}),e.command(`start`).description(`Start production server`).option(`-e, --entry <file>`,`Entry file`,`dist/index.js`).option(`-p, --port <port>`,`Port number`).action(e=>{let t={NODE_ENV:`production`};e.port&&(t.PORT=String(e.port)),Me(e.entry,t)}),e.command(`dev:debug`).description(`Start dev server with Node.js inspector attached`).option(`-e, --entry <file>`,`Entry file`,`src/index.ts`).option(`-p, --port <port>`,`Port number`).option(`--inspect-port <port>`,`Inspector port`,`9229`).action(async e=>{let t=e.inspectPort??`9229`;process.env.NODE_OPTIONS=`--inspect=0.0.0.0:${t}`,console.log(` Debugger: ws://0.0.0.0:${t}`);try{await Gr(e.entry,e.port)}catch(e){console.error(`
3676
+ Dev server (debug) failed:`,e.message??e),process.exit(1)}})}function qr(e){e.command(`info`).description(`Print system and framework info`).action(()=>{console.log(`
3491
3677
  KickJS CLI
3492
3678
 
3493
3679
  System:
3494
- OS: ${ye()} ${be()} (${ve()})
3680
+ OS: ${xe()} ${Se()} (${be()})
3495
3681
  Node: ${process.version}
3496
3682
 
3497
3683
  Packages:
3498
3684
  @forinda/kickjs workspace
3499
3685
  @forinda/kickjs-vite workspace
3500
3686
  @forinda/kickjs-cli workspace
3501
- `)})}const{bold:K,dim:q,green:Mr,red:Nr,yellow:Pr,blue:Fr}=O;function Ir(e){let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60),i=e%60,a=[];return t&&a.push(`${t}d`),n&&a.push(`${n}h`),r&&a.push(`${r}m`),a.push(`${i}s`),a.join(` `)}async function Lr(e){let t=await fetch(e,{signal:AbortSignal.timeout(5e3)});if(!t.ok)throw Error(`${t.status} ${t.statusText}`);return t.json()}async function J(e,t){try{return await Lr(`${e}${t}`)}catch{return null}}async function Rr(e){let[t,n,r,i,a]=await Promise.all([J(e,`/health`),J(e,`/metrics`),J(e,`/routes`),J(e,`/container`),J(e,`/ws`)]);return{health:t,metrics:n,routes:r,container:i,ws:a}}function zr(e,t){let{health:n,metrics:r,routes:i,container:a,ws:o}=t,s=q(`─`.repeat(60));if(console.log(),console.log(K(` KickJS Inspector`)+q(` → ${e}`)),console.log(s),n){let e=n.status===`healthy`?Mr(`● healthy`):Nr(`● `+n.status);console.log(` ${K(`Health:`)} ${e}`)}else console.log(` ${K(`Health:`)} ${Nr(`● unreachable`)}`);if(r){let e=((r.errorRate??0)*100).toFixed(1),t=r.errorRate>.1?Nr:r.errorRate>0?Pr:Mr;console.log(` ${K(`Uptime:`)} ${Ir(r.uptimeSeconds)}`),console.log(` ${K(`Requests:`)} ${r.requests}`),console.log(` ${K(`Errors:`)} ${r.serverErrors} server, ${r.clientErrors??0} client ${q(`(`)}${t(e+`%`)}${q(`)`)}`)}if(a&&console.log(` ${K(`DI:`)} ${a.count} bindings`),o&&o.enabled&&console.log(` ${K(`WS:`)} ${o.connections??0} connections, ${o.namespaces??0} namespaces`),i?.routes?.length){console.log(),console.log(K(` Routes`)),console.log(s),console.log(` ${q(`METHOD`)} ${q(`PATH`.padEnd(36))} ${q(`CONTROLLER`)}`);for(let e of i.routes){let t=e.path.length>36?e.path.slice(0,33)+`...`:e.path.padEnd(36);console.log(` ${lt(e.method)} ${t} ${Fr(e.controller)}.${q(e.handler)}`)}}console.log(s),console.log()}function Br(e){e.command(`inspect [url]`).description(`Connect to a running KickJS app and display debug info`).option(`-p, --port <port>`,`Override port`).option(`-w, --watch`,`Poll every 5 seconds`).option(`-j, --json`,`Output raw JSON`).action(async(e,t)=>{let n=e??`http://localhost:3000`;if(t.port)try{let e=new URL(n);e.port=t.port,n=e.origin}catch{n=`http://localhost:${t.port}`}let r=`${n.replace(/\/$/,``)}/_debug`,i=async()=>{try{let e=await Rr(r);t.json?console.log(JSON.stringify(e,null,2)):zr(n,e)}catch(e){t.json?console.log(JSON.stringify({error:String(e)})):(console.error(Nr(` ✖ Could not connect to ${n}`)),console.error(q(` ${e instanceof Error?e.message:String(e)}`))),t.watch||(process.exitCode=1)}};if(t.watch){let e=async()=>{process.stdout.write(`\x1B[2J\x1B[H`),await i()};await e(),setInterval(e,5e3)}else await i()})}function Vr(e,t){let n=e.toLowerCase();return t.every(e=>n.includes(e.toLowerCase()))}function Y(e,t){let n=e.toLowerCase();return t.some(e=>n.includes(e.toLowerCase()))}const Hr=[{match(e,t){let n=Vr(e,[`config`,`get`])&&Y(e,[`undefined`,`null`]),r=e.includes(`@Value`)&&Y(e,[`undefined`,`is not defined`]);return!n&&!r?null:{confidence:n&&r?90:75,diagnosis:{id:`env-schema-not-registered`,title:`ConfigService.get() returns undefined for user-defined keys`,explanation:`Your src/index.ts is missing \`import "./config"\`. That side-effect import
3687
+ `)})}const{bold:J,dim:Y,green:Jr,red:Yr,yellow:Xr,blue:Zr}=k;function Qr(e){let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60),i=e%60,a=[];return t&&a.push(`${t}d`),n&&a.push(`${n}h`),r&&a.push(`${r}m`),a.push(`${i}s`),a.join(` `)}async function $r(e){let t=await fetch(e,{signal:AbortSignal.timeout(5e3)});if(!t.ok)throw Error(`${t.status} ${t.statusText}`);return t.json()}async function X(e,t){try{return await $r(`${e}${t}`)}catch{return null}}async function ei(e){let[t,n,r,i,a]=await Promise.all([X(e,`/health`),X(e,`/metrics`),X(e,`/routes`),X(e,`/container`),X(e,`/ws`)]);return{health:t,metrics:n,routes:r,container:i,ws:a}}function ti(e,t){let{health:n,metrics:r,routes:i,container:a,ws:o}=t,s=Y(`─`.repeat(60));if(console.log(),console.log(J(` KickJS Inspector`)+Y(` → ${e}`)),console.log(s),n){let e=n.status===`healthy`?Jr(`● healthy`):Yr(`● `+n.status);console.log(` ${J(`Health:`)} ${e}`)}else console.log(` ${J(`Health:`)} ${Yr(`● unreachable`)}`);if(r){let e=((r.errorRate??0)*100).toFixed(1),t=r.errorRate>.1?Yr:r.errorRate>0?Xr:Jr;console.log(` ${J(`Uptime:`)} ${Qr(r.uptimeSeconds)}`),console.log(` ${J(`Requests:`)} ${r.requests}`),console.log(` ${J(`Errors:`)} ${r.serverErrors} server, ${r.clientErrors??0} client ${Y(`(`)}${t(e+`%`)}${Y(`)`)}`)}if(a&&console.log(` ${J(`DI:`)} ${a.count} bindings`),o&&o.enabled&&console.log(` ${J(`WS:`)} ${o.connections??0} connections, ${o.namespaces??0} namespaces`),i?.routes?.length){console.log(),console.log(J(` Routes`)),console.log(s),console.log(` ${Y(`METHOD`)} ${Y(`PATH`.padEnd(36))} ${Y(`CONTROLLER`)}`);for(let e of i.routes){let t=e.path.length>36?e.path.slice(0,33)+`...`:e.path.padEnd(36);console.log(` ${ft(e.method)} ${t} ${Zr(e.controller)}.${Y(e.handler)}`)}}console.log(s),console.log()}function ni(e){e.command(`inspect [url]`).description(`Connect to a running KickJS app and display debug info`).option(`-p, --port <port>`,`Override port`).option(`-w, --watch`,`Poll every 5 seconds`).option(`-j, --json`,`Output raw JSON`).action(async(e,t)=>{let n=e??`http://localhost:3000`;if(t.port)try{let e=new URL(n);e.port=t.port,n=e.origin}catch{n=`http://localhost:${t.port}`}let r=`${n.replace(/\/$/,``)}/_debug`,i=async()=>{try{let e=await ei(r);t.json?console.log(JSON.stringify(e,null,2)):ti(n,e)}catch(e){t.json?console.log(JSON.stringify({error:String(e)})):(console.error(Yr(` ✖ Could not connect to ${n}`)),console.error(Y(` ${e instanceof Error?e.message:String(e)}`))),t.watch||(process.exitCode=1)}};if(t.watch){let e=async()=>{process.stdout.write(`\x1B[2J\x1B[H`),await i()};await e(),setInterval(e,5e3)}else await i()})}function ri(e,t){let n=e.toLowerCase();return t.every(e=>n.includes(e.toLowerCase()))}function Z(e,t){let n=e.toLowerCase();return t.some(e=>n.includes(e.toLowerCase()))}const ii=[{match(e,t){let n=ri(e,[`config`,`get`])&&Z(e,[`undefined`,`null`]),r=e.includes(`@Value`)&&Z(e,[`undefined`,`is not defined`]);return!n&&!r?null:{confidence:n&&r?90:75,diagnosis:{id:`env-schema-not-registered`,title:`ConfigService.get() returns undefined for user-defined keys`,explanation:`Your src/index.ts is missing \`import "./config"\`. That side-effect import
3502
3688
  registers the env schema with kickjs at module-load time. Without it,
3503
3689
  ConfigService falls back to the base schema (PORT/NODE_ENV/LOG_LEVEL only)
3504
3690
  and every user-defined key reads as undefined. @Value() may *appear* to
@@ -3510,7 +3696,7 @@ import { modules } from './modules'
3510
3696
  import './config' // ← add this — registers env schema
3511
3697
  import { bootstrap } from '@forinda/kickjs'
3512
3698
  import { modules } from './modules'
3513
- `,docs:`https://forinda.github.io/kick-js/guide/configuration.html#wiring-the-schema-at-startup`}}}},{match(e,t){let n=Y(e,[`vitest`,`test`,`spec`,`__tests__`,`.test.`]);return Y(e,[`already registered`,`already exists`,`duplicate`,`has been registered`])?{confidence:n?85:60,diagnosis:{id:`container-not-reset-in-tests`,title:`DI container leaks between test cases`,explanation:`KickJS decorators register classes on the global Container at import time.
3699
+ `,docs:`https://forinda.github.io/kick-js/guide/configuration.html#wiring-the-schema-at-startup`}}}},{match(e,t){let n=Z(e,[`vitest`,`test`,`spec`,`__tests__`,`.test.`]);return Z(e,[`already registered`,`already exists`,`duplicate`,`has been registered`])?{confidence:n?85:60,diagnosis:{id:`container-not-reset-in-tests`,title:`DI container leaks between test cases`,explanation:`KickJS decorators register classes on the global Container at import time.
3514
3700
  When vitest re-imports your modules across tests, the same class can be
3515
3701
  registered twice and the container throws. The fix is to wipe the
3516
3702
  container between tests so each case starts fresh.`,fix:`Add Container.reset() to a beforeEach hook in the failing test file:`,codeAfter:`import { describe, it, beforeEach } from 'vitest'
@@ -3520,7 +3706,7 @@ describe('UserController', () => {
3520
3706
  beforeEach(() => Container.reset())
3521
3707
 
3522
3708
  it('does the thing', async () => { /* ... */ })
3523
- })`,docs:`https://forinda.github.io/kick-js/guide/testing.html`}}:null}},{match(e,t){return e.includes(`@Module`)||Vr(e,[`Module`,`is not a function`])||Vr(e,[`Module`,`no exported member`])?{confidence:80,diagnosis:{id:`module-decorator-not-found`,title:`KickJS does not have a @Module decorator (different pattern from NestJS)`,explanation:`NestJS uses @Module({ controllers, providers }). KickJS uses an interface
3709
+ })`,docs:`https://forinda.github.io/kick-js/guide/testing.html`}}:null}},{match(e,t){return e.includes(`@Module`)||ri(e,[`Module`,`is not a function`])||ri(e,[`Module`,`no exported member`])?{confidence:80,diagnosis:{id:`module-decorator-not-found`,title:`KickJS does not have a @Module decorator (different pattern from NestJS)`,explanation:`NestJS uses @Module({ controllers, providers }). KickJS uses an interface
3524
3710
  pattern instead: a class implements AppModule and exposes routes() that
3525
3711
  returns the controller wiring. This was a deliberate choice — modules
3526
3712
  become explicit values rather than metadata, which makes them easier to
@@ -3547,7 +3733,7 @@ KickRoutes["POST /users"]. The new form is per-controller, per-method,
3547
3733
  and matches the actual class names so refactors propagate via
3548
3734
  rename-symbol instead of grep.`,fix:`Update the Ctx<...> type parameter to use the namespace form:`,codeBefore:`@Post('/', { body: createUserSchema })
3549
3735
  create(ctx: Ctx<KickRoutes['POST /users']>) { /* ... */ }`,codeAfter:`@Post('/', { body: createUserSchema, name: 'CreateUser' })
3550
- create(ctx: Ctx<KickRoutes.UserController['create']>) { /* ... */ }`,docs:`https://forinda.github.io/kick-js/guide/typegen.html`}}:null}},{match(e,t){let n=Y(e,[`cluster`,`workers`,`two ports`,`duplicate server`]),r=Y(e,[`kick dev`,`vite`,`eaddrinuse`,`5173`,`5174`,`two servers`]);return!n||!r?null:{confidence:85,diagnosis:{id:`cluster-in-vite-dev`,title:"Cluster mode is incompatible with `kick dev` (Vite owns the server)",explanation:`In dev mode, Vite owns the HTTP server. If your bootstrap passes
3736
+ create(ctx: Ctx<KickRoutes.UserController['create']>) { /* ... */ }`,docs:`https://forinda.github.io/kick-js/guide/typegen.html`}}:null}},{match(e,t){let n=Z(e,[`cluster`,`workers`,`two ports`,`duplicate server`]),r=Z(e,[`kick dev`,`vite`,`eaddrinuse`,`5173`,`5174`,`two servers`]);return!n||!r?null:{confidence:85,diagnosis:{id:`cluster-in-vite-dev`,title:"Cluster mode is incompatible with `kick dev` (Vite owns the server)",explanation:`In dev mode, Vite owns the HTTP server. If your bootstrap passes
3551
3737
  cluster: { workers: N }, the framework forks N workers, each of which
3552
3738
  spins up its own Vite instance on a separate port. The fix landed in
3553
3739
  v2.2.5: McpAdapter (and bootstrap()) now detects Vite dev mode and
@@ -3555,7 +3741,7 @@ silently skips cluster, with a warning. If you see this on an older
3555
3741
  version, upgrade or guard the cluster option behind NODE_ENV.`,fix:`Either upgrade to v2.2.5+ or gate cluster mode on production:`,codeAfter:`export const app = await bootstrap({
3556
3742
  modules,
3557
3743
  cluster: process.env.NODE_ENV === 'production' ? { workers: 4 } : false,
3558
- })`,docs:`https://forinda.github.io/kick-js/guide/cluster.html`}}}},{match(e,t){return Y(e,[`reflect-metadata`,`Reflect.getMetadata is not a function`,`Reflect.defineMetadata`,`design:type`,`design:paramtypes`])?{confidence:90,diagnosis:{id:`reflect-metadata-missing`,title:`reflect-metadata is not loaded — DI cannot read decorator types`,explanation:`The DI container reads constructor parameter types via the
3744
+ })`,docs:`https://forinda.github.io/kick-js/guide/cluster.html`}}}},{match(e,t){return Z(e,[`reflect-metadata`,`Reflect.getMetadata is not a function`,`Reflect.defineMetadata`,`design:type`,`design:paramtypes`])?{confidence:90,diagnosis:{id:`reflect-metadata-missing`,title:`reflect-metadata is not loaded — DI cannot read decorator types`,explanation:`The DI container reads constructor parameter types via the
3559
3745
  reflect-metadata polyfill. The polyfill must be imported once,
3560
3746
  before any decorator runs. Most projects do this at the top of
3561
3747
  src/index.ts; missing the import causes obscure "design:paramtypes"
@@ -3564,32 +3750,32 @@ import './config'
3564
3750
  import { bootstrap } from '@forinda/kickjs'
3565
3751
  import { modules } from './modules'
3566
3752
 
3567
- export const app = await bootstrap({ modules })`,docs:`https://forinda.github.io/kick-js/guide/dependency-injection.html`}}:null}},{match(e,t){return Y(e,[`404`,`cannot get`,`cannot post`,`no route`])?{confidence:50,diagnosis:{id:`module-not-registered`,title:`A 404 may indicate a module is not in the modules array`,explanation:`KickJS only mounts modules listed in \`src/modules/index.ts\`. If you
3753
+ export const app = await bootstrap({ modules })`,docs:`https://forinda.github.io/kick-js/guide/dependency-injection.html`}}:null}},{match(e,t){return Z(e,[`404`,`cannot get`,`cannot post`,`no route`])?{confidence:50,diagnosis:{id:`module-not-registered`,title:`A 404 may indicate a module is not in the modules array`,explanation:`KickJS only mounts modules listed in \`src/modules/index.ts\`. If you
3568
3754
  generated a module via \`kick g module foo\` but the routes don't appear,
3569
3755
  the most likely cause is that the module is missing from the exported
3570
3756
  array. The CLI usually wires this automatically, but a hand-edit can
3571
- drop the entry.`,fix:`Open src/modules/index.ts and verify the module is in the array:`,codeAfter:`import type { AppModuleClass } from '@forinda/kickjs'
3757
+ drop the entry.`,fix:`Open src/modules/index.ts and verify the module is in the array:`,codeAfter:`import type { AppModuleEntry } from '@forinda/kickjs'
3572
3758
  import { UserModule } from './users/user.module'
3573
3759
  import { TaskModule } from './tasks/task.module' // ← was this missing?
3574
3760
 
3575
- export const modules: AppModuleClass[] = [UserModule, TaskModule]`,docs:`https://forinda.github.io/kick-js/guide/project-structure.html`}}:null}}];function Ur(e,t){let n=null;for(let r of Hr){let i=null;try{i=r.match(e,t)}catch{continue}!i||i.confidence<40||(!n||i.confidence>n.confidence)&&(n=i)}return n}async function Wr(e){let t=e.provider??`openai`,n=process.env.OPENAI_API_KEY;if(t===`openai`&&!n)return{kind:`unavailable`,reason:`OPENAI_API_KEY environment variable is not set`,suggestion:`Set OPENAI_API_KEY in your shell, e.g.
3761
+ export const modules: AppModuleEntry[] = [UserModule(), TaskModule()]`,docs:`https://forinda.github.io/kick-js/guide/project-structure.html`}}:null}}];function ai(e,t){let n=null;for(let r of ii){let i=null;try{i=r.match(e,t)}catch{continue}!i||i.confidence<40||(!n||i.confidence>n.confidence)&&(n=i)}return n}async function oi(e){let t=e.provider??`openai`,n=process.env.OPENAI_API_KEY;if(t===`openai`&&!n)return{kind:`unavailable`,reason:`OPENAI_API_KEY environment variable is not set`,suggestion:`Set OPENAI_API_KEY in your shell, e.g.
3576
3762
  export OPENAI_API_KEY="sk-..."
3577
3763
 
3578
3764
  Then re-run \`kick explain --ai "<your error>"\`.`};let r;try{r=await import(`@forinda/kickjs-ai`)}catch{return{kind:`unavailable`,reason:`@forinda/kickjs-ai is not installed`,suggestion:`Install the AI package to enable the LLM fallback:
3579
3765
  kick add ai
3580
3766
 
3581
3767
  Or manually:
3582
- pnpm add @forinda/kickjs-ai`}}let{OpenAIProvider:i}=r,a=new i({apiKey:n,defaultChatModel:e.model??`gpt-4o-mini`}),o=Gr(e.cwd),s=`Error or stack trace:\n\n${e.input.trim()}`;try{let e=Kr((await a.chat({messages:[{role:`system`,content:o},{role:`user`,content:s}]})).content);return e?{kind:`ok`,diagnosis:e}:{kind:`error`,message:`The LLM responded but the payload was not valid JSON in the expected shape. Try again, or file an issue with the error text.`}}catch(e){return{kind:`error`,message:`LLM request failed: ${e instanceof Error?e.message:String(e)}`}}}function Gr(e){return[`You are a diagnostic assistant for KickJS, a decorator-driven Node.js`,`framework built on Express 5 and TypeScript. KickJS projects use:`,` - @Controller, @Get, @Post, @Autowired, @Service, @Value decorators`,` - An AppModule interface with a routes() method (NOT a @Module decorator)`,` - Zod schemas as both runtime validators and OpenAPI sources`,` - Ctx<KickRoutes.ControllerName['method']> for typed request context`,` - src/config/index.ts with defineEnv/loadEnv for env schema`,' - A side-effect `import "./config"` in src/index.ts to register the schema',` - Container.reset() in beforeEach for DI test isolation`,``,`When the user gives you an error message or stack trace, produce a`,`structured diagnosis that helps them fix the bug. You MUST respond`,`with a single JSON object (no surrounding prose, no markdown fences)`,`matching this shape:`,``,`{`,` "id": "<kebab-case-identifier>",`,` "title": "<one-line problem summary>",`,` "explanation": "<multi-line explanation of what is wrong>",`,` "fix": "<multi-line instructions for fixing the problem>",`,` "codeBefore": "<optional: broken code snippet>",`,` "codeAfter": "<optional: corrected code snippet>",`,` "docs": "<optional: KickJS doc URL that discusses this topic>"`,`}`,``,`The KickJS docs live at https://forinda.github.io/kick-js/ — prefer`,`that domain for any doc links you suggest.`,e?`The project is located at ${e}.`:``].filter(e=>e.length>0).join(`
3583
- `)}function Kr(e){let t=[e,qr(e),Jr(e)].filter(e=>e!==null);for(let e of t)try{let t=JSON.parse(e);if(Yr(t))return t}catch{continue}return null}function qr(e){let t=e.match(/```(?:json)?\s*\n([\s\S]*?)```/);return t?t[1]?.trim()??null:null}function Jr(e){let t=e.indexOf(`{`);if(t===-1)return null;let n=0,r=!1,i=!1;for(let a=t;a<e.length;a++){let o=e[a];if(i){i=!1;continue}if(o===`\\`&&r){i=!0;continue}if(o===`"`){r=!r;continue}if(!r&&(o===`{`&&n++,o===`}`&&(n--,n===0)))return e.slice(t,a+1)}return null}function Yr(e){if(typeof e!=`object`||!e)return!1;let t=e;return typeof t.id==`string`&&typeof t.title==`string`&&typeof t.explanation==`string`&&typeof t.fix==`string`}function Xr(e){e.command(`explain [message]`).description(`Explain a KickJS error and suggest a fix`).option(`-m, --message <text>`,`Error message to explain (alternative to positional arg)`).option(`--ai`,`Fall back to LLM if no known-issue matches (requires @forinda/kickjs-ai)`).option(`--model <name>`,`Model name for the --ai fallback`,`gpt-4o-mini`).option(`--json`,`Output the diagnosis as JSON for tooling integration`).action(async(e,t)=>{let n=await $r(e,t.message);(!n||n.trim().length===0)&&(process.stderr.write(`Error: no input provided.
3768
+ pnpm add @forinda/kickjs-ai`}}let{OpenAIProvider:i}=r,a=new i({apiKey:n,defaultChatModel:e.model??`gpt-4o-mini`}),o=si(e.cwd),s=`Error or stack trace:\n\n${e.input.trim()}`;try{let e=ci((await a.chat({messages:[{role:`system`,content:o},{role:`user`,content:s}]})).content);return e?{kind:`ok`,diagnosis:e}:{kind:`error`,message:`The LLM responded but the payload was not valid JSON in the expected shape. Try again, or file an issue with the error text.`}}catch(e){return{kind:`error`,message:`LLM request failed: ${e instanceof Error?e.message:String(e)}`}}}function si(e){return[`You are a diagnostic assistant for KickJS, a decorator-driven Node.js`,`framework built on Express 5 and TypeScript. KickJS projects use:`,` - @Controller, @Get, @Post, @Autowired, @Service, @Value decorators`,` - An AppModule interface with a routes() method (NOT a @Module decorator)`,` - Zod schemas as both runtime validators and OpenAPI sources`,` - Ctx<KickRoutes.ControllerName['method']> for typed request context`,` - src/config/index.ts with defineEnv/loadEnv for env schema`,' - A side-effect `import "./config"` in src/index.ts to register the schema',` - Container.reset() in beforeEach for DI test isolation`,``,`When the user gives you an error message or stack trace, produce a`,`structured diagnosis that helps them fix the bug. You MUST respond`,`with a single JSON object (no surrounding prose, no markdown fences)`,`matching this shape:`,``,`{`,` "id": "<kebab-case-identifier>",`,` "title": "<one-line problem summary>",`,` "explanation": "<multi-line explanation of what is wrong>",`,` "fix": "<multi-line instructions for fixing the problem>",`,` "codeBefore": "<optional: broken code snippet>",`,` "codeAfter": "<optional: corrected code snippet>",`,` "docs": "<optional: KickJS doc URL that discusses this topic>"`,`}`,``,`The KickJS docs live at https://forinda.github.io/kick-js/ — prefer`,`that domain for any doc links you suggest.`,e?`The project is located at ${e}.`:``].filter(e=>e.length>0).join(`
3769
+ `)}function ci(e){let t=[e,li(e),ui(e)].filter(e=>e!==null);for(let e of t)try{let t=JSON.parse(e);if(di(t))return t}catch{continue}return null}function li(e){let t=e.match(/```(?:json)?\s*\n([\s\S]*?)```/);return t?t[1]?.trim()??null:null}function ui(e){let t=e.indexOf(`{`);if(t===-1)return null;let n=0,r=!1,i=!1;for(let a=t;a<e.length;a++){let o=e[a];if(i){i=!1;continue}if(o===`\\`&&r){i=!0;continue}if(o===`"`){r=!r;continue}if(!r&&(o===`{`&&n++,o===`}`&&(n--,n===0)))return e.slice(t,a+1)}return null}function di(e){if(typeof e!=`object`||!e)return!1;let t=e;return typeof t.id==`string`&&typeof t.title==`string`&&typeof t.explanation==`string`&&typeof t.fix==`string`}function fi(e){e.command(`explain [message]`).description(`Explain a KickJS error and suggest a fix`).option(`-m, --message <text>`,`Error message to explain (alternative to positional arg)`).option(`--ai`,`Fall back to LLM if no known-issue matches (requires @forinda/kickjs-ai)`).option(`--model <name>`,`Model name for the --ai fallback`,`gpt-4o-mini`).option(`--json`,`Output the diagnosis as JSON for tooling integration`).action(async(e,t)=>{let n=await hi(e,t.message);(!n||n.trim().length===0)&&(process.stderr.write(`Error: no input provided.
3584
3770
 
3585
3771
  Pass a message as a positional arg, --message flag, or pipe via stdin:
3586
3772
  kick explain "config.get returned undefined"
3587
3773
  pnpm test 2>&1 | kick explain
3588
- `),process.exit(1));let r=ti(),i=Ur(n,r);if(t.json&&i){process.stdout.write(JSON.stringify({matched:!0,...i},null,2)+`
3589
- `);return}if(i){ni(n,i.diagnosis,i.confidence);return}t.ai||(t.json&&(process.stdout.write(JSON.stringify({matched:!1},null,2)+`
3590
- `),process.exit(2)),ri(n,!1),process.exit(2));let a=await Wr({input:n,model:t.model,cwd:r.cwd});t.json&&(process.stdout.write(JSON.stringify(Zr(a),null,2)+`
3591
- `),process.exit(a.kind===`ok`?0:2)),Qr(n,a),process.exit(a.kind===`ok`?0:2)})}function Zr(e){return e.kind===`ok`?{matched:!0,source:`ai`,diagnosis:e.diagnosis}:e.kind===`unavailable`?{matched:!1,aiUnavailable:!0,reason:e.reason}:{matched:!1,aiError:!0,error:e.message}}function Qr(e,t){if(t.kind===`ok`){ni(e,t.diagnosis,-1,!0);return}if(t.kind===`unavailable`){process.stdout.write(`\n Explaining: ${Z(e.trim(),200)}\n\n`),process.stdout.write(` AI fallback unavailable: ${t.reason}\n\n`),process.stdout.write(`${X(t.suggestion,` `)}\n\n`);return}process.stdout.write(`\n Explaining: ${Z(e.trim(),200)}\n\n`),process.stdout.write(` AI fallback error: ${t.message}\n\n`)}async function $r(e,t){return e&&e.trim().length>0?e:t&&t.trim().length>0?t:process.stdin.isTTY?``:ei()}function ei(){return new Promise((e,t)=>{let n=``;process.stdin.setEncoding(`utf8`),process.stdin.on(`data`,e=>{n+=e}),process.stdin.on(`end`,()=>e(n)),process.stdin.on(`error`,t)})}function ti(){let e=process.cwd();return{cwd:e,hasFile:t=>h(S(e,t))}}function ni(e,t,n,r=!1){let i=Z(e.trim(),200),a=r?`AI-generated — verify before applying`:ii(n);process.stdout.write(`\n Explaining: ${i}\n`),process.stdout.write(`\n Match: ${t.id} (${a})\n`),process.stdout.write(` Title: ${t.title}\n`),process.stdout.write(`\n Diagnosis:\n${X(t.explanation,` `)}\n`),process.stdout.write(`\n Fix:\n${X(t.fix,` `)}\n`),t.codeBefore&&process.stdout.write(`\n Before:\n${X(t.codeBefore,` `)}\n`),t.codeAfter&&process.stdout.write(`\n After:\n${X(t.codeAfter,` `)}\n`),t.docs&&process.stdout.write(`\n Docs: ${t.docs}\n`),process.stdout.write(`
3592
- `)}function ri(e,t){let n=Z(e.trim(),200);process.stdout.write(`\n Explaining: ${n}\n\n`),t?process.stdout.write(` No known-issue matched, and --ai fallback is not yet wired.
3774
+ `),process.exit(1));let r=_i(),i=ai(n,r);if(t.json&&i){process.stdout.write(JSON.stringify({matched:!0,...i},null,2)+`
3775
+ `);return}if(i){vi(n,i.diagnosis,i.confidence);return}t.ai||(t.json&&(process.stdout.write(JSON.stringify({matched:!1},null,2)+`
3776
+ `),process.exit(2)),yi(n,!1),process.exit(2));let a=await oi({input:n,model:t.model,cwd:r.cwd});t.json&&(process.stdout.write(JSON.stringify(pi(a),null,2)+`
3777
+ `),process.exit(a.kind===`ok`?0:2)),mi(n,a),process.exit(a.kind===`ok`?0:2)})}function pi(e){return e.kind===`ok`?{matched:!0,source:`ai`,diagnosis:e.diagnosis}:e.kind===`unavailable`?{matched:!1,aiUnavailable:!0,reason:e.reason}:{matched:!1,aiError:!0,error:e.message}}function mi(e,t){if(t.kind===`ok`){vi(e,t.diagnosis,-1,!0);return}if(t.kind===`unavailable`){process.stdout.write(`\n Explaining: ${xi(e.trim(),200)}\n\n`),process.stdout.write(` AI fallback unavailable: ${t.reason}\n\n`),process.stdout.write(`${bi(t.suggestion,` `)}\n\n`);return}process.stdout.write(`\n Explaining: ${xi(e.trim(),200)}\n\n`),process.stdout.write(` AI fallback error: ${t.message}\n\n`)}async function hi(e,t){return e&&e.trim().length>0?e:t&&t.trim().length>0?t:process.stdin.isTTY?``:gi()}function gi(){return new Promise((e,t)=>{let n=``;process.stdin.setEncoding(`utf8`),process.stdin.on(`data`,e=>{n+=e}),process.stdin.on(`end`,()=>e(n)),process.stdin.on(`error`,t)})}function _i(){let e=process.cwd();return{cwd:e,hasFile:t=>h(C(e,t))}}function vi(e,t,n,r=!1){let i=xi(e.trim(),200),a=r?`AI-generated — verify before applying`:Si(n);process.stdout.write(`\n Explaining: ${i}\n`),process.stdout.write(`\n Match: ${t.id} (${a})\n`),process.stdout.write(` Title: ${t.title}\n`),process.stdout.write(`\n Diagnosis:\n${bi(t.explanation,` `)}\n`),process.stdout.write(`\n Fix:\n${bi(t.fix,` `)}\n`),t.codeBefore&&process.stdout.write(`\n Before:\n${bi(t.codeBefore,` `)}\n`),t.codeAfter&&process.stdout.write(`\n After:\n${bi(t.codeAfter,` `)}\n`),t.docs&&process.stdout.write(`\n Docs: ${t.docs}\n`),process.stdout.write(`
3778
+ `)}function yi(e,t){let n=xi(e.trim(),200);process.stdout.write(`\n Explaining: ${n}\n\n`),t?process.stdout.write(` No known-issue matched, and --ai fallback is not yet wired.
3593
3779
  When @forinda/kickjs-ai ships its provider implementations,
3594
3780
  this command will call the configured LLM with the error +
3595
3781
  project context and return a structured fix.
@@ -3606,12 +3792,12 @@ Pass a message as a positional arg, --message flag, or pipe via stdin:
3606
3792
  3. File an issue with the error text:
3607
3793
  https://github.com/forinda/kick-js/issues/new
3608
3794
 
3609
- `)}function X(e,t){return e.split(`
3795
+ `)}function bi(e,t){return e.split(`
3610
3796
  `).map(e=>`${t}${e}`).join(`
3611
- `)}function Z(e,t){return e.length<=t?e:e.slice(0,t-1)+`…`}function ii(e){return e>=90?`high confidence`:e>=70?`good match`:e>=50?`medium confidence`:`low confidence — verify manually`}function ai(e){let t=e.command(`mcp`).description(`Model Context Protocol commands (start | init)`);t.command(`start`,{isDefault:!0}).description(`Run the built application as an MCP server over stdio`).option(`-e, --entry <file>`,`Entry file`,`dist/index.js`).option(`--node-arg <arg...>`,`Extra arguments to pass to node`).action(oi),t.command(`init`).description(`Generate .mcp.json for Claude Code / Cursor / Zed`).option(`-n, --name <name>`,`Server name (defaults to package.json name)`).option(`-o, --out <file>`,`Output file`,`.mcp.json`).option(`-f, --force`,`Overwrite an existing entry without prompting`).option(`--global`,`Write to ~/.mcp.json instead of the project root`).action(si)}function oi(e){let t=S(e.entry);h(t)||(process.stderr.write(`Error: entry file not found: ${t}\n\nBuild the app first with \`kick build\`, or pass a custom entry:\n kick mcp -e dist/server.js\n`),process.exit(1));let n=[...e.nodeArg??[],t],r=ue(process.execPath,n,{stdio:`inherit`,env:{...process.env,KICK_MCP_STDIO:`1`,NODE_ENV:process.env.NODE_ENV??`production`}});r.on(`error`,e=>{process.stderr.write(`Failed to start MCP server: ${e.message}\n`),process.exit(1)}),r.on(`exit`,(e,t)=>{if(t){process.kill(process.pid,t);return}process.exit(e??0)});let i=e=>{r.killed||r.kill(e)};process.on(`SIGINT`,()=>i(`SIGINT`)),process.on(`SIGTERM`,()=>i(`SIGTERM`))}function si(e){let t=process.cwd(),n=ci(t)??ie(t),r=e.name??n,i=e.global?S(process.env.HOME??`.`,`.mcp.json`):S(t,e.out),a={command:`kick`,args:[`mcp`],cwd:t},o={mcpServers:{}};if(h(i))try{let e=_(i,`utf8`),t=JSON.parse(e);t&&typeof t==`object`&&t.mcpServers&&(o={mcpServers:{...t.mcpServers}})}catch(e){let t=e instanceof Error?e.message:String(e);process.stderr.write(`Error: existing ${i} is not valid JSON (${t}).\nFix the file or pass --force to overwrite the entry.\n`),process.exit(1)}o.mcpServers[r]&&!e.force&&(process.stderr.write(`Error: an entry for "${r}" already exists in ${i}.\nPass --force to overwrite it, or use --name to pick a different key.\n`),process.exit(1)),o.mcpServers[r]=a,re(i,JSON.stringify(o,null,2)+`
3612
- `,`utf8`),process.stdout.write(`\n ✓ Wrote MCP server entry "${r}" to ${i}\n\n To activate it:\n 1. Build your app: kick build\n 2. Restart your MCP client (Claude Code, Cursor, Zed)\n 3. The server should appear in the client's tool picker\n\n`)}function ci(e){let t=S(e,`package.json`);if(!h(t))return null;try{let e=_(t,`utf8`),n=JSON.parse(e);return typeof n.name==`string`?n.name:null}catch{return null}}function li(e){e.command(`tinker`).description(`Interactive REPL with DI container and services loaded`).option(`-e, --entry <file>`,`Entry file to load`,`src/index.ts`).action(async e=>{let t=process.cwd(),n=S(t,e.entry);h(n)||(console.error(`\n Error: ${e.entry} not found.\n`),process.exit(1));let r=di(t,`tsx`);r||(console.error(`
3797
+ `)}function xi(e,t){return e.length<=t?e:e.slice(0,t-1)+`…`}function Si(e){return e>=90?`high confidence`:e>=70?`good match`:e>=50?`medium confidence`:`low confidence — verify manually`}function Ci(e){let t=e.command(`mcp`).description(`Model Context Protocol commands (start | init)`);t.command(`start`,{isDefault:!0}).description(`Run the built application as an MCP server over stdio`).option(`-e, --entry <file>`,`Entry file`,`dist/index.js`).option(`--node-arg <arg...>`,`Extra arguments to pass to node`).action(wi),t.command(`init`).description(`Generate .mcp.json for Claude Code / Cursor / Zed`).option(`-n, --name <name>`,`Server name (defaults to package.json name)`).option(`-o, --out <file>`,`Output file`,`.mcp.json`).option(`-f, --force`,`Overwrite an existing entry without prompting`).option(`--global`,`Write to ~/.mcp.json instead of the project root`).action(Ti)}function wi(e){let t=C(e.entry);h(t)||(process.stderr.write(`Error: entry file not found: ${t}\n\nBuild the app first with \`kick build\`, or pass a custom entry:\n kick mcp -e dist/server.js\n`),process.exit(1));let n=[...e.nodeArg??[],t],r=le(process.execPath,n,{stdio:`inherit`,env:{...process.env,KICK_MCP_STDIO:`1`,NODE_ENV:process.env.NODE_ENV??`production`}});r.on(`error`,e=>{process.stderr.write(`Failed to start MCP server: ${e.message}\n`),process.exit(1)}),r.on(`exit`,(e,t)=>{if(t){process.kill(process.pid,t);return}process.exit(e??0)});let i=e=>{r.killed||r.kill(e)};process.on(`SIGINT`,()=>i(`SIGINT`)),process.on(`SIGTERM`,()=>i(`SIGTERM`))}function Ti(e){let t=process.cwd(),n=Ei(t)??re(t),r=e.name??n,i=e.global?C(process.env.HOME??`.`,`.mcp.json`):C(t,e.out),a={command:`kick`,args:[`mcp`],cwd:t},o={mcpServers:{}};if(h(i))try{let e=_(i,`utf8`),t=JSON.parse(e);t&&typeof t==`object`&&t.mcpServers&&(o={mcpServers:{...t.mcpServers}})}catch(e){let t=e instanceof Error?e.message:String(e);process.stderr.write(`Error: existing ${i} is not valid JSON (${t}).\nFix the file or pass --force to overwrite the entry.\n`),process.exit(1)}o.mcpServers[r]&&!e.force&&(process.stderr.write(`Error: an entry for "${r}" already exists in ${i}.\nPass --force to overwrite it, or use --name to pick a different key.\n`),process.exit(1)),o.mcpServers[r]=a,ne(i,JSON.stringify(o,null,2)+`
3798
+ `,`utf8`),process.stdout.write(`\n ✓ Wrote MCP server entry "${r}" to ${i}\n\n To activate it:\n 1. Build your app: kick build\n 2. Restart your MCP client (Claude Code, Cursor, Zed)\n 3. The server should appear in the client's tool picker\n\n`)}function Ei(e){let t=C(e,`package.json`);if(!h(t))return null;try{let e=_(t,`utf8`),n=JSON.parse(e);return typeof n.name==`string`?n.name:null}catch{return null}}function Di(e){e.command(`tinker`).description(`Interactive REPL with DI container and services loaded`).option(`-e, --entry <file>`,`Entry file to load`,`src/index.ts`).action(async e=>{let t=process.cwd(),n=C(t,e.entry);h(n)||(console.error(`\n Error: ${e.entry} not found.\n`),process.exit(1));let r=ki(t,`tsx`);r||(console.error(`
3613
3799
  Error: tsx not found. Install it: pnpm add -D tsx
3614
- `),process.exit(1));let i=ui(n,e.entry),a=b(t,`.kick-tinker.mjs`),{writeFileSync:o,unlinkSync:s}=await import(`node:fs`);o(a,i,`utf-8`);try{let e=le(a,[],{cwd:t,execPath:r,stdio:`inherit`});await new Promise(t=>{e.on(`exit`,()=>t())})}finally{try{s(a)}catch{}}})}function ui(e,t){return`
3800
+ `),process.exit(1));let i=Oi(n,e.entry),a=x(t,`.kick-tinker.mjs`),{writeFileSync:o,unlinkSync:s}=await import(`node:fs`);o(a,i,`utf-8`);try{let e=ce(a,[],{cwd:t,execPath:r,stdio:`inherit`});await new Promise(t=>{e.on(`exit`,()=>t())})}finally{try{s(a)}catch{}}})}function Oi(e,t){return`
3615
3801
  import 'reflect-metadata'
3616
3802
 
3617
3803
  // Prevent bootstrap() from starting the HTTP server
@@ -3636,7 +3822,7 @@ try {
3636
3822
 
3637
3823
  // Load entry to trigger decorator registration
3638
3824
  try {
3639
- await import('${C(e).href}')
3825
+ await import('${w(e).href}')
3640
3826
  } catch (err) {
3641
3827
  console.warn(' Warning: ' + err.message)
3642
3828
  console.warn(' Container may be partially initialized.\\n')
@@ -3665,21 +3851,30 @@ server.on('exit', () => {
3665
3851
  console.log('\\n Goodbye!\\n')
3666
3852
  process.exit(0)
3667
3853
  })
3668
- `}function di(e,t){let n=e;for(;;){let e=b(n,`node_modules`,`.bin`,t);if(h(e))return e;let r=S(n,`..`);if(r===n)break;n=r}return null}async function fi(e){let{name:t,modulesDir:n,force:r}=e,i=e.pluralize!==!1,a=B(t),o=R(t),s=i?V(a):a,c=b(n,s);if(!await N(c)){console.log(`\n Module not found: ${c}\n`);return}if(!r&&!await F({message:O.red(`Delete module '${s}' at ${c}? This cannot be undone.`),initialValue:!1})){console.log(`
3854
+ `}function ki(e,t){let n=e;for(;;){let e=x(n,`node_modules`,`.bin`,t);if(h(e))return e;let r=C(n,`..`);if(r===n)break;n=r}return null}async function Ai(e){let{name:t,modulesDir:n,force:r}=e,i=e.pluralize!==!1,a=B(t),o=R(t),s=i?V(a):a,c=x(n,s);if(!await Re(c)){console.log(`\n Module not found: ${c}\n`);return}if(!r&&!await F({message:k.red(`Delete module '${s}' at ${c}? This cannot be undone.`),initialValue:!1})){console.log(`
3669
3855
  Cancelled.
3670
- `);return}await me(c,{recursive:!0,force:!0}),console.log(` Deleted: ${c}`);let l=b(n,`index.ts`);if(await N(l)){let e=await T(l,`utf-8`),t=e,n=RegExp(`^import\\s*\\{\\s*${o}Module\\s*\\}\\s*from\\s*['"][^'"]*${s}(?:/[^'"]*)?['"].*\\n?`,`gm`);e=e.replace(n,``),e=e.replace(RegExp(`\\s*,?\\s*${o}Module\\s*,?`,`g`),e=>{let t=e.trimStart().startsWith(`,`),n=e.trimEnd().endsWith(`,`);return t&&n?`,`:``}),e=e.replace(/,(\s*])/,`$1`),e=e.replace(/\n{3,}/g,`
3856
+ `);return}await he(c,{recursive:!0,force:!0}),console.log(` Deleted: ${c}`);let l=x(n,`index.ts`);if(await Re(l)){let e=await E(l,`utf-8`),t=e,n=RegExp(`^import\\s*\\{\\s*${o}Module\\s*\\}\\s*from\\s*['"][^'"]*${s}(?:/[^'"]*)?['"].*\\n?`,`gm`);e=e.replace(n,``),e=e.replace(RegExp(`\\s*,?\\s*${o}Module(?:\\s*\\(\\s*\\))?\\s*,?`,`g`),e=>{let t=e.trimStart().startsWith(`,`),n=e.trimEnd().endsWith(`,`);return t&&n?`,`:``}),e=e.replace(/,(\s*])/,`$1`),e=e.replace(/\n{3,}/g,`
3671
3857
 
3672
- `),e!==t&&(await E(l,e,`utf-8`),console.log(` Unregistered: ${o}Module from ${l}`))}console.log(`\n Module '${s}' removed.\n`)}function pi(e){e.command(`remove`).alias(`rm`).description(`Remove generated code`).command(`module <names...>`).description(`Remove one or more modules (e.g. kick rm module user task)`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular module name`).option(`-f, --force`,`Skip confirmation prompt`).action(async(e,t)=>{let i=n(await r(process.cwd())),a=t.modulesDir??i.dir??`src/modules`,o=t.pluralize===!1?!1:i.pluralize??!0;for(let n of e)await fi({name:n,modulesDir:S(a),force:t.force,pluralize:o})})}function mi(e){if(e!==void 0){if(e===`false`||e===`off`||e===`none`)return!1;if(e===`zod`)return`zod`;console.warn(` kick typegen: unknown --schema-validator '${e}' (only 'zod' and 'false' are supported). Falling back to project config.`)}}function hi(e){if(e!==void 0)return e===`false`||e===`off`||e===`none`?!1:e}function gi(e){e.command(`typegen`).description(`Generate type-safe DI registry and module types into .kickjs/types/`).option(`-w, --watch`,`Watch source files and regenerate on change`).option(`-s, --src <dir>`,`Source directory to scan`,`src`).option(`-o, --out <dir>`,`Output directory`,`.kickjs/types`).option(`--silent`,`Suppress output`).option(`--allow-duplicates`,`Auto-namespace duplicate class names instead of failing (use with caution)`).option(`--schema-validator <name>`,`Schema validator for body/query/params typing (currently 'zod' or 'false')`).option(`--env-file <path>`,`Path to env schema file for KickEnv typing (default 'src/env.ts'; pass 'false' to disable)`).option(`--check`,`CI gate: fail on plugin-typegen drift instead of writing`).option(`--list`,"List every registered typegen plugin id (use to populate `typegen.disable`)").action(async e=>{let t=process.cwd(),n=await r(t);if(e.list){let{mergeCliPlugins:e}=await import(`./plugin-B56zClEX.mjs`).then(e=>e.t),{builtinCliPlugins:t}=await Promise.resolve().then(()=>Xi),r=e([...t,...n?.plugins??[]],n?.commands??[]),i=new Set(n?.typegen?.disable??[]);if(r.typegens.length===0){console.log(` No typegen plugins registered.`);return}let a=Math.max(...r.typegens.map(e=>e.id.length));console.log(`
3858
+ `),e!==t&&(await D(l,e,`utf-8`),console.log(` Unregistered: ${o}Module from ${l}`))}console.log(`\n Module '${s}' removed.\n`)}function ji(e){e.command(`remove`).alias(`rm`).description(`Remove generated code`).command(`module <names...>`).description(`Remove one or more modules (e.g. kick rm module user task)`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular module name`).option(`-f, --force`,`Skip confirmation prompt`).action(async(e,t)=>{let i=n(await r(process.cwd())),a=t.modulesDir??i.dir??`src/modules`,o=t.pluralize===!1?!1:i.pluralize??!0;for(let n of e)await Ai({name:n,modulesDir:C(a),force:t.force,pluralize:o})})}function Mi(e){if(e!==void 0){if(e===`false`||e===`off`||e===`none`)return!1;if(e===`zod`)return`zod`;console.warn(` kick typegen: unknown --schema-validator '${e}' (only 'zod' and 'false' are supported). Falling back to project config.`)}}function Ni(e){if(e!==void 0)return e===`false`||e===`off`||e===`none`?!1:e}function Pi(e){e.command(`typegen`).description(`Generate type-safe DI registry and module types into .kickjs/types/`).option(`-w, --watch`,`Watch source files and regenerate on change`).option(`-s, --src <dir>`,`Source directory to scan`,`src`).option(`-o, --out <dir>`,`Output directory`,`.kickjs/types`).option(`--silent`,`Suppress output`).option(`--allow-duplicates`,`Auto-namespace duplicate class names instead of failing (use with caution)`).option(`--schema-validator <name>`,`Schema validator for body/query/params typing (currently 'zod' or 'false')`).option(`--env-file <path>`,`Path to env schema file for KickEnv typing (default 'src/env.ts'; pass 'false' to disable)`).option(`--check`,`CI gate: fail on plugin-typegen drift instead of writing`).option(`--list`,"List every registered typegen plugin id (use to populate `typegen.disable`)").action(async e=>{let t=process.cwd(),n=await r(t);if(e.list){let{mergeCliPlugins:e}=await import(`./plugin-D0C4ISZA.mjs`).then(e=>e.t),{builtinCliPlugins:t}=await Promise.resolve().then(()=>ga),r=e([...t,...n?.plugins??[]],n?.commands??[]),i=new Set(n?.typegen?.disable??[]);if(r.typegens.length===0){console.log(` No typegen plugins registered.`);return}let a=Math.max(...r.typegens.map(e=>e.id.length));console.log(`
3673
3859
  Registered typegen plugins:
3674
- `);for(let e of r.typegens){let t=i.has(e.id)?` (disabled)`:``;console.log(` ${e.id.padEnd(a+2)}inputs: ${e.inputs.join(`, `)||`(none)`}${t}`)}console.log();return}let i=mi(e.schemaValidator)??n?.typegen?.schemaValidator??`zod`,a=hi(e.envFile)??n?.typegen?.envFile,o={cwd:t,srcDir:e.src??n?.typegen?.srcDir,outDir:e.out??n?.typegen?.outDir,silent:e.silent,allowDuplicates:e.allowDuplicates,schemaValidator:i,envFile:a,assetMap:n?.assetMap,runPlugins:!1};try{if(e.watch){let t=await u(o);e.silent||console.log(` kick typegen: watching for changes (Ctrl-C to exit)`);let n=()=>{t(),process.exit(0)};process.on(`SIGINT`,n),process.on(`SIGTERM`,n),await new Promise(()=>{})}else{await f(o);let r=await Sr({cwd:t,config:n??null,silent:e.silent,check:e.check});e.check&&r.some(e=>e.status===`written`)&&process.exit(1)}}catch(e){e instanceof c?console.error(`
3860
+ `);for(let e of r.typegens){let t=i.has(e.id)?` (disabled)`:``;console.log(` ${e.id.padEnd(a+2)}inputs: ${e.inputs.join(`, `)||`(none)`}${t}`)}console.log();return}let i=Mi(e.schemaValidator)??n?.typegen?.schemaValidator??`zod`,a=Ni(e.envFile)??n?.typegen?.envFile,o={cwd:t,srcDir:e.src??n?.typegen?.srcDir,outDir:e.out??n?.typegen?.outDir,silent:e.silent,allowDuplicates:e.allowDuplicates,schemaValidator:i,envFile:a,assetMap:n?.assetMap,runPlugins:!1};try{if(e.watch){let t=await u(o);e.silent||console.log(` kick typegen: watching for changes (Ctrl-C to exit)`);let n=()=>{t(),process.exit(0)};process.on(`SIGINT`,n),process.on(`SIGTERM`,n),await new Promise(()=>{})}else{await f(o);let r=await Rr({cwd:t,config:n??null,silent:e.silent,check:e.check});e.check&&r.some(e=>e.status===`written`)&&process.exit(1)}}catch(e){e instanceof c?console.error(`
3675
3861
  `+e.message+`
3676
- `):e instanceof Error?console.error(`\n kick typegen failed: ${e.message}`):console.error(`\n kick typegen failed: ${JSON.stringify(e)}`),process.exit(1)}})}function _i(e){let t=[];if(!h(e))return t;let n=ee(e,{withFileTypes:!0});for(let r of n){let n=b(e,r.name);if(r.isDirectory()){if([`node_modules`,`dist`,`.kickjs`,`.git`].includes(r.name))continue;t.push(..._i(n))}else r.isFile()&&/\.tsx?$/.test(r.name)&&!r.name.endsWith(`.d.ts`)&&t.push(n)}return t}function vi(e){try{return _(e,`utf-8`)}catch{return``}}const yi=new Set([`secret`,`changeme`,`password`,`test`,`default`,``]);function bi(e,t){let n=vi(b(e,`.env`));if(n){let e=n.match(/^JWT_SECRET\s*=\s*['"]?([^'"\n]*)['"]?/m);if(e){let t=e[1].trim();if(yi.has(t.toLowerCase())||t.length<32)return{severity:`CRITICAL`,message:`JWT_SECRET appears to be a default value or too short (< 32 chars) — change it`}}}for(let e of t)for(let t of[/JWT_SECRET['"]?\s*[:=]\s*['"]?(secret|changeme|password|test|default)['"]?/i,/secret\s*[:=]\s*['"]?(secret|changeme|password|test|default)['"]?/i])if(t.test(e))return{severity:`CRITICAL`,message:`JWT_SECRET appears to be a default value in source code — use an environment variable`};return null}function xi(e){for(let t of e)if(/cors\s*\(/.test(t)&&/origin\s*:\s*['"]\*['"]/.test(t))return{severity:`CRITICAL`,message:`CORS origin is '*' — restrict to your domains`};return null}function Si(e){for(let t of e)if(/rateLimit/i.test(t)||/@RateLimit/i.test(t))return null;return{severity:`WARNING`,message:`No rate limiting detected — add rateLimit() middleware or @RateLimit decorator`}}function Ci(){return process.env.NODE_ENV===`production`?null:{severity:`WARNING`,message:`NODE_ENV is '${process.env.NODE_ENV??`undefined`}', not 'production'`}}function wi(e){let t=!1,n=!1;for(let r of e)/tokenStore/i.test(r)&&(t=!0),/MemoryTokenStore/i.test(r)&&(n=!0);return n?{severity:`WARNING`,message:`MemoryTokenStore detected — use a persistent store (Redis, DB) for production deployments`}:t?null:{severity:`WARNING`,message:`No token revocation store detected — consider adding one for auth token management`}}function Ti(e){for(let t of e)if(/helmet\s*\(/.test(t))return/security\s*\.\s*helmet\s*.*false/.test(t)?{severity:`WARNING`,message:`Helmet security headers are disabled — enable them for production`}:{severity:`INFO`,message:`Helmet security headers active`};return{severity:`WARNING`,message:`Helmet not detected — add helmet() middleware for security headers`}}function Ei(e){for(let t of e)if(/AuthAdapter/i.test(t))return{severity:`INFO`,message:`AuthAdapter configured`};return{severity:`INFO`,message:`No AuthAdapter detected — add one if your app requires authentication`}}function Di(e){let t=_i(b(e,`src`)).map(e=>vi(e)),n=[],r=bi(e,t);r&&n.push(r);let i=xi(t);i&&n.push(i);let a=Si(t);a&&n.push(a);let o=Ci();o&&n.push(o);let s=wi(t);return s&&n.push(s),n.push(Ti(t)),n.push(Ei(t)),n}function Oi(e){e.command(`check`).description(`Audit project for common issues`).option(`--deploy`,`Run production readiness checks`).action(e=>{if(!e.deploy){console.log(`
3862
+ `):e instanceof Error?console.error(`\n kick typegen failed: ${e.message}`):console.error(`\n kick typegen failed: ${JSON.stringify(e)}`),process.exit(1)}})}function Fi(e){let t=[];if(!h(e))return t;let n=ee(e,{withFileTypes:!0});for(let r of n){let n=x(e,r.name);if(r.isDirectory()){if([`node_modules`,`dist`,`.kickjs`,`.git`].includes(r.name))continue;t.push(...Fi(n))}else r.isFile()&&/\.tsx?$/.test(r.name)&&!r.name.endsWith(`.d.ts`)&&t.push(n)}return t}function Ii(e){try{return _(e,`utf-8`)}catch{return``}}const Li=new Set([`secret`,`changeme`,`password`,`test`,`default`,``]);function Ri(e,t){let n=Ii(x(e,`.env`));if(n){let e=n.match(/^JWT_SECRET\s*=\s*['"]?([^'"\n]*)['"]?/m);if(e){let t=e[1].trim();if(Li.has(t.toLowerCase())||t.length<32)return{severity:`CRITICAL`,message:`JWT_SECRET appears to be a default value or too short (< 32 chars) — change it`}}}for(let e of t)for(let t of[/JWT_SECRET['"]?\s*[:=]\s*['"]?(secret|changeme|password|test|default)['"]?/i,/secret\s*[:=]\s*['"]?(secret|changeme|password|test|default)['"]?/i])if(t.test(e))return{severity:`CRITICAL`,message:`JWT_SECRET appears to be a default value in source code — use an environment variable`};return null}function zi(e){for(let t of e)if(/cors\s*\(/.test(t)&&/origin\s*:\s*['"]\*['"]/.test(t))return{severity:`CRITICAL`,message:`CORS origin is '*' — restrict to your domains`};return null}function Bi(e){for(let t of e)if(/rateLimit/i.test(t)||/@RateLimit/i.test(t))return null;return{severity:`WARNING`,message:`No rate limiting detected — add rateLimit() middleware or @RateLimit decorator`}}function Vi(){return process.env.NODE_ENV===`production`?null:{severity:`WARNING`,message:`NODE_ENV is '${process.env.NODE_ENV??`undefined`}', not 'production'`}}function Hi(e){let t=!1,n=!1;for(let r of e)/tokenStore/i.test(r)&&(t=!0),/MemoryTokenStore/i.test(r)&&(n=!0);return n?{severity:`WARNING`,message:`MemoryTokenStore detected — use a persistent store (Redis, DB) for production deployments`}:t?null:{severity:`WARNING`,message:`No token revocation store detected — consider adding one for auth token management`}}function Ui(e){for(let t of e)if(/helmet\s*\(/.test(t))return/security\s*\.\s*helmet\s*.*false/.test(t)?{severity:`WARNING`,message:`Helmet security headers are disabled — enable them for production`}:{severity:`INFO`,message:`Helmet security headers active`};return{severity:`WARNING`,message:`Helmet not detected — add helmet() middleware for security headers`}}function Wi(e){for(let t of e)if(/AuthAdapter/i.test(t))return{severity:`INFO`,message:`AuthAdapter configured`};return{severity:`INFO`,message:`No AuthAdapter detected — add one if your app requires authentication`}}function Gi(e){let t=Fi(x(e,`src`)).map(e=>Ii(e)),n=[],r=Ri(e,t);r&&n.push(r);let i=zi(t);i&&n.push(i);let a=Bi(t);a&&n.push(a);let o=Vi();o&&n.push(o);let s=Hi(t);return s&&n.push(s),n.push(Ui(t)),n.push(Wi(t)),n}function Ki(e){e.command(`check`).description(`Audit project for common issues`).option(`--deploy`,`Run production readiness checks`).action(e=>{if(!e.deploy){console.log(`
3677
3863
  Usage: kick check --deploy
3678
3864
 
3679
3865
  Available checks:
3680
3866
  --deploy Audit for production readiness (security, config, best practices)
3681
- `);return}let t=process.cwd();dt(`KickJS Deploy Check`);let n=gt();n.start(`Scanning project...`);let r=Di(t);n.stop(`Scan complete`);let i={CRITICAL:0,WARNING:1,INFO:2};r.sort((e,t)=>i[e.severity]-i[t.severity]);for(let e of r)I.message(`${ut(e.severity)} ${e.message}`);let a=r.filter(e=>e.severity===`CRITICAL`).length,o=r.filter(e=>e.severity===`WARNING`).length,s=r.filter(e=>e.severity===`INFO`).length,c=o===1?`warning`:`warnings`,l=[a>0?O.red(`${a} critical`):`${a} critical`,o>0?O.yellow(`${o} ${c}`):`${o} ${c}`,`${s} info`].join(`, `);a>0?(P(O.red(`${l} — fix critical issues before deploying`)),process.exit(1)):P(O.green(`${l} — looking good!`))})}async function Q(e){return Oe({configPath:v.resolve(process.cwd(),e.config)})}async function $(e){if(e.adapter){let t=await e.adapter();return{adapter:t,cleanup:async()=>t.close()}}if(!e.connectionString)throw Error(`kickjs-db: no adapter resolved — set db.connectionString (or DATABASE_URL) in kick.config.ts, or supply db.adapter() factory`);let t=e.dialect??`postgres`;if(t!==`postgres`)throw Error(`kickjs-db: built-in CLI adapter only supports postgres in M1 (dialect=${t}); use db.adapter() factory for other dialects`);let[{pgAdapter:n},r]=await Promise.all([import(`@forinda/kickjs-db-pg`),import(`pg`)]),i=new r.default.Pool({connectionString:e.connectionString}),a=n({pool:i});return{adapter:a,cleanup:async()=>{await a.close(),await i.end()}}}function ki(e){if(e.length===0){console.log(`No migrations.`);return}console.table(e.map(e=>({id:e.id,state:e.state,batch:e.batch??`-`,reviewed:e.reviewed,applied:e.appliedAt??`-`})))}function Ai(e){let t=e.command(`db`).description(`Database commands (kickjs-db)`);t.command(`generate <name>`).description(`Generate a new migration from schema diff`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`-e, --empty`,`Skip schema diff and create an empty migration shell (data migration, seed, freeform SQL)`).action(async(e,t)=>{let n=process.cwd(),r=await xe({name:e,config:await Q(t),cwd:n,empty:t.empty});if(r.status===`no-changes`){console.log(`No schema changes detected.`);return}if(r.empty){console.log(`Created empty migration ${r.migrationDir} (author up.sql + down.sql).`);return}let i=r.changeCount===1?``:`s`;console.log(`Created migration ${r.migrationDir} (${r.changeCount} change${i}).`)});let n=t.command(`migrate`).description(`Migration runner subcommands`);n.command(`latest`).description(`Apply all pending migrations in a new batch`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`--confirm-enum-drop`,"Allow migrations carrying the `-- KICK ENUM REMOVE` header to apply",!1).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let r=await Ce({adapter:n,migrationsDir:t.migrationsDir,confirmEnumDrop:e.confirmEnumDrop});r.applied.length===0?console.log(`No pending migrations.`):console.log(`Applied batch ${r.batch}: ${r.applied.join(`, `)}`)}finally{await r()}}),n.command(`up`).description(`Apply the next single pending migration`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`--confirm-enum-drop`,"Allow migrations carrying the `-- KICK ENUM REMOVE` header to apply",!1).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let r=await Ee({adapter:n,migrationsDir:t.migrationsDir,confirmEnumDrop:e.confirmEnumDrop});r.applied.length===0?console.log(`No pending migrations.`):console.log(`Applied ${r.applied[0]} (batch ${r.batch})`)}finally{await r()}}),n.command(`down`).description(`Reverse the most recent applied migration`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let e=await Se({adapter:n,migrationsDir:t.migrationsDir});e.reversed?console.log(`Reversed ${e.reversed}.`):console.log(`Nothing to reverse.`)}finally{await r()}}),n.command(`rollback`).description(`Reverse the entire last batch as a single unit`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let e=await we({adapter:n,migrationsDir:t.migrationsDir});e.reversed.length===0?console.log(`Nothing to roll back.`):console.log(`Rolled back batch ${e.batch}: ${e.reversed.join(`, `)}`)}finally{await r()}}),n.command(`status`).description(`Print applied + pending migrations`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{ki(await Te({adapter:n,migrationsDir:t.migrationsDir}))}finally{await r()}}),t.command(`introspect`).description(`Generate a TypeScript schema file from a live database`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`--out <path>`,`Output file (defaults to db.schemaPath from config)`).option(`--json`,`Print the raw SchemaSnapshot JSON to stdout instead of writing TS source`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let r=await n.introspect();if(e.json){console.log(JSON.stringify(r,null,2));return}let i=e.out??t.schemaPath;await E(i,De(r),`utf8`);let a=Object.keys(r.tables).length;console.log(`Wrote ${i} (${a} table${a===1?``:`s`}).`)}finally{await r()}})}const ji=[`src/db/schema.ts`,`src/db/schema/index.ts`,`src/db/schema`],Mi=()=>({id:`kick/db`,inputs:[`src/db/schema.ts`,`src/db/schema/**/*.ts`],async generate(e){let t=Ni(e.cwd);if(!t)return null;let n=v.resolve(e.cwd,`.kickjs/types`);return[`import type { SchemaToTypes, SchemaToRelationsRegister, KickDbClient } from '@forinda/kickjs-db'`,`import type * as appSchema from '${Pi(v.relative(n,t)).replace(/\.ts$/,``).replace(/\/index$/,``)}'`,``,`declare global {`,` interface KickDbSchema extends SchemaToTypes<typeof appSchema> {}`,`}`,``,`declare module '@forinda/kickjs-db' {`,` interface KickDbRegister {`,` db: KickDbClient<KickDbSchema>`,` }`,``,` interface KickDbRelationsRegister {`,` db: SchemaToRelationsRegister<typeof appSchema>`,` }`,`}`].join(`
3682
- `)}});function Ni(e){for(let t of ji){let n=v.resolve(e,t);if(t.endsWith(`.ts`)){if(h(n))return n}else{let e=v.join(n,`index.ts`);if(h(e))return e}}return null}function Pi(e){return e.replace(/\\/g,`/`)}const Fi=()=>({id:`kick/assets`,inputs:[`kick.config.ts`,`kick.config.js`,`kick.config.mjs`],async generate(e){if(!h(v.resolve(e.cwd,`kick.config.ts`)))return null;let t=await r(e.cwd);if(!t?.assetMap)return null;let n=s(t.assetMap,e.cwd);return n.count===0?null:l(n)}}),Ii="/* eslint-disable */\n// AUTO-GENERATED by `kick typegen`. DO NOT EDIT.\n// Re-run with `kick typegen` or rely on `kick dev` to refresh.\n";function Li(e,t,n){if(e.length===0)return`${Ii}
3867
+ `);return}let t=process.cwd();mt(`KickJS Deploy Check`);let n=yt();n.start(`Scanning project...`);let r=Gi(t);n.stop(`Scan complete`);let i={CRITICAL:0,WARNING:1,INFO:2};r.sort((e,t)=>i[e.severity]-i[t.severity]);for(let e of r)I.message(`${pt(e.severity)} ${e.message}`);let a=r.filter(e=>e.severity===`CRITICAL`).length,o=r.filter(e=>e.severity===`WARNING`).length,s=r.filter(e=>e.severity===`INFO`).length,c=o===1?`warning`:`warnings`,l=[a>0?k.red(`${a} critical`):`${a} critical`,o>0?k.yellow(`${o} ${c}`):`${o} ${c}`,`${s} info`].join(`, `);a>0?(P(k.red(`${l} — fix critical issues before deploying`)),process.exit(1)):P(k.green(`${l} — looking good!`))})}async function Q(e){return Ae({configPath:y.resolve(process.cwd(),e.config)})}async function $(e){if(e.adapter){let t=await e.adapter();return{adapter:t,cleanup:async()=>t.close()}}if(!e.connectionString)throw Error(`kickjs-db: no adapter resolved — set db.connectionString (or DATABASE_URL) in kick.config.ts, or supply db.adapter() factory`);let t=e.dialect??`postgres`;if(t!==`postgres`)throw Error(`kickjs-db: built-in CLI adapter only supports postgres in M1 (dialect=${t}); use db.adapter() factory for other dialects`);let[{pgAdapter:n},r]=await Promise.all([import(`@forinda/kickjs-db-pg`),import(`pg`)]),i=new r.default.Pool({connectionString:e.connectionString}),a=n({pool:i});return{adapter:a,cleanup:async()=>{await a.close(),await i.end()}}}function qi(e){if(e.length===0){console.log(`No migrations.`);return}console.table(e.map(e=>({id:e.id,state:e.state,batch:e.batch??`-`,reviewed:e.reviewed,applied:e.appliedAt??`-`})))}function Ji(e){let t=e.command(`db`).description(`Database commands (kickjs-db)`);t.command(`generate <name>`).description(`Generate a new migration from schema diff`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`-e, --empty`,`Skip schema diff and create an empty migration shell (data migration, seed, freeform SQL)`).action(async(e,t)=>{let n=process.cwd(),r=await Ce({name:e,config:await Q(t),cwd:n,empty:t.empty});if(r.status===`no-changes`){console.log(`No schema changes detected.`);return}if(r.empty){console.log(`Created empty migration ${r.migrationDir} (author up.sql + down.sql).`);return}let i=r.changeCount===1?``:`s`;console.log(`Created migration ${r.migrationDir} (${r.changeCount} change${i}).`)});let n=t.command(`migrate`).description(`Migration runner subcommands`);n.command(`latest`).description(`Apply all pending migrations in a new batch`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`--confirm-enum-drop`,"Allow migrations carrying the `-- KICK ENUM REMOVE` header to apply",!1).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let r=await Te({adapter:n,migrationsDir:t.migrationsDir,confirmEnumDrop:e.confirmEnumDrop});r.applied.length===0?console.log(`No pending migrations.`):console.log(`Applied batch ${r.batch}: ${r.applied.join(`, `)}`)}finally{await r()}}),n.command(`up`).description(`Apply the next single pending migration`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`--confirm-enum-drop`,"Allow migrations carrying the `-- KICK ENUM REMOVE` header to apply",!1).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let r=await Oe({adapter:n,migrationsDir:t.migrationsDir,confirmEnumDrop:e.confirmEnumDrop});r.applied.length===0?console.log(`No pending migrations.`):console.log(`Applied ${r.applied[0]} (batch ${r.batch})`)}finally{await r()}}),n.command(`down`).description(`Reverse the most recent applied migration`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let e=await we({adapter:n,migrationsDir:t.migrationsDir});e.reversed?console.log(`Reversed ${e.reversed}.`):console.log(`Nothing to reverse.`)}finally{await r()}}),n.command(`rollback`).description(`Reverse the entire last batch as a single unit`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let e=await Ee({adapter:n,migrationsDir:t.migrationsDir});e.reversed.length===0?console.log(`Nothing to roll back.`):console.log(`Rolled back batch ${e.batch}: ${e.reversed.join(`, `)}`)}finally{await r()}}),n.command(`status`).description(`Print applied + pending migrations`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{qi(await De({adapter:n,migrationsDir:t.migrationsDir}))}finally{await r()}}),t.command(`introspect`).description(`Generate a TypeScript schema file from a live database`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`--out <path>`,`Output file (defaults to db.schemaPath from config)`).option(`--json`,`Print the raw SchemaSnapshot JSON to stdout instead of writing TS source`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let r=await n.introspect();if(e.json){console.log(JSON.stringify(r,null,2));return}let i=e.out??t.schemaPath;await D(i,ke(r),`utf8`);let a=Object.keys(r.tables).length;console.log(`Wrote ${i} (${a} table${a===1?``:`s`}).`)}finally{await r()}})}function Yi(e){return e.optsWithGlobals().dryRun??!1}function Xi(e){e.command(`codemod`).description(`Codebase migration commands (AST-style rewrites distinct from db migrate)`).command(`modules`).description(`Rewrite module declarations between class form and the defineModule factory.
3868
+ Direction defaults to \`modules.style\` from kick.config (or "define").
3869
+ --target define|class Override the migration direction.
3870
+ --apply Apply the changes (default: dry-run preview).
3871
+ --experimental Acknowledge that AST migration is experimental.`).option(`--modules-dir <dir>`,`Modules directory (default: src/modules from kick.config)`).option(`--apply`,`Apply the migration to disk (default: dry-run)`).option(`--experimental`,`Acknowledge that this command is experimental`).option(`--target <style>`,`Migration direction — 'define' or 'class'`).option(`--no-backup`,`Skip the .kickjs/codemod-backups/ snapshot (default: backup on)`).action(async(e,t)=>{let i=Yi(t)||!e.apply;A(i),e.experimental||(console.error(`
3872
+ `+k.red(`Error:`)+` kick codemod modules is experimental — pass --experimental to acknowledge.
3873
+ The regex-based rewrite handles the shapes our templates produce.
3874
+ Hand-rolled modules with non-standard structures may be skipped.
3875
+ Always commit before running with --apply.
3876
+ `),process.exit(1));let a=n(await r(process.cwd())),o=C(e.modulesDir??a.dir??`src/modules`),s;e.target===`define`||e.target===`class`?s=e.target:e.target===void 0?s=a.style??`define`:(console.error(`\n ${k.red(`Error:`)} --target must be 'define' or 'class' (got '${e.target}').\n`),process.exit(1));let c=k.dim(`→ ${s}`),l=i?k.dim(`(dry-run)`):k.bold(`(applying)`);console.log(`\n ${k.bold(`kick codemod modules`)} ${c} ${l}`),console.log(` modulesDir: ${k.dim(o)}\n`);let u=e.backup!==!1&&!i,d=await rr(o,{dryRun:i,target:s,backup:u});if(d.backupDir){let e=d.backupDir;console.log(` ${k.green(`✓`)} backup: ${k.dim(e)}\n ${k.dim(`(restore: rm -rf <modulesDir> && mv "<backup>" <modulesDir>)`)}\n`)}else !i&&e.backup===!1&&console.log(` ${k.dim(`(--no-backup — skipping snapshot)`)}\n`);let f=0,p=0;for(let e of d.files)if(e.status===`migrated`)f++,console.log(` ${k.green(`✓`)} ${e.path}`);else{p++;let t=k.dim(`(${e.reason??`skipped`})`);console.log(` ${k.dim(`-`)} ${e.path} ${t}`)}if(console.log(),d.indexStatus===`migrated`)console.log(` ${k.green(`✓`)} ${d.indexPath}`);else if(d.indexStatus===`skipped`){let e=k.dim(`(${d.indexReason??`skipped`})`);console.log(` ${k.dim(`-`)} ${d.indexPath} ${e}`)}else console.log(` ${k.dim(`-`)} ${d.indexPath} ${k.dim(`(not found)`)}`);let m=i?k.dim(` (dry-run — pass --apply to write)`):``;console.log(`\n ${k.bold(String(f))} migrated, ${k.bold(String(p))} skipped${m}\n`)})}const Zi=[`src/db/schema.ts`,`src/db/schema/index.ts`,`src/db/schema`],Qi=()=>({id:`kick/db`,inputs:[`src/db/schema.ts`,`src/db/schema/**/*.ts`],async generate(e){let t=$i(e.cwd);if(!t)return null;let n=y.resolve(e.cwd,`.kickjs/types`);return[`import type { SchemaToTypes, SchemaToRelationsRegister, KickDbClient } from '@forinda/kickjs-db'`,`import type * as appSchema from '${ea(y.relative(n,t)).replace(/\.ts$/,``).replace(/\/index$/,``)}'`,``,`declare global {`,` interface KickDbSchema extends SchemaToTypes<typeof appSchema> {}`,`}`,``,`declare module '@forinda/kickjs-db' {`,` interface KickDbRegister {`,` db: KickDbClient<KickDbSchema>`,` }`,``,` interface KickDbRelationsRegister {`,` db: SchemaToRelationsRegister<typeof appSchema>`,` }`,`}`].join(`
3877
+ `)}});function $i(e){for(let t of Zi){let n=y.resolve(e,t);if(t.endsWith(`.ts`)){if(h(n))return n}else{let e=y.join(n,`index.ts`);if(h(e))return e}}return null}function ea(e){return e.replace(/\\/g,`/`)}const ta=()=>({id:`kick/assets`,inputs:[`kick.config.ts`,`kick.config.js`,`kick.config.mjs`],async generate(e){if(!h(y.resolve(e.cwd,`kick.config.ts`)))return null;let t=await r(e.cwd);if(!t?.assetMap)return null;let n=s(t.assetMap,e.cwd);return n.count===0?null:l(n)}}),na="/* eslint-disable */\n// AUTO-GENERATED by `kick typegen`. DO NOT EDIT.\n// Re-run with `kick typegen` or rely on `kick dev` to refresh.\n";function ra(e,t,n){if(e.length===0)return`${na}
3683
3878
  // (no routes discovered yet — annotate a controller method with
3684
3879
  // @Get/@Post/@Put/@Delete/@Patch and re-run \`kick typegen\`)
3685
3880
  declare global {
@@ -3688,8 +3883,8 @@ declare global {
3688
3883
  }
3689
3884
 
3690
3885
  export {}
3691
- `;let r=new Map;for(let t of e){let e=r.get(t.controller)??[];e.push(t),r.set(t.controller,e)}let i=new Map,a=(e,r)=>{let a=Bi(e,r,t,n,i);return a?`import('zod').infer<typeof ${a}>`:null},o=[];for(let[e,t]of r){let n=[` interface ${e} {`];for(let e of t){let t=e.pathParams.length>0?`{ ${e.pathParams.map(e=>`${e}: string`).join(`; `)} }`:`{}`,r=a(e.bodySchema,e.filePath),i=a(e.querySchema,e.filePath),o=a(e.paramsSchema,e.filePath)??t,s=r??`unknown`,c=i??Ri(e),l=zi(e);n.push(` /**`,` * ${e.httpMethod} ${e.path}`,...l.map(e=>` * ${e}`),` */`,` ${e.method}: {`,` params: ${o}`,` body: ${s}`,` query: ${c}`,` response: unknown`,` }`)}n.push(` }`),o.push(n.join(`
3692
- `))}return`${Ii}${Vi(i)}
3886
+ `;let r=new Map;for(let t of e){let e=r.get(t.controller)??[];e.push(t),r.set(t.controller,e)}let i=new Map,a=(e,r)=>{let a=oa(e,r,t,n,i);return a?`import('zod').infer<typeof ${a}>`:null},o=[];for(let[e,t]of r){let n=[` interface ${e} {`];for(let e of t){let t=e.pathParams.length>0?`{ ${e.pathParams.map(e=>`${e}: string`).join(`; `)} }`:`{}`,r=a(e.bodySchema,e.filePath),i=a(e.querySchema,e.filePath),o=a(e.paramsSchema,e.filePath)??t,s=r??`unknown`,c=i??ia(e),l=aa(e);n.push(` /**`,` * ${e.httpMethod} ${e.path}`,...l.map(e=>` * ${e}`),` */`,` ${e.method}: {`,` params: ${o}`,` body: ${s}`,` query: ${c}`,` response: unknown`,` }`)}n.push(` }`),o.push(n.join(`
3887
+ `))}return`${na}${sa(i)}
3693
3888
  declare global {
3694
3889
  // eslint-disable-next-line @typescript-eslint/no-namespace
3695
3890
  namespace KickRoutes {
@@ -3699,9 +3894,9 @@ ${o.join(`
3699
3894
  }
3700
3895
 
3701
3896
  export {}
3702
- `}function Ri(e){if(e.queryFilterable===null)return`unknown`;let t=e.querySortable??[];return`{ filter?: string | string[]; sort?: ${t.length>0?t.flatMap(e=>[`'${e}'`,`'-${e}'`]).join(` | `):`string`}; q?: string; page?: string; limit?: string }`}function zi(e){let t=[];return e.queryFilterable&&e.queryFilterable.length>0&&t.push(`Filterable: ${e.queryFilterable.join(`, `)}`),e.querySortable&&e.querySortable.length>0&&t.push(`Sortable: ${e.querySortable.join(`, `)}`),e.querySearchable&&e.querySearchable.length>0&&t.push(`Searchable: ${e.querySearchable.join(`, `)}`),t}function Bi(e,t,n,r,i){if(!e||r!==`zod`||e.source===null)return null;let a=Hi(e.source,t,n);if(a===`unknown`)return null;let o=`${a}::${e.identifier}`,s=i.get(o)?.specifier;return s?s=i.get(o).specifier:(s=`_S${i.size}`,i.set(o,{identifier:e.identifier,specifier:s})),s}function Vi(e){if(e.size===0)return``;let t=[];for(let[n,r]of e){let[e]=n.split(`::`);t.push(`import type { ${r.identifier} as ${r.specifier} } from '${e}'`)}return t.join(`
3897
+ `}function ia(e){if(e.queryFilterable===null)return`unknown`;let t=e.querySortable??[];return`{ filter?: string | string[]; sort?: ${t.length>0?t.flatMap(e=>[`'${e}'`,`'-${e}'`]).join(` | `):`string`}; q?: string; page?: string; limit?: string }`}function aa(e){let t=[];return e.queryFilterable&&e.queryFilterable.length>0&&t.push(`Filterable: ${e.queryFilterable.join(`, `)}`),e.querySortable&&e.querySortable.length>0&&t.push(`Sortable: ${e.querySortable.join(`, `)}`),e.querySearchable&&e.querySearchable.length>0&&t.push(`Searchable: ${e.querySearchable.join(`, `)}`),t}function oa(e,t,n,r,i){if(!e||r!==`zod`||e.source===null)return null;let a=ca(e.source,t,n);if(a===`unknown`)return null;let o=`${a}::${e.identifier}`,s=i.get(o)?.specifier;return s?s=i.get(o).specifier:(s=`_S${i.size}`,i.set(o,{identifier:e.identifier,specifier:s})),s}function sa(e){if(e.size===0)return``;let t=[];for(let[n,r]of e){let[e]=n.split(`::`);t.push(`import type { ${r.identifier} as ${r.specifier} } from '${e}'`)}return t.join(`
3703
3898
  `)+`
3704
- `}function Hi(e,t,n){if(e===null)return`unknown`;let r=y(n);if(e===``){let e=x(r,t).split(se).join(`/`);return e=e.replace(/\.(ts|tsx|mts|cts)$/i,``),e.startsWith(`.`)||(e=`./`+e),e}if(!e.startsWith(`.`)&&!e.startsWith(`/`))return e;let i=x(r,S(y(t),e)).split(se).join(`/`);return i=i.replace(/\.(ts|tsx|mts|cts)$/i,``),i.startsWith(`.`)||(i=`./`+i),i}const Ui=()=>({id:`kick/routes`,outExtension:`.ts`,inputs:[`src/**/*.controller.ts`,`src/**/*.module.ts`],async generate(e){let t=await e.getScanResult({root:Wi(e),cwd:e.cwd,envFile:Gi(e)}),n=e.config?.typegen?.schemaValidator??`zod`,r=v.resolve(e.cwd,`.kickjs/types/kick__routes.ts`);return Li(t.routes,r,n)}});function Wi(e){return v.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function Gi(e){let t=e.config?.typegen?.envFile;if(t!==!1)return t}function Ki(e,t){if(!e)return null;let n=x(y(t),e.filePath).split(se).join(`/`);return n=n.replace(/\.(ts|tsx|mts|cts)$/i,``),n.startsWith(`.`)||(n=`./`+n),`/* eslint-disable */
3899
+ `}function ca(e,t,n){if(e===null)return`unknown`;let r=b(n);if(e===``){let e=S(r,t).split(oe).join(`/`);return e=e.replace(/\.(ts|tsx|mts|cts)$/i,``),e.startsWith(`.`)||(e=`./`+e),e}if(!e.startsWith(`.`)&&!e.startsWith(`/`))return e;let i=S(r,C(b(t),e)).split(oe).join(`/`);return i=i.replace(/\.(ts|tsx|mts|cts)$/i,``),i.startsWith(`.`)||(i=`./`+i),i}const la=()=>({id:`kick/routes`,outExtension:`.ts`,inputs:[`src/**/*.controller.ts`,`src/**/*.module.ts`],async generate(e){let t=await e.getScanResult({root:ua(e),cwd:e.cwd,envFile:da(e)}),n=e.config?.typegen?.schemaValidator??`zod`,r=y.resolve(e.cwd,`.kickjs/types/kick__routes.ts`);return ra(t.routes,r,n)}});function ua(e){return y.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function da(e){let t=e.config?.typegen?.envFile;if(t!==!1)return t}function fa(e,t){if(!e)return null;let n=S(b(t),e.filePath).split(oe).join(`/`);return n=n.replace(/\.(ts|tsx|mts|cts)$/i,``),n.startsWith(`.`)||(n=`./`+n),`/* eslint-disable */
3705
3900
  // AUTO-GENERATED by \`kick typegen\`. DO NOT EDIT.
3706
3901
  // Re-run with \`kick typegen\` or rely on \`kick dev\` to refresh.
3707
3902
 
@@ -3737,4 +3932,4 @@ declare global {
3737
3932
  }
3738
3933
 
3739
3934
  export {}
3740
- `}const qi=()=>({id:`kick/env`,outExtension:`.ts`,inputs:[`src/env.ts`,`src/**/env.ts`,`src/**/*.env.ts`],async generate(e){let t=Yi(e);if(t===!1)return null;let n=await e.getScanResult({root:Ji(e),cwd:e.cwd,envFile:t});if(!n.env)return null;let r=v.resolve(e.cwd,`.kickjs/types/kick__env.ts`);return Ki(n.env,r)}});function Ji(e){return v.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function Yi(e){return e.config?.typegen?.envFile}var Xi=e({builtinCliPlugins:()=>Zi});const Zi=[o({name:`kick/init`,register:Et}),o({name:`kick/generate`,register:_r}),o({name:`kick/run`,register:Ar}),o({name:`kick/info`,register:jr}),o({name:`kick/inspect`,register:Br}),o({name:`kick/add`,register:wt}),o({name:`kick/list`,register:Ct}),o({name:`kick/explain`,register:Xr}),o({name:`kick/mcp`,register:ai}),o({name:`kick/tinker`,register:li}),o({name:`kick/remove`,register:pi}),o({name:`kick/typegen`,register:gi}),o({name:`kick/check`,register:Oi}),o({name:`kick/db`,register:Ai,typegens:[Mi()]}),o({name:`kick/assets`,typegens:[Fi()]}),o({name:`kick/routes`,typegens:[Ui()]}),o({name:`kick/env`,typegens:[qi()]})];export{ke as i,Xi as n,xr as r,Zi as t};
3935
+ `}const pa=()=>({id:`kick/env`,outExtension:`.ts`,inputs:[`src/env.ts`,`src/**/env.ts`,`src/**/*.env.ts`],async generate(e){let t=ha(e);if(t===!1)return null;let n=await e.getScanResult({root:ma(e),cwd:e.cwd,envFile:t});if(!n.env)return null;let r=y.resolve(e.cwd,`.kickjs/types/kick__env.ts`);return fa(n.env,r)}});function ma(e){return y.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function ha(e){return e.config?.typegen?.envFile}var ga=e({builtinCliPlugins:()=>_a});const _a=[o({name:`kick/init`,register:kt}),o({name:`kick/generate`,register:Nr}),o({name:`kick/run`,register:Kr}),o({name:`kick/info`,register:qr}),o({name:`kick/inspect`,register:ni}),o({name:`kick/add`,register:Dt}),o({name:`kick/list`,register:Et}),o({name:`kick/explain`,register:fi}),o({name:`kick/mcp`,register:Ci}),o({name:`kick/tinker`,register:Di}),o({name:`kick/remove`,register:ji}),o({name:`kick/typegen`,register:Pi}),o({name:`kick/check`,register:Ki}),o({name:`kick/db`,register:Ji,typegens:[Qi()]}),o({name:`kick/codemod`,register:Xi}),o({name:`kick/assets`,typegens:[ta()]}),o({name:`kick/routes`,typegens:[la()]}),o({name:`kick/env`,typegens:[pa()]})];export{je as i,ga as n,Lr as r,_a as t};