@forinda/kickjs-cli 5.2.1 → 5.2.3

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 (34) hide show
  1. package/dist/builtins-BdvmVAJ1.mjs +3740 -0
  2. package/dist/builtins-Du70nybS.mjs +1066 -0
  3. package/dist/builtins-Du70nybS.mjs.map +1 -0
  4. package/dist/cli.mjs +2 -120
  5. package/dist/config-Dzw8Ws4d.mjs +11 -0
  6. package/dist/config-lCKbrRnt.mjs +12 -0
  7. package/dist/{config-CRi3zCxk.mjs.map → config-lCKbrRnt.mjs.map} +1 -1
  8. package/dist/generator-extension-Cp5FUUAw.mjs +2687 -0
  9. package/dist/generator-extension-Cp5FUUAw.mjs.map +1 -0
  10. package/dist/index.mjs +2 -5
  11. package/dist/plugin-Dv2gKsuC.mjs +11 -0
  12. package/dist/plugin-VPl_QQGb.mjs +12 -0
  13. package/dist/{plugin-DfomEcef.mjs.map → plugin-VPl_QQGb.mjs.map} +1 -1
  14. package/dist/rolldown-runtime-B6QC8dMY.mjs +11 -0
  15. package/dist/{run-plugins-D9abb5Nx.mjs → run-plugins-CM1Af-4B.mjs} +2 -3
  16. package/dist/typegen-C6ZfoYTC.mjs +114 -0
  17. package/dist/typegen-CBI7dNXr.mjs +115 -0
  18. package/dist/{typegen-B9S81bOx.mjs.map → typegen-CBI7dNXr.mjs.map} +1 -1
  19. package/dist/types-n4LRUF_c.mjs +12 -0
  20. package/dist/{types-CU89yUxU.mjs.map → types-n4LRUF_c.mjs.map} +1 -1
  21. package/package.json +5 -5
  22. package/dist/builtins-B0dptoXq.mjs +0 -4182
  23. package/dist/builtins-B0dptoXq.mjs.map +0 -1
  24. package/dist/builtins-N3mDa6bM.mjs +0 -8538
  25. package/dist/config-Bc6ERRTE.mjs +0 -169
  26. package/dist/config-CRi3zCxk.mjs +0 -171
  27. package/dist/generator-extension-C-HwKvFf.mjs +0 -4380
  28. package/dist/generator-extension-C-HwKvFf.mjs.map +0 -1
  29. package/dist/plugin-DfomEcef.mjs +0 -71
  30. package/dist/plugin-b7ig7Uxv.mjs +0 -80
  31. package/dist/rolldown-runtime-CV_zlh2d.mjs +0 -24
  32. package/dist/typegen-B9S81bOx.mjs +0 -1353
  33. package/dist/typegen-BKUAdp_3.mjs +0 -1351
  34. package/dist/types-CU89yUxU.mjs +0 -25
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtins-Du70nybS.mjs","names":["colors","runTypegen","promptConfirm","runPluginTypegens","runTypegen","colors","colors","runTypegen","createSpinner","colors","resolveSrcDir","resolveEnvFile"],"sources":["../src/commands/add.ts","../src/commands/init.ts","../src/generators/plugin.ts","../src/generators/config.ts","../src/generators/agent-docs.ts","../src/generators/auth-scaffold.ts","../src/generators/job.ts","../src/generators/scaffold.ts","../src/generators/test.ts","../src/commands/generate.ts","../src/utils/shell.ts","../src/typegen/runner.ts","../src/typegen/disable-filter.ts","../src/typegen/run-plugins.ts","../src/asset-manager/build.ts","../src/commands/run.ts","../src/commands/info.ts","../src/commands/inspect.ts","../src/explain/known-issues.ts","../src/explain/ai-fallback.ts","../src/commands/explain.ts","../src/commands/mcp.ts","../src/commands/tinker.ts","../src/generators/remove-module.ts","../src/commands/remove.ts","../src/commands/typegen.ts","../src/commands/check.ts","../src/commands/db.ts","../src/typegen/builtin/db.ts","../src/typegen/builtin/assets.ts","../src/typegen/render/routes.ts","../src/typegen/builtin/routes.ts","../src/typegen/render/env.ts","../src/typegen/builtin/env.ts","../src/plugin/builtins.ts"],"sourcesContent":["import { execSync } from 'node:child_process'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport type { Command } from 'commander'\nimport { loadKickConfig, PACKAGE_MANAGERS, type PackageManager } from '../config'\n\ninterface PackageEntry {\n pkg: string\n peers: string[]\n description: string\n dev?: boolean\n /**\n * `true` for packages every project needs (framework + Vite plugin +\n * CLI). `kick new` installs these regardless of options chosen, and\n * future package-removal flows refuse to drop them.\n */\n core?: boolean\n}\n\n/** Registry of KickJS packages and their required peer dependencies */\nconst PACKAGE_REGISTRY: Record<string, PackageEntry> = {\n // Core (always installed by kick new — required for the framework to run)\n kickjs: {\n pkg: '@forinda/kickjs',\n peers: ['express'],\n description: 'Unified framework: DI, decorators, routing, middleware',\n core: true,\n },\n vite: {\n pkg: '@forinda/kickjs-vite',\n peers: ['vite'],\n description: 'Vite plugin: dev server, HMR, module discovery',\n dev: true,\n core: true,\n },\n cli: {\n pkg: '@forinda/kickjs-cli',\n peers: [],\n description: 'CLI tool and code generators',\n dev: true,\n core: true,\n },\n\n // API\n swagger: {\n pkg: '@forinda/kickjs-swagger',\n peers: [],\n description: 'OpenAPI spec + Swagger UI + ReDoc',\n },\n // Database\n db: {\n pkg: '@forinda/kickjs-db',\n peers: [],\n description: 'kick/db core — schema DSL, migrations, KickDbClient, customType',\n },\n 'db-pg': {\n pkg: '@forinda/kickjs-db-pg',\n peers: ['pg'],\n description: 'kick/db PostgreSQL dialect + adapter (pgDialect, pgAdapter)',\n },\n drizzle: {\n pkg: '@forinda/kickjs-drizzle',\n peers: ['drizzle-orm'],\n description: 'Drizzle ORM adapter + query builder',\n },\n prisma: {\n pkg: '@forinda/kickjs-prisma',\n peers: ['@prisma/client'],\n description: 'Prisma adapter + query builder',\n },\n\n // Real-time\n ws: {\n pkg: '@forinda/kickjs-ws',\n peers: ['socket.io'],\n description: 'WebSocket with @WsController decorators',\n },\n\n // DevTools\n devtools: {\n pkg: '@forinda/kickjs-devtools',\n peers: [],\n description: 'Development dashboard — routes, DI, metrics, health',\n dev: true,\n },\n\n // Auth\n auth: {\n pkg: '@forinda/kickjs-auth',\n peers: ['jsonwebtoken'],\n description: 'Authentication — JWT, API key, and custom strategies',\n },\n\n // Queue\n queue: {\n pkg: '@forinda/kickjs-queue',\n peers: [],\n description: 'Queue adapter (BullMQ/RabbitMQ/Kafka)',\n },\n 'queue:bullmq': {\n pkg: '@forinda/kickjs-queue',\n peers: ['bullmq', 'ioredis'],\n description: 'Queue with BullMQ + Redis',\n },\n 'queue:rabbitmq': {\n pkg: '@forinda/kickjs-queue',\n peers: ['amqplib'],\n description: 'Queue with RabbitMQ',\n },\n 'queue:kafka': {\n pkg: '@forinda/kickjs-queue',\n peers: ['kafkajs'],\n description: 'Queue with Kafka',\n },\n\n // MCP — Model Context Protocol server\n mcp: {\n pkg: '@forinda/kickjs-mcp',\n peers: ['@modelcontextprotocol/sdk'],\n description: 'Model Context Protocol server — expose @Controller endpoints as AI tools',\n },\n\n // Testing\n testing: {\n pkg: '@forinda/kickjs-testing',\n peers: [],\n description: 'Test utilities and TestModule builder',\n dev: true,\n },\n}\n\n/**\n * Walk up from `fromDir` to filesystem root, returning the first\n * directory that contains `name`. Lets monorepo sub-packages pick up\n * lockfiles and `packageManager` fields living at the workspace root.\n */\nfunction findUp(name: string, fromDir = process.cwd()): string | null {\n let current = fromDir\n while (true) {\n if (existsSync(resolve(current, name))) return current\n const parent = dirname(current)\n if (parent === current) return null\n current = parent\n }\n}\n\nfunction detectFromLockfile(): PackageManager | null {\n if (findUp('pnpm-lock.yaml')) return 'pnpm'\n if (findUp('yarn.lock')) return 'yarn'\n if (findUp('bun.lockb') || findUp('bun.lock')) return 'bun'\n if (findUp('package-lock.json')) return 'npm'\n return null\n}\n\n/**\n * Read `packageManager` from the nearest ancestor `package.json` that\n * declares the field (corepack convention: `\"pnpm@10.0.0\"`). Climbs so\n * monorepo sub-packages inherit the workspace pm even when their own\n * package.json omits the field.\n */\nfunction packageManagerFromPackageJson(): PackageManager | null {\n let dir: string | null = process.cwd()\n while (dir) {\n const pkgPath = resolve(dir, 'package.json')\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))\n const field: unknown = pkg.packageManager\n if (typeof field === 'string') {\n const name = field.split('@')[0] as PackageManager\n if (PACKAGE_MANAGERS.includes(name)) return name\n }\n } catch {\n // ignore — keep climbing\n }\n }\n const parent = dirname(dir)\n if (parent === dir) return null\n dir = parent\n }\n return null\n}\n\nexport type PackageManagerSource = 'flag' | 'config' | 'package.json' | 'lockfile' | 'default'\n\n/**\n * Resolve which package manager to use, in priority order:\n * 1. `--pm` CLI flag\n * 2. `packageManager` in kick.config\n * 3. `packageManager` in nearest ancestor package.json (corepack)\n * 4. Nearest ancestor lockfile (pnpm-lock.yaml → yarn.lock → bun.lock → package-lock.json)\n * 5. `'npm'` fallback\n *\n * Returns the chosen pm plus the source for callers that want to log\n * the resolution path.\n */\nexport async function resolvePackageManagerWithSource(\n flagPm: string | undefined,\n): Promise<{ pm: PackageManager; source: PackageManagerSource }> {\n if (flagPm && PACKAGE_MANAGERS.includes(flagPm as PackageManager)) {\n return { pm: flagPm as PackageManager, source: 'flag' }\n }\n\n const config = await loadKickConfig(process.cwd())\n if (config?.packageManager && PACKAGE_MANAGERS.includes(config.packageManager)) {\n return { pm: config.packageManager, source: 'config' }\n }\n\n const fromPkg = packageManagerFromPackageJson()\n if (fromPkg) return { pm: fromPkg, source: 'package.json' }\n\n const fromLock = detectFromLockfile()\n if (fromLock) return { pm: fromLock, source: 'lockfile' }\n\n return { pm: 'npm', source: 'default' }\n}\n\n/** Convenience wrapper for callers that don't care about the source. */\nexport async function resolvePackageManager(flagPm: string | undefined): Promise<PackageManager> {\n const { pm } = await resolvePackageManagerWithSource(flagPm)\n return pm\n}\n\n/**\n * Print the package catalog. By default shows just the three core\n * packages every project always has — the optional list churns\n * (packages added, deprecated, removed) and a long enumeration in CLI\n * output / docs goes stale within a release. Pass `all = true` to dump\n * everything; that's what `kick add --list --all` triggers when an\n * adopter genuinely wants the live catalog.\n */\nexport function printPackageList(all = false): void {\n const entries = Object.entries(PACKAGE_REGISTRY)\n const maxName = Math.max(...entries.map(([k]) => k.length))\n const core = entries.filter(([, info]) => info.core)\n const optional = entries.filter(([, info]) => !info.core)\n\n const formatRow = ([name, info]: [string, PackageEntry]): string => {\n const padded = name.padEnd(maxName + 2)\n const peers = info.peers.length ? ` (+ ${info.peers.join(', ')})` : ''\n return ` ${padded} ${info.description}${peers}`\n }\n\n console.log('\\n Core packages (always installed by `kick new`):\\n')\n for (const row of core) console.log(formatRow(row))\n\n if (all) {\n console.log('\\n Optional packages (add as needed):\\n')\n for (const row of optional) console.log(formatRow(row))\n } else {\n console.log(`\\n Plus ${optional.length} optional packages (auth, swagger, db, queue, …).`)\n console.log(' Run `kick add --list --all` for the full catalog.')\n }\n\n console.log('\\n Usage: kick add auth drizzle swagger')\n console.log(' kick add queue:bullmq')\n console.log()\n}\n\nexport function registerListCommand(program: Command): void {\n program\n .command('list')\n .alias('ls')\n .description('List KickJS packages (core only; pair with --all for the full catalog)')\n .option('--all', 'Include the full optional catalog')\n .action((opts: { all?: boolean }) => {\n printPackageList(Boolean(opts.all))\n })\n}\n\nexport function registerAddCommand(program: Command): void {\n program\n .command('add [packages...]')\n .description('Add KickJS packages with their required dependencies')\n .option('--pm <manager>', 'Package manager override')\n .option('-D, --dev', 'Install as dev dependency')\n .option('--list', 'List packages (core only by default; pair with --all)')\n .option('--all', 'When listing, include the full optional catalog')\n .action(async (packages: string[], opts: any) => {\n // List mode\n if (opts.list || packages.length === 0) {\n printPackageList(Boolean(opts.all))\n return\n }\n\n const { pm, source } = await resolvePackageManagerWithSource(opts.pm)\n console.log(`\\n Using ${pm} (resolved from ${source})`)\n const forceDevFlag = opts.dev\n const prodDeps = new Set<string>()\n const devDeps = new Set<string>()\n const unknown: string[] = []\n\n for (const name of packages) {\n const entry = PACKAGE_REGISTRY[name]\n if (!entry) {\n unknown.push(name)\n continue\n }\n const target = forceDevFlag || entry.dev ? devDeps : prodDeps\n target.add(entry.pkg)\n for (const peer of entry.peers) {\n target.add(peer)\n }\n }\n\n if (unknown.length > 0) {\n console.log(`\\n Unknown packages: ${unknown.join(', ')}`)\n console.log(' Run \"kick add --list\" to see available packages.\\n')\n if (prodDeps.size === 0 && devDeps.size === 0) return\n }\n\n // Install production dependencies\n if (prodDeps.size > 0) {\n const deps = Array.from(prodDeps)\n const cmd = `${pm} add ${deps.join(' ')}`\n console.log(`\\n Installing ${deps.length} dependency(ies):`)\n for (const dep of deps) console.log(` + ${dep}`)\n console.log()\n try {\n execSync(cmd, { stdio: 'inherit' })\n } catch {\n console.log(`\\n Installation failed. Run manually:\\n ${cmd}\\n`)\n }\n }\n\n // Install dev dependencies\n if (devDeps.size > 0) {\n const deps = Array.from(devDeps)\n const cmd = `${pm} add -D ${deps.join(' ')}`\n console.log(`\\n Installing ${deps.length} dev dependency(ies):`)\n for (const dep of deps) console.log(` + ${dep} (dev)`)\n console.log()\n try {\n execSync(cmd, { stdio: 'inherit' })\n } catch {\n console.log(`\\n Installation failed. Run manually:\\n ${cmd}\\n`)\n }\n }\n\n console.log(' Done!\\n')\n })\n}\n","import { resolve, basename } from 'node:path'\nimport { existsSync, readdirSync, rmSync } from 'node:fs'\nimport type { Command } from 'commander'\nimport { initProject } from '../generators/project'\nimport { intro, outro, text, select, multiSelect, confirm, log } from '../utils/prompts'\nimport { colors } from '../utils/colors'\nimport { resolvePackageManager } from './add'\n\n/** All optional packages available for selection */\nconst OPTIONAL_PACKAGES = [\n { value: 'auth', label: 'Auth', hint: 'JWT, OAuth, API keys' },\n { value: 'swagger', label: 'Swagger', hint: 'OpenAPI docs' },\n { value: 'ws', label: 'WebSocket', hint: 'rooms, heartbeat' },\n { value: 'queue', label: 'Queue', hint: 'BullMQ/RabbitMQ/Kafka' },\n { value: 'devtools', label: 'DevTools', hint: 'debug dashboard' },\n] as const\n\nexport function registerInitCommand(program: Command): void {\n program\n .command('new [name]')\n .alias('init')\n .description('Create a new KickJS project (use \".\" for current directory)')\n .option('-d, --directory <dir>', 'Target directory (defaults to project name)')\n .option('--pm <manager>', 'Package manager: pnpm | npm | yarn | bun')\n .option('--git', 'Initialize git repository')\n .option('--no-git', 'Skip git initialization')\n .option('--install', 'Install dependencies after scaffolding')\n .option('--no-install', 'Skip dependency installation')\n .option('-f, --force', 'Remove existing files without prompting')\n .option('-t, --template <type>', 'Project template: rest | ddd | cqrs | minimal')\n .option('-r, --repo <type>', 'Default repository: prisma | drizzle | inmemory | custom')\n .option(\n '--packages <packages>',\n 'Comma-separated packages to include (e.g. auth,swagger,ws,queue)',\n )\n .option(\n '-y, --yes',\n 'Pick safe defaults for every prompt (template=minimal, repo=inmemory, no extras, git+install on)',\n )\n .option('--non-interactive', 'alias for --yes')\n .action(async (name: string | undefined, opts: any) => {\n intro('KickJS — Create a new project')\n\n // `--yes` / `--non-interactive` swap every \"would prompt\" branch\n // for a sensible default. Explicit flags still override; the\n // existing dir-clear safety prompt is skipped only when --force\n // is also set, so we never silently nuke existing work.\n const yes = Boolean(opts.yes || opts.nonInteractive)\n\n // ── Project name ──────────────────────────────────────────────\n if (!name) {\n if (yes) {\n name = 'my-api'\n } else {\n name = await text({\n message: 'Project name',\n placeholder: 'my-api',\n defaultValue: 'my-api',\n })\n }\n }\n\n let directory: string\n if (name === '.') {\n directory = resolve('.')\n name = basename(directory)\n } else {\n directory = resolve(opts.directory || name)\n }\n\n // ── Check existing directory ──────────────────────────────────\n if (existsSync(directory)) {\n const entries = readdirSync(directory)\n if (entries.length > 0) {\n if (opts.force) {\n log.warn(`Clearing existing files in ${directory}`)\n } else if (yes) {\n // --yes implies \"use defaults\", not \"destroy work\". Bail\n // with a clear message; the user adds --force if they\n // really mean to clear the directory.\n log.warn(`Directory \"${name}\" is not empty. Pass --force to clear it.`)\n outro('Aborted.')\n return\n } else {\n log.warn(`Directory \"${name}\" is not empty:`)\n const shown = entries.slice(0, 5)\n for (const entry of shown) {\n log.message(` - ${entry}`)\n }\n if (entries.length > 5) {\n log.message(` ... and ${entries.length - 5} more`)\n }\n const shouldClear = await confirm({\n message: colors.red('Remove all existing files and proceed?'),\n initialValue: false,\n })\n if (!shouldClear) {\n outro('Aborted.')\n return\n }\n }\n for (const entry of entries) {\n rmSync(resolve(directory, entry), { recursive: true, force: true })\n }\n }\n }\n\n // ── Template ──────────────────────────────────────────────────\n let template = opts.template\n if (!template) {\n if (yes) {\n template = 'minimal'\n } else {\n template = await select({\n message: 'Project template',\n options: [\n { value: 'rest', label: 'REST API', hint: 'Express + Swagger' },\n { value: 'ddd', label: 'DDD', hint: 'Domain-Driven Design modules' },\n { value: 'cqrs', label: 'CQRS', hint: 'Commands, Queries, Events + WS/Queue' },\n { value: 'minimal', label: 'Minimal', hint: 'bare Express' },\n ],\n })\n }\n }\n\n // ── Package manager ───────────────────────────────────────────\n let packageManager = opts.pm\n if (!packageManager) {\n if (yes) {\n // Reuse the same resolution chain `kick add` uses so a\n // monorepo's corepack pin / lockfile picks the right pm\n // even in non-interactive mode.\n packageManager = await resolvePackageManager(undefined)\n } else {\n packageManager = await select({\n message: 'Package manager',\n options: [\n { value: 'pnpm', label: 'pnpm' },\n { value: 'npm', label: 'npm' },\n { value: 'yarn', label: 'yarn' },\n { value: 'bun', label: 'bun' },\n ],\n })\n }\n }\n\n // ── Repository type ───────────────────────────────────────────\n let defaultRepo = opts.repo\n if (!defaultRepo) {\n if (yes) {\n defaultRepo = 'inmemory'\n } else {\n defaultRepo = await select({\n message: 'Default repository/ORM',\n options: [\n { value: 'prisma', label: 'Prisma' },\n { value: 'drizzle', label: 'Drizzle' },\n { value: 'inmemory', label: 'In-Memory' },\n { value: 'custom', label: 'Custom', hint: 'specify later' },\n ],\n })\n\n if (defaultRepo === 'custom') {\n defaultRepo = await text({\n message: 'Custom repository name',\n defaultValue: 'custom',\n })\n }\n }\n }\n\n // ── Optional packages ─────────────────────────────────────────\n let selectedPackages: string[]\n if (opts.packages !== undefined) {\n // Accept empty string / \"none\" / \"false\" as \"skip the prompt, no packages\".\n const raw = opts.packages.trim().toLowerCase()\n if (raw === '' || raw === 'none' || raw === 'false') {\n selectedPackages = []\n } else {\n selectedPackages = opts.packages\n .split(',')\n .map((p: string) => p.trim())\n .filter(Boolean)\n }\n } else if (yes) {\n selectedPackages = []\n } else {\n selectedPackages = await multiSelect({\n message: 'Select packages to include',\n options: [...OPTIONAL_PACKAGES],\n required: false,\n })\n }\n\n // ── Git init ──────────────────────────────────────────────────\n let initGit: boolean\n if (opts.git === undefined) {\n initGit = yes\n ? true\n : await confirm({\n message: 'Initialize git repository?',\n initialValue: true,\n })\n } else {\n initGit = opts.git\n }\n\n // ── Install deps ──────────────────────────────────────────────\n let installDeps: boolean\n if (opts.install === undefined) {\n installDeps = yes\n ? true\n : await confirm({\n message: 'Install dependencies?',\n initialValue: true,\n })\n } else {\n installDeps = opts.install\n }\n\n // ── Scaffold ──────────────────────────────────────────────────\n await initProject({\n name,\n directory,\n packageManager,\n initGit,\n installDeps,\n template,\n defaultRepo,\n packages: selectedPackages,\n })\n\n outro(`Done! Next steps: ${colors.cyan(`cd ${name} && ${packageManager} dev`)}`)\n })\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\n\ninterface GeneratePluginOptions {\n name: string\n outDir: string\n}\n\n/**\n * Scaffold a `definePlugin()` factory under `src/plugins/<name>.plugin.ts`.\n *\n * v4 standardised on the `definePlugin()` factory pattern (architecture\n * §21.2.2) — same surface as `defineAdapter()`, so adopters learn one\n * mental model. The generated template uses the factory shape with a\n * typed config object, defaults block, and a build function returning\n * the underlying KickPlugin hooks.\n */\nexport async function generatePlugin(options: GeneratePluginOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.plugin.ts`)\n await writeFileSafe(\n filePath,\n `import {\n definePlugin,\n type AppAdapter,\n type AppModuleClass,\n type Container,\n type ContributorRegistrations,\n} from '@forinda/kickjs'\n\n/**\n * Configuration for the ${pascal} plugin.\n *\n * Plugins typically take a small config object so callers can tune\n * behaviour at bootstrap time. Keep the shape narrow — anything\n * derived from the environment should be read inside the build\n * function via getEnv(), not forced onto the caller.\n */\nexport interface ${pascal}PluginConfig {\n // Add your plugin config here, e.g.:\n // enabled?: boolean\n // apiKey?: string\n}\n\n/**\n * ${pascal} plugin — built via \\`definePlugin()\\` so callers get the\n * factory's call / \\`.scoped()\\` / \\`.async()\\` surfaces for free.\n *\n * A plugin bundles DI bindings, modules, adapters, and middleware\n * into one object that can be added to \\`bootstrap({ plugins })\\`.\n *\n * Lifecycle order (each hook is optional — delete the ones you don't\n * need and keep only the surface your plugin actually uses):\n *\n * 1. \\`register(container)\\` — runs before user modules load. Use\n * it to bind services that modules depend on.\n * 2. \\`modules()\\` — plugin modules load before user modules.\n * 3. \\`adapters()\\` — plugin adapters mount before user adapters.\n * 4. \\`middleware()\\` — plugin middleware runs before user middleware.\n * 5. \\`contributors()\\` — Context Contributors merged into every route.\n * 6. \\`onReady(container)\\` — runs after the app has fully bootstrapped.\n * 7. \\`shutdown()\\` — runs on graceful shutdown.\n *\n * @example\n * \\`\\`\\`ts\n * import { bootstrap } from '@forinda/kickjs'\n * import { ${pascal}Plugin } from './plugins/${kebab}.plugin'\n *\n * export const app = await bootstrap({\n * modules,\n * plugins: [${pascal}Plugin({ /* config overrides *\\\\/ })],\n * })\n * \\`\\`\\`\n */\nexport const ${pascal}Plugin = definePlugin<${pascal}PluginConfig>({\n name: '${pascal}Plugin',\n defaults: {\n // Default config values go here\n },\n build: (_config, { name: _name }) => ({\n /**\n * Register DI bindings before modules load.\n * Use \\`container.registerInstance(TOKEN, value)\\` for singletons\n * and \\`container.registerFactory(TOKEN, () => ...)\\` for lazy\n * constructions.\n */\n register(_container: Container): void {\n // Example: _container.registerInstance(MY_TOKEN, new MyService(_config))\n },\n\n /**\n * Return module classes this plugin contributes to the app.\n * These load before user modules, so plugin controllers and\n * services are available for user code to \\`@Autowired\\`.\n */\n modules(): AppModuleClass[] {\n return [\n // ExampleModule,\n ]\n },\n\n /**\n * Return adapter instances to be added to the application.\n * Plugin adapters mount before user adapters.\n */\n adapters(): AppAdapter[] {\n return [\n // MyAdapter({ ... }),\n ]\n },\n\n /**\n * Return Express middleware entries to be added to the global\n * pipeline. Plugin middleware runs before user-defined middleware.\n */\n middleware(): unknown[] {\n return [\n // helmet(),\n // myCustomMiddleware(_config),\n ]\n },\n\n /**\n * Return Context Contributors to merge into every route's pipeline.\n * Plugins contribute at the same \\`'adapter'\\` precedence level as\n * adapters — overrideable per-route at the method / class / module\n * level. See https://forinda.github.io/kick-js/guide/context-decorators\n *\n * Delete this hook if your plugin doesn't ship typed per-request values.\n */\n contributors(): ContributorRegistrations {\n return [\n // Example:\n // import { defineHttpContextDecorator } from '@forinda/kickjs'\n // declare module '@forinda/kickjs' { interface ContextMeta { ${kebab}: { foo: string } } }\n // const Load${pascal} = defineHttpContextDecorator({\n // key: '${kebab}',\n // resolve: (ctx) => ({ foo: ctx.req.headers['x-${kebab}'] as string }),\n // })\n // return [Load${pascal}.registration]\n ]\n },\n\n /**\n * Called after the application has fully bootstrapped. Use this\n * for post-startup work like logging, health checks, or warming\n * a cache. Runs once per process.\n */\n async onReady(_container: Container): Promise<void> {\n // const log = _container.resolve(Logger)\n // log.info('${pascal} plugin ready')\n },\n\n /**\n * Called during graceful shutdown. Clean up any long-lived\n * resources this plugin owns (connections, timers, subscriptions).\n */\n async shutdown(): Promise<void> {\n // Example: await this.connection?.close()\n },\n }),\n})\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { existsSync } from 'node:fs'\nimport { writeFileSafe } from '../utils/fs'\nimport { confirm } from '../utils/prompts'\n\ninterface GenerateConfigOptions {\n outDir: string\n modulesDir?: string\n defaultRepo?: string\n force?: boolean\n}\n\nexport async function generateConfig(options: GenerateConfigOptions): Promise<string[]> {\n const filePath = join(options.outDir, 'kick.config.ts')\n const modulesDir = options.modulesDir ?? 'src/modules'\n const defaultRepo = options.defaultRepo ?? 'inmemory'\n\n if (existsSync(filePath) && !options.force) {\n const overwrite = await confirm({\n message: 'kick.config.ts already exists. Overwrite?',\n initialValue: false,\n })\n if (!overwrite) {\n console.log('\\n Skipped — existing kick.config.ts preserved.')\n return []\n }\n }\n\n await writeFileSafe(\n filePath,\n `import { defineConfig } from '@forinda/kickjs-cli'\n\nexport default defineConfig({\n modules: {\n dir: '${modulesDir}',\n repo: '${defaultRepo}',\n pluralize: true,\n },\n\n typegen: {\n schemaValidator: 'zod',\n },\n\n commands: [\n {\n name: 'test',\n description: 'Run tests with Vitest',\n steps: 'npx vitest run',\n },\n {\n name: 'format',\n description: 'Format code with Prettier',\n steps: 'npx prettier --write src/',\n },\n {\n name: 'format:check',\n description: 'Check formatting without writing',\n steps: 'npx prettier --check src/',\n },\n {\n name: 'ci:check',\n description: 'Run typecheck + format check',\n steps: ['npx tsc --noEmit', 'npx prettier --check src/'],\n aliases: ['verify'],\n },\n ],\n})\n`,\n )\n\n return [filePath]\n}\n","import { join } from 'node:path'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { writeFileSafe } from '../utils/fs'\nimport { confirm } from '../utils/prompts'\nimport { generateClaude, generateAgents, generateKickJsSkills } from './templates/project-docs'\nimport { loadKickConfig } from '../config'\n\ntype ProjectTemplate = 'rest' | 'ddd' | 'cqrs' | 'minimal'\n\nexport interface GenerateAgentDocsOptions {\n outDir: string\n /** Override project name (defaults to package.json `name`). */\n name?: string\n /** Override package manager (defaults to package.json `packageManager` field, then 'pnpm'). */\n pm?: string\n /** Override template (defaults to kick.config.ts `pattern`, then 'ddd'). */\n template?: ProjectTemplate\n /**\n * Which file(s) to (re)generate.\n * - `agents` → AGENTS.md only\n * - `claude` → CLAUDE.md only\n * - `skills` → kickjs-skills.md only\n * - `both` → AGENTS.md + CLAUDE.md (legacy default)\n * - `all` → AGENTS.md + CLAUDE.md + kickjs-skills.md\n */\n only?: 'agents' | 'claude' | 'skills' | 'both' | 'all'\n /** Skip the overwrite prompt. */\n force?: boolean\n}\n\nconst VALID_TEMPLATES = new Set<ProjectTemplate>(['rest', 'ddd', 'cqrs', 'minimal'])\n\nfunction detectName(outDir: string, override?: string): string {\n if (override) return override\n try {\n const pkg = JSON.parse(readFileSync(join(outDir, 'package.json'), 'utf-8')) as { name?: string }\n if (pkg.name) return pkg.name.replace(/^@[^/]+\\//, '')\n } catch {\n // No package.json — fall back to folder name\n }\n return outDir.split('/').findLast(Boolean) ?? 'app'\n}\n\nfunction detectPm(outDir: string, override?: string): string {\n if (override) return override\n try {\n const pkg = JSON.parse(readFileSync(join(outDir, 'package.json'), 'utf-8')) as {\n packageManager?: string\n }\n if (pkg.packageManager) return pkg.packageManager.split('@')[0]\n } catch {\n // ignore\n }\n return 'pnpm'\n}\n\nasync function detectTemplate(\n outDir: string,\n override?: ProjectTemplate,\n): Promise<ProjectTemplate> {\n if (override) return override\n try {\n const cfg = await loadKickConfig(outDir)\n const pattern = cfg?.pattern as ProjectTemplate | undefined\n if (pattern && VALID_TEMPLATES.has(pattern)) return pattern\n } catch {\n // ignore\n }\n return 'ddd'\n}\n\nexport async function generateAgentDocs(options: GenerateAgentDocsOptions): Promise<string[]> {\n const only = options.only ?? 'all'\n const name = detectName(options.outDir, options.name)\n const pm = detectPm(options.outDir, options.pm)\n const template = await detectTemplate(options.outDir, options.template)\n\n const wantsAgents = only === 'agents' || only === 'both' || only === 'all'\n const wantsClaude = only === 'claude' || only === 'both' || only === 'all'\n const wantsSkills = only === 'skills' || only === 'all'\n\n const targets: { file: string; render: () => string }[] = []\n if (wantsAgents) {\n targets.push({\n file: join(options.outDir, 'AGENTS.md'),\n render: () => generateAgents(name, template, pm),\n })\n }\n if (wantsClaude) {\n targets.push({\n file: join(options.outDir, 'CLAUDE.md'),\n render: () => generateClaude(name, template, pm),\n })\n }\n if (wantsSkills) {\n targets.push({\n file: join(options.outDir, 'kickjs-skills.md'),\n render: () => generateKickJsSkills(name, template, pm),\n })\n }\n\n const written: string[] = []\n for (const { file, render } of targets) {\n if (existsSync(file) && !options.force) {\n const overwrite = await confirm({\n message: `${file.replace(options.outDir + '/', '')} already exists. Overwrite?`,\n initialValue: false,\n })\n if (!overwrite) {\n console.log(` Skipped — existing ${file.replace(options.outDir + '/', '')} preserved.`)\n continue\n }\n }\n await writeFileSafe(file, render())\n written.push(file)\n }\n return written\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\n\nexport interface AuthScaffoldOptions {\n /** Auth strategy: 'jwt' | 'session'. Default: 'jwt' */\n strategy?: 'jwt' | 'session'\n /** Output directory. Default: 'src/modules/auth' */\n outDir?: string\n /** Generate role-based guards. Default: true */\n roleGuards?: boolean\n}\n\n/**\n * Generate a complete auth module with registration, login, logout,\n * and password hashing. Uses PasswordService and the configured strategy.\n */\nexport async function generateAuthScaffold(options: AuthScaffoldOptions = {}): Promise<string[]> {\n const strategy = options.strategy ?? 'jwt'\n const outDir = options.outDir ?? 'src/modules/auth'\n const dtoDir = join(outDir, 'dto')\n const files: string[] = []\n\n // ── auth.module.ts ──────────────────────────────────────────────────\n const modulePath = join(outDir, 'auth.module.ts')\n await writeFileSafe(\n modulePath,\n `import { Module } from '@forinda/kickjs'\nimport { AuthController } from './auth.controller'\nimport { AuthService } from './auth.service'\n\n@Module({\n controllers: [AuthController],\n services: [AuthService],\n})\nexport class AuthModule {}\n`,\n )\n files.push(modulePath)\n\n // ── auth.controller.ts ──────────────────────────────────────────────\n const controllerPath = join(outDir, 'auth.controller.ts')\n const controllerContent =\n strategy === 'jwt' ? jwtControllerTemplate() : sessionControllerTemplate()\n await writeFileSafe(controllerPath, controllerContent)\n files.push(controllerPath)\n\n // ── auth.service.ts ─────────────────────────────────────────────────\n const servicePath = join(outDir, 'auth.service.ts')\n const serviceContent = strategy === 'jwt' ? jwtServiceTemplate() : sessionServiceTemplate()\n await writeFileSafe(servicePath, serviceContent)\n files.push(servicePath)\n\n // ── dto/register.dto.ts ─────────────────────────────────────────────\n const registerDtoPath = join(dtoDir, 'register.dto.ts')\n await writeFileSafe(\n registerDtoPath,\n `import { z } from 'zod'\n\nexport const RegisterDto = z.object({\n email: z.string().email(),\n password: z.string().min(8),\n name: z.string().min(1).optional(),\n})\n\nexport type RegisterInput = z.infer<typeof RegisterDto>\n`,\n )\n files.push(registerDtoPath)\n\n // ── dto/login.dto.ts ────────────────────────────────────────────────\n const loginDtoPath = join(dtoDir, 'login.dto.ts')\n await writeFileSafe(\n loginDtoPath,\n `import { z } from 'zod'\n\nexport const LoginDto = z.object({\n email: z.string().email(),\n password: z.string().min(1),\n})\n\nexport type LoginInput = z.infer<typeof LoginDto>\n`,\n )\n files.push(loginDtoPath)\n\n // ── auth.test.ts ────────────────────────────────────────────────────\n const testPath = join(outDir, 'auth.test.ts')\n await writeFileSafe(\n testPath,\n `import { describe, it, expect } from 'vitest'\n\ndescribe('Auth Module', () => {\n it.todo('POST /register — creates a new user')\n it.todo('POST /login — returns token for valid credentials')\n it.todo('POST /login — rejects invalid credentials')\n it.todo('POST /logout — invalidates session/token')\n it.todo('GET /me — returns authenticated user')\n})\n`,\n )\n files.push(testPath)\n\n // ── auth.guard.ts (optional) ────────────────────────────────────────\n if (options.roleGuards !== false) {\n const guardPath = join(outDir, 'auth.guard.ts')\n await writeFileSafe(\n guardPath,\n `import { Roles } from '@forinda/kickjs-auth'\n\n/**\n * Role-based access guard.\n * Usage: @Roles('admin') on a controller method.\n *\n * The AuthAdapter extracts the user's roles from the JWT/session\n * and the framework checks them automatically.\n */\nexport const AdminOnly = Roles('admin')\nexport const ManagerOnly = Roles('manager')\n`,\n )\n files.push(guardPath)\n }\n\n return files\n}\n\n// ── JWT Templates ───────────────────────────────────────────────────────\n\nfunction jwtControllerTemplate(): string {\n return `import { Controller, Post, Get } from '@forinda/kickjs'\nimport { Authenticated, Public } from '@forinda/kickjs-auth'\nimport type { RequestContext } from '@forinda/kickjs'\nimport { Autowired } from '@forinda/kickjs'\nimport { AuthService } from './auth.service'\n\n@Controller()\n@Authenticated()\nexport class AuthController {\n @Autowired() private authService!: AuthService\n\n @Post('/register')\n @Public()\n async register(ctx: RequestContext) {\n const result = await this.authService.register(ctx.body)\n return ctx.created(result)\n }\n\n @Post('/login')\n @Public()\n async login(ctx: RequestContext) {\n const result = await this.authService.login(ctx.body)\n if (!result) return ctx.badRequest('Invalid credentials')\n return ctx.json(result)\n }\n\n @Post('/logout')\n async logout(ctx: RequestContext) {\n return ctx.json({ message: 'Logged out' })\n }\n\n @Get('/me')\n async me(ctx: RequestContext) {\n return ctx.json({ user: ctx.user })\n }\n}\n`\n}\n\nfunction jwtServiceTemplate(): string {\n return `import { Service, Autowired } from '@forinda/kickjs'\nimport { PasswordService } from '@forinda/kickjs-auth'\nimport type { RegisterInput } from './dto/register.dto'\nimport type { LoginInput } from './dto/login.dto'\n\n// TODO: Replace with your User repository\nconst users = new Map<string, { id: string; email: string; name?: string; passwordHash: string }>()\n\n@Service()\nexport class AuthService {\n @Autowired() private password!: PasswordService\n\n async register(input: RegisterInput) {\n const { email, password, name } = input\n\n if (users.has(email)) {\n throw new Error('User already exists')\n }\n\n const passwordHash = await this.password.hash(password)\n const id = crypto.randomUUID()\n users.set(email, { id, email, name, passwordHash })\n\n return { id, email, name }\n }\n\n async login(input: LoginInput) {\n const { email, password } = input\n const user = users.get(email)\n if (!user) return null\n\n const valid = await this.password.verify(user.passwordHash, password)\n if (!valid) return null\n\n // TODO: Generate JWT token here\n // const token = jwt.sign({ sub: user.id, email: user.email }, process.env.JWT_SECRET!)\n return { user: { id: user.id, email: user.email, name: user.name } }\n }\n}\n`\n}\n\n// ── Session Templates ───────────────────────────────────────────────────\n\nfunction sessionControllerTemplate(): string {\n return `import { Controller, Post, Get } from '@forinda/kickjs'\nimport { Authenticated, Public } from '@forinda/kickjs-auth'\nimport { sessionLogin, sessionLogout } from '@forinda/kickjs-auth'\nimport type { RequestContext } from '@forinda/kickjs'\nimport { Autowired } from '@forinda/kickjs'\nimport { AuthService } from './auth.service'\n\n@Controller()\n@Authenticated()\nexport class AuthController {\n @Autowired() private authService!: AuthService\n\n @Post('/register')\n @Public()\n async register(ctx: RequestContext) {\n const result = await this.authService.register(ctx.body)\n return ctx.created(result)\n }\n\n @Post('/login')\n @Public()\n async login(ctx: RequestContext) {\n const user = await this.authService.login(ctx.body)\n if (!user) return ctx.badRequest('Invalid credentials')\n await sessionLogin(ctx.session, user)\n return ctx.json({ message: 'Logged in', user })\n }\n\n @Post('/logout')\n async logout(ctx: RequestContext) {\n await sessionLogout(ctx.session)\n return ctx.json({ message: 'Logged out' })\n }\n\n @Get('/me')\n async me(ctx: RequestContext) {\n return ctx.json({ user: ctx.user })\n }\n}\n`\n}\n\nfunction sessionServiceTemplate(): string {\n return `import { Service, Autowired } from '@forinda/kickjs'\nimport { PasswordService } from '@forinda/kickjs-auth'\nimport type { RegisterInput } from './dto/register.dto'\nimport type { LoginInput } from './dto/login.dto'\n\n// TODO: Replace with your User repository\nconst users = new Map<string, { id: string; email: string; name?: string; passwordHash: string }>()\n\n@Service()\nexport class AuthService {\n @Autowired() private password!: PasswordService\n\n async register(input: RegisterInput) {\n const { email, password, name } = input\n\n if (users.has(email)) {\n throw new Error('User already exists')\n }\n\n const passwordHash = await this.password.hash(password)\n const id = crypto.randomUUID()\n users.set(email, { id, email, name, passwordHash })\n\n return { id, email, name }\n }\n\n async login(input: LoginInput) {\n const { email, password } = input\n const user = users.get(email)\n if (!user) return null\n\n const valid = await this.password.verify(user.passwordHash, password)\n if (!valid) return null\n\n return { id: user.id, email: user.email, name: user.name }\n }\n}\n`\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\n\ninterface GenerateJobOptions {\n name: string\n outDir: string\n queue?: string\n}\n\nexport async function generateJob(options: GenerateJobOptions): Promise<string[]> {\n const { name, outDir } = options\n const pascal = toPascalCase(name)\n const kebab = toKebabCase(name)\n const camel = toCamelCase(name)\n const queueName = options.queue ?? `${kebab}-queue`\n const files: string[] = []\n\n const write = async (relativePath: string, content: string) => {\n const fullPath = join(outDir, relativePath)\n await writeFileSafe(fullPath, content)\n files.push(fullPath)\n }\n\n await write(\n `${kebab}.job.ts`,\n `import { Inject } from '@forinda/kickjs'\nimport { Job, Process, QUEUE_MANAGER, type QueueService } from '@forinda/kickjs-queue'\n\n/**\n * ${pascal} Job Processor\n *\n * Decorators:\n * @Job(queueName) — marks this class as a job processor for a queue\n * @Process(jobName?) — marks a method as the handler for a specific job type\n * - Without a name: handles all jobs in the queue\n * - With a name: handles only jobs matching that name\n *\n * To add jobs to this queue from a service or controller:\n * @Inject(QUEUE_MANAGER) private queue: QueueService\n * await this.queue.add('${queueName}', '${camel}', { ... })\n */\n@Job('${queueName}')\nexport class ${pascal}Job {\n @Process()\n async handle(job: { name: string; data: any; id?: string }) {\n console.log(\\`Processing \\${job.name} (id: \\${job.id})\\`, job.data)\n\n // TODO: Implement job logic here\n // Example:\n // await this.emailService.send(job.data.to, job.data.subject, job.data.body)\n }\n\n @Process('${camel}.priority')\n async handlePriority(job: { name: string; data: any; id?: string }) {\n console.log(\\`Priority job: \\${job.name}\\`, job.data)\n // Handle high-priority variant of this job\n }\n}\n`,\n )\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe, fileExists } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase, pluralize, pluralizePascal } from '../utils/naming'\nimport { readFile, writeFile } from 'node:fs/promises'\n\n// ── Field Parsing ───────────────────────────────────────────────────────\n\nexport interface FieldDef {\n name: string\n type: string\n tsType: string\n zodType: string\n optional: boolean\n}\n\n/**\n * Supported field types and their mappings:\n * string → z.string()\n * text → z.string() (alias, hints at longer content)\n * number → z.number()\n * int → z.number().int()\n * float → z.number()\n * boolean → z.boolean()\n * date → z.string().datetime()\n * email → z.string().email()\n * url → z.string().url()\n * uuid → z.string().uuid()\n * json → z.any()\n * enum:a,b → z.enum(['a','b'])\n *\n * Mark optional fields — three equivalent syntaxes:\n * body:text:optional ← recommended (shell-safe, no quoting needed)\n * body?:text ← needs quoting in bash/zsh (\"body?:text\")\n * body:text? ← needs quoting in bash/zsh (\"body:text?\")\n */\nconst TYPE_MAP: Record<string, { ts: string; zod: string }> = {\n string: { ts: 'string', zod: 'z.string()' },\n text: { ts: 'string', zod: 'z.string()' },\n number: { ts: 'number', zod: 'z.number()' },\n int: { ts: 'number', zod: 'z.number().int()' },\n float: { ts: 'number', zod: 'z.number()' },\n boolean: { ts: 'boolean', zod: 'z.boolean()' },\n date: { ts: 'string', zod: 'z.string().datetime()' },\n email: { ts: 'string', zod: 'z.string().email()' },\n url: { ts: 'string', zod: 'z.string().url()' },\n uuid: { ts: 'string', zod: 'z.string().uuid()' },\n json: { ts: 'any', zod: 'z.any()' },\n}\n\nexport function parseFields(raw: string[]): FieldDef[] {\n return raw.map((f) => {\n const colonIdx = f.indexOf(':')\n if (colonIdx === -1) {\n throw new Error(`Invalid field: \"${f}\". Use format: name:type (e.g. title:string)`)\n }\n let namePart = f.slice(0, colonIdx)\n let typePart = f.slice(colonIdx + 1)\n if (!namePart || !typePart) {\n throw new Error(`Invalid field: \"${f}\". Use format: name:type (e.g. title:string)`)\n }\n\n // Support three optional syntaxes:\n // name:type:optional ← shell-safe (recommended)\n // name?:type ← needs quoting in bash/zsh\n // name:type? ← needs quoting in bash/zsh\n let optional = false\n\n // Check for trailing :optional segment (but not enum:val1,val2)\n if (typePart.endsWith(':optional')) {\n typePart = typePart.slice(0, -':optional'.length)\n optional = true\n }\n\n // Check for ? on the name side: \"body?:text\"\n if (namePart.endsWith('?')) {\n namePart = namePart.slice(0, -1)\n optional = true\n }\n\n // Check for ? on the type side: \"body:text?\"\n if (typePart.endsWith('?')) {\n typePart = typePart.slice(0, -1)\n optional = true\n }\n\n const cleanType = typePart\n\n // Handle enum:val1,val2\n if (cleanType.startsWith('enum:')) {\n const values = cleanType.slice(5).split(',')\n return {\n name: namePart,\n type: 'enum',\n tsType: values.map((v) => `'${v}'`).join(' | '),\n zodType: `z.enum([${values.map((v) => `'${v}'`).join(', ')}])`,\n optional,\n }\n }\n\n const mapped = TYPE_MAP[cleanType]\n if (!mapped) {\n const validTypes = [...Object.keys(TYPE_MAP), 'enum:a,b,c'].join(', ')\n throw new Error(`Unknown field type: \"${cleanType}\". Valid types: ${validTypes}`)\n }\n\n return {\n name: namePart,\n type: cleanType,\n tsType: mapped.ts,\n zodType: mapped.zod,\n optional,\n }\n })\n}\n\n// ── Scaffold Generator ──────────────────────────────────────────────────\n\ninterface ScaffoldOptions {\n name: string\n fields: FieldDef[]\n modulesDir: string\n noEntity?: boolean\n noTests?: boolean\n repo?: 'inmemory'\n pluralize?: boolean\n /**\n * DI-token scope prefix (e.g. `'mycorp'`, `'app'`). Substituted into\n * every emitted `createToken<T>('<scope>/<area>/<key>')`. Caller\n * resolves this from `kick.config.ts > tokenScope` or package.json\n * before invoking the scaffold; defaults to `'app'` here so existing\n * callers stay backward-compatible.\n */\n tokenScope?: string\n}\n\nexport async function generateScaffold(options: ScaffoldOptions): Promise<string[]> {\n const {\n name,\n fields,\n modulesDir,\n noEntity,\n noTests: _noTests,\n repo = 'inmemory',\n tokenScope = 'app',\n } = options\n const shouldPluralize = options.pluralize !== false\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const _camel = toCamelCase(name)\n const plural = shouldPluralize ? pluralize(kebab) : kebab\n const pluralPascal = shouldPluralize ? pluralizePascal(pascal) : pascal\n const moduleDir = join(modulesDir, plural)\n\n const files: string[] = []\n\n const write = async (relativePath: string, content: string) => {\n const fullPath = join(moduleDir, relativePath)\n await writeFileSafe(fullPath, content)\n files.push(fullPath)\n }\n\n // ── Module file (named `<kebab>.module.ts` so Vite's module-discovery plugin picks it up)\n await write(`${kebab}.module.ts`, genModuleIndex(pascal, kebab, plural))\n\n // ── Constants\n await write('constants.ts', genConstants(pascal, fields))\n\n // ── Controller\n await write(\n `presentation/${kebab}.controller.ts`,\n genController(pascal, kebab, plural, pluralPascal),\n )\n\n // ── DTOs\n await write(`application/dtos/create-${kebab}.dto.ts`, genCreateDTO(pascal, fields))\n await write(`application/dtos/update-${kebab}.dto.ts`, genUpdateDTO(pascal, fields))\n await write(`application/dtos/${kebab}-response.dto.ts`, genResponseDTO(pascal, fields))\n\n // ── Use Cases\n const useCases = genUseCases(pascal, kebab, plural, pluralPascal)\n for (const uc of useCases) {\n await write(`application/use-cases/${uc.file}`, uc.content)\n }\n\n // ── Domain: Repository Interface\n await write(\n `domain/repositories/${kebab}.repository.ts`,\n genRepositoryInterface(pascal, kebab, tokenScope),\n )\n\n // ── Domain: Service\n await write(`domain/services/${kebab}-domain.service.ts`, genDomainService(pascal, kebab))\n\n // ── Infrastructure: Repository\n if (repo === 'inmemory') {\n await write(\n `infrastructure/repositories/in-memory-${kebab}.repository.ts`,\n genInMemoryRepository(pascal, kebab, fields),\n )\n }\n\n // ── Entity & Value Objects\n if (!noEntity) {\n await write(`domain/entities/${kebab}.entity.ts`, genEntity(pascal, kebab, fields))\n await write(`domain/value-objects/${kebab}-id.vo.ts`, genValueObject(pascal))\n }\n\n // ── Auto-register in modules index\n await autoRegisterModule(modulesDir, pascal, plural, kebab)\n\n return files\n}\n\n// ── Template Generators ─────────────────────────────────────────────────\n\nfunction genCreateDTO(pascal: string, fields: FieldDef[]): string {\n const zodFields = fields\n .map((f) => {\n const base = f.zodType\n return ` ${f.name}: ${base}${f.optional ? '.optional()' : ''},`\n })\n .join('\\n')\n\n return `import { z } from 'zod'\n\nexport const create${pascal}Schema = z.object({\n${zodFields}\n})\n\nexport type Create${pascal}DTO = z.infer<typeof create${pascal}Schema>\n`\n}\n\nfunction genUpdateDTO(pascal: string, fields: FieldDef[]): string {\n const zodFields = fields.map((f) => ` ${f.name}: ${f.zodType}.optional(),`).join('\\n')\n\n return `import { z } from 'zod'\n\nexport const update${pascal}Schema = z.object({\n${zodFields}\n})\n\nexport type Update${pascal}DTO = z.infer<typeof update${pascal}Schema>\n`\n}\n\nfunction genResponseDTO(pascal: string, fields: FieldDef[]): string {\n const tsFields = fields.map((f) => ` ${f.name}${f.optional ? '?' : ''}: ${f.tsType}`).join('\\n')\n\n return `export interface ${pascal}ResponseDTO {\n id: string\n${tsFields}\n createdAt: string\n updatedAt: string\n}\n`\n}\n\nfunction genConstants(pascal: string, fields: FieldDef[]): string {\n const stringFields = fields.filter((f) => f.tsType === 'string').map((f) => `'${f.name}'`)\n const _numberFields = fields.filter((f) => f.tsType === 'number').map((f) => `'${f.name}'`)\n const allFieldNames = fields.map((f) => `'${f.name}'`)\n\n const filterable = [...allFieldNames].join(', ')\n const sortable = [...allFieldNames, \"'createdAt'\", \"'updatedAt'\"].join(', ')\n const searchable = stringFields.length > 0 ? stringFields.join(', ') : \"'name'\"\n\n return `import type { ApiQueryParamsConfig } from '@forinda/kickjs'\n\nexport const ${pascal.toUpperCase()}_QUERY_CONFIG: ApiQueryParamsConfig = {\n filterable: [${filterable}],\n sortable: [${sortable}],\n searchable: [${searchable}],\n}\n`\n}\n\nfunction genInMemoryRepository(pascal: string, kebab: string, fields: FieldDef[]): string {\n const fieldAssignments = fields.map((f) => ` ${f.name}: dto.${f.name},`).join('\\n')\n const fieldSpread = '...dto'\n\n return `import { randomUUID } from 'node:crypto'\nimport { Repository, HttpException } from '@forinda/kickjs'\nimport type { ParsedQuery } from '@forinda/kickjs'\nimport type { I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '../../application/dtos/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '../../application/dtos/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '../../application/dtos/update-${kebab}.dto'\n\n@Repository()\nexport class InMemory${pascal}Repository implements I${pascal}Repository {\n private store = new Map<string, ${pascal}ResponseDTO>()\n\n async findById(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.store.get(id) ?? null\n }\n\n async findAll(): Promise<${pascal}ResponseDTO[]> {\n return Array.from(this.store.values())\n }\n\n async findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }> {\n const all = Array.from(this.store.values())\n const data = all.slice(parsed.pagination.offset, parsed.pagination.offset + parsed.pagination.limit)\n return { data, total: all.length }\n }\n\n async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const now = new Date().toISOString()\n const entity: ${pascal}ResponseDTO = {\n id: randomUUID(),\n${fieldAssignments}\n createdAt: now,\n updatedAt: now,\n }\n this.store.set(entity.id, entity)\n return entity\n }\n\n async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const existing = this.store.get(id)\n if (!existing) throw HttpException.notFound('${pascal} not found')\n const updated = { ...existing, ${fieldSpread}, updatedAt: new Date().toISOString() }\n this.store.set(id, updated)\n return updated\n }\n\n async delete(id: string): Promise<void> {\n if (!this.store.has(id)) throw HttpException.notFound('${pascal} not found')\n this.store.delete(id)\n }\n}\n`\n}\n\nfunction genEntity(pascal: string, kebab: string, fields: FieldDef[]): string {\n const propsInterface = fields\n .map((f) => ` ${f.name}${f.optional ? '?' : ''}: ${f.tsType}`)\n .join('\\n')\n const createParams = fields\n .filter((f) => !f.optional)\n .map((f) => `${f.name}: ${f.tsType}`)\n .join('; ')\n const createAssignments = fields\n .filter((f) => !f.optional)\n .map((f) => ` ${f.name}: params.${f.name},`)\n .join('\\n')\n const getters = fields\n .map(\n (f) => ` get ${f.name}(): ${f.tsType}${f.optional ? ' | undefined' : ''} {\n return this.props.${f.name}\n }`,\n )\n .join('\\n')\n const toJsonFields = fields.map((f) => ` ${f.name}: this.props.${f.name},`).join('\\n')\n\n return `import { ${pascal}Id } from '../value-objects/${kebab}-id.vo'\n\ninterface ${pascal}Props {\n id: ${pascal}Id\n${propsInterface}\n createdAt: Date\n updatedAt: Date\n}\n\nexport class ${pascal} {\n private constructor(private props: ${pascal}Props) {}\n\n static create(params: { ${createParams} }): ${pascal} {\n const now = new Date()\n return new ${pascal}({\n id: ${pascal}Id.create(),\n${createAssignments}\n createdAt: now,\n updatedAt: now,\n })\n }\n\n static reconstitute(props: ${pascal}Props): ${pascal} {\n return new ${pascal}(props)\n }\n\n get id(): ${pascal}Id { return this.props.id }\n${getters}\n get createdAt(): Date { return this.props.createdAt }\n get updatedAt(): Date { return this.props.updatedAt }\n\n toJSON() {\n return {\n id: this.props.id.toString(),\n${toJsonFields}\n createdAt: this.props.createdAt.toISOString(),\n updatedAt: this.props.updatedAt.toISOString(),\n }\n }\n}\n`\n}\n\nfunction genValueObject(pascal: string): string {\n return `import { randomUUID } from 'node:crypto'\n\nexport class ${pascal}Id {\n private constructor(private readonly value: string) {}\n\n static create(): ${pascal}Id { return new ${pascal}Id(randomUUID()) }\n\n static from(id: string): ${pascal}Id {\n if (!id || id.trim().length === 0) throw new Error('${pascal}Id cannot be empty')\n return new ${pascal}Id(id)\n }\n\n toString(): string { return this.value }\n equals(other: ${pascal}Id): boolean { return this.value === other.value }\n}\n`\n}\n\n// These reuse the same patterns as the existing module generator\n\nfunction genModuleIndex(pascal: string, kebab: string, plural: string): string {\n return `import { type AppModule, type ModuleRoutes, Container, buildRoutes } from '@forinda/kickjs'\nimport { ${pascal}Controller } from './presentation/${kebab}.controller'\nimport { ${pascal.toUpperCase()}_REPOSITORY } from './domain/repositories/${kebab}.repository'\nimport { InMemory${pascal}Repository } from './infrastructure/repositories/in-memory-${kebab}.repository'\n\n// Eagerly load decorated classes so @Service()/@Repository() decorators\n// register in the DI container before the application bootstraps.\nimport.meta.glob(\n ['./domain/services/**/*.ts', './application/use-cases/**/*.ts', '!./**/*.test.ts'],\n { eager: true },\n)\n\nexport class ${pascal}Module implements AppModule {\n /**\n * Bind the repository token to its concrete implementation.\n * Decorator-managed classes (@Service, @Controller, @Repository) are\n * registered automatically — only token-to-impl bindings need to live here.\n */\n register(container: Container): void {\n container.registerFactory(\n ${pascal.toUpperCase()}_REPOSITORY,\n () => container.resolve(InMemory${pascal}Repository),\n )\n }\n\n routes(): ModuleRoutes {\n return {\n path: '/${plural}',\n router: buildRoutes(${pascal}Controller),\n controller: ${pascal}Controller,\n }\n }\n}\n`\n}\n\nfunction genController(\n pascal: string,\n kebab: string,\n plural: string,\n pluralPascal: string,\n): string {\n return `import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'\nimport { ApiTags } from '@forinda/kickjs-swagger'\nimport { Create${pascal}UseCase } from '../application/use-cases/create-${kebab}.use-case'\nimport { Get${pascal}UseCase } from '../application/use-cases/get-${kebab}.use-case'\nimport { List${pluralPascal}UseCase } from '../application/use-cases/list-${plural}.use-case'\nimport { Update${pascal}UseCase } from '../application/use-cases/update-${kebab}.use-case'\nimport { Delete${pascal}UseCase } from '../application/use-cases/delete-${kebab}.use-case'\nimport { create${pascal}Schema } from '../application/dtos/create-${kebab}.dto'\nimport { update${pascal}Schema } from '../application/dtos/update-${kebab}.dto'\nimport { ${pascal.toUpperCase()}_QUERY_CONFIG } from '../constants'\n\n// Each handler annotates its \\`ctx\\` with \\`Ctx<KickRoutes.${pascal}Controller['<method>']>\\`\n// so \\`ctx.params\\`, \\`ctx.body\\`, and \\`ctx.query\\` are typed end-to-end.\n// The \\`KickRoutes\\` namespace is generated by \\`kick typegen\\` (auto-run on\n// \\`kick dev\\`) — see https://forinda.github.io/kick-js/guide/typegen.\n\n@Controller()\nexport class ${pascal}Controller {\n @Autowired() private readonly create${pascal}UseCase!: Create${pascal}UseCase\n @Autowired() private readonly get${pascal}UseCase!: Get${pascal}UseCase\n @Autowired() private readonly list${pluralPascal}UseCase!: List${pluralPascal}UseCase\n @Autowired() private readonly update${pascal}UseCase!: Update${pascal}UseCase\n @Autowired() private readonly delete${pascal}UseCase!: Delete${pascal}UseCase\n\n @Get('/')\n @ApiTags('${pascal}')\n @ApiQueryParams(${pascal.toUpperCase()}_QUERY_CONFIG)\n async list(ctx: Ctx<KickRoutes.${pascal}Controller['list']>) {\n return ctx.paginate(\n (parsed) => this.list${pluralPascal}UseCase.execute(parsed),\n ${pascal.toUpperCase()}_QUERY_CONFIG,\n )\n }\n\n @Get('/:id')\n @ApiTags('${pascal}')\n async getById(ctx: Ctx<KickRoutes.${pascal}Controller['getById']>) {\n const result = await this.get${pascal}UseCase.execute(ctx.params.id)\n if (!result) return ctx.notFound('${pascal} not found')\n ctx.json(result)\n }\n\n @Post('/', { body: create${pascal}Schema, name: 'Create${pascal}' })\n @ApiTags('${pascal}')\n async create(ctx: Ctx<KickRoutes.${pascal}Controller['create']>) {\n const result = await this.create${pascal}UseCase.execute(ctx.body)\n ctx.created(result)\n }\n\n @Put('/:id', { body: update${pascal}Schema, name: 'Update${pascal}' })\n @ApiTags('${pascal}')\n async update(ctx: Ctx<KickRoutes.${pascal}Controller['update']>) {\n const result = await this.update${pascal}UseCase.execute(ctx.params.id, ctx.body)\n ctx.json(result)\n }\n\n @Delete('/:id')\n @ApiTags('${pascal}')\n async remove(ctx: Ctx<KickRoutes.${pascal}Controller['remove']>) {\n await this.delete${pascal}UseCase.execute(ctx.params.id)\n ctx.noContent()\n }\n}\n`\n}\n\nfunction genRepositoryInterface(pascal: string, kebab: string, tokenScope: string): string {\n return `import { createToken } from '@forinda/kickjs'\nimport type { ${pascal}ResponseDTO } from '../../application/dtos/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '../../application/dtos/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '../../application/dtos/update-${kebab}.dto'\nimport type { ParsedQuery } from '@forinda/kickjs'\n\nexport interface I${pascal}Repository {\n findById(id: string): Promise<${pascal}ResponseDTO | null>\n findAll(): Promise<${pascal}ResponseDTO[]>\n findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }>\n create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO>\n update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO>\n delete(id: string): Promise<void>\n}\n\n/**\n * Collision-safe DI token bound to \\`I${pascal}Repository\\`.\n * \\`container.resolve(${pascal.toUpperCase()}_REPOSITORY)\\` and\n * \\`@Inject(${pascal.toUpperCase()}_REPOSITORY)\\` both return the typed\n * interface — no manual generic, no \\`any\\` cast.\n *\n * The \\`'${tokenScope}/'\\` prefix matches the project scope so\n * \\`kick-lint\\`'s \\`token-reserved-prefix\\` rule never fires —\n * adopters must NOT use the reserved \\`'kick/'\\` namespace.\n */\nexport const ${pascal.toUpperCase()}_REPOSITORY = createToken<I${pascal}Repository>('${tokenScope}/${pascal}/repository')\n`\n}\n\nfunction genDomainService(pascal: string, kebab: string): string {\n return `import { Service, Inject, HttpException } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../repositories/${kebab}.repository'\n\n@Service()\nexport class ${pascal}DomainService {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async ensureExists(id: string): Promise<void> {\n const entity = await this.repo.findById(id)\n if (!entity) throw HttpException.notFound('${pascal} not found')\n }\n}\n`\n}\n\nfunction genUseCases(\n pascal: string,\n kebab: string,\n plural: string,\n pluralPascal: string,\n): Array<{ file: string; content: string }> {\n return [\n {\n file: `create-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { Create${pascal}DTO } from '../dtos/create-${kebab}.dto'\n\n@Service()\nexport class Create${pascal}UseCase {\n constructor(@Inject(${pascal.toUpperCase()}_REPOSITORY) private repo: I${pascal}Repository) {}\n async execute(dto: Create${pascal}DTO) { return this.repo.create(dto) }\n}\n`,\n },\n {\n file: `get-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\n\n@Service()\nexport class Get${pascal}UseCase {\n constructor(@Inject(${pascal.toUpperCase()}_REPOSITORY) private repo: I${pascal}Repository) {}\n async execute(id: string) { return this.repo.findById(id) }\n}\n`,\n },\n {\n file: `list-${plural}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs'\nimport type { ParsedQuery } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\n\n@Service()\nexport class List${pluralPascal}UseCase {\n constructor(@Inject(${pascal.toUpperCase()}_REPOSITORY) private repo: I${pascal}Repository) {}\n async execute(parsed: ParsedQuery) { return this.repo.findPaginated(parsed) }\n}\n`,\n },\n {\n file: `update-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { Update${pascal}DTO } from '../dtos/update-${kebab}.dto'\n\n@Service()\nexport class Update${pascal}UseCase {\n constructor(@Inject(${pascal.toUpperCase()}_REPOSITORY) private repo: I${pascal}Repository) {}\n async execute(id: string, dto: Update${pascal}DTO) { return this.repo.update(id, dto) }\n}\n`,\n },\n {\n file: `delete-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\n\n@Service()\nexport class Delete${pascal}UseCase {\n constructor(@Inject(${pascal.toUpperCase()}_REPOSITORY) private repo: I${pascal}Repository) {}\n async execute(id: string) { return this.repo.delete(id) }\n}\n`,\n },\n ]\n}\n\n// ── Auto-register ───────────────────────────────────────────────────────\n\nasync function autoRegisterModule(\n modulesDir: string,\n pascal: string,\n plural: string,\n kebab: string,\n): Promise<void> {\n const indexPath = join(modulesDir, 'index.ts')\n const exists = await fileExists(indexPath)\n const importPath = `./${plural}/${kebab}.module`\n\n if (!exists) {\n await writeFileSafe(\n indexPath,\n `import type { AppModuleClass } from '@forinda/kickjs'\\nimport { ${pascal}Module } from '${importPath}'\\n\\nexport const modules: AppModuleClass[] = [${pascal}Module]\\n`,\n )\n return\n }\n\n let content = await readFile(indexPath, 'utf-8')\n const importLine = `import { ${pascal}Module } from '${importPath}'`\n\n if (!content.includes(`${pascal}Module`)) {\n const lastImportIdx = content.lastIndexOf('import ')\n if (lastImportIdx !== -1) {\n const lineEnd = content.indexOf('\\n', lastImportIdx)\n content = content.slice(0, lineEnd + 1) + importLine + '\\n' + content.slice(lineEnd + 1)\n } else {\n content = importLine + '\\n' + content\n }\n\n content = content.replace(/(=\\s*\\[)([\\s\\S]*?)(])/, (_match, open, existing, close) => {\n const trimmed = existing.trim()\n if (!trimmed) return `${open}${pascal}Module${close}`\n const needsComma = trimmed.endsWith(',') ? '' : ','\n return `${open}${existing.trimEnd()}${needsComma} ${pascal}Module${close}`\n })\n }\n\n await writeFile(indexPath, content, 'utf-8')\n}\n","import { join, resolve } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, pluralize } from '../utils/naming'\n\ninterface GenerateTestOptions {\n name: string\n outDir?: string\n moduleName?: string\n modulesDir?: string\n pluralize?: boolean\n}\n\nexport async function generateTest(options: GenerateTestOptions): Promise<string[]> {\n const { name, moduleName, modulesDir } = options\n const shouldPluralize = options.pluralize ?? true\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n // Resolve output directory\n let outDir: string\n if (options.outDir) {\n outDir = resolve(options.outDir)\n } else if (moduleName) {\n const modKebab = toKebabCase(moduleName)\n const modFolder = shouldPluralize ? pluralize(modKebab) : modKebab\n const modDir = modulesDir ?? 'src/modules'\n outDir = resolve(join(modDir, modFolder, '__tests__'))\n } else {\n outDir = resolve('src/__tests__')\n }\n\n const filePath = join(outDir, `${kebab}.test.ts`)\n await writeFileSafe(\n filePath,\n `import { describe, it, expect, beforeEach } from 'vitest'\nimport { Container } from '@forinda/kickjs'\n\ndescribe('${pascal}', () => {\n beforeEach(() => {\n Container.reset()\n })\n\n it('should be defined', () => {\n // TODO: Import and test your class/function here\n expect(true).toBe(true)\n })\n\n it('should handle the happy path', async () => {\n // TODO: Set up test data and assertions\n expect(true).toBe(true)\n })\n\n it('should handle edge cases', async () => {\n // TODO: Test error handling, empty inputs, etc.\n expect(true).toBe(true)\n })\n})\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { resolve } from 'node:path'\nimport type { Command } from 'commander'\nimport { listPluginGenerators, tryDispatchPluginGenerator } from '../generator-extension'\nimport { mergeCliPlugins } from '../plugin'\nimport { generateModule } from '../generators/module'\nimport { resolveRepoType, type RepoType } from '../generators/module'\nimport { generateAdapter } from '../generators/adapter'\nimport { generatePlugin } from '../generators/plugin'\nimport { generateMiddleware } from '../generators/middleware'\nimport { generateGuard } from '../generators/guard'\nimport { generateService } from '../generators/service'\nimport { generateController } from '../generators/controller'\nimport { generateDto } from '../generators/dto'\nimport { generateConfig } from '../generators/config'\nimport { generateAgentDocs } from '../generators/agent-docs'\nimport { generateAuthScaffold } from '../generators/auth-scaffold'\nimport { generateJob } from '../generators/job'\nimport { generateScaffold, parseFields } from '../generators/scaffold'\nimport { generateTest } from '../generators/test'\nimport {\n loadKickConfig,\n resolveModuleConfig,\n resolveTokenScope,\n type ProjectPattern,\n} from '../config'\nimport { setDryRun } from '../utils/fs'\nimport { runTypegen } from '../typegen'\nimport { select, confirm as promptConfirm } from '../utils/prompts'\n\n/** Options accepted by `kick g module` and the bare `kick g <name>` shortcut. */\ninterface ModuleGenOpts {\n entity?: boolean\n tests?: boolean\n repo?: RepoType\n pattern?: ProjectPattern\n minimal?: boolean\n modulesDir?: string\n pluralize?: boolean\n force?: boolean\n}\n\n/** Options on the parent `generate` command — module flags + global flags. */\ninterface GenerateRootOpts extends ModuleGenOpts {\n list?: boolean\n dryRun?: boolean\n}\n\n/** Generators that drop a single file at a configurable directory. */\ninterface OutDirOpts {\n out: string\n}\n\n/** Generators that scope output into a module folder. */\ninterface ModuleScopedOpts {\n out?: string\n module?: string\n}\n\ninterface JobOpts extends OutDirOpts {\n queue?: string\n}\n\ninterface ScaffoldOpts {\n entity?: boolean\n tests?: boolean\n pluralize?: boolean\n modulesDir?: string\n}\n\ninterface AuthScaffoldOpts {\n strategy?: 'jwt' | 'session'\n roleGuards?: boolean\n out: string\n}\n\ninterface ConfigOpts {\n modulesDir: string\n repo: string\n force?: boolean\n}\n\ninterface AgentDocsOpts {\n only?: 'agents' | 'claude' | 'skills' | 'both' | 'all'\n name?: string\n pm?: string\n template?: 'rest' | 'ddd' | 'cqrs' | 'minimal'\n force?: boolean\n}\n\nconst AGENT_DOCS_ONLY_VALUES = ['agents', 'claude', 'skills', 'both', 'all'] as const\n\n/** Check if --dry-run was passed on the parent generate command */\nfunction isDryRun(cmd: Command): boolean {\n return (cmd.parent?.opts() as { dryRun?: boolean } | undefined)?.dryRun ?? false\n}\n\nfunction printGenerated(files: string[], dryRun = false): void {\n const cwd = process.cwd()\n const label = dryRun ? 'Would generate' : 'Generated'\n console.log(`\\n ${label} ${files.length} file${files.length === 1 ? '' : 's'}:`)\n for (const f of files) {\n console.log(` ${f.replace(cwd + '/', '')}`)\n }\n if (dryRun) console.log('\\n (dry run — no files were written)')\n console.log()\n}\n\n/**\n * Refresh `.kickjs/types/*` after a generator that emitted controllers,\n * so the new `Ctx<KickRoutes.X['method']>` references resolve in the\n * user's editor without waiting for `kick dev`.\n *\n * Loads `kick.config.ts` for `typegen.schemaValidator`. Failures are\n * non-fatal — typegen problems should never block code generation.\n */\nasync function runPostTypegen(dryRun: boolean): Promise<void> {\n if (dryRun) return\n try {\n const cfg = await loadKickConfig(process.cwd())\n await runTypegen({\n cwd: process.cwd(),\n allowDuplicates: true,\n silent: true,\n schemaValidator: cfg?.typegen?.schemaValidator ?? 'zod',\n envFile: cfg?.typegen?.envFile,\n srcDir: cfg?.typegen?.srcDir,\n outDir: cfg?.typegen?.outDir,\n })\n } catch {\n // Typegen failures are surfaced when the user runs `kick typegen`\n // explicitly. Don't block scaffolding on a regex bug.\n }\n}\n\nconst GENERATORS = [\n { name: 'module <name>', description: 'Full DDD module (controller, DTOs, use-cases, repo)' },\n { name: 'scaffold <name> <fields...>', description: 'CRUD module from field definitions' },\n { name: 'controller <name>', description: '@Controller() class [-m module]' },\n { name: 'service <name>', description: '@Service() singleton [-m module]' },\n { name: 'middleware <name>', description: 'Express middleware function [-m module]' },\n { name: 'guard <name>', description: 'Route guard (auth, roles, etc.) [-m module]' },\n { name: 'dto <name>', description: 'Zod DTO schema [-m module]' },\n { name: 'adapter <name>', description: 'AppAdapter with lifecycle hooks (app-level only)' },\n { name: 'test <name>', description: 'Vitest test scaffold [-m module]' },\n { name: 'job <name>', description: 'Queue @Job processor' },\n { name: 'config', description: 'Generate kick.config.ts' },\n {\n name: 'agents',\n description: 'Regenerate AGENTS.md + CLAUDE.md + kickjs-skills.md from upstream templates',\n },\n]\n\nasync function printGeneratorList(): Promise<void> {\n console.log('\\n Built-in generators:\\n')\n const maxName = Math.max(...GENERATORS.map((g) => g.name.length))\n for (const g of GENERATORS) {\n console.log(` kick g ${g.name.padEnd(maxName + 2)} ${g.description}`)\n }\n\n // Surface plugin-shipped generators alongside the built-ins so adopters\n // can discover what's available without grepping their node_modules.\n // Config-supplied generators (kick.config.ts > plugins[]) take priority\n // over package.json-discovered ones; the discovery merge in\n // listPluginGenerators handles the dedup.\n const config = await loadKickConfig(process.cwd())\n const merged = mergeCliPlugins(config?.plugins ?? [], config?.commands ?? [])\n const discovery = await listPluginGenerators(process.cwd(), merged.generators)\n if (discovery.generators.length > 0) {\n console.log('\\n Plugin generators:\\n')\n const pluginMax = Math.max(...discovery.generators.map((g) => `${g.spec.name} <name>`.length))\n for (const { source, spec } of discovery.generators) {\n const usage = `${spec.name} <name>`\n console.log(` kick g ${usage.padEnd(pluginMax + 2)} ${spec.description} [${source}]`)\n }\n }\n\n if (discovery.failed.length > 0) {\n console.log('\\n Failed to load:\\n')\n for (const { source, reason } of discovery.failed) {\n console.log(` ${source} — ${reason}`)\n }\n }\n\n console.log()\n}\n\n/**\n * Generate one or more modules. Shared by `kick g module <names...>` and\n * the bare `kick g <names...>` shortcut.\n */\nasync function runModuleGeneration(\n names: string[],\n opts: ModuleGenOpts,\n dryRun: boolean,\n): Promise<void> {\n const config = await loadKickConfig(process.cwd())\n const mc = resolveModuleConfig(config)\n const modulesDir = opts.modulesDir ?? mc.dir ?? 'src/modules'\n const repo: RepoType = opts.repo ?? resolveRepoType(mc.repo)\n const pattern = opts.pattern ?? config?.pattern ?? 'ddd'\n const shouldPluralize = opts.pluralize === false ? false : (mc.pluralize ?? true)\n const tokenScope = resolveTokenScope(config, process.cwd())\n\n const allFiles: string[] = []\n for (const name of names) {\n const files = await generateModule({\n name,\n modulesDir: resolve(modulesDir),\n noEntity: opts.entity === false,\n noTests: opts.tests === false,\n repo,\n minimal: opts.minimal,\n force: opts.force,\n pattern,\n dryRun,\n pluralize: shouldPluralize,\n prismaClientPath: mc.prismaClientPath,\n tokenScope,\n })\n allFiles.push(...files)\n }\n printGenerated(allFiles, dryRun)\n await runPostTypegen(dryRun)\n}\n\nexport function registerGenerateCommand(program: Command): void {\n const gen = program\n .command('generate [names...]')\n .alias('g')\n .description(\n 'Generate code scaffolds — bare form `kick g <name>` is shorthand for `kick g module <name>`',\n )\n .option('--list', 'List all available generators')\n .option('--dry-run', 'Preview files that would be generated without writing them')\n .option('--no-entity', 'Skip entity and value object generation (module shortcut)')\n .option('--no-tests', 'Skip test file generation (module shortcut)')\n .option('--repo <type>', 'Repository implementation: inmemory | drizzle | prisma')\n .option('--pattern <pattern>', 'Override project pattern: rest | ddd | cqrs | minimal')\n .option('--minimal', 'Shorthand for --pattern minimal')\n .option('--modules-dir <dir>', 'Modules directory')\n .option('--no-pluralize', 'Use singular names (skip auto-pluralization)')\n .option('-f, --force', 'Overwrite existing files without prompting')\n .action(async (names: string[], opts: GenerateRootOpts, cmd: Command) => {\n if (opts.list) {\n await printGeneratorList()\n return\n }\n if (!names || names.length === 0) {\n gen.help()\n return\n }\n const dryRun = isDryRun(cmd)\n setDryRun(dryRun)\n\n // Try plugin generators first — `kick g <name> <itemName>` where\n // `<name>` matches a discovered plugin generator wins over the\n // bare-module shortcut. This lets `kick g command Order` route\n // to a CQRS plugin without colliding with `kick g <module-name>`.\n // Config-supplied generators (kick.config.ts > plugins[]) take\n // priority over the legacy package.json discovery path.\n if (names.length >= 2) {\n const [generatorName, itemName, ...rest] = names\n const cfg = await loadKickConfig(process.cwd())\n const merged = mergeCliPlugins(cfg?.plugins ?? [], cfg?.commands ?? [])\n const result = await tryDispatchPluginGenerator(\n {\n generatorName,\n itemName,\n args: rest,\n flags: opts as unknown as Record<string, string | boolean>,\n cwd: process.cwd(),\n },\n merged.generators,\n )\n if (result) {\n printGenerated(result.files, dryRun)\n return\n }\n }\n\n await runModuleGeneration(names, opts, dryRun)\n })\n\n // ── kick g module <name> ────────────────────────────────────────────\n gen\n .command('module <names...>')\n .description('Generate one or more modules (e.g. kick g module user task project)')\n .option('--no-entity', 'Skip entity and value object generation')\n .option('--no-tests', 'Skip test file generation')\n .option('--repo <type>', 'Repository implementation: inmemory | drizzle | prisma')\n .option('--pattern <pattern>', 'Override project pattern: rest | ddd | cqrs | minimal')\n .option('--minimal', 'Shorthand for --pattern minimal')\n .option('--modules-dir <dir>', 'Modules directory')\n .option('--no-pluralize', 'Use singular names (skip auto-pluralization)')\n .option('-f, --force', 'Overwrite existing files without prompting')\n .action(async (names: string[], opts: ModuleGenOpts, cmd: Command) => {\n const dryRun = isDryRun(cmd)\n setDryRun(dryRun)\n await runModuleGeneration(names, opts, dryRun)\n })\n\n // ── kick g adapter <name> ──────────────────────────────────────────\n gen\n .command('adapter <name>')\n .description('Generate an AppAdapter with lifecycle hooks and middleware support')\n .option('-o, --out <dir>', 'Output directory', 'src/adapters')\n .action(async (name: string, opts: OutDirOpts, cmd: Command) => {\n const dryRun = isDryRun(cmd)\n setDryRun(dryRun)\n const files = await generateAdapter({ name, outDir: resolve(opts.out) })\n printGenerated(files, dryRun)\n })\n\n // ── kick g plugin <name> ────────────────────────────────────────────\n gen\n .command('plugin <name>')\n .description(\n 'Generate a KickPlugin with DI, modules, adapters, middleware, and lifecycle hooks',\n )\n .option('-o, --out <dir>', 'Output directory', 'src/plugins')\n .action(async (name: string, opts: OutDirOpts, cmd: Command) => {\n const dryRun = isDryRun(cmd)\n setDryRun(dryRun)\n const files = await generatePlugin({ name, outDir: resolve(opts.out) })\n printGenerated(files, dryRun)\n })\n\n // ── kick g middleware <name> ────────────────────────────────────────\n gen\n .command('middleware <name>')\n .description(\n 'Generate an Express middleware function\\n' +\n ' Use -m to scope it to a module: kick g middleware auth -m users',\n )\n .option('-o, --out <dir>', 'Output directory (overrides --module)')\n .option('-m, --module <module>', 'Place inside a module folder')\n .action(async (name: string, opts: ModuleScopedOpts, cmd: Command) => {\n const dryRun = isDryRun(cmd)\n setDryRun(dryRun)\n const config = await loadKickConfig(process.cwd())\n const mc = resolveModuleConfig(config)\n const modulesDir = mc.dir ?? 'src/modules'\n const files = await generateMiddleware({\n name,\n outDir: opts.out,\n moduleName: opts.module,\n modulesDir,\n pattern: config?.pattern,\n pluralize: mc.pluralize ?? true,\n })\n printGenerated(files, dryRun)\n })\n\n // ── kick g guard <name> ────────────────────────────────────────────\n gen\n .command('guard <name>')\n .description(\n 'Generate a route guard (auth, roles, etc.)\\n' +\n ' Use -m to scope it to a module: kick g guard admin -m users',\n )\n .option('-o, --out <dir>', 'Output directory (overrides --module)')\n .option('-m, --module <module>', 'Place inside a module folder')\n .action(async (name: string, opts: ModuleScopedOpts, cmd: Command) => {\n const dryRun = isDryRun(cmd)\n setDryRun(dryRun)\n const config = await loadKickConfig(process.cwd())\n const mc = resolveModuleConfig(config)\n const modulesDir = mc.dir ?? 'src/modules'\n const files = await generateGuard({\n name,\n outDir: opts.out,\n moduleName: opts.module,\n modulesDir,\n pattern: config?.pattern,\n pluralize: mc.pluralize ?? true,\n })\n printGenerated(files, dryRun)\n })\n\n // ── kick g service <name> ──────────────────────────────────────────\n gen\n .command('service <name>')\n .description(\n 'Generate a @Service() class\\n' +\n ' Use -m to scope it to a module: kick g service payment -m orders',\n )\n .option('-o, --out <dir>', 'Output directory (overrides --module)')\n .option('-m, --module <module>', 'Place inside a module folder')\n .action(async (name: string, opts: ModuleScopedOpts, cmd: Command) => {\n const dryRun = isDryRun(cmd)\n setDryRun(dryRun)\n const config = await loadKickConfig(process.cwd())\n const mc = resolveModuleConfig(config)\n const modulesDir = mc.dir ?? 'src/modules'\n const files = await generateService({\n name,\n outDir: opts.out,\n moduleName: opts.module,\n modulesDir,\n pattern: config?.pattern,\n pluralize: mc.pluralize ?? true,\n })\n printGenerated(files, dryRun)\n })\n\n // ── kick g controller <name> ───────────────────────────────────────\n gen\n .command('controller <name>')\n .description(\n 'Generate a @Controller() class with basic routes\\n' +\n ' Use -m to scope it to a module: kick g controller auth -m users',\n )\n .option('-o, --out <dir>', 'Output directory (overrides --module)')\n .option('-m, --module <module>', 'Place inside a module folder')\n .action(async (name: string, opts: ModuleScopedOpts, cmd: Command) => {\n const dryRun = isDryRun(cmd)\n setDryRun(dryRun)\n const config = await loadKickConfig(process.cwd())\n const mc = resolveModuleConfig(config)\n const modulesDir = mc.dir ?? 'src/modules'\n const files = await generateController({\n name,\n outDir: opts.out,\n moduleName: opts.module,\n modulesDir,\n pattern: config?.pattern,\n pluralize: mc.pluralize ?? true,\n })\n printGenerated(files, dryRun)\n await runPostTypegen(dryRun)\n })\n\n // ── kick g dto <name> ──────────────────────────────────────────────\n gen\n .command('dto <name>')\n .description(\n 'Generate a Zod DTO schema\\n' +\n ' Use -m to scope it to a module: kick g dto create-user -m users',\n )\n .option('-o, --out <dir>', 'Output directory (overrides --module)')\n .option('-m, --module <module>', 'Place inside a module folder')\n .action(async (name: string, opts: ModuleScopedOpts, cmd: Command) => {\n const dryRun = isDryRun(cmd)\n setDryRun(dryRun)\n const config = await loadKickConfig(process.cwd())\n const mc = resolveModuleConfig(config)\n const modulesDir = mc.dir ?? 'src/modules'\n const files = await generateDto({\n name,\n outDir: opts.out,\n moduleName: opts.module,\n modulesDir,\n pattern: config?.pattern,\n pluralize: mc.pluralize ?? true,\n })\n printGenerated(files, dryRun)\n })\n\n // ── kick g test <name> ────────────────────────────────────────────────\n gen\n .command('test <name>')\n .description(\n 'Generate a Vitest test scaffold\\n' +\n ' Use -m to scope it to a module: kick g test user-service -m users',\n )\n .option('-o, --out <dir>', 'Output directory (overrides --module)')\n .option('-m, --module <module>', \"Place inside a module's __tests__/ folder\")\n .action(async (name: string, opts: ModuleScopedOpts, cmd: Command) => {\n const dryRun = isDryRun(cmd)\n setDryRun(dryRun)\n const config = await loadKickConfig(process.cwd())\n const mc = resolveModuleConfig(config)\n const modulesDir = mc.dir ?? 'src/modules'\n const files = await generateTest({\n name,\n outDir: opts.out,\n moduleName: opts.module,\n modulesDir,\n pluralize: mc.pluralize ?? true,\n })\n printGenerated(files, dryRun)\n })\n\n // ── kick g job <name> ────────────────────────────────────────────────\n gen\n .command('job <name>')\n .description('Generate a @Job queue processor with @Process handlers')\n .option('-o, --out <dir>', 'Output directory', 'src/jobs')\n .option('-q, --queue <name>', 'Queue name (default: <name>-queue)')\n .action(async (name: string, opts: JobOpts, cmd: Command) => {\n const dryRun = isDryRun(cmd)\n setDryRun(dryRun)\n const files = await generateJob({ name, outDir: resolve(opts.out), queue: opts.queue })\n printGenerated(files, dryRun)\n })\n\n // ── kick g scaffold <name> <fields...> ─────────────────────────────\n gen\n .command('scaffold <name> [fields...]')\n .description(\n 'Generate a full CRUD module from field definitions\\n' +\n ' Example: kick g scaffold Post title:string body:text:optional published:boolean:optional\\n' +\n ' Types: string, text, number, int, float, boolean, date, email, url, uuid, json, enum:a,b,c\\n' +\n ' Optional: append :optional (shell-safe): description:text:optional\\n' +\n ' or use ? with quoting: \"description:text?\" or \"description?:text\"',\n )\n .option('--no-entity', 'Skip entity and value object generation')\n .option('--no-tests', 'Skip test file generation')\n .option('--no-pluralize', 'Use singular names (skip auto-pluralization)')\n .option('--modules-dir <dir>', 'Modules directory')\n .action(async (name: string, rawFields: string[], opts: ScaffoldOpts, cmd: Command) => {\n const dryRun = isDryRun(cmd)\n setDryRun(dryRun)\n if (rawFields.length === 0) {\n console.error(\n '\\n Error: At least one field is required.\\n' +\n ' Usage: kick g scaffold <name> <field:type> [field:type...]\\n' +\n ' Example: kick g scaffold Post title:string body:text:optional published:boolean:optional\\n' +\n ' Optional: append :optional (shell-safe, no quoting needed)\\n',\n )\n process.exit(1)\n }\n const config = await loadKickConfig(process.cwd())\n const mc = resolveModuleConfig(config)\n const modulesDir = opts.modulesDir ?? mc.dir ?? 'src/modules'\n const fields = parseFields(rawFields)\n const tokenScope = resolveTokenScope(config, process.cwd())\n const files = await generateScaffold({\n name,\n fields,\n modulesDir: resolve(modulesDir),\n noEntity: opts.entity === false,\n noTests: opts.tests === false,\n pluralize: opts.pluralize === false ? false : (mc.pluralize ?? true),\n tokenScope,\n })\n console.log(`\\n Scaffolded ${name} with ${fields.length} field(s):`)\n for (const f of fields) {\n console.log(` ${f.name}: ${f.type}${f.optional ? ' (optional)' : ''}`)\n }\n printGenerated(files, dryRun)\n await runPostTypegen(dryRun)\n })\n\n // ── kick g auth-scaffold ─────────────────────────────────────────────\n gen\n .command('auth-scaffold')\n .description(\n 'Generate a complete auth module (register, login, logout, password hashing)\\n' +\n ' Includes controller, service, DTOs, and test stubs.',\n )\n .option('-s, --strategy <type>', 'Auth strategy: jwt | session')\n .option('--role-guards', 'Generate role-based guards (default: true)')\n .option('--no-role-guards', 'Skip role-based guard generation')\n .option('-o, --out <dir>', 'Output directory', 'src/modules/auth')\n .action(async (opts: AuthScaffoldOpts, cmd: Command) => {\n const dryRun = isDryRun(cmd)\n setDryRun(dryRun)\n\n // Interactive prompts when flags not provided\n let strategy = opts.strategy\n if (!strategy) {\n strategy = await select({\n message: 'Auth strategy',\n options: [\n { value: 'jwt', label: 'JWT', hint: 'stateless token-based auth' },\n { value: 'session', label: 'Session', hint: 'server-side session with cookies' },\n ],\n })\n }\n\n let roleGuards = opts.roleGuards\n if (roleGuards === undefined) {\n roleGuards = await promptConfirm({\n message: 'Generate role-based guards?',\n initialValue: true,\n })\n }\n\n const files = await generateAuthScaffold({\n strategy,\n outDir: opts.out,\n roleGuards,\n })\n printGenerated(files, dryRun)\n })\n\n // ── kick g config ────────────────────────────────────────────────────\n gen\n .command('config')\n .description('Generate a kick.config.ts at the project root')\n .option('--modules-dir <dir>', 'Modules directory path', 'src/modules')\n .option('--repo <type>', 'Default repository type: inmemory | drizzle | prisma', 'inmemory')\n .option('-f, --force', 'Overwrite existing kick.config.ts without prompting')\n .action(async (opts: ConfigOpts, cmd: Command) => {\n const dryRun = isDryRun(cmd)\n setDryRun(dryRun)\n const files = await generateConfig({\n outDir: resolve('.'),\n modulesDir: opts.modulesDir,\n defaultRepo: opts.repo,\n force: opts.force,\n })\n printGenerated(files, dryRun)\n })\n\n // ── kick g agents ───────────────────────────────────────────────────\n // Regenerate AGENTS.md and/or CLAUDE.md from the latest CLI templates.\n // Runs against the project root by design — these files always live at\n // the top level and serve as the canonical AI-agent reference. Aliases\n // include `agent-docs` and `ai-docs` so users can pick whichever name\n // sticks; the docs/pitfalls list points at `kick g agents`.\n gen\n .command('agents')\n .alias('agent-docs')\n .alias('ai-docs')\n .description(\n 'Regenerate AGENTS.md + CLAUDE.md + kickjs-skills.md (sync after framework upgrades)',\n )\n .option(\n '--only <which>',\n 'Limit scope: agents | claude | skills | both (agents+claude) | all (default: all)',\n 'all',\n )\n .option('--name <name>', 'Project name (defaults to package.json name)')\n .option('--pm <pm>', 'Package manager (defaults to package.json packageManager)')\n .option('--template <template>', 'Template: rest | ddd | cqrs | minimal')\n .option('-f, --force', 'Overwrite existing files without prompting')\n .action(async (opts: AgentDocsOpts, cmd: Command) => {\n const dryRun = isDryRun(cmd)\n setDryRun(dryRun)\n const only = opts.only ?? 'all'\n if (!AGENT_DOCS_ONLY_VALUES.includes(only as (typeof AGENT_DOCS_ONLY_VALUES)[number])) {\n console.error(\n ` Invalid --only value: ${only}. Expected: ${AGENT_DOCS_ONLY_VALUES.join(' | ')}`,\n )\n process.exitCode = 1\n return\n }\n const files = await generateAgentDocs({\n outDir: resolve('.'),\n only: only as AgentDocsOpts['only'],\n name: opts.name,\n pm: opts.pm,\n template: opts.template,\n force: opts.force,\n })\n printGenerated(files, dryRun)\n })\n}\n","import { execSync, spawnSync } from 'node:child_process'\n\n/**\n * Run a shell command synchronously, printing output.\n *\n * On Windows, `execSync` spawns via `cmd.exe` by default, which means\n * POSIX-style inline env prefixes like `FOO=bar node app.js` do NOT work.\n * Callers that need environment variables should pass them in the `env`\n * option instead of prepending them to the command string — see\n * `runNodeWithEnv` for the cross-platform helper that avoids a shell\n * entirely.\n */\nexport function runShellCommand(command: string, cwd?: string, env?: NodeJS.ProcessEnv): void {\n execSync(command, {\n cwd,\n stdio: 'inherit',\n env: env ? { ...process.env, ...env } : process.env,\n })\n}\n\n/**\n * Cross-platform way to launch a Node.js process with a set of\n * environment variables. Uses `spawnSync` with an argument array so no\n * shell is involved — the `VAR=value node ...` POSIX prefix syntax that\n * `runShellCommand` relied on breaks on cmd.exe and PowerShell.\n */\nexport function runNodeWithEnv(entry: string, env: NodeJS.ProcessEnv, cwd?: string): void {\n const result = spawnSync(process.execPath, [entry], {\n cwd,\n stdio: 'inherit',\n env: { ...process.env, ...env },\n })\n if (result.status !== 0) {\n process.exit(result.status ?? 1)\n }\n}\n","// Typegen runner — M2.B-T7.\n//\n// Sequentially invokes each plugin's generate(), prepends a banner, and\n// writes the result to `.kickjs/types/<id>.d.ts` only when the content\n// changes. `--check` mode skips writing and throws on the first drift —\n// CI gate so generated files in git stay in sync with code.\n//\n// Sequential (not parallel) on purpose: plugins are cheap, ordering keeps\n// log output readable, and adopter plugins may share state via the cwd\n// filesystem. Parallel can be added later if a profiler flags it.\n\nimport path from 'node:path'\nimport { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { existsSync } from 'node:fs'\nimport { pathToFileURL } from 'node:url'\n\nimport type { KickConfig } from '../config'\nimport { scanProject, type ScanOptions, type ScanResult } from './scanner'\nimport type { TypegenContext, TypegenPlugin, TypegenPluginResult } from './plugin'\n\nconst TYPES_DIR = '.kickjs/types'\nconst BANNER_PREFIX = '/* AUTO-GENERATED by kick typegen — do not edit. Plugin: '\n\nexport interface RunTypegenOptions {\n cwd: string\n config: KickConfig\n plugins: TypegenPlugin[]\n /** When true: do not write; throw on the first plugin whose output differs. */\n check?: boolean\n /**\n * Optional scanner stub for tests. When set, `ctx.getScanResult()`\n * routes here instead of running the real scanner. Production\n * callers leave this undefined.\n */\n scan?: (opts: ScanOptions) => Promise<ScanResult>\n}\n\nexport async function runTypegen(opts: RunTypegenOptions): Promise<TypegenPluginResult[]> {\n const typesDirAbs = path.resolve(opts.cwd, TYPES_DIR)\n await mkdir(typesDirAbs, { recursive: true })\n\n // Per-pass memoization of scanProject. Cache keyed by a stable\n // serialization of the resolved scan options so two plugins asking\n // for the same scan share one walk + extraction. Different option\n // shapes (different srcDir, different envFile) get separate scans.\n // We deliberately do NOT use JSON.stringify on the raw options: its\n // output is sensitive to property insertion order, so two plugins\n // that build their options object in different orders would miss\n // the cache and trigger duplicate scans.\n const scanCache = new Map<string, Promise<ScanResult>>()\n const scanFn = opts.scan ?? scanProject\n const getScanResult = (scanOpts: ScanOptions): Promise<ScanResult> => {\n const key = stableScanKey(scanOpts)\n let pending = scanCache.get(key)\n if (!pending) {\n pending = scanFn(scanOpts)\n scanCache.set(key, pending)\n }\n return pending\n }\n\n const ctx: TypegenContext = {\n cwd: opts.cwd,\n config: opts.config,\n async importTs(abs) {\n return (await import(pathToFileURL(abs).href)) as never\n },\n async writeFile(relPath, contents) {\n const abs = path.resolve(opts.cwd, relPath)\n await mkdir(path.dirname(abs), { recursive: true })\n await writeFile(abs, contents, 'utf8')\n },\n getScanResult,\n log: console,\n }\n\n const results: TypegenPluginResult[] = []\n for (const plugin of opts.plugins) {\n const out = await plugin.generate(ctx)\n if (out === null) {\n results.push({ id: plugin.id, status: 'skipped' })\n continue\n }\n\n // Slashes in ids (kick/db) → __ on disk so they're valid filenames.\n // Default extension is .d.ts (declaration only); plugins emitting\n // hoisted top-level imports (kick/routes) override to .ts so the\n // bundler resolver sees them as proper modules.\n const ext = plugin.outExtension ?? '.d.ts'\n const file = path.join(typesDirAbs, `${plugin.id.replace(/\\//g, '__')}${ext}`)\n const banner = `${BANNER_PREFIX}${plugin.id} */\\n\\n`\n const next = banner + out + '\\n'\n\n let prev = ''\n if (existsSync(file)) prev = await readFile(file, 'utf8')\n\n if (prev === next) {\n results.push({ id: plugin.id, status: 'unchanged', outFile: file })\n continue\n }\n\n if (opts.check) {\n throw new Error(`kick typegen --check: drift detected for ${plugin.id} (${file})`)\n }\n await writeFile(file, next, 'utf8')\n results.push({ id: plugin.id, status: 'written', outFile: file })\n }\n\n return results\n}\n\n/**\n * Order-independent cache key for `ScanOptions`. Builds the key from\n * the known fields in a fixed order so semantically equal options\n * always produce the same key regardless of how the caller built the\n * object literal. Arrays (extensions / exclude) are sorted before\n * joining so `['.ts', '.tsx']` and `['.tsx', '.ts']` collide.\n */\nfunction stableScanKey(opts: ScanOptions): string {\n const extensions = (opts.extensions ?? []).slice().toSorted().join(',')\n const exclude = (opts.exclude ?? []).slice().toSorted().join(',')\n return [\n `root=${opts.root}`,\n `cwd=${opts.cwd}`,\n `extensions=${extensions}`,\n `exclude=${exclude}`,\n `envFile=${opts.envFile ?? ''}`,\n ].join('|')\n}\n","// Pure filter for `kick.config.ts > typegen.disable`. Lives in its own\n// module (instead of run-plugins.ts) so unit tests can import it\n// without dragging plugin/builtins → commands/init → package.json\n// reads into the test module graph.\n\nimport type { TypegenPlugin } from './plugin'\n\n/**\n * Returns three buckets:\n * - `enabled`: plugins that should run\n * - `skipped`: plugins explicitly disabled by id\n * - `unknown`: disable ids that didn't match any registered plugin\n * — surfaced as a warning to catch typos without breaking the run\n */\nexport function applyDisableFilter(\n typegens: readonly TypegenPlugin[],\n disable: readonly string[],\n): { enabled: TypegenPlugin[]; skipped: TypegenPlugin[]; unknown: string[] } {\n const disabledSet = new Set(disable)\n const enabled: TypegenPlugin[] = []\n const skipped: TypegenPlugin[] = []\n const matchedDisable = new Set<string>()\n\n for (const tg of typegens) {\n if (disabledSet.has(tg.id)) {\n skipped.push(tg)\n matchedDisable.add(tg.id)\n } else {\n enabled.push(tg)\n }\n }\n\n const unknown = [...disabledSet].filter((id) => !matchedDisable.has(id))\n return { enabled, skipped, unknown }\n}\n","// Helper that wires the plugin-typegen pipeline into `kick dev`,\n// `kick typegen`, and `kick typegen --watch` without duplicating the\n// merge + filter + invoke dance at every call site.\n//\n// Loads built-ins + adopter plugins, merges them, filters by\n// `kick.config.ts > typegen.disable`, runs every typegen via the T7\n// runner. In silent mode errors are swallowed so a transiently-broken\n// plugin doesn't crash the dev loop. Returns the per-plugin status\n// array — callers may log it, exit non-zero on drift (--check), etc.\n\nimport type { KickConfig } from '../config'\nimport { mergeCliPlugins } from '../plugin'\nimport { builtinCliPlugins } from '../plugin/builtins'\nimport { runTypegen as runPluginTypegens } from './runner'\nimport type { TypegenPluginResult } from './plugin'\nimport { applyDisableFilter } from './disable-filter'\n\n// Re-export so the existing public surface (cli/index.ts) still resolves.\nexport { applyDisableFilter } from './disable-filter'\n\nexport interface RunAllPluginTypegensOptions {\n cwd: string\n /** Pre-loaded kick.config.ts (saves a re-read). */\n config: KickConfig | null\n /** Suppress per-plugin status logging. Errors still swallowed when true. */\n silent?: boolean\n /** CI gate — fail (do not write) on the first plugin whose output drifted. */\n check?: boolean\n}\n\nexport async function runAllPluginTypegens(\n opts: RunAllPluginTypegensOptions,\n): Promise<TypegenPluginResult[]> {\n const allPlugins = [...builtinCliPlugins, ...(opts.config?.plugins ?? [])]\n const merged = mergeCliPlugins(allPlugins, opts.config?.commands ?? [])\n\n const { enabled, skipped, unknown } = applyDisableFilter(\n merged.typegens,\n opts.config?.typegen?.disable ?? [],\n )\n\n if (!opts.silent && skipped.length > 0) {\n for (const tg of skipped) {\n console.log(` ${tg.id}: disabled (typegen.disable)`)\n }\n }\n\n // Unrecognised disable ids are non-fatal — surface as a warning so\n // typos surface early without breaking the dev loop. `kick typegen\n // --list` prints the canonical id list.\n if (!opts.silent && unknown.length > 0) {\n console.warn(\n ` kick typegen: disable list references unknown id(s): ${unknown\n .map((id) => `'${id}'`)\n .join(', ')}. Run \\`kick typegen --list\\` to see registered ids.`,\n )\n }\n\n if (enabled.length === 0) return []\n\n try {\n const results = await runPluginTypegens({\n cwd: opts.cwd,\n config: opts.config ?? ({} as never),\n plugins: enabled,\n check: opts.check,\n })\n if (!opts.silent) {\n for (const r of results) console.log(` ${r.id}: ${r.status}`)\n }\n return results\n } catch (err) {\n if (!opts.silent) {\n const msg = err instanceof Error ? err.message : String(err)\n console.warn(` kick typegen plugins: skipped (${msg})`)\n }\n return []\n }\n}\n","/**\n * Build pipeline for `assetMap` entries (asset-manager PR 2).\n *\n * For each entry, walks `src/<...>` matching the configured `glob`,\n * copies matches into `dest` (default `dist/<name>/`), then emits a\n * `dist/.kickjs-assets.json` manifest mapping logical\n * `<namespace>/<key>` keys to repo-relative paths inside `dist/`.\n *\n * Pure function on top of `node:fs` + `glob` — no shell, no side\n * effects beyond the configured directory writes. The build entry-\n * point in `commands/run.ts` calls `buildAssets` after the existing\n * `copyDirs` step.\n *\n * @module @forinda/kickjs-cli/asset-manager/build\n */\n\nimport { cpSync, existsSync, mkdirSync, statSync, writeFileSync } from 'node:fs'\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path'\nimport { glob } from 'glob'\nimport { groupAssetKeys } from '@forinda/kickjs'\nimport type { AssetMapEntry, KickConfig } from '../config'\n\n/** Wire-format version for `dist/.kickjs-assets.json`. Bump on shape change. */\nexport const ASSET_MANIFEST_VERSION = 1 as const\n\n/** On-disk manifest format (`dist/.kickjs-assets.json`). */\nexport interface AssetManifest {\n version: typeof ASSET_MANIFEST_VERSION\n /**\n * Logical key → manifest-relative path. Logical key is\n * `<namespace>/<key>` where `<key>` is the file path under `src`\n * with the extension stripped + path separators normalised.\n *\n * Path values are relative to the manifest file's directory so the\n * runtime can resolve them with a single `path.resolve(manifestDir,\n * entry)` regardless of where dist/ lives.\n */\n entries: Record<string, string>\n}\n\nexport interface BuildAssetsOptions {\n /** Project root — resolved for every relative path in the entry. */\n cwd: string\n /**\n * Output dir for the manifest + per-namespace asset copies. When\n * omitted, falls back to `config.build?.outDir` (resolved against\n * `cwd`), then to `dist/` under cwd. Adopters with a custom Vite\n * `build.outDir` should set `kick.config.ts.build.outDir` to match.\n */\n distDir?: string\n /** Suppress per-entry log lines. Default: false. */\n silent?: boolean\n}\n\n/** One entry in the per-build summary returned by `buildAssets`. */\nexport interface BuildAssetsEntryResult {\n namespace: string\n src: string\n dest: string\n /** Number of files matched + copied. */\n filesCopied: number\n}\n\n/** Aggregated outcome of `buildAssets`. */\nexport interface BuildAssetsResult {\n manifestPath: string\n entries: BuildAssetsEntryResult[]\n /** `entries` merged into a single record — useful for tests + tooling. */\n manifest: AssetManifest\n}\n\n/**\n * Run the full asset build for a loaded config:\n *\n * 1. For each `assetMap` entry, glob → copy → manifest stub.\n * 2. Write `dist/.kickjs-assets.json`.\n *\n * Returns a summary including the manifest contents. No-op (and no\n * manifest written) when `assetMap` is empty / missing — the build\n * pipeline shouldn't litter `dist/` with empty manifests for\n * adopters who don't use the feature.\n */\nexport async function buildAssets(\n config: KickConfig | null,\n opts: BuildAssetsOptions,\n): Promise<BuildAssetsResult | null> {\n const { cwd, silent = false } = opts\n // Resolution order: explicit opts.distDir → config.build.outDir → 'dist'.\n // The CLI build command passes nothing explicit, so adopters control\n // the output via kick.config.ts.build.outDir alone.\n const distDir = opts.distDir ?? config?.build?.outDir ?? 'dist'\n const map = config?.assetMap\n if (!map || Object.keys(map).length === 0) return null\n\n const log = silent ? () => {} : console.log\n\n const distAbs = resolve(cwd, distDir)\n mkdirSync(distAbs, { recursive: true })\n\n const summary: BuildAssetsEntryResult[] = []\n const manifestEntries: Record<string, string> = {}\n\n for (const [namespace, entry] of Object.entries(map)) {\n const result = await processEntry(namespace, entry, cwd, distAbs)\n summary.push(result.entrySummary)\n Object.assign(manifestEntries, result.manifestSlice)\n log(\n ` ✓ ${namespace}: ${result.entrySummary.filesCopied} file(s) → ${result.entrySummary.dest}`,\n )\n }\n\n const manifest: AssetManifest = {\n version: ASSET_MANIFEST_VERSION,\n entries: manifestEntries,\n }\n const manifestPath = join(distAbs, '.kickjs-assets.json')\n writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\\n', 'utf-8')\n log(\n ` ✓ wrote manifest → ${relative(cwd, manifestPath)} (${Object.keys(manifestEntries).length} entries)`,\n )\n\n return { manifestPath, entries: summary, manifest }\n}\n\n/** Per-entry inner pipeline — extracted for unit-test reuse. */\nasync function processEntry(\n namespace: string,\n entry: AssetMapEntry,\n cwd: string,\n distAbs: string,\n): Promise<{\n entrySummary: BuildAssetsEntryResult\n manifestSlice: Record<string, string>\n}> {\n const srcAbs = resolve(cwd, entry.src)\n const destAbs = entry.dest ? resolve(cwd, entry.dest) : join(distAbs, namespace)\n\n // Defensive: refuse to write outside the project root (cwd) even\n // though validateAssetMap warned about it at config-load time. The\n // build step shouldn't trust upstream warnings — a typo like\n // `dest: '../../'` would otherwise sprinkle files outside the\n // workspace despite the warning being printed.\n if (escapesRoot(destAbs, cwd)) {\n console.warn(\n ` ⚠ assetMap.${namespace}.dest ('${entry.dest}') resolves outside the project root — skipping copy`,\n )\n return {\n entrySummary: { namespace, src: entry.src, dest: relative(cwd, destAbs), filesCopied: 0 },\n manifestSlice: {},\n }\n }\n\n // Treat src-not-a-directory the same as src-missing — `glob` would\n // throw if pointed at a file, surfacing as a generic build failure\n // instead of a clean 0-files entry. Matches the validator's warning\n // shape (already emitted at config-load time for the missing case).\n if (!existsSync(srcAbs) || !isDirectorySync(srcAbs)) {\n return {\n entrySummary: { namespace, src: entry.src, dest: relative(cwd, destAbs), filesCopied: 0 },\n manifestSlice: {},\n }\n }\n\n const pattern = entry.glob ?? '**/*'\n // `glob` returns paths relative to `cwd` when `cwd` is set —\n // exactly the slugs we want for the manifest keys.\n const matches = await glob(pattern, {\n cwd: srcAbs,\n nodir: true,\n dot: false,\n posix: true,\n })\n\n mkdirSync(destAbs, { recursive: true })\n\n const manifestSlice: Record<string, string> = {}\n // Sort the walk so the manifest is byte-stable across platforms.\n // The `groupAssetKeys` helper preserves input order, so a stable\n // sort here makes the resulting manifest stable too.\n const sorted = [...matches].toSorted()\n const { pairs, collisionGroupsResolved } = groupAssetKeys(namespace, sorted, {\n strategy: entry.keys ?? 'auto',\n })\n\n // Copy the files and write the keyed manifest slice. Both come from\n // the same `pairs` order so the on-disk layout matches the\n // manifest's iteration order — easier to grep in cold-start\n // debugging.\n for (const { rel: relPath, key } of pairs) {\n const srcFile = join(srcAbs, relPath)\n const destFile = join(destAbs, relPath)\n mkdirSync(dirname(destFile), { recursive: true })\n cpSync(srcFile, destFile)\n manifestSlice[key] = toManifestRelative(distAbs, destFile)\n }\n\n if (collisionGroupsResolved > 0) {\n console.log(\n ` ℹ assetMap.${namespace}: auto-resolved ${collisionGroupsResolved} basename collision(s) by keeping extensions ` +\n `(set 'keys: \"strip\"' to opt back into legacy last-write-wins behaviour, or 'keys: \"with-extension\"' to keep all keys verbose).`,\n )\n }\n\n return {\n entrySummary: {\n namespace,\n src: entry.src,\n dest: relative(cwd, destAbs),\n filesCopied: matches.length,\n },\n manifestSlice,\n }\n}\n\n/**\n * Make `destFile` relative to the manifest's directory + force POSIX\n * separators so the manifest is byte-stable across platforms.\n */\nfunction toManifestRelative(manifestDir: string, destFile: string): string {\n const rel = relative(manifestDir, destFile)\n // path.relative returns OS-native separators; the manifest is JSON\n // and the runtime uses path.resolve which handles either, but a\n // forward-slash manifest is grep-friendly + diff-stable.\n return rel.split(/[\\\\/]/).filter(Boolean).join('/')\n}\n\n/**\n * Pure manifest writer — handy for tests that want to assert against\n * a hand-crafted manifest without exercising the full pipeline.\n */\nexport function writeAssetManifest(distDir: string, manifest: AssetManifest): string {\n const path = join(distDir, '.kickjs-assets.json')\n mkdirSync(distDir, { recursive: true })\n writeFileSync(path, JSON.stringify(manifest, null, 2) + '\\n', 'utf-8')\n return path\n}\n\n/**\n * Read + parse a manifest from disk. Returns `null` on missing or\n * malformed file rather than throwing — the runtime resolver wants\n * to fall through to dev-mode lookup in that case.\n */\nexport function readAssetManifest(distDir: string): AssetManifest | null {\n const path = join(distDir, '.kickjs-assets.json')\n if (!existsSync(path)) return null\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fs = require('node:fs') as typeof import('node:fs')\n const raw = fs.readFileSync(path, 'utf-8')\n const parsed = JSON.parse(raw) as Partial<AssetManifest>\n if (parsed.version !== ASSET_MANIFEST_VERSION) return null\n if (!parsed.entries || typeof parsed.entries !== 'object') return null\n return parsed as AssetManifest\n } catch {\n return null\n }\n}\n\n/**\n * Project-root escape check that's safe across symlinks + drive letters.\n * `path.relative` returns `..` segments when the target sits above root,\n * and an absolute path when the two live on different roots (Windows).\n * `startsWith(root)` would miss both cases.\n */\nfunction escapesRoot(path: string, root: string): boolean {\n const rel = relative(root, path)\n if (rel === '') return false\n return rel.startsWith('..') || isAbsolute(rel)\n}\n\n/** Pure helper — `false` for missing, non-dir, or unreadable paths. */\nfunction isDirectorySync(path: string): boolean {\n try {\n return statSync(path).isDirectory()\n } catch {\n return false\n }\n}\n","import { cpSync, existsSync, mkdirSync } from 'node:fs'\nimport { resolve, join } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { Command } from 'commander'\nimport { runNodeWithEnv } from '../utils/shell'\nimport { loadKickConfig } from '../config'\nimport { runTypegen } from '../typegen'\nimport { runAllPluginTypegens } from '../typegen/run-plugins'\nimport { buildAssets } from '../asset-manager/build'\n\n/**\n * Start the Vite dev server with @forinda/kickjs-vite plugin.\n *\n * The plugin (configured in the user's vite.config.ts) handles:\n * - SSR environment setup (kickjs:core)\n * - Module auto-discovery (kickjs:module-discovery)\n * - Selective HMR invalidation (kickjs:hmr)\n * - Virtual module generation (kickjs:virtual-modules)\n * - Express mounting + httpServer piping (kickjs:dev-server)\n *\n * This function just creates the Vite server, listens, and handles shutdown.\n * Vite owns the HTTP port — Express runs as post-middleware on Vite's server.\n */\n/**\n * Resolve whether the dev server's chokidar should poll instead of\n * relying on `fs.watch` events. CLI flag wins over env var; default\n * is event-based (faster, lower CPU). Polling is the right choice in\n * Docker bind mounts, WSL crossing the WSL/Windows boundary, NFS,\n * and some old Linux kernels where new-file events get dropped.\n */\nfunction resolvePolling(flag: boolean | undefined): boolean {\n if (typeof flag === 'boolean') return flag\n const env = process.env.KICKJS_WATCH_POLLING\n return env === '1' || env === 'true'\n}\n\nasync function startDevServer(\n _entry: string,\n port?: string,\n opts: { polling?: boolean } = {},\n): Promise<void> {\n if (port) process.env.PORT = port\n const polling = resolvePolling(opts.polling)\n\n // Generate `.kickjs/types/*.d.ts` once before Vite starts so the\n // user's tsc has fresh type info from the very first request.\n // `allowDuplicates: true` so an in-progress class rename can never\n // block the dev server — the colliding entries are auto-namespaced\n // and the warning is printed instead.\n const cwd = process.cwd()\n const devConfig = await loadKickConfig(cwd)\n const schemaValidator = devConfig?.typegen?.schemaValidator ?? 'zod'\n const envFile = devConfig?.typegen?.envFile\n try {\n await runTypegen({\n cwd,\n allowDuplicates: true,\n schemaValidator,\n envFile,\n srcDir: devConfig?.typegen?.srcDir,\n outDir: devConfig?.typegen?.outDir,\n assetMap: devConfig?.assetMap,\n // We invoke the plugin pipeline explicitly below; opting out\n // here keeps it from running twice on `kick dev` startup.\n runPlugins: false,\n })\n } catch (err: any) {\n console.warn(` kick typegen: skipped (${err?.message ?? err})`)\n }\n\n // Plugin typegens (kick/db + adopter plugins from kick.config.ts).\n // Same swallow-on-error semantics as the legacy pass — a broken\n // plugin shouldn't block the dev server from coming up.\n await runAllPluginTypegens({ cwd, config: devConfig })\n\n // Resolve vite from the user's project, not the CLI package.\n // On Windows, require.resolve returns an absolute path like\n // `C:\\...\\vite\\dist\\node\\index.js`, which Node's ESM loader rejects\n // (`Received protocol 'c:'`). Wrap in `pathToFileURL` so the loader\n // gets a valid `file://` URL on every platform.\n const { createRequire } = await import('node:module')\n const require = createRequire(resolve('package.json'))\n const vitePath = require.resolve('vite')\n const { createServer } = await import(pathToFileURL(vitePath).href)\n\n const server = await createServer({\n configFile: resolve('vite.config.ts'),\n server: {\n // Pass the port to Vite — it creates the httpServer\n port: port ? parseInt(port, 10) : undefined,\n // Polling chokidar — opt-in via --polling / KICKJS_WATCH_POLLING.\n // The default (event-based) is faster + lower CPU on bare metal,\n // but `add` events get dropped under Docker bind mounts, WSL\n // crossings, NFS, and some kernel/filesystem combos. Switching\n // to polling is the standard mitigation; 100ms interval matches\n // chokidar's documented sane default.\n ...(polling ? { watch: { usePolling: true as const, interval: 100 } } : {}),\n },\n })\n\n // Resolve the absolute paths of every assetMap.<ns>.src directory so\n // the watcher can treat any file change beneath them as an asset\n // change — regardless of extension, since adopters drop .ejs / .html\n // / .json / .md / .pug / etc. into a templates folder.\n const assetSrcRoots: readonly string[] = devConfig?.assetMap\n ? Object.values(devConfig.assetMap)\n .map((entry) => entry?.src)\n .filter((src): src is string => typeof src === 'string' && src.length > 0)\n .map((src) => resolve(cwd, src))\n : []\n const isAssetFile = (file: string): boolean =>\n assetSrcRoots.some((root) => file === root || file.startsWith(`${root}/`))\n\n // Re-run typegen whenever a source file changes. Vite already\n // owns a chokidar watcher, so we piggy-back on it instead of\n // adding our own — same files, no extra fd cost.\n //\n // Two trigger paths share one debounced run:\n // 1. .ts/.tsx/.mts/.cts changes → controllers / services / @Asset\n // keys re-discovered, registry + augmentation files refresh.\n // 2. anything inside an `assetMap.<ns>.src` dir → KickAssets\n // augmentation refreshes so TypeScript sees newly added templates.\n // Runtime resolution of new templates is handled in @forinda/kickjs\n // itself — the dev-mode resolver skips its module-level cache so each\n // `assets.x.y()` call re-walks. No Vite full-reload needed.\n let typegenTimer: ReturnType<typeof setTimeout> | null = null\n const scheduleTypegen = (file: string) => {\n if (file.includes('.kickjs')) return\n if (file.endsWith('.d.ts')) return\n const isTs = /\\.(ts|tsx|mts|cts)$/.test(file)\n const isAsset = isAssetFile(file)\n if (!isTs && !isAsset) return\n if (typegenTimer) clearTimeout(typegenTimer)\n typegenTimer = setTimeout(() => {\n runTypegen({\n cwd,\n silent: true,\n allowDuplicates: true,\n schemaValidator,\n envFile,\n srcDir: devConfig?.typegen?.srcDir,\n outDir: devConfig?.typegen?.outDir,\n assetMap: devConfig?.assetMap,\n // Plugin pipeline runs separately just below; opting out here\n // avoids double-running it on every debounced trigger.\n runPlugins: false,\n }).catch(() => {})\n // Plugin typegens piggy-back on the same debounce — kick/db\n // re-emits kick__db.d.ts when the schema (or anything else\n // touched) changes. silent so the dev console stays quiet.\n runAllPluginTypegens({ cwd, config: devConfig, silent: true }).catch(() => {})\n }, 100)\n }\n server.watcher.on('add', scheduleTypegen)\n server.watcher.on('unlink', scheduleTypegen)\n server.watcher.on('change', scheduleTypegen)\n // Vite's default watcher ignores extensions it doesn't compile;\n // explicitly subscribe asset src dirs so .ejs / .html changes land\n // in the typegen pipeline.\n if (assetSrcRoots.length > 0) {\n server.watcher.add(assetSrcRoots)\n }\n\n await server.listen()\n server.printUrls()\n\n console.log(`\\n KickJS dev server running (Vite + @forinda/kickjs-vite)\\n`)\n\n // Graceful shutdown — Vite closes the server + all HMR connections\n const shutdown = async () => {\n if (typegenTimer) clearTimeout(typegenTimer)\n await server.close()\n process.exit(0)\n }\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n}\n\nexport function registerRunCommands(program: Command): void {\n program\n .command('dev')\n .description('Start development server with Vite HMR (zero-downtime reload)')\n .option('-e, --entry <file>', 'Entry file', 'src/index.ts')\n .option('-p, --port <port>', 'Port number')\n .option(\n '--polling',\n 'Force chokidar to poll for file changes (Docker / WSL / NFS / older kernels)',\n )\n .action(async (opts: any) => {\n try {\n await startDevServer(opts.entry, opts.port, { polling: opts.polling })\n } catch (err: any) {\n if (err.code === 'ERR_MODULE_NOT_FOUND' && err.message?.includes('vite')) {\n console.error('\\n Error: vite is not installed.\\n Run: pnpm add -D vite unplugin-swc\\n')\n } else {\n console.error('\\n Dev server failed:', err.message ?? err)\n }\n process.exit(1)\n }\n })\n\n program\n .command('build')\n .description('Build for production via Vite')\n .action(async () => {\n console.log('\\n Building for production...\\n')\n\n // Resolve vite from the user's project, not the CLI package.\n // `pathToFileURL` fixes the Windows ESM loader rejection of bare\n // absolute paths (see `startDevServer` above).\n const { createRequire } = await import('node:module')\n const require = createRequire(resolve('package.json'))\n const vitePath = require.resolve('vite')\n const { build } = await import(pathToFileURL(vitePath).href)\n await build({ configFile: resolve('vite.config.ts') })\n\n // Copy static directories to dist (e.g., templates, public assets)\n const config = await loadKickConfig(process.cwd())\n const copyDirs = config?.copyDirs ?? []\n\n if (copyDirs.length > 0) {\n console.log('\\n Copying directories to dist...')\n for (const entry of copyDirs) {\n const src = typeof entry === 'string' ? entry : entry.src\n const dest =\n typeof entry === 'string' ? join('dist', entry) : (entry.dest ?? join('dist', src))\n const srcPath = resolve(src)\n const destPath = resolve(dest)\n\n if (!existsSync(srcPath)) {\n console.log(` ⚠ Skipped ${src} (not found)`)\n continue\n }\n\n mkdirSync(destPath, { recursive: true })\n cpSync(srcPath, destPath, { recursive: true })\n console.log(` ✓ ${src} → ${dest}`)\n }\n }\n\n // Asset manager (assets-plan.md PR 2). Drives its own copy +\n // emits dist/.kickjs-assets.json for the runtime resolver. No-op\n // when assetMap is missing — silent for adopters who don't use it.\n if (config?.assetMap && Object.keys(config.assetMap).length > 0) {\n console.log('\\n Building asset map...')\n try {\n await buildAssets(config, { cwd: process.cwd() })\n } catch (err) {\n console.error(\n ` ✗ asset build failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n process.exit(1)\n }\n }\n\n console.log('\\n Build complete.\\n')\n })\n\n program\n .command('build:assets')\n .description(\n 'Rebuild the .kickjs-assets.json manifest under the configured outDir (no JS rebuild)',\n )\n .action(async () => {\n const config = await loadKickConfig(process.cwd())\n if (!config?.assetMap || Object.keys(config.assetMap).length === 0) {\n console.log(' No assetMap entries — nothing to build.')\n return\n }\n console.log('\\n Building asset map...')\n try {\n await buildAssets(config, { cwd: process.cwd() })\n console.log('\\n Asset build complete.\\n')\n } catch (err) {\n console.error(` ✗ ${err instanceof Error ? err.message : String(err)}`)\n process.exit(1)\n }\n })\n\n program\n .command('start')\n .description('Start production server')\n .option('-e, --entry <file>', 'Entry file', 'dist/index.js')\n .option('-p, --port <port>', 'Port number')\n .action((opts: any) => {\n // Use `runNodeWithEnv` so env vars go through `child_process.env`\n // instead of a POSIX `FOO=bar node ...` prefix — the prefix form\n // only works under bash/zsh and breaks on Windows cmd.exe and\n // PowerShell, which is what broke `kick start` on Windows dev.\n const env: NodeJS.ProcessEnv = { NODE_ENV: 'production' }\n if (opts.port) env.PORT = String(opts.port)\n runNodeWithEnv(opts.entry, env)\n })\n\n program\n .command('dev:debug')\n .description('Start dev server with Node.js inspector attached')\n .option('-e, --entry <file>', 'Entry file', 'src/index.ts')\n .option('-p, --port <port>', 'Port number')\n .option('--inspect-port <port>', 'Inspector port', '9229')\n .action(async (opts: any) => {\n // For debug mode, we need --inspect on the Node.js process itself.\n // Re-launch the dev command with NODE_OPTIONS=--inspect\n const inspectPort = opts.inspectPort ?? '9229'\n process.env.NODE_OPTIONS = `--inspect=0.0.0.0:${inspectPort}`\n console.log(` Debugger: ws://0.0.0.0:${inspectPort}`)\n\n try {\n await startDevServer(opts.entry, opts.port)\n } catch (err: any) {\n console.error('\\n Dev server (debug) failed:', err.message ?? err)\n process.exit(1)\n }\n })\n}\n","import { platform, release, arch } from 'node:os'\nimport type { Command } from 'commander'\n\nexport function registerInfoCommand(program: Command): void {\n program\n .command('info')\n .description('Print system and framework info')\n .action(() => {\n console.log(`\n KickJS CLI\n\n System:\n OS: ${platform()} ${release()} (${arch()})\n Node: ${process.version}\n\n Packages:\n @forinda/kickjs workspace\n @forinda/kickjs-vite workspace\n @forinda/kickjs-cli workspace\n`)\n })\n}\n","import type { Command } from 'commander'\nimport { colors, httpMethodColor } from '../utils/colors'\n\nconst { bold, dim, green, red, yellow, blue } = colors\n\nfunction formatUptime(seconds: number): string {\n const d = Math.floor(seconds / 86400)\n const h = Math.floor((seconds % 86400) / 3600)\n const m = Math.floor((seconds % 3600) / 60)\n const s = seconds % 60\n const parts: string[] = []\n if (d) parts.push(`${d}d`)\n if (h) parts.push(`${h}h`)\n if (m) parts.push(`${m}m`)\n parts.push(`${s}s`)\n return parts.join(' ')\n}\n\n// ── Fetch helpers ───────────────────────────────────────────────────────────\n\nasync function fetchJson(url: string): Promise<any> {\n const res = await fetch(url, { signal: AbortSignal.timeout(5000) })\n if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)\n return res.json()\n}\n\nasync function fetchEndpoint(base: string, path: string): Promise<any> {\n try {\n return await fetchJson(`${base}${path}`)\n } catch {\n return null\n }\n}\n\ninterface InspectData {\n health: any\n metrics: any\n routes: any\n container: any\n ws: any\n}\n\nasync function fetchAll(base: string): Promise<InspectData> {\n const [health, metrics, routes, container, ws] = await Promise.all([\n fetchEndpoint(base, '/health'),\n fetchEndpoint(base, '/metrics'),\n fetchEndpoint(base, '/routes'),\n fetchEndpoint(base, '/container'),\n fetchEndpoint(base, '/ws'),\n ])\n return { health, metrics, routes, container, ws }\n}\n\n// ── Display ─────────────────────────────────────────────────────────────────\n\nfunction printSummary(base: string, data: InspectData): void {\n const { health, metrics, routes, container, ws } = data\n const line = dim('─'.repeat(60))\n\n console.log()\n console.log(bold(` KickJS Inspector`) + dim(` → ${base}`))\n console.log(line)\n\n // Health\n if (health) {\n const statusText = health.status === 'healthy' ? green('● healthy') : red('● ' + health.status)\n console.log(` ${bold('Health:')} ${statusText}`)\n } else {\n console.log(` ${bold('Health:')} ${red('● unreachable')}`)\n }\n\n // Metrics\n if (metrics) {\n const rate = ((metrics.errorRate ?? 0) * 100).toFixed(1)\n const rateColor = metrics.errorRate > 0.1 ? red : metrics.errorRate > 0 ? yellow : green\n console.log(` ${bold('Uptime:')} ${formatUptime(metrics.uptimeSeconds)}`)\n console.log(` ${bold('Requests:')} ${metrics.requests}`)\n console.log(\n ` ${bold('Errors:')} ${metrics.serverErrors} server, ${metrics.clientErrors ?? 0} client ${dim('(')}${rateColor(rate + '%')}${dim(')')}`,\n )\n }\n\n // Container\n if (container) {\n console.log(` ${bold('DI:')} ${container.count} bindings`)\n }\n\n // WebSocket\n if (ws && ws.enabled) {\n console.log(\n ` ${bold('WS:')} ${ws.connections ?? 0} connections, ${ws.namespaces ?? 0} namespaces`,\n )\n }\n\n // Routes table\n if (routes?.routes?.length) {\n console.log()\n console.log(bold(' Routes'))\n console.log(line)\n console.log(` ${dim('METHOD')} ${dim('PATH'.padEnd(36))} ${dim('CONTROLLER')}`)\n for (const r of routes.routes) {\n const path = r.path.length > 36 ? r.path.slice(0, 33) + '...' : r.path.padEnd(36)\n console.log(` ${httpMethodColor(r.method)} ${path} ${blue(r.controller)}.${dim(r.handler)}`)\n }\n }\n\n console.log(line)\n console.log()\n}\n\n// ── Command Registration ────────────────────────────────────────────────────\n\nexport function registerInspectCommand(program: Command): void {\n program\n .command('inspect [url]')\n .description('Connect to a running KickJS app and display debug info')\n .option('-p, --port <port>', 'Override port')\n .option('-w, --watch', 'Poll every 5 seconds')\n .option('-j, --json', 'Output raw JSON')\n .action(\n async (url: string | undefined, opts: { port?: string; watch?: boolean; json?: boolean }) => {\n let base = url ?? 'http://localhost:3000'\n\n // Override port if provided\n if (opts.port) {\n try {\n const parsed = new URL(base)\n parsed.port = opts.port\n base = parsed.origin\n } catch {\n base = `http://localhost:${opts.port}`\n }\n }\n\n const debugBase = `${base.replace(/\\/$/, '')}/_debug`\n\n const run = async () => {\n try {\n const data = await fetchAll(debugBase)\n\n if (opts.json) {\n console.log(JSON.stringify(data, null, 2))\n } else {\n printSummary(base, data)\n }\n } catch (err) {\n if (opts.json) {\n console.log(JSON.stringify({ error: String(err) }))\n } else {\n console.error(red(` ✖ Could not connect to ${base}`))\n console.error(dim(` ${err instanceof Error ? err.message : String(err)}`))\n }\n if (!opts.watch) process.exitCode = 1\n }\n }\n\n if (opts.watch) {\n const poll = async () => {\n process.stdout.write('\\x1b[2J\\x1b[H') // clear screen\n await run()\n }\n await poll()\n setInterval(poll, 5000)\n } else {\n await run()\n }\n },\n )\n}\n","/**\n * Known-issues registry for `kick explain`.\n *\n * Each entry is a pattern matcher + a diagnosis. The matcher receives\n * the user's error text (and optionally project context) and returns\n * either a `Match` object (with confidence + extracted captures) or\n * `null` if it doesn't apply. `kick explain` runs every matcher,\n * picks the highest-confidence match, and prints its diagnosis.\n *\n * This file is the single source of truth for KickJS-specific pitfalls.\n * Adding a new entry takes ~30 lines and gives every user a permanent\n * fix path for that error. Keep entries focused: one issue per entry,\n * targeted matchers (avoid over-broad regexes), specific fixes.\n *\n * Confidence scoring (0–100):\n * 100 — certainty (error message has the exact symbol we're looking for)\n * 80 — high (multiple correlated signals)\n * 60 — medium (single strong signal)\n * 40 — low (heuristic match, mention to user as a guess)\n * < 40 — discarded\n *\n * The matcher should never throw — always catch and return null on\n * unexpected input.\n */\n\nexport interface ExplainContext {\n /** Project root if known (cwd of `kick explain`). */\n cwd?: string\n /** Set of file paths the matcher can check for existence. */\n hasFile?: (path: string) => boolean\n}\n\nexport interface Diagnosis {\n /** Stable identifier — used in tests, telemetry, and bug reports. */\n id: string\n /** Short human-readable title shown above the explanation. */\n title: string\n /** Multi-paragraph explanation of what's wrong and why it happens. */\n explanation: string\n /**\n * The fix to apply, written as instructions a human can follow. May\n * include code snippets via the `codeBefore` / `codeAfter` fields.\n */\n fix: string\n /** Optional snippet showing the broken state. */\n codeBefore?: string\n /** Optional snippet showing the corrected state. */\n codeAfter?: string\n /** Doc URL for further reading. */\n docs?: string\n}\n\nexport interface Match {\n /** 0–100; matchers below 40 are discarded by `findBestMatch`. */\n confidence: number\n diagnosis: Diagnosis\n}\n\nexport interface KnownIssue {\n match(input: string, ctx?: ExplainContext): Match | null\n}\n\n// ── Helper: case-insensitive multi-pattern check ─────────────────────────\n\nfunction includesAll(haystack: string, needles: string[]): boolean {\n const lower = haystack.toLowerCase()\n return needles.every((n) => lower.includes(n.toLowerCase()))\n}\n\nfunction includesAny(haystack: string, needles: string[]): boolean {\n const lower = haystack.toLowerCase()\n return needles.some((n) => lower.includes(n.toLowerCase()))\n}\n\n// ── Issue 1: env schema not registered ───────────────────────────────────\n\nconst envSchemaNotRegistered: KnownIssue = {\n match(input, _ctx) {\n // Strong signals: error mentions config.get + undefined, OR\n // mentions @Value returning unexpected value\n const hasConfigGetUndefined =\n includesAll(input, ['config', 'get']) && includesAny(input, ['undefined', 'null'])\n const hasValueUndefined =\n input.includes('@Value') && includesAny(input, ['undefined', 'is not defined'])\n\n if (!hasConfigGetUndefined && !hasValueUndefined) return null\n\n return {\n confidence: hasConfigGetUndefined && hasValueUndefined ? 90 : 75,\n diagnosis: {\n id: 'env-schema-not-registered',\n title: 'ConfigService.get() returns undefined for user-defined keys',\n explanation:\n 'Your src/index.ts is missing `import \"./config\"`. That side-effect import\\n' +\n 'registers the env schema with kickjs at module-load time. Without it,\\n' +\n 'ConfigService falls back to the base schema (PORT/NODE_ENV/LOG_LEVEL only)\\n' +\n 'and every user-defined key reads as undefined. @Value() may *appear* to\\n' +\n 'work via a raw process.env fallback, but Zod coercion and schema defaults\\n' +\n 'are silently skipped.',\n fix: 'Add this line to src/index.ts near the top, before bootstrap() runs:',\n codeBefore:\n \"import 'reflect-metadata'\\n\" +\n \"import { bootstrap } from '@forinda/kickjs'\\n\" +\n \"import { modules } from './modules'\\n\",\n codeAfter:\n \"import 'reflect-metadata'\\n\" +\n \"import './config' // ← add this — registers env schema\\n\" +\n \"import { bootstrap } from '@forinda/kickjs'\\n\" +\n \"import { modules } from './modules'\\n\",\n docs: 'https://forinda.github.io/kick-js/guide/configuration.html#wiring-the-schema-at-startup',\n },\n }\n },\n}\n\n// ── Issue 2: missing Container.reset() in tests ──────────────────────────\n\nconst containerNotReset: KnownIssue = {\n match(input, _ctx) {\n const hasTestContext = includesAny(input, ['vitest', 'test', 'spec', '__tests__', '.test.'])\n const hasDuplicate = includesAny(input, [\n 'already registered',\n 'already exists',\n 'duplicate',\n 'has been registered',\n ])\n if (!hasDuplicate) return null\n\n return {\n confidence: hasTestContext ? 85 : 60,\n diagnosis: {\n id: 'container-not-reset-in-tests',\n title: 'DI container leaks between test cases',\n explanation:\n 'KickJS decorators register classes on the global Container at import time.\\n' +\n 'When vitest re-imports your modules across tests, the same class can be\\n' +\n 'registered twice and the container throws. The fix is to wipe the\\n' +\n 'container between tests so each case starts fresh.',\n fix: 'Add Container.reset() to a beforeEach hook in the failing test file:',\n codeAfter:\n \"import { describe, it, beforeEach } from 'vitest'\\n\" +\n \"import { Container } from '@forinda/kickjs'\\n\\n\" +\n \"describe('UserController', () => {\\n\" +\n ' beforeEach(() => Container.reset())\\n\\n' +\n \" it('does the thing', async () => { /* ... */ })\\n\" +\n '})',\n docs: 'https://forinda.github.io/kick-js/guide/testing.html',\n },\n }\n },\n}\n\n// ── Issue 3: @Module decorator (NestJS-style) ────────────────────────────\n\nconst moduleDecoratorNotFound: KnownIssue = {\n match(input, _ctx) {\n const hasModuleSymbol =\n input.includes('@Module') ||\n includesAll(input, ['Module', 'is not a function']) ||\n includesAll(input, ['Module', 'no exported member'])\n if (!hasModuleSymbol) return null\n\n return {\n confidence: 80,\n diagnosis: {\n id: 'module-decorator-not-found',\n title: 'KickJS does not have a @Module decorator (different pattern from NestJS)',\n explanation:\n 'NestJS uses @Module({ controllers, providers }). KickJS uses an interface\\n' +\n 'pattern instead: a class implements AppModule and exposes routes() that\\n' +\n 'returns the controller wiring. This was a deliberate choice — modules\\n' +\n 'become explicit values rather than metadata, which makes them easier to\\n' +\n 'compose, test, and serialize.',\n fix: 'Replace the @Module decorator with an AppModule class:',\n codeBefore:\n \"import { Module } from '@forinda/kickjs' // ← does not exist\\n\" +\n \"import { UserController } from './user.controller'\\n\\n\" +\n '@Module({\\n' +\n ' controllers: [UserController],\\n' +\n '})\\n' +\n 'export class UserModule {}',\n codeAfter:\n \"import { type AppModule, type ModuleRoutes, buildRoutes } from '@forinda/kickjs'\\n\" +\n \"import { UserController } from './user.controller'\\n\\n\" +\n 'export class UserModule implements AppModule {\\n' +\n ' routes(): ModuleRoutes {\\n' +\n ' return {\\n' +\n \" path: '/users',\\n\" +\n ' router: buildRoutes(UserController),\\n' +\n ' controller: UserController,\\n' +\n ' }\\n' +\n ' }\\n' +\n '}',\n docs: 'https://forinda.github.io/kick-js/guide/project-structure.html',\n },\n }\n },\n}\n\n// ── Issue 4: legacy KickRoutes['POST /users'] syntax ─────────────────────\n\nconst legacyRoutesSyntax: KnownIssue = {\n match(input, _ctx) {\n // Old syntax: KickRoutes['POST /something'] — check for the bracket\n // form with HTTP verbs that doesn't match the new namespace shape.\n const hasBracketSyntax = /KickRoutes\\s*\\[\\s*['\"](GET|POST|PUT|PATCH|DELETE)/i.test(input)\n if (!hasBracketSyntax) return null\n\n return {\n confidence: 95,\n diagnosis: {\n id: 'legacy-kick-routes-bracket-syntax',\n title: \"KickRoutes['POST /users'] is the legacy v1 syntax\",\n explanation:\n 'KickJS v2 changed the typegen output from a flat string-keyed map to a\\n' +\n 'namespaced shape: KickRoutes.UserController[\"create\"] instead of\\n' +\n 'KickRoutes[\"POST /users\"]. The new form is per-controller, per-method,\\n' +\n 'and matches the actual class names so refactors propagate via\\n' +\n 'rename-symbol instead of grep.',\n fix: 'Update the Ctx<...> type parameter to use the namespace form:',\n codeBefore:\n \"@Post('/', { body: createUserSchema })\\n\" +\n \"create(ctx: Ctx<KickRoutes['POST /users']>) { /* ... */ }\",\n codeAfter:\n \"@Post('/', { body: createUserSchema, name: 'CreateUser' })\\n\" +\n \"create(ctx: Ctx<KickRoutes.UserController['create']>) { /* ... */ }\",\n docs: 'https://forinda.github.io/kick-js/guide/typegen.html',\n },\n }\n },\n}\n\n// ── Issue 5: cluster + Vite dev mode → duplicate servers ────────────────\n\nconst clusterInDevMode: KnownIssue = {\n match(input, _ctx) {\n const hasCluster = includesAny(input, ['cluster', 'workers', 'two ports', 'duplicate server'])\n const hasDevSignal = includesAny(input, [\n 'kick dev',\n 'vite',\n 'eaddrinuse',\n '5173',\n '5174',\n 'two servers',\n ])\n if (!hasCluster || !hasDevSignal) return null\n\n return {\n confidence: 85,\n diagnosis: {\n id: 'cluster-in-vite-dev',\n title: 'Cluster mode is incompatible with `kick dev` (Vite owns the server)',\n explanation:\n 'In dev mode, Vite owns the HTTP server. If your bootstrap passes\\n' +\n 'cluster: { workers: N }, the framework forks N workers, each of which\\n' +\n 'spins up its own Vite instance on a separate port. The fix landed in\\n' +\n 'v2.2.5: McpAdapter (and bootstrap()) now detects Vite dev mode and\\n' +\n 'silently skips cluster, with a warning. If you see this on an older\\n' +\n 'version, upgrade or guard the cluster option behind NODE_ENV.',\n fix: 'Either upgrade to v2.2.5+ or gate cluster mode on production:',\n codeAfter:\n 'export const app = await bootstrap({\\n' +\n ' modules,\\n' +\n \" cluster: process.env.NODE_ENV === 'production' ? { workers: 4 } : false,\\n\" +\n '})',\n docs: 'https://forinda.github.io/kick-js/guide/cluster.html',\n },\n }\n },\n}\n\n// ── Issue 6: missing reflect-metadata import ─────────────────────────────\n\nconst reflectMetadataMissing: KnownIssue = {\n match(input, _ctx) {\n const hasReflectError = includesAny(input, [\n 'reflect-metadata',\n 'Reflect.getMetadata is not a function',\n 'Reflect.defineMetadata',\n 'design:type',\n 'design:paramtypes',\n ])\n if (!hasReflectError) return null\n\n return {\n confidence: 90,\n diagnosis: {\n id: 'reflect-metadata-missing',\n title: 'reflect-metadata is not loaded — DI cannot read decorator types',\n explanation:\n 'The DI container reads constructor parameter types via the\\n' +\n 'reflect-metadata polyfill. The polyfill must be imported once,\\n' +\n 'before any decorator runs. Most projects do this at the top of\\n' +\n 'src/index.ts; missing the import causes obscure \"design:paramtypes\"\\n' +\n 'or \"Reflect.getMetadata is not a function\" errors at runtime.',\n fix: 'Add the import at the very top of src/index.ts:',\n codeAfter:\n \"import 'reflect-metadata' // ← must be the FIRST import\\n\" +\n \"import './config'\\n\" +\n \"import { bootstrap } from '@forinda/kickjs'\\n\" +\n \"import { modules } from './modules'\\n\\n\" +\n 'export const app = await bootstrap({ modules })',\n docs: 'https://forinda.github.io/kick-js/guide/dependency-injection.html',\n },\n }\n },\n}\n\n// ── Issue 7: forgot to register module in modules array ──────────────────\n\nconst moduleNotRegistered: KnownIssue = {\n match(input, _ctx) {\n const hasNotFound = includesAny(input, ['404', 'cannot get', 'cannot post', 'no route'])\n if (!hasNotFound) return null\n\n return {\n confidence: 50,\n diagnosis: {\n id: 'module-not-registered',\n title: 'A 404 may indicate a module is not in the modules array',\n explanation:\n 'KickJS only mounts modules listed in `src/modules/index.ts`. If you\\n' +\n \"generated a module via `kick g module foo` but the routes don't appear,\\n\" +\n 'the most likely cause is that the module is missing from the exported\\n' +\n 'array. The CLI usually wires this automatically, but a hand-edit can\\n' +\n 'drop the entry.',\n fix: 'Open src/modules/index.ts and verify the module is in the array:',\n codeAfter:\n \"import type { AppModuleClass } from '@forinda/kickjs'\\n\" +\n \"import { UserModule } from './users/user.module'\\n\" +\n \"import { TaskModule } from './tasks/task.module' // ← was this missing?\\n\\n\" +\n 'export const modules: AppModuleClass[] = [UserModule, TaskModule]',\n docs: 'https://forinda.github.io/kick-js/guide/project-structure.html',\n },\n }\n },\n}\n\n// ── Registry ──────────────────────────────────────────────────────────────\n\nexport const KNOWN_ISSUES: KnownIssue[] = [\n envSchemaNotRegistered,\n containerNotReset,\n moduleDecoratorNotFound,\n legacyRoutesSyntax,\n clusterInDevMode,\n reflectMetadataMissing,\n moduleNotRegistered,\n]\n\n/**\n * Run every matcher against the input and return the highest-confidence\n * hit, or `null` if no matcher cleared the 40-confidence threshold.\n */\nexport function findBestMatch(input: string, ctx?: ExplainContext): Match | null {\n let best: Match | null = null\n for (const issue of KNOWN_ISSUES) {\n let match: Match | null = null\n try {\n match = issue.match(input, ctx)\n } catch {\n // Matchers should never throw, but if one does, ignore it rather\n // than letting a buggy entry crash the whole explain command.\n continue\n }\n if (!match || match.confidence < 40) continue\n if (!best || match.confidence > best.confidence) {\n best = match\n }\n }\n return best\n}\n","/**\n * LLM fallback for `kick explain --ai`.\n *\n * When the pattern-based known-issues registry doesn't match, this\n * module asks a real LLM provider to produce a structured diagnosis\n * in the same shape. The CLI prints the result through the same\n * formatter as a known-issue match, so the user sees a uniform\n * output regardless of which path produced the answer.\n *\n * `@forinda/kickjs-ai` is imported dynamically: users who don't use\n * any AI features never pay the dependency cost, and if the package\n * is missing the CLI prints a friendly \"run `kick add ai`\" message\n * instead of crashing. Same graceful-degradation pattern as\n * `kick dev`'s handling of `vite`.\n *\n * Provider selection and credentials:\n * - Default provider is OpenAI via `OPENAI_API_KEY`\n * - `--model` on the CLI overrides the default model\n * - Providers other than OpenAI ship in later workstreams; the\n * function signature is ready to accept them\n *\n * @module @forinda/kickjs-cli/explain/ai-fallback\n */\n\nimport type { Diagnosis } from './known-issues'\n\n/** Options for the AI fallback call. */\nexport interface AskAiOptions {\n /** The error text the user wants explained. */\n input: string\n /** Model override. Defaults to whatever the provider picks. */\n model?: string\n /**\n * Provider key — only `'openai'` is wired in this commit. Anthropic,\n * Google, and Ollama slot in once their provider classes land.\n */\n provider?: 'openai'\n /** Project root, so the fallback can mention file paths the user recognizes. */\n cwd?: string\n}\n\n/**\n * One of three discriminated results from the fallback:\n * - `ok`: the LLM returned a valid structured diagnosis\n * - `unavailable`: AI package not installed, or API key missing\n * - `error`: the request went through but failed (bad JSON, 4xx, etc.)\n *\n * The CLI inspects the kind and prints the appropriate message — the\n * function itself never throws on expected failure modes.\n */\nexport type AskAiResult =\n | { kind: 'ok'; diagnosis: Diagnosis }\n | { kind: 'unavailable'; reason: string; suggestion: string }\n | { kind: 'error'; message: string }\n\n/**\n * Ask the configured LLM for a diagnosis of `options.input`.\n *\n * Returns a discriminated result; callers should never assume the\n * LLM was reachable or produced valid output. The function catches\n * every expected failure mode and maps it to a friendly `unavailable`\n * or `error` result — the CLI can then decide how to present it.\n */\nexport async function askAi(options: AskAiOptions): Promise<AskAiResult> {\n const provider = options.provider ?? 'openai'\n\n // Check credentials before importing the package. Avoids the\n // noisy \"can't find @forinda/kickjs-ai\" error for the very common\n // case where the user forgot to set OPENAI_API_KEY.\n const apiKey = process.env.OPENAI_API_KEY\n if (provider === 'openai' && !apiKey) {\n return {\n kind: 'unavailable',\n reason: 'OPENAI_API_KEY environment variable is not set',\n suggestion:\n 'Set OPENAI_API_KEY in your shell, e.g.\\n' +\n ' export OPENAI_API_KEY=\"sk-...\"\\n' +\n '\\n' +\n 'Then re-run `kick explain --ai \"<your error>\"`.',\n }\n }\n\n // Dynamically import @forinda/kickjs-ai so users who never opt\n // into AI features don't pay the dependency cost. If the package\n // isn't installed, surface install instructions rather than\n // letting Node's module resolution error bubble up.\n let aiModule: typeof import('@forinda/kickjs-ai')\n try {\n // eslint-disable-next-line @typescript-eslint/consistent-type-imports\n aiModule = (await import('@forinda/kickjs-ai')) as typeof import('@forinda/kickjs-ai')\n } catch {\n return {\n kind: 'unavailable',\n reason: '@forinda/kickjs-ai is not installed',\n suggestion:\n 'Install the AI package to enable the LLM fallback:\\n' +\n ' kick add ai\\n' +\n '\\n' +\n 'Or manually:\\n' +\n ' pnpm add @forinda/kickjs-ai',\n }\n }\n\n const { OpenAIProvider } = aiModule\n const instance = new OpenAIProvider({\n apiKey: apiKey as string,\n defaultChatModel: options.model ?? 'gpt-4o-mini',\n })\n\n const systemPrompt = buildSystemPrompt(options.cwd)\n const userPrompt = `Error or stack trace:\\n\\n${options.input.trim()}`\n\n try {\n const response = await instance.chat({\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt },\n ],\n })\n const diagnosis = parseDiagnosisFromResponse(response.content)\n if (!diagnosis) {\n return {\n kind: 'error',\n message:\n 'The LLM responded but the payload was not valid JSON in the expected shape. ' +\n 'Try again, or file an issue with the error text.',\n }\n }\n return { kind: 'ok', diagnosis }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n return { kind: 'error', message: `LLM request failed: ${message}` }\n }\n}\n\n// ── Prompt and parsing ────────────────────────────────────────────────────\n\n/**\n * Build the system prompt that tells the LLM what KickJS is and how\n * to structure its response. The prompt is deliberately prescriptive:\n * the caller needs a JSON payload it can render via the same formatter\n * the known-issues path uses, so freeform text doesn't work.\n *\n * Keep this prompt short — every token counts at inference time and\n * the CLI is often called interactively.\n */\nfunction buildSystemPrompt(cwd?: string): string {\n return [\n 'You are a diagnostic assistant for KickJS, a decorator-driven Node.js',\n 'framework built on Express 5 and TypeScript. KickJS projects use:',\n ' - @Controller, @Get, @Post, @Autowired, @Service, @Value decorators',\n ' - An AppModule interface with a routes() method (NOT a @Module decorator)',\n ' - Zod schemas as both runtime validators and OpenAPI sources',\n \" - Ctx<KickRoutes.ControllerName['method']> for typed request context\",\n ' - src/config/index.ts with defineEnv/loadEnv for env schema',\n ' - A side-effect `import \"./config\"` in src/index.ts to register the schema',\n ' - Container.reset() in beforeEach for DI test isolation',\n '',\n 'When the user gives you an error message or stack trace, produce a',\n 'structured diagnosis that helps them fix the bug. You MUST respond',\n 'with a single JSON object (no surrounding prose, no markdown fences)',\n 'matching this shape:',\n '',\n '{',\n ' \"id\": \"<kebab-case-identifier>\",',\n ' \"title\": \"<one-line problem summary>\",',\n ' \"explanation\": \"<multi-line explanation of what is wrong>\",',\n ' \"fix\": \"<multi-line instructions for fixing the problem>\",',\n ' \"codeBefore\": \"<optional: broken code snippet>\",',\n ' \"codeAfter\": \"<optional: corrected code snippet>\",',\n ' \"docs\": \"<optional: KickJS doc URL that discusses this topic>\"',\n '}',\n '',\n 'The KickJS docs live at https://forinda.github.io/kick-js/ — prefer',\n 'that domain for any doc links you suggest.',\n cwd ? `The project is located at ${cwd}.` : '',\n ]\n .filter((line) => line.length > 0)\n .join('\\n')\n}\n\n/**\n * Extract a `Diagnosis` object from the LLM response content.\n *\n * Tries three strategies in order:\n * 1. Parse the whole content as JSON directly\n * 2. Strip a surrounding markdown fence (```json ... ```)\n * 3. Find the first balanced `{ ... }` block and parse that\n *\n * Returns null if none of the strategies produce a valid object with\n * at least the required fields (id, title, explanation, fix).\n */\nfunction parseDiagnosisFromResponse(content: string): Diagnosis | null {\n const attempts = [content, stripMarkdownFence(content), extractFirstJsonObject(content)].filter(\n (s): s is string => s !== null,\n )\n\n for (const attempt of attempts) {\n try {\n const parsed: unknown = JSON.parse(attempt)\n if (isValidDiagnosis(parsed)) {\n return parsed\n }\n } catch {\n continue\n }\n }\n return null\n}\n\nfunction stripMarkdownFence(text: string): string | null {\n const match = text.match(/```(?:json)?\\s*\\n([\\s\\S]*?)```/)\n return match ? (match[1]?.trim() ?? null) : null\n}\n\nfunction extractFirstJsonObject(text: string): string | null {\n // Walk forward counting braces; return the first balanced block.\n const start = text.indexOf('{')\n if (start === -1) return null\n let depth = 0\n let inString = false\n let escape = false\n for (let i = start; i < text.length; i++) {\n const ch = text[i]\n if (escape) {\n escape = false\n continue\n }\n if (ch === '\\\\' && inString) {\n escape = true\n continue\n }\n if (ch === '\"') {\n inString = !inString\n continue\n }\n if (inString) continue\n if (ch === '{') depth++\n if (ch === '}') {\n depth--\n if (depth === 0) return text.slice(start, i + 1)\n }\n }\n return null\n}\n\nfunction isValidDiagnosis(value: unknown): value is Diagnosis {\n if (value === null || typeof value !== 'object') return false\n const v = value as Record<string, unknown>\n return (\n typeof v.id === 'string' &&\n typeof v.title === 'string' &&\n typeof v.explanation === 'string' &&\n typeof v.fix === 'string'\n )\n}\n","import { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport type { Command } from 'commander'\nimport { findBestMatch, type Diagnosis, type ExplainContext } from '../explain/known-issues'\nimport { askAi, type AskAiResult } from '../explain/ai-fallback'\n\n/**\n * `kick explain` — explain a KickJS error and suggest a fix.\n *\n * The command takes an error message (positional arg, --message flag,\n * or stdin), runs it through a registry of known KickJS pitfalls, and\n * prints the highest-confidence diagnosis with a code fix and a doc\n * link. If no matcher hits, it prints a \"no match\" message — the\n * --ai flag (planned) will fall back to an LLM call against the\n * registered AiProvider.\n *\n * The known-issues registry lives in src/explain/known-issues.ts and\n * is the single source of truth for KickJS-specific advice. Adding a\n * new entry takes ~30 lines and gives every user a permanent fix path.\n *\n * @example\n * ```bash\n * # As a positional arg\n * kick explain \"config.get('DATABASE_URL') returned undefined\"\n *\n * # Via stdin (pipe a stack trace)\n * pnpm test 2>&1 | kick explain\n *\n * # Via --message flag\n * kick explain --message \"Reflect.getMetadata is not a function\"\n * ```\n */\nexport function registerExplainCommand(program: Command): void {\n program\n .command('explain [message]')\n .description('Explain a KickJS error and suggest a fix')\n .option('-m, --message <text>', 'Error message to explain (alternative to positional arg)')\n .option('--ai', 'Fall back to LLM if no known-issue matches (requires @forinda/kickjs-ai)')\n .option('--model <name>', 'Model name for the --ai fallback', 'gpt-4o-mini')\n .option('--json', 'Output the diagnosis as JSON for tooling integration')\n .action(async (positional: string | undefined, opts: ExplainOptions) => {\n const input = await resolveInput(positional, opts.message)\n\n if (!input || input.trim().length === 0) {\n process.stderr.write(\n 'Error: no input provided.\\n' +\n '\\n' +\n 'Pass a message as a positional arg, --message flag, or pipe via stdin:\\n' +\n ' kick explain \"config.get returned undefined\"\\n' +\n ' pnpm test 2>&1 | kick explain\\n',\n )\n process.exit(1)\n }\n\n const ctx = buildExplainContext()\n const match = findBestMatch(input, ctx)\n\n if (opts.json && match) {\n process.stdout.write(JSON.stringify({ matched: true, ...match }, null, 2) + '\\n')\n return\n }\n\n if (match) {\n printDiagnosis(input, match.diagnosis, match.confidence)\n return\n }\n\n // No local match. If --ai was set, try the LLM fallback; otherwise\n // print the \"no match\" guidance and exit with code 2 so callers can\n // detect \"I have no answer\" programmatically.\n if (!opts.ai) {\n if (opts.json) {\n process.stdout.write(JSON.stringify({ matched: false }, null, 2) + '\\n')\n process.exit(2)\n }\n printNoMatch(input, false)\n process.exit(2)\n }\n\n const result = await askAi({\n input,\n model: opts.model,\n cwd: ctx.cwd,\n })\n\n if (opts.json) {\n process.stdout.write(JSON.stringify(aiResultToJson(result), null, 2) + '\\n')\n process.exit(result.kind === 'ok' ? 0 : 2)\n }\n\n printAiResult(input, result)\n process.exit(result.kind === 'ok' ? 0 : 2)\n })\n}\n\ninterface ExplainOptions {\n message?: string\n ai?: boolean\n model?: string\n json?: boolean\n}\n\n/** Serialize an AskAiResult for `--json` output. */\nfunction aiResultToJson(result: AskAiResult): Record<string, unknown> {\n if (result.kind === 'ok') {\n return { matched: true, source: 'ai', diagnosis: result.diagnosis }\n }\n if (result.kind === 'unavailable') {\n return { matched: false, aiUnavailable: true, reason: result.reason }\n }\n return { matched: false, aiError: true, error: result.message }\n}\n\n/** Render an AskAiResult to stdout using the same formatting as local matches. */\nfunction printAiResult(input: string, result: AskAiResult): void {\n if (result.kind === 'ok') {\n // Confidence for the AI path is not numeric — the LLM doesn't\n // surface a probability we can trust. Use a fixed label that\n // clearly marks the answer as AI-generated rather than pattern-\n // matched, so users know to sanity-check it.\n printDiagnosis(input, result.diagnosis, -1, /* aiLabel */ true)\n return\n }\n if (result.kind === 'unavailable') {\n process.stdout.write(`\\n Explaining: ${truncate(input.trim(), 200)}\\n\\n`)\n process.stdout.write(` AI fallback unavailable: ${result.reason}\\n\\n`)\n process.stdout.write(`${indent(result.suggestion, ' ')}\\n\\n`)\n return\n }\n process.stdout.write(`\\n Explaining: ${truncate(input.trim(), 200)}\\n\\n`)\n process.stdout.write(` AI fallback error: ${result.message}\\n\\n`)\n}\n\n// ── Input resolution ──────────────────────────────────────────────────────\n\n/**\n * Resolve the error text from positional arg, --message flag, or stdin.\n *\n * Precedence: positional > flag > stdin. We only read stdin if neither\n * of the first two were provided AND stdin is not a TTY (i.e. something\n * is being piped in). Reading from a real TTY would hang waiting for\n * the user to type, which is never what they want.\n */\nasync function resolveInput(positional?: string, flag?: string): Promise<string> {\n if (positional && positional.trim().length > 0) return positional\n if (flag && flag.trim().length > 0) return flag\n if (process.stdin.isTTY) return ''\n return readStdinAll()\n}\n\nfunction readStdinAll(): Promise<string> {\n return new Promise((resolve, reject) => {\n let buffer = ''\n process.stdin.setEncoding('utf8')\n process.stdin.on('data', (chunk: string) => {\n buffer += chunk\n })\n process.stdin.on('end', () => resolve(buffer))\n process.stdin.on('error', reject)\n })\n}\n\n// ── Project context ───────────────────────────────────────────────────────\n\n/**\n * Build a small context object the matchers can use to check project\n * state — e.g. \"does this project have a src/config/index.ts?\".\n *\n * Kept intentionally minimal to avoid pulling the full kick.config\n * loader into a fast-path command. Matchers should treat this as\n * best-effort and degrade gracefully when ctx is undefined.\n */\nfunction buildExplainContext(): ExplainContext {\n const cwd = process.cwd()\n return {\n cwd,\n hasFile: (path: string) => existsSync(resolve(cwd, path)),\n }\n}\n\n// ── Output ────────────────────────────────────────────────────────────────\n\nfunction printDiagnosis(input: string, d: Diagnosis, confidence: number, aiLabel = false): void {\n const inputSnippet = truncate(input.trim(), 200)\n const label = aiLabel ? 'AI-generated — verify before applying' : labelConfidence(confidence)\n\n process.stdout.write(`\\n Explaining: ${inputSnippet}\\n`)\n process.stdout.write(`\\n Match: ${d.id} (${label})\\n`)\n process.stdout.write(` Title: ${d.title}\\n`)\n process.stdout.write(`\\n Diagnosis:\\n${indent(d.explanation, ' ')}\\n`)\n process.stdout.write(`\\n Fix:\\n${indent(d.fix, ' ')}\\n`)\n\n if (d.codeBefore) {\n process.stdout.write(`\\n Before:\\n${indent(d.codeBefore, ' ')}\\n`)\n }\n if (d.codeAfter) {\n process.stdout.write(`\\n After:\\n${indent(d.codeAfter, ' ')}\\n`)\n }\n if (d.docs) {\n process.stdout.write(`\\n Docs: ${d.docs}\\n`)\n }\n process.stdout.write('\\n')\n}\n\nfunction printNoMatch(input: string, aiRequested?: boolean): void {\n const snippet = truncate(input.trim(), 200)\n process.stdout.write(`\\n Explaining: ${snippet}\\n\\n`)\n if (aiRequested) {\n process.stdout.write(\n ' No known-issue matched, and --ai fallback is not yet wired.\\n' +\n ' When @forinda/kickjs-ai ships its provider implementations,\\n' +\n ' this command will call the configured LLM with the error +\\n' +\n ' project context and return a structured fix.\\n\\n',\n )\n } else {\n process.stdout.write(\n ' No known-issue matched. Things you can try:\\n' +\n '\\n' +\n ' 1. Check the framework docs for the error keywords:\\n' +\n ' https://forinda.github.io/kick-js/\\n' +\n '\\n' +\n ' 2. Re-run with --ai to fall back to an LLM (requires\\n' +\n ' @forinda/kickjs-ai with a configured provider):\\n' +\n ' kick explain --ai \"<your error>\"\\n' +\n '\\n' +\n ' 3. File an issue with the error text:\\n' +\n ' https://github.com/forinda/kick-js/issues/new\\n\\n',\n )\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────\n\nfunction indent(text: string, prefix: string): string {\n return text\n .split('\\n')\n .map((line) => `${prefix}${line}`)\n .join('\\n')\n}\n\nfunction truncate(text: string, max: number): string {\n if (text.length <= max) return text\n return text.slice(0, max - 1) + '…'\n}\n\nfunction labelConfidence(score: number): string {\n if (score >= 90) return 'high confidence'\n if (score >= 70) return 'good match'\n if (score >= 50) return 'medium confidence'\n return 'low confidence — verify manually'\n}\n","import { spawn } from 'node:child_process'\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs'\nimport { basename, resolve } from 'node:path'\nimport type { Command } from 'commander'\n\n/**\n * `kick mcp` — Model Context Protocol commands.\n *\n * Two subcommands:\n * - `kick mcp` (default → `start`): runs the built application as an\n * MCP server over stdio. The user's app must already wire `McpAdapter`\n * from `@forinda/kickjs-mcp` into its bootstrap. The CLI just spawns\n * the built entry as a subprocess with `KICK_MCP_STDIO=1`, which the\n * adapter detects and uses to switch its transport from\n * StreamableHTTP to stdio. The subprocess inherits stdin/stdout/stderr\n * so the MCP wire protocol flows directly between the parent process\n * (the MCP client — Claude Code, Cursor, etc.) and the child app.\n * - `kick mcp init`: generates a `.mcp.json` config file pointing at\n * this project, ready to drop into a Claude Code / Cursor workspace.\n *\n * Logs MUST go to stderr in stdio mode — anything written to stdout\n * corrupts the JSON-RPC protocol stream. Pino's default stream is\n * stderr already, so this works out of the box for KickJS apps using\n * the framework's bundled logger.\n */\nexport function registerMcpCommand(program: Command): void {\n const mcp = program.command('mcp').description('Model Context Protocol commands (start | init)')\n\n // ── kick mcp [start] — run as MCP server over stdio ────────────────\n mcp\n .command('start', { isDefault: true })\n .description('Run the built application as an MCP server over stdio')\n .option('-e, --entry <file>', 'Entry file', 'dist/index.js')\n .option('--node-arg <arg...>', 'Extra arguments to pass to node')\n .action(runMcpServer)\n\n // ── kick mcp init — generate .mcp.json for client tools ────────────\n mcp\n .command('init')\n .description('Generate .mcp.json for Claude Code / Cursor / Zed')\n .option('-n, --name <name>', 'Server name (defaults to package.json name)')\n .option('-o, --out <file>', 'Output file', '.mcp.json')\n .option('-f, --force', 'Overwrite an existing entry without prompting')\n .option('--global', 'Write to ~/.mcp.json instead of the project root')\n .action(initMcpConfig)\n}\n\n// ── Subcommand: start ─────────────────────────────────────────────────────\n\ninterface StartOptions {\n entry: string\n nodeArg?: string[]\n}\n\nfunction runMcpServer(opts: StartOptions): void {\n const entry = resolve(opts.entry)\n\n if (!existsSync(entry)) {\n process.stderr.write(\n `Error: entry file not found: ${entry}\\n` +\n `\\n` +\n `Build the app first with \\`kick build\\`, or pass a custom entry:\\n` +\n ` kick mcp -e dist/server.js\\n`,\n )\n process.exit(1)\n }\n\n // Spawn node with the user's entry. The KICK_MCP_STDIO env var tells\n // McpAdapter to switch its transport to stdio. We inherit all three\n // stdio streams so the MCP wire protocol flows directly between the\n // MCP client (parent process) and the user's app.\n const nodeArgs = [...(opts.nodeArg ?? []), entry]\n const child = spawn(process.execPath, nodeArgs, {\n stdio: 'inherit',\n env: {\n ...process.env,\n KICK_MCP_STDIO: '1',\n NODE_ENV: process.env.NODE_ENV ?? 'production',\n },\n })\n\n child.on('error', (err) => {\n process.stderr.write(`Failed to start MCP server: ${err.message}\\n`)\n process.exit(1)\n })\n\n child.on('exit', (code, signal) => {\n if (signal) {\n process.kill(process.pid, signal)\n return\n }\n process.exit(code ?? 0)\n })\n\n // Forward shutdown signals so the child can clean up gracefully\n const forward = (signal: NodeJS.Signals) => {\n if (!child.killed) child.kill(signal)\n }\n process.on('SIGINT', () => forward('SIGINT'))\n process.on('SIGTERM', () => forward('SIGTERM'))\n}\n\n// ── Subcommand: init ──────────────────────────────────────────────────────\n\ninterface InitOptions {\n name?: string\n out: string\n force?: boolean\n global?: boolean\n}\n\ninterface McpServerEntry {\n command: string\n args: string[]\n cwd?: string\n env?: Record<string, string>\n}\n\ninterface McpConfigFile {\n mcpServers: Record<string, McpServerEntry>\n}\n\nfunction initMcpConfig(opts: InitOptions): void {\n const cwd = process.cwd()\n const projectName = readPackageName(cwd) ?? basename(cwd)\n const serverName = opts.name ?? projectName\n\n const outPath = opts.global\n ? resolve(process.env.HOME ?? '.', '.mcp.json')\n : resolve(cwd, opts.out)\n\n // Build the server entry. The cwd field is set so the MCP client can\n // launch the command from anywhere — Claude Code / Cursor pass through\n // to spawn() and the built path is resolved relative to cwd.\n const entry: McpServerEntry = {\n command: 'kick',\n args: ['mcp'],\n cwd,\n }\n\n // Merge with an existing file rather than clobbering it. Multiple\n // projects often share a single .mcp.json — we want to add a new\n // server entry alongside whatever is already there.\n let config: McpConfigFile = { mcpServers: {} }\n if (existsSync(outPath)) {\n try {\n const raw = readFileSync(outPath, 'utf8')\n const parsed = JSON.parse(raw) as Partial<McpConfigFile>\n if (parsed && typeof parsed === 'object' && parsed.mcpServers) {\n config = { mcpServers: { ...parsed.mcpServers } }\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n process.stderr.write(\n `Error: existing ${outPath} is not valid JSON (${message}).\\n` +\n `Fix the file or pass --force to overwrite the entry.\\n`,\n )\n process.exit(1)\n }\n }\n\n if (config.mcpServers[serverName] && !opts.force) {\n process.stderr.write(\n `Error: an entry for \"${serverName}\" already exists in ${outPath}.\\n` +\n `Pass --force to overwrite it, or use --name to pick a different key.\\n`,\n )\n process.exit(1)\n }\n\n config.mcpServers[serverName] = entry\n\n writeFileSync(outPath, JSON.stringify(config, null, 2) + '\\n', 'utf8')\n\n process.stdout.write(\n `\\n ✓ Wrote MCP server entry \"${serverName}\" to ${outPath}\\n` +\n `\\n` +\n ` To activate it:\\n` +\n ` 1. Build your app: kick build\\n` +\n ` 2. Restart your MCP client (Claude Code, Cursor, Zed)\\n` +\n ` 3. The server should appear in the client's tool picker\\n` +\n `\\n`,\n )\n}\n\n/**\n * Read the `name` field from the project's `package.json`. Returns\n * null if the file is missing or unparseable — callers fall back to\n * the directory name in that case.\n */\nfunction readPackageName(cwd: string): string | null {\n const pkgPath = resolve(cwd, 'package.json')\n if (!existsSync(pkgPath)) return null\n try {\n const raw = readFileSync(pkgPath, 'utf8')\n const parsed = JSON.parse(raw) as { name?: unknown }\n if (typeof parsed.name === 'string') return parsed.name\n return null\n } catch {\n return null\n }\n}\n","import { resolve, join } from 'node:path'\nimport { existsSync } from 'node:fs'\nimport { pathToFileURL } from 'node:url'\nimport { fork } from 'node:child_process'\nimport type { Command } from 'commander'\n\nexport function registerTinkerCommand(program: Command): void {\n program\n .command('tinker')\n .description('Interactive REPL with DI container and services loaded')\n .option('-e, --entry <file>', 'Entry file to load', 'src/index.ts')\n .action(async (opts: any) => {\n const cwd = process.cwd()\n const entryPath = resolve(cwd, opts.entry)\n\n if (!existsSync(entryPath)) {\n console.error(`\\n Error: ${opts.entry} not found.\\n`)\n process.exit(1)\n }\n\n // Find tsx for TypeScript + decorator support\n const tsxBin = findBin(cwd, 'tsx')\n if (!tsxBin) {\n console.error('\\n Error: tsx not found. Install it: pnpm add -D tsx\\n')\n process.exit(1)\n }\n\n // Write a temporary tinker script that loads the app and starts REPL\n const tinkerScript = generateTinkerScript(entryPath, opts.entry)\n const tmpFile = join(cwd, '.kick-tinker.mjs')\n\n const { writeFileSync, unlinkSync } = await import('node:fs')\n writeFileSync(tmpFile, tinkerScript, 'utf-8')\n\n try {\n // Run the tinker script under tsx (inherits stdio for interactive REPL)\n const child = fork(tmpFile, [], {\n cwd,\n execPath: tsxBin,\n stdio: 'inherit',\n })\n\n await new Promise<void>((resolve) => {\n child.on('exit', () => resolve())\n })\n } finally {\n // Clean up temp file\n try {\n unlinkSync(tmpFile)\n } catch {\n // ignore\n }\n }\n })\n}\n\nfunction generateTinkerScript(entryPath: string, displayPath: string): string {\n const entryUrl = pathToFileURL(entryPath).href\n\n return `\nimport 'reflect-metadata'\n\n// Prevent bootstrap() from starting the HTTP server\nprocess.env.KICK_TINKER = '1'\n\nconsole.log('\\\\n 🔧 KickJS Tinker')\nconsole.log(' Loading: ${displayPath}\\\\n')\n\n// Load core\nlet Container, Logger, HttpException, HttpStatus\ntry {\n const core = await import('@forinda/kickjs')\n Container = core.Container\n Logger = core.Logger\n HttpException = core.HttpException\n HttpStatus = core.HttpStatus\n} catch {\n console.error(' Error: @forinda/kickjs not found.')\n console.error(' Install it: pnpm add @forinda/kickjs\\\\n')\n process.exit(1)\n}\n\n// Load entry to trigger decorator registration\ntry {\n await import('${entryUrl}')\n} catch (err) {\n console.warn(' Warning: ' + err.message)\n console.warn(' Container may be partially initialized.\\\\n')\n}\n\nconst container = Container.getInstance()\n\n// Start REPL\nconst repl = await import('node:repl')\nconst server = repl.start({ prompt: 'kick> ', useGlobal: true })\n\nserver.context.container = container\nserver.context.Container = Container\nserver.context.resolve = (token) => container.resolve(token)\nserver.context.Logger = Logger\nserver.context.HttpException = HttpException\nserver.context.HttpStatus = HttpStatus\n\nconsole.log(' Available globals:')\nconsole.log(' container — DI container instance')\nconsole.log(' resolve(T) — shorthand for container.resolve(T)')\nconsole.log(' Container, Logger, HttpException, HttpStatus')\nconsole.log()\n\nserver.on('exit', () => {\n console.log('\\\\n Goodbye!\\\\n')\n process.exit(0)\n})\n`\n}\n\nfunction findBin(startDir: string, name: string): string | null {\n let dir = startDir\n while (true) {\n const candidate = join(dir, 'node_modules', '.bin', name)\n if (existsSync(candidate)) return candidate\n const parent = resolve(dir, '..')\n if (parent === dir) break\n dir = parent\n }\n return null\n}\n","import { join } from 'node:path'\nimport { readFile, writeFile, rm } from 'node:fs/promises'\nimport { toPascalCase, toKebabCase, pluralize } from '../utils/naming'\nimport { confirm } from '../utils/prompts'\nimport { colors } from '../utils/colors'\nimport { fileExists } from '../utils/fs'\n\ninterface RemoveModuleOptions {\n name: string\n modulesDir: string\n force?: boolean\n pluralize?: boolean\n}\n\n/**\n * Remove a module — deletes its directory and unregisters it from the modules index.\n */\nexport async function removeModule(options: RemoveModuleOptions): Promise<void> {\n const { name, modulesDir, force } = options\n const shouldPluralize = options.pluralize !== false\n\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const plural = shouldPluralize ? pluralize(kebab) : kebab\n const moduleDir = join(modulesDir, plural)\n\n // Check if module exists\n if (!(await fileExists(moduleDir))) {\n console.log(`\\n Module not found: ${moduleDir}\\n`)\n return\n }\n\n // Confirm deletion unless --force\n if (!force) {\n const confirmed = await confirm({\n message: colors.red(`Delete module '${plural}' at ${moduleDir}? This cannot be undone.`),\n initialValue: false,\n })\n if (!confirmed) {\n console.log('\\n Cancelled.\\n')\n return\n }\n }\n\n // 1. Remove the module directory\n await rm(moduleDir, { recursive: true, force: true })\n console.log(` Deleted: ${moduleDir}`)\n\n // 2. Unregister from modules/index.ts\n const indexPath = join(modulesDir, 'index.ts')\n if (await fileExists(indexPath)) {\n let content = await readFile(indexPath, 'utf-8')\n const originalContent = content\n\n // Remove import line — matches both legacy `'./<plural>'` and current `'./<plural>/<kebab>.module'`\n const importPattern = new RegExp(\n `^import\\\\s*\\\\{\\\\s*${pascal}Module\\\\s*\\\\}\\\\s*from\\\\s*['\"][^'\"]*${plural}(?:/[^'\"]*)?['\"].*\\\\n?`,\n 'gm',\n )\n content = content.replace(importPattern, '')\n\n // Remove from modules array — handle: ModuleName, or ModuleName (last entry)\n content = content.replace(new RegExp(`\\\\s*,?\\\\s*${pascal}Module\\\\s*,?`, 'g'), (match) => {\n // If match starts and ends with comma, keep one comma\n const startsWithComma = match.trimStart().startsWith(',')\n const endsWithComma = match.trimEnd().endsWith(',')\n if (startsWithComma && endsWithComma) return ','\n return ''\n })\n\n // Clean up dangling commas before ]\n content = content.replace(/,(\\s*])/, '$1')\n\n // Clean up double blank lines\n content = content.replace(/\\n{3,}/g, '\\n\\n')\n\n if (content !== originalContent) {\n await writeFile(indexPath, content, 'utf-8')\n console.log(` Unregistered: ${pascal}Module from ${indexPath}`)\n }\n }\n\n console.log(`\\n Module '${plural}' removed.\\n`)\n}\n","import { resolve } from 'node:path'\nimport type { Command } from 'commander'\nimport { removeModule } from '../generators/remove-module'\nimport { loadKickConfig, resolveModuleConfig } from '../config'\n\nexport function registerRemoveCommand(program: Command): void {\n const remove = program.command('remove').alias('rm').description('Remove generated code')\n\n remove\n .command('module <names...>')\n .description('Remove one or more modules (e.g. kick rm module user task)')\n .option('--modules-dir <dir>', 'Modules directory')\n .option('--no-pluralize', 'Use singular module name')\n .option('-f, --force', 'Skip confirmation prompt')\n .action(async (names: string[], opts: any) => {\n const config = await loadKickConfig(process.cwd())\n const mc = resolveModuleConfig(config)\n const modulesDir = opts.modulesDir ?? mc.dir ?? 'src/modules'\n const shouldPluralize = opts.pluralize === false ? false : (mc.pluralize ?? true)\n\n for (const name of names) {\n await removeModule({\n name,\n modulesDir: resolve(modulesDir),\n force: opts.force,\n pluralize: shouldPluralize,\n })\n }\n })\n}\n","/**\n * `kick typegen` — generate type-safe DI registry and module manifests\n * inside `.kickjs/types/`. Mirrors the React Router `.react-router/types/`\n * pattern.\n *\n * Usage:\n * kick typegen # one-shot\n * kick typegen --watch # rebuild on file changes\n */\n\nimport type { Command } from 'commander'\nimport { runTypegen, TokenCollisionError, watchTypegen } from '../typegen'\nimport { runAllPluginTypegens } from '../typegen/run-plugins'\nimport { loadKickConfig } from '../config'\n\ninterface TypegenCliOptions {\n watch?: boolean\n src: string\n out: string\n silent?: boolean\n allowDuplicates?: boolean\n schemaValidator?: string\n envFile?: string\n check?: boolean\n list?: boolean\n}\n\n/**\n * Parse the `--schema-validator` CLI flag. Returns `undefined` if the\n * flag was not passed (so the config default applies), `'zod'` if a\n * supported value was passed, or `false` if explicitly disabled.\n */\nfunction parseSchemaValidatorFlag(value: string | undefined): 'zod' | false | undefined {\n if (value === undefined) return undefined\n if (value === 'false' || value === 'off' || value === 'none') return false\n if (value === 'zod') return 'zod'\n console.warn(\n ` kick typegen: unknown --schema-validator '${value}' (only 'zod' and 'false' are supported). ` +\n `Falling back to project config.`,\n )\n return undefined\n}\n\n/**\n * Parse the `--env-file` CLI flag. Returns `undefined` to fall through\n * to the config default, `false` when the user disables env typing\n * with `--env-file false`, or the literal path string otherwise.\n */\nfunction parseEnvFileFlag(value: string | undefined): string | false | undefined {\n if (value === undefined) return undefined\n if (value === 'false' || value === 'off' || value === 'none') return false\n return value\n}\n\nexport function registerTypegenCommand(program: Command): void {\n program\n .command('typegen')\n .description('Generate type-safe DI registry and module types into .kickjs/types/')\n .option('-w, --watch', 'Watch source files and regenerate on change')\n .option('-s, --src <dir>', 'Source directory to scan', 'src')\n .option('-o, --out <dir>', 'Output directory', '.kickjs/types')\n .option('--silent', 'Suppress output')\n .option(\n '--allow-duplicates',\n 'Auto-namespace duplicate class names instead of failing (use with caution)',\n )\n .option(\n '--schema-validator <name>',\n \"Schema validator for body/query/params typing (currently 'zod' or 'false')\",\n )\n .option(\n '--env-file <path>',\n \"Path to env schema file for KickEnv typing (default 'src/env.ts'; pass 'false' to disable)\",\n )\n .option('--check', 'CI gate: fail on plugin-typegen drift instead of writing')\n .option('--list', 'List every registered typegen plugin id (use to populate `typegen.disable`)')\n .action(async (opts: TypegenCliOptions) => {\n const cwd = process.cwd()\n\n // CLI flag wins over kick.config.ts; the config sets the project default.\n const config = await loadKickConfig(cwd)\n\n // --list short-circuits: print the registered typegen ids + their\n // owning plugin and exit. Adopters use this to discover what to\n // put in `typegen.disable`.\n if (opts.list) {\n const { mergeCliPlugins } = await import('../plugin')\n const { builtinCliPlugins } = await import('../plugin/builtins')\n const allPlugins = [...builtinCliPlugins, ...(config?.plugins ?? [])]\n const merged = mergeCliPlugins(allPlugins, config?.commands ?? [])\n const disabled = new Set(config?.typegen?.disable ?? [])\n if (merged.typegens.length === 0) {\n console.log(' No typegen plugins registered.')\n return\n }\n const idWidth = Math.max(...merged.typegens.map((t) => t.id.length))\n console.log('\\n Registered typegen plugins:\\n')\n for (const tg of merged.typegens) {\n const status = disabled.has(tg.id) ? ' (disabled)' : ''\n console.log(\n ` ${tg.id.padEnd(idWidth + 2)}inputs: ${tg.inputs.join(', ') || '(none)'}${status}`,\n )\n }\n console.log()\n return\n }\n const cliValidator = parseSchemaValidatorFlag(opts.schemaValidator)\n const schemaValidator = cliValidator ?? config?.typegen?.schemaValidator ?? 'zod'\n const envFile = parseEnvFileFlag(opts.envFile) ?? config?.typegen?.envFile\n\n const baseOpts = {\n cwd,\n srcDir: opts.src ?? config?.typegen?.srcDir,\n outDir: opts.out ?? config?.typegen?.outDir,\n silent: opts.silent,\n allowDuplicates: opts.allowDuplicates,\n schemaValidator,\n envFile,\n // Asset typegen (assets-plan.md PR 4) — drives `KickAssets`\n // augmentation generation. No-op when assetMap is empty.\n assetMap: config?.assetMap,\n // The CLI command drives the plugin pipeline directly (see\n // runAllPluginTypegens below) so it can surface --check drift\n // and per-plugin status. Opting out of `runTypegen`'s built-in\n // pass prevents a double run on every `kick typegen` invocation.\n runPlugins: false,\n }\n\n try {\n if (opts.watch) {\n const stop = await watchTypegen(baseOpts)\n if (!opts.silent) {\n console.log(' kick typegen: watching for changes (Ctrl-C to exit)')\n }\n const shutdown = () => {\n stop()\n process.exit(0)\n }\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n // Keep the event loop alive until shutdown\n await new Promise<void>(() => {})\n } else {\n await runTypegen(baseOpts)\n\n // Plugin-typegen pipeline runs after the legacy pass. The\n // helper handles merging builtins with user plugins, applies\n // the `typegen.disable` filter, logs per-plugin status, and\n // surfaces drift for the --check exit code.\n const results = await runAllPluginTypegens({\n cwd,\n config: config ?? null,\n silent: opts.silent,\n check: opts.check,\n })\n if (opts.check && results.some((r) => r.status === 'written')) {\n process.exit(1)\n }\n }\n } catch (err: unknown) {\n if (err instanceof TokenCollisionError) {\n console.error('\\n' + err.message + '\\n')\n } else if (err instanceof Error) {\n console.error(`\\n kick typegen failed: ${err.message}`)\n } else {\n console.error(`\\n kick typegen failed: ${JSON.stringify(err)}`)\n }\n process.exit(1)\n }\n })\n}\n","import { readFileSync, existsSync, readdirSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { Command } from 'commander'\nimport { colors, severityColor } from '../utils/colors'\nimport { intro, outro, spinner as createSpinner, log } from '../utils/prompts'\n\n/* ── Severity levels ──────────────────────────────────────────────── */\n\ntype Severity = 'CRITICAL' | 'WARNING' | 'INFO'\n\ninterface CheckResult {\n severity: Severity\n message: string\n}\n\n/* ── File scanner ─────────────────────────────────────────────────── */\n\n/** Recursively collect all .ts files under a directory */\nfunction collectTsFiles(dir: string): string[] {\n const files: string[] = []\n if (!existsSync(dir)) return files\n\n const entries = readdirSync(dir, { withFileTypes: true })\n for (const entry of entries) {\n const fullPath = join(dir, entry.name)\n if (entry.isDirectory()) {\n // Skip node_modules, dist, .kickjs\n if (['node_modules', 'dist', '.kickjs', '.git'].includes(entry.name)) continue\n files.push(...collectTsFiles(fullPath))\n } else if (entry.isFile() && /\\.tsx?$/.test(entry.name) && !entry.name.endsWith('.d.ts')) {\n files.push(fullPath)\n }\n }\n return files\n}\n\n/** Read a file safely, returning empty string on failure */\nfunction safeRead(filepath: string): string {\n try {\n return readFileSync(filepath, 'utf-8')\n } catch {\n return ''\n }\n}\n\n/* ── Individual checks ────────────────────────────────────────────── */\n\nconst WEAK_SECRETS = new Set(['secret', 'changeme', 'password', 'test', 'default', ''])\n\nfunction checkJwtSecret(cwd: string, sourceContents: string[]): CheckResult | null {\n // Check .env file\n const envPath = join(cwd, '.env')\n const envContent = safeRead(envPath)\n\n if (envContent) {\n const match = envContent.match(/^JWT_SECRET\\s*=\\s*['\"]?([^'\"\\n]*)['\"]?/m)\n if (match) {\n const value = match[1].trim()\n if (WEAK_SECRETS.has(value.toLowerCase()) || value.length < 32) {\n return {\n severity: 'CRITICAL',\n message: 'JWT_SECRET appears to be a default value or too short (< 32 chars) — change it',\n }\n }\n }\n }\n\n // Check source files for hardcoded weak secrets\n for (const content of sourceContents) {\n // Look for JWT_SECRET assignments with weak values\n const patterns = [\n /JWT_SECRET['\"]?\\s*[:=]\\s*['\"]?(secret|changeme|password|test|default)['\"]?/i,\n /secret\\s*[:=]\\s*['\"]?(secret|changeme|password|test|default)['\"]?/i,\n ]\n for (const pattern of patterns) {\n if (pattern.test(content)) {\n return {\n severity: 'CRITICAL',\n message:\n 'JWT_SECRET appears to be a default value in source code — use an environment variable',\n }\n }\n }\n }\n\n return null\n}\n\nfunction checkCorsOrigin(sourceContents: string[]): CheckResult | null {\n for (const content of sourceContents) {\n // Match cors({ ... origin: '*' ... }) or cors({ origin: ['*'] })\n if (/cors\\s*\\(/.test(content) && /origin\\s*:\\s*['\"]\\*['\"]/.test(content)) {\n return {\n severity: 'CRITICAL',\n message: \"CORS origin is '*' — restrict to your domains\",\n }\n }\n }\n return null\n}\n\nfunction checkRateLimiting(sourceContents: string[]): CheckResult | null {\n for (const content of sourceContents) {\n if (/rateLimit/i.test(content) || /@RateLimit/i.test(content)) {\n return null // Found rate limiting\n }\n }\n return {\n severity: 'WARNING',\n message: 'No rate limiting detected — add rateLimit() middleware or @RateLimit decorator',\n }\n}\n\nfunction checkNodeEnv(): CheckResult | null {\n if (process.env.NODE_ENV !== 'production') {\n return {\n severity: 'WARNING',\n message: `NODE_ENV is '${process.env.NODE_ENV ?? 'undefined'}', not 'production'`,\n }\n }\n return null\n}\n\nfunction checkTokenStore(sourceContents: string[]): CheckResult | null {\n let hasTokenStore = false\n let usesMemoryStore = false\n\n for (const content of sourceContents) {\n if (/tokenStore/i.test(content)) hasTokenStore = true\n if (/MemoryTokenStore/i.test(content)) usesMemoryStore = true\n }\n\n if (usesMemoryStore) {\n return {\n severity: 'WARNING',\n message:\n 'MemoryTokenStore detected — use a persistent store (Redis, DB) for production deployments',\n }\n }\n if (!hasTokenStore) {\n return {\n severity: 'WARNING',\n message: 'No token revocation store detected — consider adding one for auth token management',\n }\n }\n return null\n}\n\nfunction checkHelmet(sourceContents: string[]): CheckResult {\n for (const content of sourceContents) {\n if (/helmet\\s*\\(/.test(content)) {\n // Check it's not disabled\n if (/security\\s*\\.\\s*helmet\\s*.*false/.test(content)) {\n return {\n severity: 'WARNING',\n message: 'Helmet security headers are disabled — enable them for production',\n }\n }\n return {\n severity: 'INFO',\n message: 'Helmet security headers active',\n }\n }\n }\n return {\n severity: 'WARNING',\n message: 'Helmet not detected — add helmet() middleware for security headers',\n }\n}\n\nfunction checkAuthAdapter(sourceContents: string[]): CheckResult {\n for (const content of sourceContents) {\n if (/AuthAdapter/i.test(content)) {\n return {\n severity: 'INFO',\n message: 'AuthAdapter configured',\n }\n }\n }\n return {\n severity: 'INFO',\n message: 'No AuthAdapter detected — add one if your app requires authentication',\n }\n}\n\n/* ── Main check runner ────────────────────────────────────────────── */\n\nfunction runDeployChecks(cwd: string): CheckResult[] {\n const srcDir = join(cwd, 'src')\n const tsFiles = collectTsFiles(srcDir)\n const sourceContents = tsFiles.map((f) => safeRead(f))\n\n const results: CheckResult[] = []\n\n // CRITICAL checks\n const jwtResult = checkJwtSecret(cwd, sourceContents)\n if (jwtResult) results.push(jwtResult)\n\n const corsResult = checkCorsOrigin(sourceContents)\n if (corsResult) results.push(corsResult)\n\n // WARNING checks\n const rateLimitResult = checkRateLimiting(sourceContents)\n if (rateLimitResult) results.push(rateLimitResult)\n\n const nodeEnvResult = checkNodeEnv()\n if (nodeEnvResult) results.push(nodeEnvResult)\n\n const tokenStoreResult = checkTokenStore(sourceContents)\n if (tokenStoreResult) results.push(tokenStoreResult)\n\n // INFO checks\n results.push(checkHelmet(sourceContents))\n results.push(checkAuthAdapter(sourceContents))\n\n return results\n}\n\n/* ── Command registration ─────────────────────────────────────────── */\n\nexport function registerCheckCommand(program: Command): void {\n program\n .command('check')\n .description('Audit project for common issues')\n .option('--deploy', 'Run production readiness checks')\n .action((opts: any) => {\n if (!opts.deploy) {\n console.log(\n '\\n Usage: kick check --deploy\\n\\n' +\n ' Available checks:\\n' +\n ' --deploy Audit for production readiness (security, config, best practices)\\n',\n )\n return\n }\n\n const cwd = process.cwd()\n\n intro('KickJS Deploy Check')\n\n const s = createSpinner()\n s.start('Scanning project...')\n const results = runDeployChecks(cwd)\n s.stop('Scan complete')\n\n // Sort: CRITICAL first, then WARNING, then INFO\n const order: Record<Severity, number> = { CRITICAL: 0, WARNING: 1, INFO: 2 }\n results.sort((a, b) => order[a.severity] - order[b.severity])\n\n for (const r of results) {\n log.message(`${severityColor(r.severity)} ${r.message}`)\n }\n\n const critical = results.filter((r) => r.severity === 'CRITICAL').length\n const warnings = results.filter((r) => r.severity === 'WARNING').length\n const info = results.filter((r) => r.severity === 'INFO').length\n\n const warnLabel = warnings === 1 ? 'warning' : 'warnings'\n const criticalText =\n critical > 0 ? colors.red(`${critical} critical`) : `${critical} critical`\n const warningText =\n warnings > 0 ? colors.yellow(`${warnings} ${warnLabel}`) : `${warnings} ${warnLabel}`\n const summary = [criticalText, warningText, `${info} info`].join(', ')\n\n if (critical > 0) {\n outro(colors.red(`${summary} — fix critical issues before deploying`))\n process.exit(1)\n } else {\n outro(colors.green(`${summary} — looking good!`))\n }\n })\n}\n","import path from 'node:path'\nimport type { Command } from 'commander'\n\nimport { writeFile } from 'node:fs/promises'\n\nimport {\n generate,\n resolveDbConfig,\n migrateLatest,\n migrateUp,\n migrateDown,\n migrateRollback,\n migrateStatus,\n renderSchemaSource,\n type DbConfig,\n type MigrationAdapter,\n} from '@forinda/kickjs-db'\n\ninterface BaseOpts {\n config: string\n}\n\nasync function loadConfig(opts: BaseOpts): Promise<DbConfig> {\n return resolveDbConfig({ configPath: path.resolve(process.cwd(), opts.config) })\n}\n\n/**\n * Resolve a MigrationAdapter from config:\n * 1. config.adapter() — explicit factory wins.\n * 2. config.connectionString — built-in pgAdapter path; we dynamically\n * import @forinda/kickjs-db-pg + pg so the CLI doesn't pull pg into\n * non-PG workflows.\n */\nasync function resolveAdapter(config: DbConfig): Promise<{\n adapter: MigrationAdapter\n cleanup: () => Promise<void>\n}> {\n if (config.adapter) {\n const adapter = await config.adapter()\n return { adapter, cleanup: async () => adapter.close() }\n }\n if (!config.connectionString) {\n throw new Error(\n 'kickjs-db: no adapter resolved — set db.connectionString (or DATABASE_URL) in kick.config.ts, or supply db.adapter() factory',\n )\n }\n const dialect = config.dialect ?? 'postgres'\n if (dialect !== 'postgres') {\n throw new Error(\n `kickjs-db: built-in CLI adapter only supports postgres in M1 (dialect=${dialect}); use db.adapter() factory for other dialects`,\n )\n }\n // Dynamic import so the CLI doesn't hard-require pg unless this path runs.\n // Both packages are devDependencies of @forinda/kickjs-cli so types resolve;\n // adopters who use this path must also install them in their own app.\n const [{ pgAdapter }, pg] = await Promise.all([import('@forinda/kickjs-db-pg'), import('pg')])\n const pool = new pg.default.Pool({ connectionString: config.connectionString })\n const adapter = pgAdapter({ pool })\n return {\n adapter,\n cleanup: async () => {\n await adapter.close()\n await pool.end()\n },\n }\n}\n\nfunction printStatusTable(status: Awaited<ReturnType<typeof migrateStatus>>): void {\n if (status.length === 0) {\n console.log('No migrations.')\n return\n }\n console.table(\n status.map((s) => ({\n id: s.id,\n state: s.state,\n batch: s.batch ?? '-',\n reviewed: s.reviewed,\n applied: s.appliedAt ?? '-',\n })),\n )\n}\n\nexport function registerDbCommands(program: Command): void {\n const db = program.command('db').description('Database commands (kickjs-db)')\n\n db.command('generate <name>')\n .description('Generate a new migration from schema diff')\n .option('-c, --config <path>', 'Path to kick.config.ts', 'kick.config.ts')\n .option(\n '-e, --empty',\n 'Skip schema diff and create an empty migration shell (data migration, seed, freeform SQL)',\n )\n .action(async (name: string, opts: BaseOpts & { empty?: boolean }) => {\n const cwd = process.cwd()\n const config = await loadConfig(opts)\n const result = await generate({ name, config, cwd, empty: opts.empty })\n\n if (result.status === 'no-changes') {\n console.log('No schema changes detected.')\n return\n }\n if (result.empty) {\n console.log(`Created empty migration ${result.migrationDir} (author up.sql + down.sql).`)\n return\n }\n const plural = result.changeCount === 1 ? '' : 's'\n console.log(\n `Created migration ${result.migrationDir} (${result.changeCount} change${plural}).`,\n )\n })\n\n // ── migrate runner subcommands ─────────────────────────────────────────\n const migrate = db.command('migrate').description('Migration runner subcommands')\n\n migrate\n .command('latest')\n .description('Apply all pending migrations in a new batch')\n .option('-c, --config <path>', 'Path to kick.config.ts', 'kick.config.ts')\n .action(async (opts: BaseOpts) => {\n const config = await loadConfig(opts)\n const { adapter, cleanup } = await resolveAdapter(config)\n try {\n const r = await migrateLatest({ adapter, migrationsDir: config.migrationsDir })\n if (r.applied.length === 0) {\n console.log('No pending migrations.')\n } else {\n console.log(`Applied batch ${r.batch}: ${r.applied.join(', ')}`)\n }\n } finally {\n await cleanup()\n }\n })\n\n migrate\n .command('up')\n .description('Apply the next single pending migration')\n .option('-c, --config <path>', 'Path to kick.config.ts', 'kick.config.ts')\n .action(async (opts: BaseOpts) => {\n const config = await loadConfig(opts)\n const { adapter, cleanup } = await resolveAdapter(config)\n try {\n const r = await migrateUp({ adapter, migrationsDir: config.migrationsDir })\n if (r.applied.length === 0) {\n console.log('No pending migrations.')\n } else {\n console.log(`Applied ${r.applied[0]} (batch ${r.batch})`)\n }\n } finally {\n await cleanup()\n }\n })\n\n migrate\n .command('down')\n .description('Reverse the most recent applied migration')\n .option('-c, --config <path>', 'Path to kick.config.ts', 'kick.config.ts')\n .action(async (opts: BaseOpts) => {\n const config = await loadConfig(opts)\n const { adapter, cleanup } = await resolveAdapter(config)\n try {\n const r = await migrateDown({ adapter, migrationsDir: config.migrationsDir })\n if (!r.reversed) {\n console.log('Nothing to reverse.')\n } else {\n console.log(`Reversed ${r.reversed}.`)\n }\n } finally {\n await cleanup()\n }\n })\n\n migrate\n .command('rollback')\n .description('Reverse the entire last batch as a single unit')\n .option('-c, --config <path>', 'Path to kick.config.ts', 'kick.config.ts')\n .action(async (opts: BaseOpts) => {\n const config = await loadConfig(opts)\n const { adapter, cleanup } = await resolveAdapter(config)\n try {\n const r = await migrateRollback({ adapter, migrationsDir: config.migrationsDir })\n if (r.reversed.length === 0) {\n console.log('Nothing to roll back.')\n } else {\n console.log(`Rolled back batch ${r.batch}: ${r.reversed.join(', ')}`)\n }\n } finally {\n await cleanup()\n }\n })\n\n migrate\n .command('status')\n .description('Print applied + pending migrations')\n .option('-c, --config <path>', 'Path to kick.config.ts', 'kick.config.ts')\n .action(async (opts: BaseOpts) => {\n const config = await loadConfig(opts)\n const { adapter, cleanup } = await resolveAdapter(config)\n try {\n const status = await migrateStatus({ adapter, migrationsDir: config.migrationsDir })\n printStatusTable(status)\n } finally {\n await cleanup()\n }\n })\n\n // ── kick db introspect ─────────────────────────────────────────────────\n db.command('introspect')\n .description('Generate a TypeScript schema file from a live database')\n .option('-c, --config <path>', 'Path to kick.config.ts', 'kick.config.ts')\n .option('--out <path>', 'Output file (defaults to db.schemaPath from config)')\n .option('--json', 'Print the raw SchemaSnapshot JSON to stdout instead of writing TS source')\n .action(async (opts: BaseOpts & { out?: string; json?: boolean }) => {\n const config = await loadConfig(opts)\n const { adapter, cleanup } = await resolveAdapter(config)\n try {\n const snapshot = await adapter.introspect()\n if (opts.json) {\n console.log(JSON.stringify(snapshot, null, 2))\n return\n }\n const out = opts.out ?? config.schemaPath\n const source = renderSchemaSource(snapshot)\n await writeFile(out, source, 'utf8')\n const tableCount = Object.keys(snapshot.tables).length\n console.log(`Wrote ${out} (${tableCount} table${tableCount === 1 ? '' : 's'}).`)\n } finally {\n await cleanup()\n }\n })\n}\n","// kick/db typegen plugin — M2.B-T9.\n//\n// Reads the adopter's schema (a `src/db/schema.ts` file or `src/db/schema/`\n// folder with a barrel index) and emits `.kickjs/types/kick__db.d.ts`\n// containing two augmentations:\n//\n// 1. A global `KickDbSchema` interface set to `SchemaToTypes<typeof\n// appSchema>` — TS computes the column-level shape at type-check time\n// from the imported schema; no runtime cost, no manual mirroring.\n// 2. A `KickDbRegister` augmentation pointing `db` at\n// `KickDbClient<KickDbSchema>`, so consumers of bare `KickDbClient`\n// widen automatically.\n//\n// Adopters who used to hand-write `src/db/register.ts` (M2.A-T6) can\n// delete that file once this plugin emits the equivalent augmentation.\n\nimport path from 'node:path'\nimport { existsSync } from 'node:fs'\n\nimport type { TypegenPlugin } from '../plugin'\n\nconst DEFAULT_SCHEMA_PATHS = [\n 'src/db/schema.ts',\n 'src/db/schema/index.ts',\n 'src/db/schema',\n] as const\n\nexport const kickDbTypegen = (): TypegenPlugin => ({\n id: 'kick/db',\n inputs: ['src/db/schema.ts', 'src/db/schema/**/*.ts'],\n async generate(ctx) {\n const schemaAbs = resolveSchema(ctx.cwd)\n if (!schemaAbs) return null\n\n // Strip the `.ts` extension and any trailing `/index` so the import\n // specifier stays stable across single-file vs barrel-folder layouts —\n // TS resolves both forms identically.\n const typesDir = path.resolve(ctx.cwd, '.kickjs/types')\n const rel = posix(path.relative(typesDir, schemaAbs))\n .replace(/\\.ts$/, '')\n .replace(/\\/index$/, '')\n\n return [\n `import type { SchemaToTypes, KickDbClient } from '@forinda/kickjs-db'`,\n `import type * as appSchema from '${rel}'`,\n ``,\n `declare global {`,\n ` interface KickDbSchema extends SchemaToTypes<typeof appSchema> {}`,\n `}`,\n ``,\n `declare module '@forinda/kickjs-db' {`,\n ` interface KickDbRegister {`,\n ` db: KickDbClient<KickDbSchema>`,\n ` }`,\n `}`,\n ].join('\\n')\n },\n})\n\nfunction resolveSchema(cwd: string): string | null {\n for (const candidate of DEFAULT_SCHEMA_PATHS) {\n const abs = path.resolve(cwd, candidate)\n if (candidate.endsWith('.ts')) {\n if (existsSync(abs)) return abs\n } else {\n // folder — look for an index.ts barrel\n const idx = path.join(abs, 'index.ts')\n if (existsSync(idx)) return idx\n }\n }\n return null\n}\n\nfunction posix(p: string): string {\n return p.replace(/\\\\/g, '/')\n}\n","// kick/assets typegen plugin — M2.B-T8.\n//\n// Walks the project's `assetMap` and emits the same KickAssets\n// augmentation the legacy generator produces, but routed through the\n// TypegenPlugin contract. During the transition both this plugin and\n// the legacy generator's `assets.d.ts` emission run; TS merges the\n// two interface declarations safely (identical content). The legacy\n// emission removes once routes/env land as plugins too.\n//\n// Resolution: returns null when there's no assetMap or it's empty.\n\nimport { existsSync } from 'node:fs'\nimport path from 'node:path'\n\nimport type { TypegenPlugin } from '../plugin'\nimport { discoverAssets, renderAssetTypes } from '../asset-types'\nimport { loadKickConfig } from '../../config'\n\nexport const kickAssetsTypegen = (): TypegenPlugin => ({\n id: 'kick/assets',\n // The watcher subscribes to kick.config.ts (so adding a new asset\n // namespace re-fires) plus a lightweight glob over each declared\n // src dir. The merge logic in run-plugins resolves this lazily.\n inputs: ['kick.config.ts', 'kick.config.js', 'kick.config.mjs'],\n async generate(ctx) {\n // The plugin runner doesn't load kick.config.ts itself — it leaves\n // each plugin in charge of its own inputs. Cheap to re-load here;\n // the cli wraps this plugin in run-plugins.ts which already has\n // the config but doesn't pass it through ctx (T8 follow-up).\n const cfgPath = path.resolve(ctx.cwd, 'kick.config.ts')\n if (!existsSync(cfgPath)) return null\n const config = await loadKickConfig(ctx.cwd)\n if (!config?.assetMap) return null\n const discovered = discoverAssets(config.assetMap, ctx.cwd)\n if (discovered.count === 0) return null\n return renderAssetTypes(discovered)\n },\n})\n","// Pure render module for the `KickRoutes` global namespace augmentation.\n//\n// Lives outside `generator.ts` so the new `kick/routes` typegen plugin\n// can call it without importing the legacy generator's monolithic\n// `generateTypes()`. Functionally identical to what `generator.ts`\n// emitted before the M2.B-T8 carve — same hoisted-import strategy,\n// same per-controller interface layout, same query-shape inference.\n//\n// The header banner is the plugin runner's responsibility (see\n// `runTypegen` in ../runner.ts), not this module's. Keep `renderRoutes`\n// returning just the augmentation source so the plugin doesn't\n// double-emit the boilerplate.\n\nimport { dirname, relative, resolve, sep } from 'node:path'\n\nimport type { DiscoveredRoute } from '../scanner'\n\nconst ROUTES_HEADER = `/* 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`\n\n/**\n * Render the `KickRoutes` global namespace augmentation. Each interface\n * inside corresponds to a controller class; each property is a single\n * route method on that controller, conforming to `RouteShape`.\n *\n * Fills `params` from URL patterns, `query` from `@ApiQueryParams`, and\n * `body`/`query`/`params` (when schema-validated) from the configured\n * schema validator. `response` is emitted as `unknown`.\n */\nexport function renderRoutes(\n routes: DiscoveredRoute[],\n routesOutFile: string,\n schemaValidator: 'zod' | false,\n): string {\n if (routes.length === 0) {\n return `${ROUTES_HEADER}\n// (no routes discovered yet — annotate a controller method with\n// @Get/@Post/@Put/@Delete/@Patch and re-run \\`kick typegen\\`)\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace KickRoutes {}\n}\n\nexport {}\n`\n }\n\n // Group routes by controller for emission\n const byController = new Map<string, DiscoveredRoute[]>()\n for (const r of routes) {\n const arr = byController.get(r.controller) ?? []\n arr.push(r)\n byController.set(r.controller, arr)\n }\n\n // Hoisted schema imports — collected during interface rendering, then\n // emitted at the top of `routes.ts` so the in-namespace type references\n // resolve correctly. (Inline `import('...').X` inside `.d.ts` files\n // silently degrades to `unknown` with `moduleResolution: 'bundler'`.)\n const schemaImports = new Map<string, { identifier: string; specifier: string }>()\n\n const renderField = (\n schema: { identifier: string; source: string | null } | null,\n routeFilePath: string,\n ): string | null => {\n const alias = planSchemaImport(\n schema,\n routeFilePath,\n routesOutFile,\n schemaValidator,\n schemaImports,\n )\n return alias ? `import('zod').infer<typeof ${alias}>` : null\n }\n\n const interfaces: string[] = []\n for (const [controller, methods] of byController) {\n const lines: string[] = [` interface ${controller} {`]\n for (const m of methods) {\n // Empty `{}` (rather than `Record<string, never>`) so that accessing\n // an unknown property on a paramless route is a type error in strict\n // mode. `Record<string, never>` returns `never` for any access which\n // unfortunately is assignable to anything and silently passes.\n const urlParamsType =\n m.pathParams.length > 0 ? `{ ${m.pathParams.map((p) => `${p}: string`).join('; ')} }` : '{}'\n\n // Schema-driven types win over the URL-pattern / `unknown` defaults\n // when the user has wired a schema in the route decorator.\n const bodySchemaType = renderField(m.bodySchema, m.filePath)\n const querySchemaType = renderField(m.querySchema, m.filePath)\n const paramsSchemaType = renderField(m.paramsSchema, m.filePath)\n\n const paramsType = paramsSchemaType ?? urlParamsType\n const bodyType = bodySchemaType ?? 'unknown'\n const queryType = querySchemaType ?? renderQueryShape(m)\n const docLines = renderQueryDocLines(m)\n lines.push(\n ` /**`,\n ` * ${m.httpMethod} ${m.path}`,\n ...docLines.map((d) => ` * ${d}`),\n ` */`,\n ` ${m.method}: {`,\n ` params: ${paramsType}`,\n ` body: ${bodyType}`,\n ` query: ${queryType}`,\n ` response: unknown`,\n ` }`,\n )\n }\n lines.push(' }')\n interfaces.push(lines.join('\\n'))\n }\n\n const importBlock = renderSchemaImports(schemaImports)\n const interfaceBlock = interfaces.join('\\n')\n\n return `${ROUTES_HEADER}${importBlock}\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace KickRoutes {\n${interfaceBlock}\n }\n}\n\nexport {}\n`\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────\n\nfunction renderQueryShape(m: DiscoveredRoute): string {\n if (m.queryFilterable === null) return 'unknown'\n const sortable = m.querySortable ?? []\n const sortType =\n sortable.length > 0 ? sortable.flatMap((f) => [`'${f}'`, `'-${f}'`]).join(' | ') : 'string'\n return `{ filter?: string | string[]; sort?: ${sortType}; q?: string; page?: string; limit?: string }`\n}\n\nfunction renderQueryDocLines(m: DiscoveredRoute): string[] {\n const lines: string[] = []\n if (m.queryFilterable && m.queryFilterable.length > 0) {\n lines.push(`Filterable: ${m.queryFilterable.join(', ')}`)\n }\n if (m.querySortable && m.querySortable.length > 0) {\n lines.push(`Sortable: ${m.querySortable.join(', ')}`)\n }\n if (m.querySearchable && m.querySearchable.length > 0) {\n lines.push(`Searchable: ${m.querySearchable.join(', ')}`)\n }\n return lines\n}\n\n/**\n * Plan a schema import for hoisting at the top of `routes.ts`. Returns\n * the alias the in-namespace code should use, or `null` if the schema\n * cannot be referenced (no validator configured, or source unresolvable).\n */\nfunction planSchemaImport(\n schema: { identifier: string; source: string | null } | null,\n routeFilePath: string,\n routesOutFile: string,\n schemaValidator: 'zod' | false,\n imports: Map<string, { identifier: string; specifier: string }>,\n): string | null {\n if (!schema || schemaValidator !== 'zod') return null\n if (schema.source === null) return null\n const specifier = resolveSchemaImportSpecifier(schema.source, routeFilePath, routesOutFile)\n if (specifier === 'unknown') return null\n const key = `${specifier}::${schema.identifier}`\n let alias = imports.get(key)?.specifier\n if (!alias) {\n alias = `_S${imports.size}`\n imports.set(key, { identifier: schema.identifier, specifier: alias })\n } else {\n alias = imports.get(key)!.specifier\n }\n return alias\n}\n\nfunction renderSchemaImports(\n imports: Map<string, { identifier: string; specifier: string }>,\n): string {\n if (imports.size === 0) return ''\n const lines: string[] = []\n for (const [key, value] of imports) {\n const [path] = key.split('::')\n lines.push(`import type { ${value.identifier} as ${value.specifier} } from '${path}'`)\n }\n return lines.join('\\n') + '\\n'\n}\n\n/**\n * Compute the import specifier the generated `routes.d.ts` should use\n * to reach a schema declared either in the controller file (empty\n * string) or imported from elsewhere (relative path or bare module).\n */\nfunction resolveSchemaImportSpecifier(\n source: string | null,\n routeFilePath: string,\n routesOutFile: string,\n): string {\n if (source === null) return 'unknown'\n const routesDir = dirname(routesOutFile)\n\n // Same-file schema — point at the controller file itself\n if (source === '') {\n let rel = relative(routesDir, routeFilePath).split(sep).join('/')\n rel = rel.replace(/\\.(ts|tsx|mts|cts)$/i, '')\n if (!rel.startsWith('.')) rel = './' + rel\n return rel\n }\n\n // Bare module name (no leading `.` and not absolute) → keep as-is\n if (!source.startsWith('.') && !source.startsWith('/')) {\n return source\n }\n\n // Relative import → resolve against the controller's directory, then\n // re-relativise against the routes.d.ts directory\n const controllerDir = dirname(routeFilePath)\n const absoluteTarget = resolve(controllerDir, source)\n let rel = relative(routesDir, absoluteTarget).split(sep).join('/')\n rel = rel.replace(/\\.(ts|tsx|mts|cts)$/i, '')\n if (!rel.startsWith('.')) rel = './' + rel\n return rel\n}\n","// kick/routes typegen plugin — M2.B-T8 carve.\n//\n// Replaces the legacy `routes.ts` emission in `generator.ts`. Reads the\n// shared scan result via `ctx.getScanResult` (memoized, so kick/env\n// and any future scan-consuming plugin share the walk), then renders\n// the `KickRoutes` augmentation via `../render/routes`.\n//\n// Output filename: `.kickjs/types/kick__routes.ts` — the plugin sets\n// `outExtension: '.ts'` so the runner writes a `.ts` (not `.d.ts`)\n// file. The runner also translates the slash in the plugin id\n// (`kick/routes`) to a double underscore on disk.\n\nimport path from 'node:path'\n\nimport { renderRoutes } from '../render/routes'\nimport type { TypegenPlugin } from '../plugin'\n\nexport const kickRoutesTypegen = (): TypegenPlugin => ({\n id: 'kick/routes',\n // Emit `.ts` (not `.d.ts`) so the hoisted `import type {...} from\n // '../../src/...'` lines inside renderRoutes resolve under\n // `moduleResolution: 'bundler'`. `.d.ts` would silently degrade\n // those references to `unknown`.\n outExtension: '.ts',\n // Re-run when any controller / route source changes. The Vite\n // watcher subscribes to these globs in `kick dev` to debounce a\n // single typegen pass per file change.\n inputs: ['src/**/*.controller.ts', 'src/**/*.module.ts'],\n async generate(ctx) {\n const scan = await ctx.getScanResult({\n root: resolveSrcDir(ctx),\n cwd: ctx.cwd,\n // envFile passed through so the shared cache key matches kick/env's\n // expected scan when both plugins run in the same pass.\n envFile: resolveEnvFile(ctx),\n })\n\n const schemaValidator = ctx.config?.typegen?.schemaValidator ?? 'zod'\n // Plugin runner writes to .kickjs/types/kick__routes.ts. The\n // hoisted-import path computation inside renderRoutes needs the\n // absolute target so it can re-relativise schema imports.\n const outFile = path.resolve(ctx.cwd, '.kickjs/types/kick__routes.ts')\n return renderRoutes(scan.routes, outFile, schemaValidator)\n },\n})\n\nfunction resolveSrcDir(ctx: { cwd: string; config: { typegen?: { srcDir?: string } } }): string {\n return path.resolve(ctx.cwd, ctx.config?.typegen?.srcDir ?? 'src')\n}\n\nfunction resolveEnvFile(ctx: {\n cwd: string\n config: {\n typegen?: { envFile?: string | false }\n }\n}): string | undefined {\n // Mirror legacy resolution semantics — `false` disables env discovery\n // (handled by kick/env plugin); undefined falls back to scanner default.\n const cfg = ctx.config?.typegen?.envFile\n if (cfg === false) return undefined\n return cfg\n}\n","// Pure render module for the `KickEnv` global namespace augmentation\n// + `NodeJS.ProcessEnv` narrowing.\n//\n// Lives outside `generator.ts` so the new `kick/env` typegen plugin\n// can call it without importing the legacy generator's monolithic\n// `generateTypes()`. Emits as a `.ts` file (not `.d.ts`) for the\n// same hoisted-import reason as renderRoutes — bundler moduleResolution\n// silently degrades top-level `import type` inside `.d.ts` to\n// `unknown`.\n\nimport { dirname, relative, sep } from 'node:path'\n\nimport type { DiscoveredEnv } from '../scanner'\n\nconst ENV_HEADER = `/* 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`\n\n/**\n * Render the `KickEnv` + `NodeJS.ProcessEnv` augmentation file from a\n * detected env schema. Returns `null` when no env file was discovered,\n * so the caller can skip emission entirely (rather than emitting an\n * empty augmentation that would shadow `KickEnv` to a useless `{}`).\n */\nexport function renderEnv(env: DiscoveredEnv | null, envOutFile: string): string | null {\n if (!env) return null\n // Compute the relative import path from the output file back to the\n // user's env schema source, stripping the extension so TS resolves it.\n const envOutDir = dirname(envOutFile)\n let rel = relative(envOutDir, env.filePath).split(sep).join('/')\n rel = rel.replace(/\\.(ts|tsx|mts|cts)$/i, '')\n if (!rel.startsWith('.')) rel = './' + rel\n\n return `${ENV_HEADER}\n// Importing the schema as a type lets us infer its shape without\n// pulling in any runtime code. \\`Awaited<>\\` strips an accidental\n// Promise wrap on dynamic-imported defaults.\nimport type _envSchema from '${rel}'\n\n// Local type alias — interfaces can only \\`extend\\` an identifier,\n// not an inline import expression, so we resolve the schema's\n// inferred shape into a named type first.\ntype _KickEnvShape = import('zod').infer<typeof _envSchema>\n\ndeclare global {\n /**\n * Typed environment registry. Augmented from \\`${env.relativePath}\\`\n * so \\`@Value('PORT')\\`, \\`Env<'PORT'>\\`, and \\`process.env.PORT\\` are\n * all type-safe and autocomplete.\n */\n interface KickEnv extends _KickEnvShape {}\n\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace NodeJS {\n /**\n * Narrow \\`process.env\\` so known keys exist as \\`string\\` (the raw\n * pre-Zod-coercion form). \\`@Value\\` and the \\`ConfigService\\` apply\n * the schema's transforms internally; access \\`process.env\\` directly\n * only when you need the raw string. Unknown keys still resolve to\n * \\`string | undefined\\` via the base @types/node declaration.\n */\n interface ProcessEnv extends Record<keyof KickEnv, string> {}\n }\n}\n\nexport {}\n`\n}\n","// kick/env typegen plugin — M2.B-T8 carve.\n//\n// Replaces the legacy `env.ts` emission in `generator.ts`. Reads the\n// shared scan result via `ctx.getScanResult` (memoized, shared with\n// `kick/routes` and any future scan-consuming plugin), then renders\n// the `KickEnv` augmentation via `../render/env`.\n//\n// Output filename: `.kickjs/types/kick__env.ts` (`.ts`, not `.d.ts`,\n// for the hoisted-import reason renderEnv documents). The plugin\n// returns null when no env schema is discovered so the runner skips\n// writing — matches the legacy \"no env file → no augmentation\" path.\n\nimport path from 'node:path'\n\nimport { renderEnv } from '../render/env'\nimport type { TypegenPlugin } from '../plugin'\n\nexport const kickEnvTypegen = (): TypegenPlugin => ({\n id: 'kick/env',\n outExtension: '.ts',\n // Re-run when the env schema file (or anywhere it could move to)\n // changes. The Vite watcher in kick dev catches src/**/*.ts already;\n // keeping the inputs broad ensures rename-the-env-file works without\n // a special case.\n inputs: ['src/env.ts', 'src/**/env.ts', 'src/**/*.env.ts'],\n async generate(ctx) {\n const envFile = resolveEnvFile(ctx)\n if (envFile === false) return null\n\n const scan = await ctx.getScanResult({\n root: resolveSrcDir(ctx),\n cwd: ctx.cwd,\n envFile,\n })\n if (!scan.env) return null\n\n const outFile = path.resolve(ctx.cwd, '.kickjs/types/kick__env.ts')\n return renderEnv(scan.env, outFile)\n },\n})\n\nfunction resolveSrcDir(ctx: { cwd: string; config: { typegen?: { srcDir?: string } } }): string {\n return path.resolve(ctx.cwd, ctx.config?.typegen?.srcDir ?? 'src')\n}\n\nfunction resolveEnvFile(ctx: {\n config: {\n typegen?: { envFile?: string | false }\n }\n}): string | false | undefined {\n // Adopters can disable env typing entirely via\n // `typegen.envFile: false`. Returning `false` from this helper\n // lets the plugin short-circuit before scanning.\n return ctx.config?.typegen?.envFile\n}\n","// Built-in CLI plugins — every command the kick CLI ships is expressed\n// as a KickCliPlugin so the same merge + conflict-detection pipeline\n// runs for built-ins and adopter plugins. Adding a new built-in command\n// = append one entry here. Replacing one = drop the entry, ship a\n// plugin with the same `name` from kick.config.ts.\n//\n// `register` is the programmatic-commander surface. We use it (not\n// declarative `commands[]`) because every built-in needs full chain\n// access (subcommands, options, async actions, etc.).\n\nimport { registerInitCommand } from '../commands/init'\nimport { registerGenerateCommand } from '../commands/generate'\nimport { registerRunCommands } from '../commands/run'\nimport { registerInfoCommand } from '../commands/info'\nimport { registerInspectCommand } from '../commands/inspect'\nimport { registerAddCommand, registerListCommand } from '../commands/add'\nimport { registerExplainCommand } from '../commands/explain'\nimport { registerMcpCommand } from '../commands/mcp'\nimport { registerTinkerCommand } from '../commands/tinker'\nimport { registerRemoveCommand } from '../commands/remove'\nimport { registerTypegenCommand } from '../commands/typegen'\nimport { registerCheckCommand } from '../commands/check'\nimport { registerDbCommands } from '../commands/db'\nimport { kickDbTypegen } from '../typegen/builtin/db'\nimport { kickAssetsTypegen } from '../typegen/builtin/assets'\nimport { kickRoutesTypegen } from '../typegen/builtin/routes'\nimport { kickEnvTypegen } from '../typegen/builtin/env'\n\nimport { defineCliPlugin, type KickCliPlugin } from './types'\n\nexport const builtinCliPlugins: readonly KickCliPlugin[] = [\n defineCliPlugin({ name: 'kick/init', register: registerInitCommand }),\n defineCliPlugin({ name: 'kick/generate', register: registerGenerateCommand }),\n defineCliPlugin({ name: 'kick/run', register: registerRunCommands }),\n defineCliPlugin({ name: 'kick/info', register: registerInfoCommand }),\n defineCliPlugin({ name: 'kick/inspect', register: registerInspectCommand }),\n defineCliPlugin({ name: 'kick/add', register: registerAddCommand }),\n defineCliPlugin({ name: 'kick/list', register: registerListCommand }),\n defineCliPlugin({ name: 'kick/explain', register: registerExplainCommand }),\n defineCliPlugin({ name: 'kick/mcp', register: registerMcpCommand }),\n defineCliPlugin({ name: 'kick/tinker', register: registerTinkerCommand }),\n defineCliPlugin({ name: 'kick/remove', register: registerRemoveCommand }),\n defineCliPlugin({ name: 'kick/typegen', register: registerTypegenCommand }),\n defineCliPlugin({ name: 'kick/check', register: registerCheckCommand }),\n defineCliPlugin({ name: 'kick/db', register: registerDbCommands, typegens: [kickDbTypegen()] }),\n // kick/assets, kick/routes are typegen-only — the asset manager\n // itself is wired via @forinda/kickjs runtime, not the CLI; routes\n // emit a `KickRoutes` global namespace augmentation. Both replace\n // sections of the legacy generator that used to live in\n // `typegen/generator.ts` before the M2.B-T8 carve.\n defineCliPlugin({ name: 'kick/assets', typegens: [kickAssetsTypegen()] }),\n defineCliPlugin({ name: 'kick/routes', typegens: [kickRoutesTypegen()] }),\n defineCliPlugin({ name: 'kick/env', typegens: [kickEnvTypegen()] }),\n]\n"],"mappings":"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"}