{"version":3,"sources":["/home/pitok/Projects/framework-bun/packages/buntok-core/dist/cli/index.cjs","../../src/cli/commands/build.ts","../../src/cli/commands/check.ts","../../src/cli/commands/create.ts","../../src/cli/generators/barrel.ts","../../src/cli/utils.ts","../../src/cli/project.ts","../../src/cli/generators/controller.ts","../../src/cli/generators/model.ts","../../src/cli/generators/repository.ts","../../src/cli/generators/schema.ts","../../src/cli/generators/service.ts","../../src/cli/routes-scan.ts","../../src/cli/commands/db.ts","../../src/cli/commands/db-seed.ts","../../src/cli/commands/debug-routes.ts","../../src/cli/commands/dev.ts","../../src/cli/env.ts","../../src/cli/commands/init.ts","../../src/cli/commands/make-docs.ts","../../src/cli/commands/make-factory.ts","../../src/cli/commands/make-middleware.ts","../../src/cli/commands/make-seeder.ts","../../src/cli/commands/make-test.ts","../../src/cli/commands/make-test-e2e.ts","../../src/cli/index.ts"],"names":["join","createInterface","declEnd","getPrismaType","getDrizzleType","getTypeORMType","existsSync","readFileSync","prefix","fs","resolve","statSync","execSync","mkdirSync","printUsage"],"mappings":"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","file":"/home/pitok/Projects/framework-bun/packages/buntok-core/dist/cli/index.cjs","sourcesContent":[null,"import { join, dirname, isAbsolute, relative } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { cp, mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\n\nconst readJson = async (path: string) => JSON.parse(await readFile(path, \"utf8\"));\n\nconst findPackageRoot = (startFile: string): string | null => {\n\tlet dir = dirname(startFile);\n\tfor (;;) {\n\t\tif (existsSync(join(dir, \"package.json\"))) return dir;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return null;\n\t\tdir = parent;\n\t}\n};\n\nconst resolvePackageRoot = (spec: string, fromDir: string): string | null => {\n\ttry {\n\t\tconst resolved = Bun.resolveSync(spec, join(fromDir, \"package.json\"));\n\t\tif (!isAbsolute(resolved) || !existsSync(resolved)) return null;\n\t\treturn findPackageRoot(resolved);\n\t} catch {\n\t\treturn null;\n\t}\n};\n\ntype PackageJson = {\n\tname?: string;\n\tdependencies?: Record<string, string>;\n\tdevDependencies?: Record<string, string>;\n\toptionalDependencies?: Record<string, string>;\n\tpeerDependencies?: Record<string, string>;\n};\n\nasync function stageServerlessOutput(\n\tprojectRoot: string,\n\toutDir: string,\n\tpkg: PackageJson,\n): Promise<number> {\n\tawait rm(join(outDir, \"node_modules\"), { recursive: true, force: true });\n\tawait rm(join(outDir, \"public\"), { recursive: true, force: true });\n\n\tconst publicDir = join(projectRoot, \"public\");\n\tif (existsSync(publicDir)) await cp(publicDir, join(outDir, \"public\"), { recursive: true });\n\n\tconst nmRoot = join(projectRoot, \"node_modules\");\n\tconst seeds = [\n\t\t...Object.keys(pkg.dependencies ?? {}),\n\t\t...Object.keys(pkg.optionalDependencies ?? {}),\n\t\t...Object.keys(pkg.peerDependencies ?? {}),\n\t];\n\tconst queue: { spec: string; from: string }[] = seeds.map((spec) => ({\n\t\tspec,\n\t\tfrom: projectRoot,\n\t}));\n\tconst visited = new Set<string>();\n\tlet copied = 0;\n\n\twhile (queue.length > 0) {\n\t\tconst item = queue.shift();\n\t\tif (!item) break;\n\t\tconst pkgRoot = resolvePackageRoot(item.spec, item.from);\n\t\tif (!pkgRoot || visited.has(pkgRoot)) continue;\n\t\tvisited.add(pkgRoot);\n\n\t\tconst rel = relative(nmRoot, pkgRoot);\n\t\tconst dest =\n\t\t\trel === \"\" || rel.startsWith(\"..\")\n\t\t\t\t? join(outDir, \"node_modules\", item.spec)\n\t\t\t\t: join(outDir, \"node_modules\", rel);\n\t\tawait mkdir(dirname(dest), { recursive: true });\n\t\tawait cp(pkgRoot, dest, { recursive: true });\n\t\tcopied++;\n\n\t\tconst depPkg: PackageJson | null = await readJson(join(pkgRoot, \"package.json\")).catch(\n\t\t\t() => null,\n\t\t);\n\t\tif (!depPkg) continue;\n\t\tfor (const dep of Object.keys({\n\t\t\t...depPkg.dependencies,\n\t\t\t...depPkg.optionalDependencies,\n\t\t\t...depPkg.peerDependencies,\n\t\t})) {\n\t\t\tqueue.push({ spec: dep, from: pkgRoot });\n\t\t}\n\t}\n\n\tconst stagedPkg = {\n\t\tname: pkg.name ? `${pkg.name}-server` : \"buntok-app\",\n\t\tprivate: true,\n\t\ttype: \"module\",\n\t};\n\tawait writeFile(join(outDir, \"package.json\"), `${JSON.stringify(stagedPkg, null, \"\\t\")}\\n`);\n\treturn copied;\n}\n\nexport async function buildCommand() {\n\tconst projectRoot = process.cwd();\n\tconst entryPoint = join(projectRoot, \"server.ts\");\n\tconst onVercel = process.env.VERCEL === \"1\" || process.env.VERCEL === \"true\";\n\tconst outDir = join(projectRoot, \"buntok\");\n\n\tif (!existsSync(entryPoint)) {\n\t\tconsole.error(\"\\x1b[31m❌ server.ts not found\\x1b[0m\");\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\t// Read user's package.json to know their dependencies\n\tlet userPkg: PackageJson = {};\n\tconst pkgPath = join(projectRoot, \"package.json\");\n\tif (existsSync(pkgPath)) userPkg = await readJson(pkgPath);\n\tconst userDeps = [\n\t\t...Object.keys(userPkg.dependencies || {}),\n\t\t...Object.keys(userPkg.devDependencies || {}),\n\t];\n\n\t// BunTok peer deps (optional, user installs these)\n\tconst peerDeps = [\n\t\t\"@apollo/server\",\n\t\t\"graphql\",\n\t\t\"graphql-yoga\",\n\t\t\"@opentelemetry/api\",\n\t\t\"@opentelemetry/sdk-node\",\n\t\t\"@opentelemetry/resources\",\n\t\t\"@opentelemetry/semantic-conventions\",\n\t\t\"@opentelemetry/sdk-trace-node\",\n\t\t\"@opentelemetry/exporter-trace-otlp-http\",\n\t\t\"ioredis\",\n\t\t\"bullmq\",\n\t\t\"amqplib\",\n\t];\n\n\tconst external = [...new Set([...peerDeps, ...userDeps])];\n\n\tconsole.log(\"\\x1b[36m🔨 Building project...\\x1b[0m\");\n\n\tconst result = await Bun.build({\n\t\tentrypoints: [entryPoint],\n\t\toutdir: outDir,\n\t\ttarget: \"bun\",\n\t\ttsconfig: join(projectRoot, \"tsconfig.json\"),\n\t\texternal,\n\t});\n\n\tif (!result.success) {\n\t\tconsole.error(\"\\x1b[31m❌ Build failed:\\x1b[0m\");\n\t\tfor (const log of result.logs) {\n\t\t\tconsole.error(log);\n\t\t}\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tif (onVercel) {\n\t\tconst copied = await stageServerlessOutput(projectRoot, outDir, userPkg);\n\t\tconsole.log(`\\x1b[32m✅ Build successful → buntok/server.js\\x1b[0m`);\n\t\tconsole.log(\n\t\t\t`\\x1b[90m  Staged Vercel output: ${copied} packages + public + package.json → buntok/\\x1b[0m`,\n\t\t);\n\t\treturn;\n\t}\n\n\tconsole.log(`\\x1b[32m✅ Build successful → buntok/server.js\\x1b[0m`);\n\tconsole.log(`\\x1b[90m  Deploy: copy buntok/ + node_modules/ + package.json to server\\x1b[0m`);\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\ninterface TSError {\n\tfile: string;\n\tline: number;\n\tcol: number;\n\tcode: string;\n\tmessage: string;\n}\n\nfunction stripAnsi(str: string): string {\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape codes\n\treturn str.replace(/\\x1B\\[[0-9;]*[mK]/g, \"\");\n}\n\nfunction parseTSErrors(output: string): TSError[] {\n\tconst clean = stripAnsi(output);\n\tconst lines = clean.split(\"\\n\");\n\tconst errors: TSError[] = [];\n\n\tfor (const line of lines) {\n\t\t// Format without --pretty: \"file.ts(1,7): error TS2322: message\"\n\t\t// Format with --pretty: \"file.ts:1:7 - error TS2322: message\"\n\t\tconst match =\n\t\t\tline.match(/^(.+?)\\((\\d+),(\\d+)\\): error (TS\\d+): (.+)$/) ||\n\t\t\tline.match(/^(.+?):(\\d+):(\\d+) - error (TS\\d+): (.+)$/);\n\t\tif (match && match[1] && match[2] && match[3] && match[4] && match[5]) {\n\t\t\terrors.push({\n\t\t\t\tfile: match[1],\n\t\t\t\tline: Number.parseInt(match[2], 10),\n\t\t\t\tcol: Number.parseInt(match[3], 10),\n\t\t\t\tcode: match[4],\n\t\t\t\tmessage: match[5],\n\t\t\t});\n\t\t}\n\t}\n\n\treturn errors;\n}\n\nfunction getUniqueFiles(errors: TSError[]): string[] {\n\treturn [...new Set(errors.map((e) => e.file))];\n}\n\nfunction countProjectFiles(cwd: string, tsconfigPath: string): number {\n\ttry {\n\t\tconst proc = Bun.spawnSync(\n\t\t\t[\"bunx\", \"tsc\", \"--noEmit\", \"--listFilesOnly\", \"--project\", tsconfigPath],\n\t\t\t{ cwd },\n\t\t);\n\t\tconst out = proc.stdout?.toString() || \"\";\n\t\treturn out\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => line.trim())\n\t\t\t.filter((line) => line.startsWith(\"/\") && !line.includes(\"node_modules\"))\n\t\t\t.length;\n\t} catch {\n\t\treturn 0;\n\t}\n}\n\nexport async function checkCommand(flags: string[] = []): Promise<void> {\n\tconst cwd = process.cwd();\n\tconst tsconfigPath = join(cwd, \"tsconfig.json\");\n\tconst isJson = flags.includes(\"--json\");\n\tconst isPlain = flags.includes(\"--plain\");\n\n\tif (!existsSync(tsconfigPath)) {\n\t\tif (isJson) {\n\t\t\tconsole.log(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: \"tsconfig.json not found. Run `buntok init` first.\",\n\t\t\t\t\terrors: [],\n\t\t\t\t\terrorCount: 0,\n\t\t\t\t\tfileCount: 0,\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tconsole.error(\n\t\t\t\t\"\\x1b[31mError: tsconfig.json not found. Run `buntok init` first.\\x1b[0m\",\n\t\t\t);\n\t\t}\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tif (!isJson) {\n\t\tif (isPlain) {\n\t\t\tconsole.log(\"  Running type check...\\n\");\n\t\t} else {\n\t\t\tconsole.log(\"\\x1b[36m  Running type check...\\x1b[0m\\n\");\n\t\t}\n\t}\n\n\tconst proc = Bun.spawnSync(\n\t\t[\"bunx\", \"tsc\", \"--noEmit\", \"--pretty\", \"--project\", tsconfigPath],\n\t\t{ cwd },\n\t);\n\n\tconst output = proc.stdout?.toString() || \"\";\n\tconst errors = parseTSErrors(output);\n\tconst files = getUniqueFiles(errors);\n\tconst errorCount = errors.length;\n\tconst fileCount = files.length;\n\n\tif (proc.exitCode === 0) {\n\t\tif (isJson) {\n\t\t\tconsole.log(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\terrors: [],\n\t\t\t\t\terrorCount: 0,\n\t\t\t\t\tfileCount: countProjectFiles(cwd, tsconfigPath),\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tif (isPlain) {\n\t\t\t\tconsole.log(\"\\n  ✓ No errors found\\n\");\n\t\t\t} else {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`\\n\\x1b[32m  ✓ No errors found\\x1b[0m\\n`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tprocess.exitCode = 0;\n\t\treturn;\n\t}\n\n\t// Errors found\n\tif (isJson) {\n\t\tconsole.log(\n\t\t\tJSON.stringify({\n\t\t\t\tsuccess: false,\n\t\t\t\terrors,\n\t\t\t\terrorCount,\n\t\t\t\tfileCount,\n\t\t\t}),\n\t\t);\n\t} else {\n\t\t// Show errors (limit to 50 for readability)\n\t\tconst maxErrors = 50;\n\t\tconst shownErrors = errors.slice(0, maxErrors);\n\n\t\tfor (const err of shownErrors) {\n\t\t\tconst file = isPlain ? err.file : `\\x1b[36m${err.file}\\x1b[0m`;\n\t\t\tconst line = isPlain ? `${err.line}` : `\\x1b[33m${err.line}\\x1b[0m`;\n\t\t\tconst col = isPlain ? `${err.col}` : `\\x1b[33m${err.col}\\x1b[0m`;\n\t\t\tconst code = isPlain ? err.code : `\\x1b[31m${err.code}\\x1b[0m`;\n\t\t\tconst msg = isPlain ? err.message : `\\x1b[31m${err.message}\\x1b[0m`;\n\n\t\t\tconsole.log(`${file}(${line},${col}): error ${code}: ${msg}`);\n\t\t}\n\n\t\tif (errorCount > maxErrors) {\n\t\t\tconsole.log(\n\t\t\t\t`\\n  ... and ${errorCount - maxErrors} more errors`,\n\t\t\t);\n\t\t}\n\n\t\t// Summary\n\t\tconst fileStr = fileCount === 1 ? \"file\" : \"files\";\n\t\tif (isPlain) {\n\t\t\tconsole.log(\n\t\t\t\t`\\n  ✗ ${errorCount} error${errorCount === 1 ? \"\" : \"s\"} in ${fileCount} ${fileStr}\\n`,\n\t\t\t);\n\t\t} else {\n\t\t\tconsole.log(\n\t\t\t\t`\\n\\x1b[31m  ✗ ${errorCount} error${errorCount === 1 ? \"\" : \"s\"} in ${fileCount} ${fileStr}\\x1b[0m\\n`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprocess.exitCode = proc.exitCode ?? 1;\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { generateBarrel } from \"../generators/barrel.js\";\nimport { generateController } from \"../generators/controller.js\";\nimport {\n\tgetEntityModel,\n\tloadPrismaSchema,\n\tparseFieldsFlag,\n\tzodLinesForFields,\n} from \"../generators/model.js\";\nimport { generateRepository } from \"../generators/repository.js\";\nimport { generateSchemaFile } from \"../generators/schema.js\";\nimport { generateService } from \"../generators/service.js\";\nimport {\n\tcollectImportedControllers,\n\ttype AppInstance,\n\tdetectAppInstances,\n\tdetectORMOrNull,\n\tescapeRegExp,\n\tensureImport,\n\tfindAppDeclarationFile,\n\tfindAppDeclEnd,\n\tfindInsertionIndex,\n\tformatWithBiome,\n\ttype ORM,\n} from \"../project.js\";\nimport { scanRoutes, warnDuplicateRoutes } from \"../routes-scan.js\";\nimport { selectFrom, toPascalCase } from \"../utils.js\";\n\ninterface CreateOptions {\n\trepo: boolean;\n\tservice: boolean;\n\tcontroller: boolean;\n\tschema: boolean;\n\tall: boolean;\n\tdryRun: boolean;\n\tforce: boolean;\n\tbase: boolean;\n\tfields?: string;\n\torm?: ORM;\n\t/** Target app instance name, e.g. `apiV1` (--app apiV1). */\n\tapp?: string;\n}\n\nfunction parseOptions(args: string[]): CreateOptions {\n\tconst options: CreateOptions = {\n\t\trepo: false,\n\t\tservice: false,\n\t\tcontroller: false,\n\t\tschema: false,\n\t\tall: true,\n\t\tdryRun: false,\n\t\tforce: false,\n\t\tbase: false,\n\t};\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i] ?? \"\";\n\t\tswitch (arg) {\n\t\t\tcase \"--repo\":\n\t\t\t\toptions.repo = true;\n\t\t\t\toptions.all = false;\n\t\t\t\tbreak;\n\t\t\tcase \"--service\":\n\t\t\t\toptions.service = true;\n\t\t\t\toptions.all = false;\n\t\t\t\tbreak;\n\t\t\tcase \"--controller\":\n\t\t\t\toptions.controller = true;\n\t\t\t\toptions.all = false;\n\t\t\t\tbreak;\n\t\t\tcase \"--schema\":\n\t\t\t\toptions.schema = true;\n\t\t\t\toptions.all = false;\n\t\t\t\tbreak;\n\t\t\tcase \"--dry-run\":\n\t\t\t\toptions.dryRun = true;\n\t\t\t\tbreak;\n\t\t\tcase \"--force\":\n\t\t\t\toptions.force = true;\n\t\t\t\tbreak;\n\t\t\tcase \"--base\":\n\t\t\t\toptions.base = true;\n\t\t\t\tbreak;\n\t\t\tcase \"--fields\":\n\t\t\t\toptions.fields = args[i + 1];\n\t\t\t\ti++;\n\t\t\t\tbreak;\n\t\t\tcase \"--app\":\n\t\t\t\toptions.app = args[i + 1];\n\t\t\t\ti++;\n\t\t\t\tbreak;\n\t\t\tcase \"--prisma\":\n\t\t\t\toptions.orm = \"prisma\";\n\t\t\t\tbreak;\n\t\t\tcase \"--drizzle\":\n\t\t\t\toptions.orm = \"drizzle\";\n\t\t\t\tbreak;\n\t\t\tcase \"--typeorm\":\n\t\t\t\toptions.orm = \"typeorm\";\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t// If any specific option is set, don't generate all\n\tif (!options.all) {\n\t\treturn options;\n\t}\n\n\t// Generate all by default\n\treturn {\n\t\trepo: true,\n\t\tservice: true,\n\t\tcontroller: true,\n\t\tschema: true,\n\t\tall: false,\n\t\tdryRun: options.dryRun,\n\t\tforce: options.force,\n\t\tbase: options.base,\n\t\tfields: options.fields,\n\t\torm: options.orm,\n\t\tapp: options.app,\n\t};\n}\n\ninterface RegistrationTarget {\n\t/** File that declares the Buntok instance(s), e.g. `src/app.ts`. */\n\tfile: string;\n\tinstances: AppInstance[];\n\t/** Instance that owns the registration, e.g. `app` or `apiV1`. */\n\tname: string;\n}\n\ntype RegistrationResolution =\n\t| { target: RegistrationTarget | null }\n\t| { error: true };\n\n/** Deterministic instance when there are several and no prompt is possible. */\nfunction pickDefaultInstance(src: string, instances: AppInstance[]): number {\n\tconst byName = instances.findIndex((i) => i.name === \"app\");\n\tif (byName !== -1) return byName;\n\tconst appKind = instances.findIndex((i) => i.kind === \"app\");\n\tif (appKind !== -1) return appKind;\n\tconst byListen = instances.findIndex((i) =>\n\t\tnew RegExp(`${escapeRegExp(i.name)}\\\\.listen\\\\s*\\\\(`).test(src),\n\t);\n\tif (byListen !== -1) return byListen;\n\tconst byExport = instances.findIndex((i) => i.exported);\n\tif (byExport !== -1) return byExport;\n\treturn 0;\n}\n\n/** Human-readable label: `apiv1 (group /api/v1)` for RouterGroup targets. */\nfunction instanceLabel(i: AppInstance): string {\n\tif (i.kind !== \"group\") return i.name;\n\treturn i.prefix !== undefined ? `${i.name} (group ${i.prefix})` : `${i.name} (group)`;\n}\n\n/**\n * Walk a group's owner chain to the instance that can host the container:\n * `apiv1 = app.group(...)` → `app` (RouterGroup has no setContainer()).\n */\nfunction resolveContainerHost(name: string, instances: AppInstance[]): string {\n\tlet current = name;\n\tfor (let i = 0; i < 10; i++) {\n\t\tconst found = instances.find((x) => x.name === current);\n\t\tif (!found || found.kind === \"app\" || !found.owner) return current;\n\t\tcurrent = found.owner;\n\t}\n\treturn current;\n}\n\n/**\n * Pick the registration file (the one declaring `new Buntok(...)`, not the\n * listener-only `server.ts`) and the target instance. Honors `--app`,\n * prompts when several instances exist on a TTY, and falls back to a\n * deterministic default in CI.\n */\nasync function resolveRegistrationTarget(\n\toptions: CreateOptions,\n): Promise<RegistrationResolution> {\n\tif (options.dryRun || !options.controller) return { target: null };\n\n\tconst file = findAppDeclarationFile();\n\tif (!file || !existsSync(file)) return { target: null };\n\n\tconst content = readFileSync(file, \"utf-8\");\n\tconst instances = detectAppInstances(content);\n\n\tif (options.app) {\n\t\tif (instances.length === 0) {\n\t\t\tconsole.error(\n\t\t\t\t`\\x1b[31mError: --app ${options.app} given, but no Buntok instance is declared in ${file}\\x1b[0m`,\n\t\t\t);\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn { error: true };\n\t\t}\n\t\tif (!instances.some((i) => i.name === options.app)) {\n\t\t\tconst available = instances.map(instanceLabel).join(\", \");\n\t\t\tconsole.error(\n\t\t\t\t`\\x1b[31mError: Buntok instance \"${options.app}\" not found in ${file} (available: ${available})\\x1b[0m`,\n\t\t\t);\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn { error: true };\n\t\t}\n\t\treturn { target: { file, instances, name: options.app } };\n\t}\n\n\tif (instances.length === 0) {\n\t\tconsole.log(\n\t\t\t`\\x1b[33m⚠ No Buntok instance declared in ${file} - registering via \"app\"\\x1b[0m`,\n\t\t);\n\t\treturn { target: { file, instances, name: \"app\" } };\n\t}\n\tif (instances.length === 1) {\n\t\treturn { target: { file, instances, name: instances[0]?.name ?? \"app\" } };\n\t}\n\n\t// Several instances (apiV1, apiV2, ...): ask on a TTY, otherwise use\n\t// the deterministic default so scripts and CI never hang.\n\tconst names = instances.map((i) => i.name);\n\tconst defaultIndex = pickDefaultInstance(content, instances);\n\tif (process.stdin.isTTY) {\n\t\tconst picked = await selectFrom(\n\t\t\t`\\x1b[36mRegister the controller to which Buntok instance in ${file}?\\x1b[0m`,\n\t\t\tinstances.map(instanceLabel),\n\t\t\tdefaultIndex,\n\t\t);\n\t\tif (picked !== null && names[picked] !== undefined) {\n\t\t\treturn { target: { file, instances, name: names[picked] } };\n\t\t}\n\t}\n\tconst chosen = names[defaultIndex] ?? names[0] ?? \"app\";\n\tconsole.log(\n\t\t`\\x1b[33m⚠ ${file} declares multiple Buntok instances (${names.join(\", \")}) - registered via \"${chosen}\". Use --app <name> to pick another.\\x1b[0m`,\n\t);\n\treturn { target: { file, instances, name: chosen } };\n}\n\nexport async function createCommand(entityName: string, args: string[]) {\n\tconst options = parseOptions(args);\n\tconst pascalName = toPascalCase(entityName);\n\tconst orm = options.orm ?? detectORMOrNull();\n\tconst moduleDir = join(\"src/modules\", entityName);\n\n\t// Resolve where (and to which Buntok instance) the controller gets\n\t// registered before touching any file, so an invalid --app fails clean.\n\tconst registration = await resolveRegistrationTarget(options);\n\tif (\"error\" in registration) return;\n\n\tconst repoPath = join(moduleDir, `${entityName}.repository.ts`);\n\tconst servicePath = join(moduleDir, `${entityName}.service.ts`);\n\tconst controllerPath = join(moduleDir, `${entityName}.controller.ts`);\n\tconst schemaPath = join(moduleDir, `${entityName}.schema.ts`);\n\n\t// Cross-generator awareness: what already exists on disk?\n\tconst hasRepo = existsSync(repoPath);\n\tconst hasService = existsSync(servicePath);\n\tconst hasController = existsSync(controllerPath);\n\tconst hasSchema = existsSync(schemaPath);\n\n\t// Template style: explicit --base wins, otherwise follow existing module\n\t// files (a module built with BaseService/BaseController stays base-styled).\n\tlet useBase = options.base;\n\tif (!options.base) {\n\t\tfor (const existing of [servicePath, controllerPath]) {\n\t\t\tif (!existsSync(existing)) continue;\n\t\t\tconst source = readFileSync(existing, \"utf-8\");\n\t\t\tif (\n\t\t\t\tsource.includes(\"extends BaseService\") ||\n\t\t\t\tsource.includes(\"extends BaseController\")\n\t\t\t) {\n\t\t\t\tuseBase = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// A service generated with a repo (or next to an existing one) gets the\n\t// wired template; a controller generated next to an existing service does\n\t// too. `--base` (or a base-styled module on disk) always implies the\n\t// wired templates - the Base* variants carry @Dependencies themselves.\n\tconst serviceWithRepo = options.repo || hasRepo || useBase;\n\tconst controllerWithService = options.service || hasService || useBase;\n\n\tconst prefix = options.dryRun ? \"\\x1b[33m[DRY RUN]\\x1b[0m \" : \"\";\n\tconsole.log(`\\n${prefix}\\x1b[36mCreating ${pascalName} entity (orm: ${orm ?? \"plain\"})...\\x1b[0m\\n`);\n\n\t// Ensure module directory exists (skip in dry-run)\n\tif (!options.dryRun) {\n\t\tif (!existsSync(moduleDir)) {\n\t\t\tawait fs.mkdir(moduleDir, { recursive: true });\n\t\t}\n\t}\n\n\tconst results: string[] = [];\n\tconst generatedFiles: string[] = [];\n\tconst tasks: Promise<void>[] = [];\n\n\t// Helper to generate file asynchronously\n\tconst generateFile = async (\n\t\tpath: string,\n\t\tcontentGenerator: () => string,\n\t\ttype: string,\n\t) => {\n\t\tif (options.dryRun) {\n\t\t\tconst content = contentGenerator();\n\t\t\tresults.push(`[DRY RUN] ${type}: ${path}`);\n\t\t\tconsole.log(`\\n\\x1b[90m--- ${path} ---\\x1b[0m`);\n\t\t\tconsole.log(content);\n\t\t\tconsole.log(`\\x1b[90m--- end ---\\x1b[0m\\n`);\n\t\t\treturn;\n\t\t}\n\t\tif (!existsSync(path) || options.force) {\n\t\t\tawait fs.writeFile(path, contentGenerator());\n\t\t\tresults.push(`✓ ${type}: ${path}`);\n\t\t\tgeneratedFiles.push(path);\n\t\t} else {\n\t\t\tresults.push(`• ${type}: ${path} (already exists)`);\n\t\t}\n\t};\n\n\tif (options.repo) {\n\t\ttasks.push(\n\t\t\tgenerateFile(\n\t\t\t\trepoPath,\n\t\t\t\t() => generateRepository(entityName, pascalName, orm, { base: useBase }),\n\t\t\t\t\"Repository\",\n\t\t\t),\n\t\t);\n\t}\n\n\tif (options.service) {\n\t\ttasks.push(\n\t\t\tgenerateFile(\n\t\t\t\tservicePath,\n\t\t\t\t() =>\n\t\t\t\t\tgenerateService(entityName, pascalName, serviceWithRepo, orm, {\n\t\t\t\t\t\tbase: useBase,\n\t\t\t\t\t}),\n\t\t\t\t\"Service\",\n\t\t\t),\n\t\t);\n\t}\n\n\tif (options.controller) {\n\t\ttasks.push(\n\t\t\tgenerateFile(\n\t\t\t\tcontrollerPath,\n\t\t\t\t() =>\n\t\t\t\t\tgenerateController(entityName, pascalName, controllerWithService, orm, {\n\t\t\t\t\t\tbase: useBase,\n\t\t\t\t\t}),\n\t\t\t\t\"Controller\",\n\t\t\t),\n\t\t);\n\t}\n\n\tif (options.schema) {\n\t\t// Model awareness: `--fields` wins, then the Prisma model, then placeholder\n\t\tlet zodLines: string[] | null = null;\n\t\tif (options.fields) {\n\t\t\tconst flagFields = parseFieldsFlag(options.fields);\n\t\t\tzodLines = flagFields.length > 0 ? zodLinesForFields(flagFields, null) : null;\n\t\t} else {\n\t\t\tconst prismaSchema = loadPrismaSchema();\n\t\t\tconst model = prismaSchema ? getEntityModel(entityName, prismaSchema) : null;\n\t\t\tif (model) {\n\t\t\t\tzodLines = zodLinesForFields(model.fields, prismaSchema);\n\t\t\t}\n\t\t}\n\t\tif (!options.dryRun) {\n\t\t\tconst result = await generateSchemaFile(entityName, moduleDir, options.force, {\n\t\t\t\tlines: zodLines,\n\t\t\t});\n\t\t\tif (result) {\n\t\t\t\tresults.push(`✓ Schema: ${result}`);\n\t\t\t\tgeneratedFiles.push(result);\n\t\t\t} else {\n\t\t\t\tresults.push(`• Schema: ${schemaPath} (already exists)`);\n\t\t\t}\n\t\t} else {\n\t\t\tresults.push(`[DRY RUN] Schema: ${schemaPath}`);\n\t\t}\n\t}\n\n\t// Execute all file generations concurrently\n\tawait Promise.all(tasks);\n\n\t// Barrel export: create when missing, otherwise append only the exports\n\t// that are not present yet (merge instead of skip).\n\tconst barrelPath = join(moduleDir, \"index.ts\");\n\tconst barrelParts = {\n\t\trepository: hasRepo || options.repo,\n\t\tservice: hasService || options.service,\n\t\tcontroller: hasController || options.controller,\n\t\tschema: hasSchema || options.schema,\n\t};\n\tif (options.dryRun) {\n\t\tresults.push(`[DRY RUN] Barrel: ${barrelPath}`);\n\t} else if (!existsSync(barrelPath)) {\n\t\tawait fs.writeFile(barrelPath, generateBarrel(entityName, pascalName, barrelParts));\n\t\tresults.push(`✓ Barrel: ${barrelPath}`);\n\t\tgeneratedFiles.push(barrelPath);\n\t} else {\n\t\tconst existingBarrel = readFileSync(barrelPath, \"utf-8\");\n\t\tconst wanted = generateBarrel(entityName, pascalName, barrelParts)\n\t\t\t.split(\"\\n\")\n\t\t\t.filter((line) => line.trim() && !existingBarrel.includes(line));\n\t\tif (wanted.length > 0) {\n\t\t\tawait fs.writeFile(\n\t\t\t\tbarrelPath,\n\t\t\t\t`${existingBarrel.trimEnd()}\\n${wanted.join(\"\\n\")}\\n`,\n\t\t\t);\n\t\t\tresults.push(`✓ Barrel: ${barrelPath} (+${wanted.length} export${wanted.length > 1 ? \"s\" : \"\"})`);\n\t\t\tgeneratedFiles.push(barrelPath);\n\t\t} else {\n\t\t\tresults.push(`• Barrel: ${barrelPath} (up to date)`);\n\t\t}\n\t}\n\n\t// Auto-format generated files with Biome if available (skip in dry-run)\n\tif (generatedFiles.length > 0 && !options.dryRun) {\n\t\tif (formatWithBiome(generatedFiles)) {\n\t\t\tconsole.log(\n\t\t\t\t\"\\x1b[90m✨ Auto-formatted generated files with Biome\\x1b[0m\\n\",\n\t\t\t);\n\t\t}\n\t}\n\n\t// Auto-register in the file that declares the Buntok instance - never the\n\t// listener-only entry (server.ts). Null on dry-run / non-controller runs.\n\tconst target = registration.target;\n\tif (target && existsSync(target.file)) {\n\t\tconst inst = target.name;\n\t\tconst ref = escapeRegExp(inst);\n\t\tconst indexContent = readFileSync(target.file, \"utf-8\");\n\t\tconst controllerName = `${pascalName}Controller`;\n\t\tconst controllerImport = `import { ${controllerName} } from \"@/modules/${entityName}\";`;\n\n\t\t// Determine registration type based on whether the generated controller\n\t\t// has @Dependencies (i.e. was generated with the wired service template)\n\t\tconst useContainer = controllerWithService;\n\n\t\t// Check if this controller is already registered on this instance\n\t\tconst rcSingleRegex = new RegExp(`${ref}\\\\.registerController\\\\((\\\\w+)\\\\)\\\\s*;`);\n\t\tconst rcArrayRegex = new RegExp(`${ref}\\\\.registerController\\\\(\\\\[([^\\\\]]*)\\\\]\\\\)\\\\s*;`);\n\t\tconst scanRegex = new RegExp(\n\t\t\t`(?:${ref}\\\\.getContainer\\\\(\\\\)|container)\\\\.scan\\\\(\\\\[([^\\\\]]*)\\\\]\\\\)\\\\s*;`,\n\t\t);\n\n\t\tconst rcSingleMatch = indexContent.match(rcSingleRegex);\n\t\tconst rcArrayMatch = indexContent.match(rcArrayRegex);\n\t\tconst scanMatch = indexContent.match(scanRegex);\n\n\t\tlet alreadyRegistered = false;\n\t\tif (useContainer && scanMatch?.[1]) {\n\t\t\talreadyRegistered = scanMatch[1].includes(controllerName);\n\t\t} else if (!useContainer && rcSingleMatch?.[1]) {\n\t\t\talreadyRegistered = rcSingleMatch[1] === controllerName;\n\t\t} else if (!useContainer && rcArrayMatch?.[1]) {\n\t\t\talreadyRegistered = rcArrayMatch[1].includes(controllerName);\n\t\t}\n\n\t\tif (!alreadyRegistered) {\n\t\t\tlet content = indexContent;\n\t\t\t// All new statements are collected first and spliced as one block so\n\t\t\t// they keep execution order and never stack on the same anchor.\n\t\t\tconst toInsert: string[] = [];\n\n\t\t\tif (useContainer) {\n\t\t\t\t// === container.scan() + registerController() flow ===\n\n\t\t\t\t// Step 1: Add Container import if missing\n\t\t\t\tconst containerImportRegex = /import\\s*\\{[^}]*Container[^}]*\\}\\s*from\\s*[\"']@buntok\\/core[\"']/;\n\t\t\t\tif (!containerImportRegex.test(content)) {\n\t\t\t\t\t// Add Container to existing @buntok/core import\n\t\t\t\t\tcontent = content.replace(\n\t\t\t\t\t\t/import\\s*\\{([^}]+)\\}\\s*from\\s*[\"']@buntok\\/core[\"']/,\n\t\t\t\t\t\t(_match, imports: string) => {\n\t\t\t\t\t\t\tif (imports.includes(\"Container\")) return _match;\n\t\t\t\t\t\t\treturn `import { ${imports.trim()}, Container } from \"@buntok/core\"`;\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Step 2: Add controller import if missing\n\t\t\t\tcontent = ensureImport(content, controllerImport);\n\n\t\t\t\t// Step 3: Add/merge container.scan()\n\t\t\t\tconst existingScan = content.match(scanRegex);\n\t\t\t\tif (existingScan?.[1]) {\n\t\t\t\t\t// Already has scan - merge\n\t\t\t\t\tconst existing = existingScan[1].trim();\n\t\t\t\t\tconst merged = existing ? `${existing}, ${controllerName}` : controllerName;\n\t\t\t\t\tcontent = content.replace(scanRegex, `container.scan([${merged}]);`);\n\t\t\t\t} else {\n\t\t\t\t\t// No scan found - collect all imported @Dependencies controllers\n\t\t\t\t\tconst allControllers = collectImportedControllers(content, true, inst);\n\t\t\t\t\tif (!allControllers.includes(controllerName)) {\n\t\t\t\t\t\tallControllers.push(controllerName);\n\t\t\t\t\t}\n\t\t\t\t\ttoInsert.push(`container.scan([${allControllers.join(\", \")}]);`);\n\t\t\t\t}\n\n\t\t\t\t// Step 4: Add <host>.setContainer(container) if missing.\n\t\t\t\t// RouterGroup has no setContainer() - attach to the owning\n\t\t\t\t// Buntok instead (`apiv1 = app.group(...)` → `app`).\n\t\t\t\tconst host = resolveContainerHost(inst, target.instances);\n\t\t\t\tconst hostRef = escapeRegExp(host);\n\t\t\t\tconst setContainerRegex = new RegExp(\n\t\t\t\t\t`${hostRef}\\\\.setContainer\\\\s*\\\\(\\\\s*container\\\\s*\\\\)\\\\s*;`,\n\t\t\t\t);\n\t\t\t\tif (!setContainerRegex.test(content)) {\n\t\t\t\t\ttoInsert.push(`${host}.setContainer(container);`);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// === registerController() flow ===\n\t\t\t\tcontent = ensureImport(content, controllerImport);\n\t\t\t}\n\n\t\t\t// Step 5: Add/merge registerController() - required for endpoint registration\n\t\t\tconst rcSingleNew = content.match(rcSingleRegex);\n\t\t\tconst rcArrayNew = content.match(rcArrayRegex);\n\t\t\tif (rcArrayNew?.[1]) {\n\t\t\t\t// Already has array - merge\n\t\t\t\tconst existing = rcArrayNew[1].trim();\n\t\t\t\tconst merged = existing ? `${existing}, ${controllerName}` : controllerName;\n\t\t\t\tcontent = content.replace(rcArrayRegex, `${inst}.registerController([${merged}]);`);\n\t\t\t} else if (rcSingleNew?.[1]) {\n\t\t\t\t// Has single - convert to array\n\t\t\t\tcontent = content.replace(\n\t\t\t\t\trcSingleRegex,\n\t\t\t\t\t`${inst}.registerController([${rcSingleNew[1]}, ${controllerName}]);`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\ttoInsert.push(`${inst}.registerController([${controllerName}]);`);\n\t\t\t}\n\n\t\t\t// Single ordered splice of all pending statements\n\t\t\tif (toInsert.length > 0) {\n\t\t\t\tconst insertion = findInsertionIndex(content, inst);\n\t\t\t\tconst rest = content.slice(insertion.index);\n\t\t\t\tconst lead = /^\\n*/.exec(rest)?.[0].length ?? 0;\n\t\t\t\tconst prefix =\n\t\t\t\t\tinsertion.index > 0 && content[insertion.index - 1] !== \"\\n\"\n\t\t\t\t\t\t? \"\\n\"\n\t\t\t\t\t\t: \"\";\n\t\t\t\t// Exactly one blank line before whatever follows: count the\n\t\t\t\t// leading newlines the remainder already provides.\n\t\t\t\tconst separator = \"\\n\".repeat(Math.max(0, 2 - Math.min(lead, 2)));\n\t\t\t\tcontent =\n\t\t\t\t\tcontent.slice(0, insertion.index) +\n\t\t\t\t\tprefix +\n\t\t\t\t\ttoInsert.join(\"\\n\\n\") +\n\t\t\t\t\tseparator +\n\t\t\t\t\trest;\n\t\t\t}\n\n\t\t\t// Step 6: declare the container right after the hosting app's\n\t\t\t// declaration (kept after the splice so it stays above every\n\t\t\t// inserted statement)\n\t\t\tif (useContainer) {\n\t\t\t\tconst containerDeclRegex = /(?:const|let|var)\\s+container\\s*=\\s*new\\s+Container\\(\\)/;\n\t\t\t\tif (!containerDeclRegex.test(content)) {\n\t\t\t\t\tconst host = resolveContainerHost(inst, target.instances);\n\t\t\t\t\tconst declEnd =\n\t\t\t\t\t\tfindAppDeclEnd(content, host) ?? findAppDeclEnd(content, inst);\n\t\t\t\t\tif (declEnd !== null) {\n\t\t\t\t\t\tcontent =\n\t\t\t\t\t\t\tcontent.slice(0, declEnd) +\n\t\t\t\t\t\t\t\"\\nconst container = new Container();\" +\n\t\t\t\t\t\t\tcontent.slice(declEnd);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// No `const <inst> = new Buntok(...)` to anchor on - declare\n\t\t\t\t\t\t// the container directly above the inserted block.\n\t\t\t\t\t\tconst insertion = findInsertionIndex(content, inst);\n\t\t\t\t\t\tconst prefix =\n\t\t\t\t\t\t\tinsertion.index > 0 &&\n\t\t\t\t\t\t\tcontent[insertion.index - 1] !== \"\\n\"\n\t\t\t\t\t\t\t\t? \"\\n\"\n\t\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\tcontent =\n\t\t\t\t\t\t\tcontent.slice(0, insertion.index) +\n\t\t\t\t\t\t\tprefix +\n\t\t\t\t\t\t\t\"const container = new Container();\\n\" +\n\t\t\t\t\t\t\tcontent.slice(insertion.index);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconsole.log(\n\t\t\t\tuseContainer\n\t\t\t\t\t? `\\x1b[32m✔ Registered ${controllerName} via ${inst} (container.scan() + registerController) in ${target.file}\\x1b[0m`\n\t\t\t\t\t: `\\x1b[32m✔ Registered ${controllerName} via ${inst} in ${target.file}\\x1b[0m`,\n\t\t\t);\n\n\t\t\tawait fs.writeFile(target.file, content);\n\t\t} else {\n\t\t\tconst location = useContainer ? \"container.scan()\" : \"registerController\";\n\t\t\tconsole.log(\n\t\t\t\t`\\x1b[90m• ${target.file}: ${controllerName} already registered in ${location} (${inst})\\x1b[0m`,\n\t\t\t);\n\t\t}\n\t}\n\n\t// Static route scan: warn when the new controller collides with existing routes\n\tif (!options.dryRun && options.controller) {\n\t\twarnDuplicateRoutes(scanRoutes());\n\t}\n\n\t// Print results\n\tconsole.log(options.dryRun ? \"\\x1b[33mWould generate:\\x1b[0m\" : \"\\x1b[32mGenerated files:\\x1b[0m\");\n\tfor (const result of results.sort()) {\n\t\tconsole.log(`  ${result}`);\n\t}\n\n\tif (options.dryRun) {\n\t\tconsole.log(`\\n\\x1b[33mRun without --dry-run to create these files.\\x1b[0m\\n`);\n\t} else {\n\t\tconsole.log(`\n\\x1b[36mNext steps:\\x1b[0m\n  1. Start dev server: \\x1b[33mbun run dev\\x1b[0m\n`);\n\t}\n}\n","export function generateBarrel(\n\tname: string,\n\tpascalName: string,\n\toptions: {\n\t\trepository: boolean;\n\t\tservice: boolean;\n\t\tcontroller: boolean;\n\t\tschema: boolean;\n\t},\n): string {\n\tconst exports: string[] = [];\n\n\tif (options.repository) {\n\t\texports.push(`export { ${pascalName}Repository } from \"./${name}.repository\";`);\n\t}\n\n\tif (options.service) {\n\t\texports.push(`export { ${pascalName}Service } from \"./${name}.service\";`);\n\t}\n\n\tif (options.controller) {\n\t\texports.push(`export { ${pascalName}Controller } from \"./${name}.controller\";`);\n\t}\n\n\tif (options.schema) {\n\t\texports.push(\n\t\t\t`export { Create${pascalName}Schema, Update${pascalName}Schema } from \"./${name}.schema\";`,\n\t\t\t`export type { Create${pascalName}Input, Update${pascalName}Input } from \"./${name}.schema\";`,\n\t\t);\n\t}\n\n\treturn exports.join(\"\\n\") + \"\\n\";\n}\n","export function toSnakeCase(str: string): string {\n\treturn str\n\t\t.replace(/([A-Z])/g, \"_$1\")\n\t\t.toLowerCase()\n\t\t.replace(/^_/, \"\")\n\t\t.replace(/-/g, \"_\");\n}\n\nexport function toPascalCase(str: string): string {\n\treturn str\n\t\t.split(/[-_\\s]+/)\n\t\t.filter(Boolean)\n\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n\t\t.join(\"\");\n}\n\nexport function toCamelCase(str: string): string {\n\tconst pascal = toPascalCase(str);\n\treturn pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\nexport function toKebabCase(str: string): string {\n\treturn toSnakeCase(str).replace(/_/g, \"-\");\n}\n\nconst IRREGULARS: Record<string, string> = {\n\tperson: \"people\",\n\tchild: \"children\",\n\tman: \"men\",\n\twoman: \"women\",\n\tmouse: \"mice\",\n\tgoose: \"geese\",\n\ttooth: \"teeth\",\n\tfoot: \"feet\",\n\tknife: \"knives\",\n\twife: \"wives\",\n\tlife: \"lives\",\n\tleaf: \"leaves\",\n\twolf: \"wolves\",\n\thalf: \"halves\",\n\tshelf: \"shelves\",\n\tpotato: \"potatoes\",\n\ttomato: \"tomatoes\",\n\thero: \"heroes\",\n\techo: \"echoes\",\n\tanalysis: \"analyses\",\n\tcrisis: \"crises\",\n\tthesis: \"theses\",\n\tdiagnosis: \"diagnoses\",\n\thypothesis: \"hypotheses\",\n\tbasis: \"bases\",\n\tquiz: \"quizzes\",\n};\n\nconst UNCOUNTABLE = new Set([\n\t\"sheep\",\n\t\"fish\",\n\t\"series\",\n\t\"species\",\n\t\"deer\",\n\t\"news\",\n\t\"information\",\n\t\"equipment\",\n\t\"money\",\n\t\"furniture\",\n\t\"software\",\n\t\"hardware\",\n\t\"feedback\",\n\t\"research\",\n]);\n\n/**\n * Minimal English pluralization for route/file names (zero-dep).\n * Handles irregulars, uncountables, consonant+-y, and -s/-x/-z/-ch/-sh endings.\n */\nexport function pluralize(word: string): string {\n\tconst lower = word.toLowerCase();\n\tif (UNCOUNTABLE.has(lower)) return word;\n\n\tconst irregular = IRREGULARS[lower];\n\tif (irregular) {\n\t\treturn /^[A-Z]/.test(word)\n\t\t\t? irregular.charAt(0).toUpperCase() + irregular.slice(1)\n\t\t\t: irregular;\n\t}\n\n\t// Already plural? (ends in plain -s, but not -ss/-us/-is which stay singular)\n\tif (/s$/i.test(word) && !/(ss|us|is)$/i.test(word)) return word;\n\n\tif (/(s|x|z|ch|sh)$/i.test(word)) return `${word}es`;\n\tif (/[^aeiou]y$/i.test(word)) return `${word.slice(0, -1)}ies`;\n\treturn `${word}s`;\n}\n\nexport interface EntityNames {\n\t/** Input as passed by the user, e.g. `blog-post` */\n\traw: string;\n\t/** User / BlogPost */\n\tpascal: string;\n\t/** user / blog_post */\n\tsnake: string;\n\t/** user / blog-post */\n\tkebab: string;\n\t/** user / blogPost */\n\tcamel: string;\n\t/** users / blog-posts (kebab, pluralized) */\n\troute: string;\n\t/** users / blog_posts (snake, pluralized) */\n\tpluralSnake: string;\n}\n\nexport function resolveNames(entity: string): EntityNames {\n\tconst snake = toSnakeCase(entity);\n\tconst kebab = snake.replace(/_/g, \"-\");\n\tconst pascal = toPascalCase(entity);\n\tconst camel = pascal.charAt(0).toLowerCase() + pascal.slice(1);\n\treturn {\n\t\traw: entity,\n\t\tpascal,\n\t\tsnake,\n\t\tkebab,\n\t\tcamel,\n\t\troute: pluralize(kebab),\n\t\tpluralSnake: pluralize(snake),\n\t};\n}\n\n/**\n * Interactively select one option from a list. Returns the chosen index,\n * or `null` when stdin is not a TTY (CI / piped input) so callers fall\n * back to a deterministic default instead of hanging.\n */\nexport async function selectFrom(\n\tquestion: string,\n\tchoices: string[],\n\tdefaultIndex = 0,\n): Promise<number | null> {\n\tif (!process.stdin.isTTY || choices.length === 0) return null;\n\tconst { createInterface } = await import(\"node:readline\");\n\tconst rl = createInterface({ input: process.stdin, output: process.stdout });\n\ttry {\n\t\tconst lines = choices\n\t\t\t.map((choice, i) => `  ${i + 1}. ${choice}`)\n\t\t\t.join(\"\\n\");\n\t\tconst answer: string = await new Promise((resolve) => {\n\t\t\tconsole.log(lines);\n\t\t\trl.question(`${question} [${defaultIndex + 1}]: `, resolve);\n\t\t});\n\t\tconst trimmed = answer.trim();\n\t\tif (trimmed === \"\") return defaultIndex;\n\t\tconst picked = Number.parseInt(trimmed, 10);\n\t\tif (Number.isNaN(picked) || picked < 1 || picked > choices.length) return null;\n\t\treturn picked - 1;\n\t} finally {\n\t\trl.close();\n\t}\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport type ORM = \"prisma\" | \"drizzle\" | \"typeorm\";\n\n/**\n * Detect the project ORM from config files first (definitive), then\n * package.json dependencies. Returns null when nothing matches.\n */\nexport function detectORMOrNull(): ORM | null {\n\t// Config / schema files\n\tif (existsSync(\"prisma/schema.prisma\") || existsSync(\"prisma/schema.ts\")) {\n\t\treturn \"prisma\";\n\t}\n\tif (existsSync(\"drizzle.config.ts\") || existsSync(\"drizzle.config.js\")) {\n\t\treturn \"drizzle\";\n\t}\n\tif (\n\t\texistsSync(\"ormconfig.json\") ||\n\t\texistsSync(\"ormconfig.ts\") ||\n\t\texistsSync(\"ormconfig.js\")\n\t) {\n\t\treturn \"typeorm\";\n\t}\n\n\t// Dependencies\n\tif (existsSync(\"package.json\")) {\n\t\ttry {\n\t\t\tconst pkg = JSON.parse(readFileSync(\"package.json\", \"utf-8\"));\n\t\t\tconst deps = { ...pkg.dependencies, ...pkg.devDependencies };\n\t\t\tif (deps[\"@prisma/client\"] || deps[\"prisma\"]) return \"prisma\";\n\t\t\tif (deps[\"drizzle-orm\"] || deps[\"drizzle-kit\"]) return \"drizzle\";\n\t\t\tif (deps[\"typeorm\"]) return \"typeorm\";\n\t\t} catch {\n\t\t\t// ignore parse errors\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/** Detect ORM with `prisma` as fallback (for generators). */\nexport function detectORM(): ORM {\n\treturn detectORMOrNull() ?? \"prisma\";\n}\n\n/** Candidate entry files, probed in order (union of dev/debug/scaffold). */\nexport const ENTRY_CANDIDATES = [\n\t\"server.ts\",\n\t\"src/index.ts\",\n\t\"src/main.ts\",\n\t\"src/buntok.ts\",\n\t\"src/app.ts\",\n\t\"src/server.ts\",\n\t\"index.ts\",\n] as const;\n\n/**\n * All existing entry files (relative path) in preference order: candidates\n * that look like they host the app (listen/new Buntok/default export) first,\n * then the remaining ones.\n */\nexport function findEntryFiles(cwd: string = process.cwd()): string[] {\n\tconst existing: string[] = [];\n\tfor (const candidate of ENTRY_CANDIDATES) {\n\t\tif (existsSync(join(cwd, candidate))) existing.push(candidate);\n\t}\n\tconst probe: string[] = [];\n\tconst rest: string[] = [];\n\tfor (const candidate of existing) {\n\t\ttry {\n\t\t\tconst src = readFileSync(join(cwd, candidate), \"utf-8\");\n\t\t\tif (/app\\.listen\\s*\\(|new\\s+(?:App|Buntok)\\s*\\(|export\\s+default\\s+app/.test(src)) {\n\t\t\t\tprobe.push(candidate);\n\t\t\t} else {\n\t\t\t\trest.push(candidate);\n\t\t\t}\n\t\t} catch {\n\t\t\t// unreadable candidate - keep looking\n\t\t\trest.push(candidate);\n\t\t}\n\t}\n\treturn [...probe, ...rest];\n}\n\n/**\n * Find the app entry file (relative path) or null when none exists.\n * Prefers a candidate that actually hosts the app (listen/new Buntok/export).\n */\nexport function findEntryFile(cwd: string = process.cwd()): string | null {\n\treturn findEntryFiles(cwd)[0] ?? null;\n}\n\n/** Escape a string for use inside a regular expression. */\nexport function escapeRegExp(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport interface AppInstance {\n\t/** Declared variable name, e.g. `app` or `apiv1`. */\n\tname: string;\n\t/** Index just past the declaration (including the optional `;`). */\n\tdeclEnd: number;\n\t/** Whether the declaration is exported. */\n\texported: boolean;\n\t/** `app` for `new Buntok(...)`, `group` for `X = Y.group(...)`. */\n\tkind: \"app\" | \"group\";\n\t/** For groups: the receiver, e.g. `app` in `const apiv1 = app.group(\"/api/v1\")`. */\n\towner?: string;\n\t/** For groups: the mount prefix string, e.g. `/api/v1`. */\n\tprefix?: string;\n}\n\nconst DECL_HEAD =\n\t/(?:export\\s+)?(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)\\s*(?::[^=]*?)?=\\s*/g;\n\nconst GROUP_CALL = /\\.\\s*group\\s*\\(/g;\n\n/**\n * End of a statement starting at `from`: past a depth-0 `;`, or at a\n * depth-0 newline once a call has closed (ASI). Skips string literals so\n * parentheses/semicolons inside them do not confuse the scan.\n */\nfunction statementEnd(src: string, from: number): number {\n\tlet depth = 0;\n\tlet seenClose = false;\n\tlet quote: string | null = null;\n\tfor (let i = from; i < src.length; i++) {\n\t\tconst ch = src[i] ?? \"\";\n\t\tif (quote !== null) {\n\t\t\tif (ch === \"\\\\\") i++;\n\t\t\telse if (ch === quote) quote = null;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === '\"' || ch === \"'\" || ch === \"`\") {\n\t\t\tquote = ch;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \"(\") depth++;\n\t\telse if (ch === \")\") {\n\t\t\tdepth = Math.max(0, depth - 1);\n\t\t\tif (depth === 0) seenClose = true;\n\t\t} else if (ch === \";\" && depth === 0) return i + 1;\n\t\telse if (ch === \"\\n\" && depth === 0 && seenClose) return i;\n\t}\n\treturn src.length;\n}\n\n/**\n * Detect every app-hosting declaration in a source file:\n * - `const app = new Buntok(...)` (generic `new Buntok<DI>()` too)\n * - `const apiv1 = app.group(\"/api/v1\")` (RouterGroup targets)\n */\nexport function detectAppInstances(src: string): AppInstance[] {\n\tconst instances: AppInstance[] = [];\n\tDECL_HEAD.lastIndex = 0;\n\tlet m: RegExpExecArray | null;\n\twhile ((m = DECL_HEAD.exec(src)) !== null) {\n\t\tconst name = m[1] ?? \"\";\n\t\tconst exported = m[0].startsWith(\"export \");\n\t\tconst rhsStart = m.index + m[0].length;\n\n\t\tif (/^new\\s+(?:App|Buntok)(?:\\s*<[^>]*>)?\\s*\\(/.test(src.slice(rhsStart, rhsStart + 200))) {\n\t\t\tconst declEnd = statementEnd(src, m.index);\n\t\t\tinstances.push({ name, declEnd, exported, kind: \"app\" });\n\t\t\tcontinue;\n\t\t}\n\n\t\t// RouterGroup: `X = <receiver>.group(...)` - the receiver is the\n\t\t// owner (a Buntok, or another group whose owner we resolve later).\n\t\tconst stmtEnd = statementEnd(src, m.index);\n\t\tconst region = src.slice(rhsStart, stmtEnd);\n\t\tGROUP_CALL.lastIndex = 0;\n\t\tlet first: RegExpExecArray | null = null;\n\t\tlet last: RegExpExecArray | null = null;\n\t\tlet g: RegExpExecArray | null;\n\t\twhile ((g = GROUP_CALL.exec(region)) !== null) {\n\t\t\tfirst ??= g;\n\t\t\tlast = g;\n\t\t}\n\t\tif (!first || !last) continue;\n\t\t// Owner = receiver before the FIRST .group( - for chains like\n\t\t// `app.group(\"/a\").group(\"/b\")` that root is still the Buntok.\n\t\tconst owner = region.slice(0, first.index).replace(/\\s+/g, \"\");\n\t\tif (!/^[A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*)*$/.test(owner)) continue;\n\t\tconst open = rhsStart + last.index + last[0].length - 1;\n\t\tconst close = findBalancedClose(src, open);\n\t\tlet declEnd = stmtEnd;\n\t\tif (close !== -1) {\n\t\t\tdeclEnd = close + 1;\n\t\t\tif (src[declEnd] === \";\") declEnd++;\n\t\t}\n\t\tconst prefix = /^\\s*[\"'`]([^\"'`]*)[\"'`]/.exec(src.slice(open + 1))?.[1];\n\t\tinstances.push({\n\t\t\tname,\n\t\t\tdeclEnd,\n\t\t\texported,\n\t\t\tkind: \"group\",\n\t\t\towner,\n\t\t\t...(prefix !== undefined ? { prefix } : {}),\n\t\t});\n\t}\n\treturn instances;\n}\n\n/**\n * Find the file that actually declares the Buntok instance (registration\n * target), e.g. `src/app.ts` / `src/index.ts`. A file that merely calls\n * `app.listen()` (the scaffolded `server.ts`) never wins over the\n * declaration file. Falls back to the entry file when no candidate\n * declares a Buntok.\n */\nexport function findAppDeclarationFile(cwd: string = process.cwd()): string | null {\n\tlet plainApp: string | null = null;\n\tlet groupOnly: string | null = null;\n\tfor (const candidate of ENTRY_CANDIDATES) {\n\t\tconst path = join(cwd, candidate);\n\t\tif (!existsSync(path)) continue;\n\t\ttry {\n\t\t\tconst src = readFileSync(path, \"utf-8\");\n\t\t\tconst instances = detectAppInstances(src);\n\t\t\tif (instances.length === 0) continue;\n\t\t\tconst apps = instances.filter((i) => i.kind === \"app\");\n\t\t\tif (apps.some((i) => i.exported)) return candidate;\n\t\t\tif (apps.length > 0) plainApp ??= candidate;\n\t\t\telse groupOnly ??= candidate;\n\t\t} catch {\n\t\t\t// unreadable candidate - keep looking\n\t\t}\n\t}\n\treturn plainApp ?? groupOnly ?? findEntryFile(cwd);\n}\n\n/** Index just past a balanced `)` starting at openIndex (points at `(`). */\nfunction findBalancedClose(src: string, openIndex: number): number {\n\tlet depth = 0;\n\tfor (let i = openIndex; i < src.length; i++) {\n\t\tconst ch = src[i];\n\t\tif (ch === \"(\") depth++;\n\t\telse if (ch === \")\") {\n\t\t\tdepth--;\n\t\t\tif (depth === 0) return i;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/**\n * End of a `const <name> = new Buntok(...)` declaration (options object\n * supported), or null when there is no declaration for that instance.\n */\nexport function findAppDeclEnd(src: string, name = \"app\"): number | null {\n\tconst instance = detectAppInstances(src).find((i) => i.name === name);\n\treturn instance ? instance.declEnd : null;\n}\n\n/**\n * Find where to insert registration lines before <name>.listen / route\n * handlers / exports, with fallback chain. Returns insertion index and\n * the matched text starting at that index.\n */\nexport function findInsertionIndex(src: string, name = \"app\"): {\n\tindex: number;\n\tbefore: string;\n} {\n\tconst ref = escapeRegExp(name);\n\t// Pattern 1: <name>.listen(\n\tconst listenMatch = src.match(new RegExp(`(${ref}\\\\.listen\\\\s*\\\\()`));\n\tif (listenMatch?.index !== undefined && listenMatch[1]) {\n\t\treturn { index: listenMatch.index, before: listenMatch[1] };\n\t}\n\t// Pattern 2: <name>.get( or <name>.post( etc (route handlers)\n\tconst routeMatch = src.match(\n\t\tnew RegExp(`\\\\n(${ref}\\\\.(?:get|post|put|delete|patch|all|use)\\\\s*\\\\()`),\n\t);\n\tif (routeMatch?.index !== undefined && routeMatch[1]) {\n\t\treturn { index: routeMatch.index + 1, before: routeMatch[1] };\n\t}\n\t// Pattern 3: export default <name>\n\tconst exportDefaultMatch = src.match(new RegExp(`\\\\n(export\\\\s+default\\\\s+${ref}\\\\b)`));\n\tif (exportDefaultMatch?.index !== undefined && exportDefaultMatch[1]) {\n\t\treturn { index: exportDefaultMatch.index + 1, before: exportDefaultMatch[1] };\n\t}\n\t// Pattern 4: <name> declaration - insert after the balanced\n\t// declaration (options object supported)\n\tconst instance = detectAppInstances(src).find((i) => i.name === name);\n\tif (instance) {\n\t\treturn { index: instance.declEnd, before: \"\" };\n\t}\n\t// Fallback: append at end\n\treturn { index: src.length, before: \"\" };\n}\n\n/** Add an import line after the last existing import (no duplicates). */\nexport function ensureImport(src: string, imp: string): string {\n\tif (src.includes(imp)) return src;\n\tconst lines = src.split(\"\\n\");\n\tlet lastImportIndex = -1;\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tif (lines[i]?.startsWith(\"import \")) {\n\t\t\tlastImportIndex = i;\n\t\t}\n\t}\n\tif (lastImportIndex !== -1) {\n\t\tlines.splice(lastImportIndex + 1, 0, imp);\n\t} else {\n\t\tlines.unshift(imp);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Collect controllers imported from @/modules/*.\n * excludeFromScan: skip controllers already passed to registerController.\n * name: the app instance variable that owns the registrations.\n */\nexport function collectImportedControllers(\n\tsrc: string,\n\texcludeFromScan: boolean,\n\tname = \"app\",\n): string[] {\n\tconst imports = src.match(\n\t\t/import\\s*\\{\\s*(\\w+Controller)\\s*\\}\\s*from\\s*[\"']@\\/modules\\/\\w+[\"']/g,\n\t);\n\tconst controllers: string[] = [];\n\tif (imports) {\n\t\tconst ref = escapeRegExp(name);\n\t\tfor (const imp of imports) {\n\t\t\tconst match = imp.match(/\\{\\s*(\\w+Controller)\\s*\\}/);\n\t\t\tif (match?.[1]) {\n\t\t\t\tif (excludeFromScan) {\n\t\t\t\t\tconst rcSingleMatch = src.match(\n\t\t\t\t\t\tnew RegExp(`${ref}\\\\.registerController\\\\((${match[1]})\\\\)`),\n\t\t\t\t\t);\n\t\t\t\t\tconst rcArrayMatch = src.match(\n\t\t\t\t\t\tnew RegExp(`${ref}\\\\.registerController\\\\(\\\\[([^\\\\]]*?${match[1]}[^\\\\]]*?)\\\\]\\\\)`),\n\t\t\t\t\t);\n\t\t\t\t\tif (rcSingleMatch || rcArrayMatch) continue;\n\t\t\t\t}\n\t\t\t\tcontrollers.push(match[1]);\n\t\t\t}\n\t\t}\n\t}\n\treturn controllers;\n}\n\n/**\n * Format files with Biome when it is installed locally.\n * Skips silently when node_modules/.bin/biome is absent (avoids `bunx`\n * resolving to an unrelated npm package).\n */\nexport function formatWithBiome(paths: string[]): boolean {\n\tif (paths.length === 0) return false;\n\tconst bin = join(process.cwd(), \"node_modules\", \".bin\", \"biome\");\n\tif (!existsSync(bin) && !existsSync(`${bin}.cmd`)) return false;\n\tconst proc = Bun.spawnSync(\n\t\t[\"bunx\", \"biome\", \"format\", \"--write\", ...paths],\n\t\t{ stdio: [\"ignore\", \"ignore\", \"ignore\"] },\n\t);\n\treturn proc.exitCode === 0;\n}\n","import { resolveNames, toCamelCase } from \"../utils.js\";\nimport { detectORMOrNull } from \"../project.js\";\nimport type { ORM } from \"./repository.js\";\n\nexport interface ControllerOptions {\n\t/** Generate the BaseController-based variant. */\n\tbase?: boolean;\n\t/** Override the route prefix (default: pluralized entity name). */\n\troute?: string;\n}\n\nfunction getPrismaType(pascalName: string): string {\n\treturn `import type { ${pascalName} } from \"@prisma/client\";`;\n}\n\nfunction getDrizzleType(_entityName: string): string {\n\tconst pascalName = _entityName.charAt(0).toUpperCase() + _entityName.slice(1);\n\treturn `import type { InferSelectModel } from \"drizzle-orm\";\nimport { ${_entityName} } from \"@/lib/db/schema\";\n\ntype ${pascalName} = InferSelectModel<typeof ${_entityName}>;`;\n}\n\nfunction getTypeORMType(pascalName: string): string {\n\treturn `import type { ${pascalName} } from \"@/lib/entities/${pascalName}\";`;\n}\n\nfunction generatePlainController(\n\tentityName: string,\n\tpascalName: string,\n\troute: string,\n): string {\n\tconst serviceProp = `${toCamelCase(entityName)}Service`;\n\treturn `import { Dependencies, Controller, Get, Post, Put, Delete } from \"@buntok/core\";\nimport type { Context } from \"@buntok/core\";\nimport { ${pascalName}Service } from \"./${entityName}.service\";\n\n@Dependencies(${pascalName}Service)\n@Controller(\"/${route}\")\nexport class ${pascalName}Controller {\n  constructor(private readonly ${serviceProp}: ${pascalName}Service) {}\n\n  private parseId(id: string): string | number {\n    if (id === \"\") return id;\n    const num = Number(id);\n    return Number.isNaN(num) ? id : num;\n  }\n\n  @Get(\"/\")\n  async getAll(ctx: Context) {\n    const data = await this.${serviceProp}.getAll();\n    return ctx.success(data);\n  }\n\n  @Get(\"/:id\")\n  async getById(ctx: Context) {\n    const id = this.parseId(ctx.params.id ?? \"\");\n    const data = await this.${serviceProp}.getById(id);\n    return ctx.success(data);\n  }\n\n  @Post(\"/\")\n  async create(ctx: Context) {\n    const body = await ctx.body();\n    const data = await this.${serviceProp}.create(body);\n    return ctx.success(data, \"Created successfully!.\", 201);\n  }\n\n  @Put(\"/:id\")\n  async update(ctx: Context) {\n    const id = this.parseId(ctx.params.id ?? \"\");\n    const body = await ctx.body();\n    const data = await this.${serviceProp}.update(id, body);\n    return ctx.success(data);\n  }\n\n  @Delete(\"/:id\")\n  async delete(ctx: Context) {\n    const id = this.parseId(ctx.params.id ?? \"\");\n    await this.${serviceProp}.delete(id);\n    return ctx.status(204);\n  }\n}\n`;\n}\n\nfunction generateBaseController(\n\tentityName: string,\n\tpascalName: string,\n\troute: string,\n\ttypeImport: string,\n\ttypeRef: string,\n): string {\n\treturn `import { Dependencies, Controller, BaseController } from \"@buntok/core\";\nimport { ${pascalName}Service } from \"./${entityName}.service\";\n${typeImport}\n\n@Dependencies(${pascalName}Service)\n@Controller(\"/${route}\")\nexport class ${pascalName}Controller extends BaseController<${typeRef}> {\n  constructor(private readonly ${toCamelCase(entityName)}Service: ${pascalName}Service) {\n    super(${toCamelCase(entityName)}Service);\n  }\n}\n`;\n}\n\nfunction generateStubController(\n\tentityName: string,\n\tpascalName: string,\n\troute: string,\n): string {\n\treturn `import { Controller, Get, Post, Put, Delete } from \"@buntok/core\";\nimport type { Context } from \"@buntok/core\";\n\n@Controller(\"/${route}\")\nexport class ${pascalName}Controller {\n  @Get(\"/\")\n  async getAll(ctx: Context) {\n    return ctx.success([], \"Records retrieved successfully\");\n  }\n\n  @Get(\"/:id\")\n  async getById(ctx: Context) {\n    return ctx.success({ id: ctx.params.id }, \"Record retrieved successfully\");\n  }\n\n  @Post(\"/\")\n  async create(ctx: Context) {\n    const data = await ctx.body<any>();\n    return ctx.success(data, \"Record created successfully\", 201);\n  }\n\n  @Put(\"/:id\")\n  async update(ctx: Context) {\n    const data = await ctx.body<any>();\n    return ctx.success({ id: ctx.params.id, ...data }, \"Record updated successfully\");\n  }\n\n  @Delete(\"/:id\")\n  async delete(ctx: Context) {\n    return ctx.success(null, \"Record deleted successfully\");\n  }\n}\n`;\n}\n\n/**\n * Generate controller file content.\n * Default is plain (explicit CRUD endpoints delegating to the service);\n * `options.base` opts into the BaseController variant.\n * Route prefix is pluralized (`category` → `/categories`), overridable via `options.route`.\n */\nexport function generateController(\n\tentityName: string,\n\tpascalName: string,\n\twithService: boolean = true,\n\torm?: ORM | null,\n\toptions?: ControllerOptions,\n): string {\n\tconst route = options?.route ?? resolveNames(entityName).route;\n\n\tif (withService) {\n\t\tconst detectedOrm = orm ?? detectORMOrNull();\n\t\tlet typeImport: string;\n\t\tlet typeRef: string;\n\n\t\tif (detectedOrm === null) {\n\t\t\ttypeImport = \"\";\n\t\t\ttypeRef = \"any\";\n\t\t} else {\n\t\t\tswitch (detectedOrm) {\n\t\t\t\tcase \"drizzle\":\n\t\t\t\t\ttypeImport = getDrizzleType(entityName);\n\t\t\t\t\ttypeRef = pascalName;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"typeorm\":\n\t\t\t\t\ttypeImport = getTypeORMType(pascalName);\n\t\t\t\t\ttypeRef = pascalName;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"prisma\":\n\t\t\t\tdefault:\n\t\t\t\t\ttypeImport = getPrismaType(pascalName);\n\t\t\t\t\ttypeRef = pascalName;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn options?.base\n\t\t\t? generateBaseController(\n\t\t\t\t\tentityName,\n\t\t\t\t\tpascalName,\n\t\t\t\t\troute,\n\t\t\t\t\ttypeImport,\n\t\t\t\t\ttypeRef,\n\t\t\t\t)\n\t\t\t: generatePlainController(entityName, pascalName, route);\n\t}\n\n\treturn generateStubController(entityName, pascalName, route);\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/**\n * Model-aware generation: a hand-rolled Prisma schema parser (zero deps)\n * with a universal `--fields` fallback for projects without Prisma.\n */\n\nexport interface FieldDef {\n\tname: string;\n\t/** Prisma scalar name (String, Int, ...) or a logical kind from `--fields`. */\n\ttype: string;\n\toptional: boolean;\n\tlist: boolean;\n\tisId: boolean;\n\tunique: boolean;\n\t/** Field has @default / @createdAt / @updatedAt → not part of create input. */\n\thasDefault: boolean;\n\tisEnum: boolean;\n}\n\nexport interface PrismaModel {\n\tname: string;\n\tfields: FieldDef[];\n}\n\nexport interface PrismaSchema {\n\tmodels: Map<string, PrismaModel>;\n\tenums: Map<string, string[]>;\n}\n\nconst SCALARS = new Set([\n\t\"String\",\n\t\"Int\",\n\t\"Float\",\n\t\"Decimal\",\n\t\"BigInt\",\n\t\"Boolean\",\n\t\"DateTime\",\n\t\"Json\",\n\t\"Bytes\",\n]);\n\nconst SCHEMA_CANDIDATES = [\"prisma/schema.prisma\", \"schema.prisma\"];\n\nfunction stripComments(source: string): string {\n\tlet out = \"\";\n\tlet inString: '\"' | \"'\" | null = null;\n\tfor (let i = 0; i < source.length; i++) {\n\t\tconst ch = source[i];\n\t\tif (inString) {\n\t\t\tout += ch;\n\t\t\tif (ch === inString && source[i - 1] !== \"\\\\\") inString = null;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === '\"' || ch === \"'\") {\n\t\t\tinString = ch;\n\t\t\tout += ch;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \"/\" && source[i + 1] === \"/\") {\n\t\t\twhile (i < source.length && source[i] !== \"\\n\") i++;\n\t\t\tout += \"\\n\";\n\t\t\tcontinue;\n\t\t}\n\t\tout += ch;\n\t}\n\treturn out;\n}\n\nfunction extractBlocks(\n\tsource: string,\n\tkind: \"model\" | \"enum\",\n): { name: string; body: string }[] {\n\tconst blocks: { name: string; body: string }[] = [];\n\tconst re = new RegExp(`\\\\b${kind}\\\\s+([A-Za-z_]\\\\w*)\\\\s*\\\\{`, \"g\");\n\tlet match: RegExpExecArray | null;\n\twhile ((match = re.exec(source)) !== null) {\n\t\tlet depth = 0;\n\t\tlet i = re.lastIndex - 1;\n\t\tfor (; i < source.length; i++) {\n\t\t\tif (source[i] === \"{\") depth++;\n\t\t\telse if (source[i] === \"}\") {\n\t\t\t\tdepth--;\n\t\t\t\tif (depth === 0) break;\n\t\t\t}\n\t\t}\n\t\tblocks.push({ name: match[1] ?? \"\", body: source.slice(re.lastIndex, i) });\n\t\tre.lastIndex = i + 1;\n\t}\n\treturn blocks;\n}\n\nfunction parseFieldLine(line: string): FieldDef | null {\n\tconst trimmed = line.trim();\n\tif (!trimmed || trimmed.startsWith(\"@@\")) return null;\n\tconst m = /^([A-Za-z_]\\w*)\\s+([A-Za-z_]\\w*)(\\[\\])?(\\?)?/.exec(trimmed);\n\tif (!m) return null;\n\tconst [, name = \"\", fieldType = \"\", list, optional] = m;\n\tconst attrs = trimmed.slice(m[0].length);\n\treturn {\n\t\tname,\n\t\ttype: fieldType,\n\t\toptional: Boolean(optional),\n\t\tlist: Boolean(list),\n\t\tisId: /@id\\b/.test(attrs),\n\t\tunique: /@unique\\b/.test(attrs),\n\t\thasDefault: /@default\\b|@updatedAt\\b/.test(attrs),\n\t\tisEnum: !SCALARS.has(fieldType),\n\t};\n}\n\nexport function parsePrismaSchema(source: string): PrismaSchema {\n\tconst clean = stripComments(source);\n\tconst models = new Map<string, PrismaModel>();\n\tconst enums = new Map<string, string[]>();\n\n\tfor (const block of extractBlocks(clean, \"enum\")) {\n\t\t// Tokenize so both single-line and multi-line enum bodies work\n\t\tconst values = block.body\n\t\t\t.split(/\\s+/)\n\t\t\t.filter((t) => /^[A-Za-z_]\\w*$/.test(t));\n\t\tenums.set(block.name.toLowerCase(), values);\n\t}\n\tfor (const block of extractBlocks(clean, \"model\")) {\n\t\tconst fields = block.body\n\t\t\t.split(\"\\n\")\n\t\t\t.map(parseFieldLine)\n\t\t\t.filter((f): f is FieldDef => f !== null);\n\t\tmodels.set(block.name.toLowerCase(), {\n\t\t\tname: block.name,\n\t\t\tfields,\n\t\t});\n\t}\n\treturn { models, enums };\n}\n\n/** Locate and parse the project's Prisma schema; null when absent/unparsable. */\nexport function loadPrismaSchema(): PrismaSchema | null {\n\tfor (const candidate of SCHEMA_CANDIDATES) {\n\t\tconst path = join(process.cwd(), candidate);\n\t\tif (!existsSync(path)) continue;\n\t\ttry {\n\t\t\treturn parsePrismaSchema(readFileSync(path, \"utf-8\"));\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\treturn null;\n}\n\n/** Case-insensitive model lookup (plural route names like `users` also match). */\nexport function getEntityModel(\n\tentityName: string,\n\tschema?: PrismaSchema | null,\n): PrismaModel | null {\n\tconst s = schema ?? loadPrismaSchema();\n\tif (!s) return null;\n\tconst key = entityName.toLowerCase().replace(/s$/, \"\");\n\tfor (const [modelKey, model] of s.models) {\n\t\tif (modelKey === key || modelKey === entityName.toLowerCase()) return model;\n\t}\n\treturn null;\n}\n\nconst FIELD_TYPE_ALIASES: Record<string, string> = {\n\tstring: \"String\",\n\tint: \"Int\",\n\tinteger: \"Int\",\n\tfloat: \"Float\",\n\tnumber: \"Float\",\n\tdecimal: \"Decimal\",\n\tbigint: \"BigInt\",\n\tboolean: \"Boolean\",\n\tbool: \"Boolean\",\n\tdate: \"DateTime\",\n\tdatetime: \"DateTime\",\n\tjson: \"Json\",\n\tbytes: \"Bytes\",\n};\n\n/**\n * Universal fallback: `--fields title:string,body:string,published:boolean?`\n * Types: string, int, float, number, decimal, boolean, date, json (+ `[]`\n * for lists, `?` for optional).\n */\nexport function parseFieldsFlag(value: string): FieldDef[] {\n\tconst fields: FieldDef[] = [];\n\tfor (const part of value.split(\",\")) {\n\t\tconst trimmed = part.trim();\n\t\tif (!trimmed) continue;\n\t\tconst m = /^([A-Za-z_]\\w*)\\s*:\\s*([A-Za-z_]\\w*)(\\[\\])?(\\?)?$/.exec(trimmed);\n\t\tif (!m) continue;\n\t\tconst [, name = \"\", rawType = \"\", list, optional] = m;\n\t\tconst type = FIELD_TYPE_ALIASES[rawType.toLowerCase()] ?? rawType;\n\t\tfields.push({\n\t\t\tname,\n\t\t\ttype,\n\t\t\toptional: Boolean(optional),\n\t\t\tlist: Boolean(list),\n\t\t\tisId: false,\n\t\t\tunique: false,\n\t\t\thasDefault: false,\n\t\t\tisEnum: !SCALARS.has(type),\n\t\t});\n\t}\n\treturn fields;\n}\n\n/** Create inputs exclude auto-generated columns (@id w/ default, @updatedAt). */\nexport function isAutoGenerated(field: FieldDef): boolean {\n\treturn field.isId || field.hasDefault;\n}\n\nfunction zodScalar(field: FieldDef, schema?: PrismaSchema | null): string {\n\tconst inner = (() => {\n\t\tswitch (field.type) {\n\t\t\tcase \"String\":\n\t\t\t\treturn \"z.string()\";\n\t\t\tcase \"Int\":\n\t\t\t\treturn \"z.number().int()\";\n\t\t\tcase \"Float\":\n\t\t\tcase \"BigInt\":\n\t\t\t\treturn \"z.number()\";\n\t\t\tcase \"Decimal\":\n\t\t\t\treturn \"z.union([z.number(), z.string()])\";\n\t\t\tcase \"Boolean\":\n\t\t\t\treturn \"z.boolean()\";\n\t\t\tcase \"DateTime\":\n\t\t\t\treturn \"z.string()\";\n\t\t\tcase \"Json\":\n\t\t\tcase \"Bytes\":\n\t\t\t\treturn \"z.any()\";\n\t\t\tdefault: {\n\t\t\t\tconst enumValues = schema?.enums.get(field.type.toLowerCase());\n\t\t\t\tif (enumValues && enumValues.length > 0) {\n\t\t\t\t\treturn `z.enum([${enumValues.map((v) => `\"${v}\"`).join(\", \")}])`;\n\t\t\t\t}\n\t\t\t\treturn \"z.string()\";\n\t\t\t}\n\t\t}\n\t})();\n\treturn inner;\n}\n\nfunction zodFor(field: FieldDef, schema?: PrismaSchema | null): string {\n\tlet out = field.list ? `z.array(${zodScalar(field, schema)})` : zodScalar(field, schema);\n\tif (field.optional) out += \".optional()\";\n\treturn out;\n}\n\n/** Zod object body lines for the create schema (update = .partial()). */\nexport function zodLinesForFields(\n\tfields: FieldDef[],\n\tschema?: PrismaSchema | null,\n): string[] {\n\tconst used = new Set<string>();\n\tconst lines: string[] = [];\n\tfor (const field of fields) {\n\t\tif (isAutoGenerated(field) || used.has(field.name)) continue;\n\t\tused.add(field.name);\n\t\tlines.push(`${field.name}: ${zodFor(field, schema)},`);\n\t}\n\treturn lines;\n}\n\nfunction fakerFor(field: FieldDef, schema?: PrismaSchema | null): string {\n\tconst enumValues = field.isEnum\n\t\t? schema?.enums.get(field.type.toLowerCase())\n\t\t: undefined;\n\tconst scalar = (() => {\n\t\tif (enumValues && enumValues.length > 0) {\n\t\t\treturn `faker.helpers.arrayElement([${enumValues.map((v) => `\"${v}\"`).join(\", \")}])`;\n\t\t}\n\t\tswitch (field.type) {\n\t\t\tcase \"String\":\n\t\t\t\tif (/mail/i.test(field.name)) return \"faker.internet.email()\";\n\t\t\t\tif (/^name$|username|full_?name|first_?name|last_?name/i.test(field.name))\n\t\t\t\t\treturn \"faker.person.fullName()\";\n\t\t\t\tif (/url|href|link/i.test(field.name)) return \"faker.internet.url()\";\n\t\t\t\tif (/slug/i.test(field.name)) return \"faker.helpers.slug()\";\n\t\t\t\treturn \"faker.lorem.words()\";\n\t\t\tcase \"Int\":\n\t\t\t\treturn \"faker.number.int({ max: 10000 })\";\n\t\t\tcase \"Float\":\n\t\t\tcase \"Decimal\":\n\t\t\tcase \"BigInt\":\n\t\t\t\treturn \"faker.number.float()\";\n\t\t\tcase \"Boolean\":\n\t\t\t\treturn \"faker.datatype.boolean()\";\n\t\t\tcase \"DateTime\":\n\t\t\t\treturn \"faker.date.recent().toISOString()\";\n\t\t\tcase \"Json\":\n\t\t\tcase \"Bytes\":\n\t\t\t\treturn \"faker.lorem.sentence()\";\n\t\t\tdefault:\n\t\t\t\treturn \"faker.lorem.words()\";\n\t\t}\n\t})();\n\treturn field.list ? `Array.from({ length: 3 }, () => ${scalar})` : scalar;\n}\n\n/** Faker assignment lines for factory `build()` bodies. */\nexport function fakerLinesForFields(\n\tfields: FieldDef[],\n\tschema?: PrismaSchema | null,\n): string[] {\n\tconst used = new Set<string>();\n\tconst lines: string[] = [];\n\tfor (const field of fields) {\n\t\tif (isAutoGenerated(field) || used.has(field.name)) continue;\n\t\tused.add(field.name);\n\t\tlines.push(`${field.name}: ${fakerFor(field, schema)},`);\n\t}\n\treturn lines;\n}\n\nfunction tsTypeFor(field: FieldDef, schema?: PrismaSchema | null): string {\n\tconst base = (() => {\n\t\tif (field.isEnum) {\n\t\t\tconst values = schema?.enums.get(field.type.toLowerCase());\n\t\t\tif (values && values.length > 0) return values.map((v) => `\"${v}\"`).join(\" | \");\n\t\t\treturn \"string\";\n\t\t}\n\t\tswitch (field.type) {\n\t\t\tcase \"Int\":\n\t\t\tcase \"Float\":\n\t\t\tcase \"Decimal\":\n\t\t\tcase \"BigInt\":\n\t\t\t\treturn \"number\";\n\t\t\tcase \"Boolean\":\n\t\t\t\treturn \"boolean\";\n\t\t\tcase \"Json\":\n\t\t\tcase \"Bytes\":\n\t\t\t\treturn \"unknown\";\n\t\t\tdefault:\n\t\t\t\treturn \"string\";\n\t\t}\n\t})();\n\treturn field.list ? `${base}[]` : base;\n}\n\n/** TypeScript member lines for an inline model type (non-Prisma factories). */\nexport function tsLinesForFields(\n\tfields: FieldDef[],\n\tschema?: PrismaSchema | null,\n): string[] {\n\tconst used = new Set<string>();\n\tconst lines: string[] = [];\n\tfor (const field of fields) {\n\t\tif (used.has(field.name)) continue;\n\t\tused.add(field.name);\n\t\tconst optional = field.optional ? \"?\" : \"\";\n\t\tlines.push(`${field.name}${optional}: ${tsTypeFor(field, schema)};`);\n\t}\n\treturn lines;\n}\n","import { detectORMOrNull, type ORM } from \"../project.js\";\nimport { toCamelCase } from \"../utils.js\";\n\nexport { detectORM } from \"../project.js\";\nexport type { ORM };\n\nexport interface RepositoryOptions {\n\t/** Generate the BaseRepository-based variant (Prisma only). */\n\tbase?: boolean;\n}\n\nfunction generatePrismaRepository(entityName: string, pascalName: string): string {\n\tconst delegate = toCamelCase(entityName);\n\treturn `import { prisma } from \"@/lib/prisma\";\nimport type { ${pascalName}, Prisma } from \"@prisma/client\";\n\nexport class ${pascalName}Repository {\n  async findAll(): Promise<${pascalName}[]> {\n    return prisma.${delegate}.findMany();\n  }\n\n  async findById(id: string | number): Promise<${pascalName} | null> {\n    return prisma.${delegate}.findUnique({ where: { id: id as any } });\n  }\n\n  async create(data: Prisma.${pascalName}CreateInput): Promise<${pascalName}> {\n    return prisma.${delegate}.create({ data });\n  }\n\n  async update(id: string | number, data: Prisma.${pascalName}UpdateInput): Promise<${pascalName}> {\n    return prisma.${delegate}.update({ where: { id: id as any }, data });\n  }\n\n  async delete(id: string | number): Promise<${pascalName}> {\n    return prisma.${delegate}.delete({ where: { id: id as any } });\n  }\n\n  async count(): Promise<number> {\n    return prisma.${delegate}.count();\n  }\n}\n`;\n}\n\nfunction generatePrismaBaseRepository(\n\tentityName: string,\n\tpascalName: string,\n): string {\n\treturn `import { BaseRepository } from \"@buntok/prisma\";\nimport { prisma } from \"@/lib/prisma\";\nimport type { ${pascalName}, Prisma } from \"@prisma/client\";\n\nexport class ${pascalName}Repository extends BaseRepository<\n  ${pascalName},\n  Prisma.${pascalName}CreateInput,\n  Prisma.${pascalName}UpdateInput\n> {\n  constructor() {\n    super(prisma, \"${entityName}\");\n  }\n}\n`;\n}\n\nfunction generateDrizzleRepository(entityName: string, pascalName: string): string {\n\treturn `import { count, eq } from \"drizzle-orm\";\nimport { db } from \"@/lib/db\";\nimport { ${entityName} } from \"@/lib/db/schema\";\n\nexport class ${pascalName}Repository {\n  async findAll() {\n    return db.select().from(${entityName});\n  }\n\n  async findById(id: number) {\n    const results = await db.select().from(${entityName}).where(eq(${entityName}.id, id));\n    return results[0] ?? null;\n  }\n\n  async create(data: typeof ${entityName}.$inferInsert) {\n    const results = await db.insert(${entityName}).values(data).returning();\n    return results[0];\n  }\n\n  async update(id: number, data: typeof ${entityName}.$inferUpdate) {\n    const results = await db.update(${entityName}).set(data).where(eq(${entityName}.id, id)).returning();\n    return results[0];\n  }\n\n  async delete(id: number) {\n    await db.delete(${entityName}).where(eq(${entityName}.id, id));\n    return true;\n  }\n\n  async count(): Promise<number> {\n    const results = await db.select({ value: count() }).from(${entityName});\n    return results[0]?.value ?? 0;\n  }\n}\n`;\n}\n\nfunction generateTypeORMRepository(entityName: string, pascalName: string): string {\n\treturn `import { AppDataSource } from \"@/lib/data-source\";\nimport { ${pascalName} } from \"@/lib/entities/${pascalName}\";\n\nexport class ${pascalName}Repository {\n  private repo = AppDataSource.getRepository(${pascalName});\n\n  async findAll() {\n    return this.repo.find();\n  }\n\n  async findById(id: number) {\n    return this.repo.findOneBy({ id });\n  }\n\n  async create(data: Partial<${pascalName}>) {\n    return this.repo.save(this.repo.create(data));\n  }\n\n  async update(id: number, data: Partial<${pascalName}>) {\n    await this.repo.update(id, data);\n    return this.repo.findOneBy({ id });\n  }\n\n  async delete(id: number) {\n    await this.repo.delete(id);\n    return true;\n  }\n\n  async count(): Promise<number> {\n    return this.repo.count();\n  }\n}\n`;\n}\n\nfunction generatePlainRepository(pascalName: string): string {\n\treturn `export class ${pascalName}Repository {\n  private items = new Map<string | number, any>();\n\n  async findAll(): Promise<any[]> {\n    return Array.from(this.items.values());\n  }\n\n  async findById(id: string | number): Promise<any | null> {\n    return this.items.get(id) ?? null;\n  }\n\n  async create(data: any): Promise<any> {\n    const id = (data?.id ?? this.items.size + 1) as string | number;\n    const item = { ...data, id };\n    this.items.set(id, item);\n    return item;\n  }\n\n  async update(id: string | number, data: any): Promise<any> {\n    const item = this.items.get(id);\n    if (!item) return null;\n    const merged = { ...item, ...data, id };\n    this.items.set(id, merged);\n    return merged;\n  }\n\n  async delete(id: string | number): Promise<boolean> {\n    return this.items.delete(id);\n  }\n\n  async count(): Promise<number> {\n    return this.items.size;\n  }\n}\n`;\n}\n\n/**\n * Generate repository file content based on ORM.\n * Uses the passed ORM, otherwise auto-detects from the project; when no ORM\n * is found it falls back to a plain in-memory repository. `options.base` opts\n * into the BaseRepository variant (Prisma only - Drizzle/TypeORM stay plain).\n */\nexport function generateRepository(\n\tentityName: string,\n\tpascalName: string,\n\torm?: ORM | null,\n\toptions?: RepositoryOptions,\n): string {\n\tconst detectedOrm = orm ?? detectORMOrNull();\n\n\tif (detectedOrm === null) {\n\t\treturn generatePlainRepository(pascalName);\n\t}\n\n\tswitch (detectedOrm) {\n\t\tcase \"drizzle\":\n\t\t\treturn generateDrizzleRepository(entityName, pascalName);\n\t\tcase \"typeorm\":\n\t\t\treturn generateTypeORMRepository(entityName, pascalName);\n\t\tcase \"prisma\":\n\t\tdefault:\n\t\t\treturn options?.base\n\t\t\t\t? generatePrismaBaseRepository(entityName, pascalName)\n\t\t\t\t: generatePrismaRepository(entityName, pascalName);\n\t}\n}\n","import { existsSync } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { toPascalCase } from \"../utils.js\";\n\ninterface SchemaOptions {\n\t/** Zod object body lines (from the Prisma model or `--fields`). */\n\tlines?: string[] | null;\n}\n\nfunction generateSchema(\n\tname: string,\n\tpascalName: string,\n\toptions?: SchemaOptions,\n): string {\n\tconst body =\n\t\toptions?.lines && options.lines.length > 0\n\t\t\t? options.lines.map((l) => `  ${l}`).join(\"\\n\")\n\t\t\t: `  name: z.string().min(1).max(100),\\n  // TODO: Add more fields`;\n\treturn `import { z } from \"@buntok/core/middlewares/validator\";\n\nexport const Create${pascalName}Schema = z.object({\n${body}\n});\n\nexport const Update${pascalName}Schema = Create${pascalName}Schema.partial();\n\nexport type Create${pascalName}Input = z.infer<typeof Create${pascalName}Schema>;\nexport type Update${pascalName}Input = z.infer<typeof Update${pascalName}Schema>;\n`;\n}\n\nexport async function generateSchemaFile(\n\tname: string,\n\tmoduleDir: string,\n\tforce = false,\n\toptions?: SchemaOptions,\n): Promise<string | null> {\n\tconst pascalName = toPascalCase(name);\n\tconst filePath = join(moduleDir, `${name}.schema.ts`);\n\n\tif (existsSync(filePath) && !force) {\n\t\treturn null;\n\t}\n\n\tawait fs.writeFile(filePath, generateSchema(name, pascalName, options));\n\treturn filePath;\n}\n","import type { ORM } from \"./repository.js\";\nimport { detectORMOrNull } from \"../project.js\";\nimport { toCamelCase } from \"../utils.js\";\n\nexport interface ServiceOptions {\n\t/** Generate the BaseService-based variant. */\n\tbase?: boolean;\n}\n\nfunction getPrismaType(pascalName: string): string {\n\treturn `import type { ${pascalName} } from \"@prisma/client\";`;\n}\n\nfunction getDrizzleType(_entityName: string): string {\n\treturn `import type { InferSelectModel } from \"drizzle-orm\";\nimport { ${_entityName} } from \"@/lib/db/schema\";\n\ntype ${_entityName.charAt(0).toUpperCase() + _entityName.slice(1)} = InferSelectModel<typeof ${_entityName}>;`;\n}\n\nfunction getTypeORMType(pascalName: string): string {\n\treturn `import type { ${pascalName} } from \"@/lib/entities/${pascalName}\";`;\n}\n\nfunction resolveTypeRef(\n\tentityName: string,\n\tpascalName: string,\n\tdetectedOrm: ORM | null,\n): { typeImport: string; typeRef: string } {\n\tif (detectedOrm === null) {\n\t\treturn { typeImport: \"\", typeRef: \"any\" };\n\t}\n\tswitch (detectedOrm) {\n\t\tcase \"drizzle\":\n\t\t\treturn { typeImport: getDrizzleType(entityName), typeRef: pascalName };\n\t\tcase \"typeorm\":\n\t\t\treturn { typeImport: getTypeORMType(pascalName), typeRef: pascalName };\n\t\tcase \"prisma\":\n\t\tdefault:\n\t\t\treturn { typeImport: getPrismaType(pascalName), typeRef: pascalName };\n\t}\n}\n\nfunction generatePlainService(\n\tentityName: string,\n\tpascalName: string,\n\ttypeImport: string,\n\ttypeRef: string,\n): string {\n\tconst repoProp = `${toCamelCase(entityName)}Repository`;\n\treturn `import { Dependencies, NotFoundError } from \"@buntok/core\";\nimport { ${pascalName}Repository } from \"./${entityName}.repository\";\n${typeImport}\n\n@Dependencies(${pascalName}Repository)\nexport class ${pascalName}Service {\n  constructor(private readonly ${repoProp}: ${pascalName}Repository) {}\n\n  async getAll(): Promise<${typeRef}[]> {\n    return this.${repoProp}.findAll();\n  }\n\n  async getById(id: string | number): Promise<${typeRef}> {\n    const item = await this.${repoProp}.findById(id);\n    if (!item) throw new NotFoundError(\\`Data dengan id \\${id} tidak ditemukan\\`);\n    return item;\n  }\n\n  async create(data: any): Promise<${typeRef}> {\n    return this.${repoProp}.create(data);\n  }\n\n  async update(id: string | number, data: any): Promise<${typeRef}> {\n    await this.getById(id);\n    return this.${repoProp}.update(id, data);\n  }\n\n  async delete(id: string | number): Promise<${typeRef} | boolean> {\n    await this.getById(id);\n    return this.${repoProp}.delete(id);\n  }\n\n  async count(): Promise<number> {\n    return this.${repoProp}.count();\n  }\n}\n`;\n}\n\nfunction generateBaseService(\n\tentityName: string,\n\tpascalName: string,\n\ttypeImport: string,\n\ttypeRef: string,\n): string {\n\tconst repoProp = `${toCamelCase(entityName)}Repository`;\n\treturn `import { Dependencies, BaseService } from \"@buntok/core\";\nimport { ${pascalName}Repository } from \"./${entityName}.repository\";\n${typeImport}\n\n@Dependencies(${pascalName}Repository)\nexport class ${pascalName}Service extends BaseService<${typeRef}> {\n  constructor(private readonly ${repoProp}: ${pascalName}Repository) {\n    super(${repoProp});\n  }\n}\n`;\n}\n\nexport function generateService(\n\tentityName: string,\n\tpascalName: string,\n\twithRepo: boolean = true,\n\torm?: ORM | null,\n\toptions?: ServiceOptions,\n): string {\n\tif (withRepo) {\n\t\tconst detectedOrm = orm ?? detectORMOrNull();\n\t\tconst { typeImport, typeRef } = resolveTypeRef(\n\t\t\tentityName,\n\t\t\tpascalName,\n\t\t\tdetectedOrm,\n\t\t);\n\n\t\treturn options?.base\n\t\t\t? generateBaseService(entityName, pascalName, typeImport, typeRef)\n\t\t\t: generatePlainService(entityName, pascalName, typeImport, typeRef);\n\t}\n\n\treturn `export class ${pascalName}Service {\n  async getAll(): Promise<any[]> {\n    return [];\n  }\n\n  async getById(id: string): Promise<any> {\n    return { id };\n  }\n\n  async create(data: any): Promise<any> {\n    return data;\n  }\n\n  async update(id: string, data: any): Promise<any> {\n    return { id, ...data };\n  }\n\n  async delete(id: string): Promise<boolean> {\n    return true;\n  }\n}\n`;\n}\n","import { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/**\n * Static route scanner: parses `@Controller` / `@Get` / `@Post` / ... string\n * literals from source files - no app import, no runtime needed.\n */\n\nexport interface ScannedRoute {\n\tfile: string;\n\tcontroller: string | null;\n\tmethod: string;\n\tpath: string;\n\tline: number;\n}\n\nconst SKIP_DIRS = new Set([\n\t\"node_modules\",\n\t\"dist\",\n\t\".buntok\",\n\t\"buntok\",\n\t\".git\",\n\t\"build\",\n\t\"coverage\",\n]);\n\nfunction walk(dir: string, out: string[]): void {\n\tlet entries: string[];\n\ttry {\n\t\tentries = readdirSync(dir);\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const entry of entries) {\n\t\tif (entry.startsWith(\".\") && entry !== \".\") continue;\n\t\tconst full = join(dir, entry);\n\t\tif (SKIP_DIRS.has(entry)) continue;\n\t\ttry {\n\t\t\tif (statSync(full).isDirectory()) walk(full, out);\n\t\t\telse if (entry.endsWith(\".ts\") && !entry.endsWith(\".d.ts\")) out.push(full);\n\t\t} catch {\n\t\t\t// unreadable entries are skipped\n\t\t}\n\t}\n}\n\nfunction normalizePath(...segments: string[]): string {\n\tlet path = segments\n\t\t.filter((s) => s !== \"\")\n\t\t.join(\"\")\n\t\t.replace(/\\/{2,}/g, \"/\");\n\tif (!path.startsWith(\"/\")) path = `/${path}`;\n\tif (path.length > 1 && path.endsWith(\"/\")) path = path.slice(0, -1);\n\treturn path;\n}\n\nfunction joinRoutePath(prefix: string, methodPath: string): string {\n\tconst left = prefix.startsWith(\"/\") ? prefix : `/${prefix}`;\n\tconst right = methodPath.startsWith(\"/\") ? methodPath : `/${methodPath}`;\n\treturn normalizePath(`${left}${right}`);\n}\n\nconst HTTP_METHODS = [\n\t\"Get\",\n\t\"Post\",\n\t\"Put\",\n\t\"Patch\",\n\t\"Delete\",\n\t\"All\",\n\t\"Options\",\n\t\"Head\",\n] as const;\n\n/** Scan one file's content for controller-prefixed HTTP routes. */\nexport function scanRoutesInFile(file: string, content: string): ScannedRoute[] {\n\tconst routes: ScannedRoute[] = [];\n\tconst controllerMatch = content.match(\n\t\t/@Controller\\s*\\(\\s*[\"'`]([^\"'`]*)[\"'`]\\s*\\)/,\n\t);\n\tconst controllerPath = controllerMatch?.[1] ?? null;\n\tconst controllerName =\n\t\tcontent.match(/export\\s+(?:default\\s+)?class\\s+(\\w+)/)?.[1] ?? null;\n\tconst controllerLine = controllerMatch\n\t\t? content.slice(0, controllerMatch.index ?? 0).split(\"\\n\").length\n\t\t: 1;\n\n\tfor (const method of HTTP_METHODS) {\n\t\tconst re = new RegExp(`@${method}\\\\s*\\\\(\\\\s*[\"'\\`]([^\"\\`]*)[\"'\\`]\\\\s*\\\\)`, \"g\");\n\t\tlet m: RegExpExecArray | null;\n\t\twhile ((m = re.exec(content)) !== null) {\n\t\t\t// Skip decorator references inside comments\n\t\t\tconst before = content.slice(0, m.index);\n\t\t\tconst lastLineStart = before.lastIndexOf(\"\\n\") + 1;\n\t\t\tif (before.slice(lastLineStart).trimStart().startsWith(\"//\")) continue;\n\t\t\troutes.push({\n\t\t\t\tfile,\n\t\t\t\tcontroller: controllerName,\n\t\t\t\tmethod: method === \"All\" ? \"ALL\" : method.toUpperCase(),\n\t\t\t\tpath: joinRoutePath(controllerPath ?? \"\", m[1] ?? \"\"),\n\t\t\t\tline: before.split(\"\\n\").length,\n\t\t\t});\n\t\t}\n\t}\n\n\t// BaseController subclasses inherit the five pre-decorated CRUD routes.\n\tif (controllerPath !== null && /extends\\s+BaseController\\b/.test(content)) {\n\t\tconst inherited: [string, string][] = [\n\t\t\t[\"GET\", \"/\"],\n\t\t\t[\"GET\", \"/:id\"],\n\t\t\t[\"POST\", \"/\"],\n\t\t\t[\"PUT\", \"/:id\"],\n\t\t\t[\"DELETE\", \"/:id\"],\n\t\t];\n\t\tconst ownKeys = new Set(routes.map((r) => `${r.method} ${r.path}`));\n\t\tfor (const [method, path] of inherited) {\n\t\t\tconst fullPath = joinRoutePath(controllerPath, path);\n\t\t\tconst key = `${method} ${fullPath}`;\n\t\t\t// Own decorators override inherited methods (base route shadowed)\n\t\t\tif (ownKeys.has(key)) continue;\n\t\t\troutes.push({\n\t\t\t\tfile,\n\t\t\t\tcontroller: controllerName,\n\t\t\t\tmethod,\n\t\t\t\tpath: fullPath,\n\t\t\t\tline: controllerLine,\n\t\t\t});\n\t\t}\n\t}\n\treturn routes;\n}\n\n/** Scan all TypeScript files under src for decorated routes. */\nexport function scanRoutes(cwd: string = process.cwd()): ScannedRoute[] {\n\tconst srcDir = join(cwd, \"src\");\n\tif (!existsSync(srcDir)) return [];\n\tconst files: string[] = [];\n\twalk(srcDir, files);\n\tconst routes: ScannedRoute[] = [];\n\tfor (const file of files) {\n\t\ttry {\n\t\t\troutes.push(...scanRoutesInFile(file, readFileSync(file, \"utf-8\")));\n\t\t} catch {\n\t\t\t// unreadable file\n\t\t}\n\t}\n\treturn routes;\n}\n\nexport interface RouteConflict {\n\tkey: string;\n\troutes: ScannedRoute[];\n}\n\n/** Group routes sharing the same METHOD + normalized path. */\nexport function findDuplicateRoutes(routes: ScannedRoute[]): RouteConflict[] {\n\tconst byKey = new Map<string, ScannedRoute[]>();\n\tfor (const route of routes) {\n\t\tconst key = `${route.method} ${route.path}`;\n\t\tconst group = byKey.get(key);\n\t\tif (group) group.push(route);\n\t\telse byKey.set(key, [route]);\n\t}\n\tconst conflicts: RouteConflict[] = [];\n\tfor (const [key, group] of byKey) {\n\t\tif (group.length > 1) conflicts.push({ key, routes: group });\n\t}\n\treturn conflicts;\n}\n\n/** Print conflicts; returns true when duplicates were found. */\nexport function warnDuplicateRoutes(\n\troutes: ScannedRoute[],\n\tcwd: string = process.cwd(),\n): boolean {\n\tconst conflicts = findDuplicateRoutes(routes);\n\tif (conflicts.length === 0) return false;\n\tfor (const conflict of conflicts) {\n\t\tconsole.log(`\\n\\x1b[33m⚠ Duplicate route: ${conflict.key}\\x1b[0m`);\n\t\tfor (const r of conflict.routes) {\n\t\t\tconst rel = r.file.startsWith(cwd) ? r.file.slice(cwd.length + 1) : r.file;\n\t\t\tconsole.log(`    ${rel}:${r.line} (${r.controller ?? \"anonymous\"})`);\n\t\t}\n\t}\n\treturn true;\n}\n","import { execSync } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createInterface } from \"node:readline\";\nimport { detectORMOrNull, type ORM } from \"../project.js\";\nimport { dbSeedCommand } from \"./db-seed.js\";\n\ntype DbCommand = \"migrate\" | \"seed\" | \"reset\" | \"generate\" | \"studio\" | \"status\";\n\nfunction runCommand(cmd: string, dryRun = false): void {\n\tif (dryRun) {\n\t\tconsole.log(`\\x1b[90m[dry-run] Would execute: ${cmd}\\x1b[0m`);\n\t\treturn;\n\t}\n\tconsole.log(`\\x1b[90m$ ${cmd}\\x1b[0m`);\n\ttry {\n\t\texecSync(cmd, { stdio: \"inherit\", cwd: process.cwd() });\n\t} catch {\n\t\t// Command already printed its error\n\t\tprocess.exitCode = 1;\n\t}\n}\n\nasync function confirm(message: string): Promise<boolean> {\n\tconst rl = createInterface({ input: process.stdin, output: process.stdout });\n\treturn new Promise((resolve) => {\n\t\trl.question(`\\x1b[33m${message} (y/N): \\x1b[0m`, (answer) => {\n\t\t\trl.close();\n\t\t\tresolve(answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\");\n\t\t});\n\t});\n}\n\nfunction prismaCommand(subcommand: string, args: string[], dryRun = false): void {\n\tconst commands: Record<string, string> = {\n\t\tmigrate: \"migrate dev\",\n\t\tseed: \"db seed\",\n\t\treset: \"migrate reset --force\",\n\t\tstatus: \"migrate status\",\n\t\tgenerate: \"generate\",\n\t\tstudio: \"studio\",\n\t};\n\tconst cmd = `npx prisma ${commands[subcommand] ?? subcommand} ${args.join(\" \")}`.trim();\n\trunCommand(cmd, dryRun);\n}\n\nfunction drizzleCommand(subcommand: string, args: string[], dryRun = false): void {\n\tconst commands: Record<string, string> = {\n\t\tmigrate: \"drizzle-kit generate\",\n\t\tseed: \"drizzle-kit seed\",\n\t\treset: \"drizzle-kit drop\",\n\t\tstatus: \"drizzle-kit check\",\n\t\tgenerate: \"drizzle-kit generate\",\n\t\tstudio: \"drizzle-kit studio\",\n\t};\n\tconst cmd = `${commands[subcommand] ?? subcommand} ${args.join(\" \")}`.trim();\n\trunCommand(cmd, dryRun);\n}\n\nfunction typeormCommand(subcommand: string, args: string[], dryRun = false): void {\n\tconst commands: Record<string, string> = {\n\t\tmigrate: \"typeorm migration:run\",\n\t\tseed: \"typeorm seed\",\n\t\treset: \"typeorm migration:revert\",\n\t\tgenerate: \"typeorm migration:generate\",\n\t\tstatus: \"typeorm migration:show\",\n\t\tstudio: \"echo 'TypeORM does not have a built-in studio command'\",\n\t};\n\tconst cmd = `${commands[subcommand] ?? subcommand} ${args.join(\" \")}`.trim();\n\trunCommand(cmd, dryRun);\n}\n\nfunction printUsage(): void {\n\tconsole.log(`\n\\x1b[36mUsage:\\x1b[0m\n  buntok db <command> [args] [flags]\n\n\\x1b[36mCommands:\\x1b[0m\n  migrate [name]      Run pending migrations\n  seed                Seed database\n  reset               Reset database (drop & recreate)\n  generate            Generate migration from schema changes\n  studio              Open database GUI\n  status              Show migration status\n\n\\x1b[36mFlags:\\x1b[0m\n  --dry-run           Preview command without executing\n\n\\x1b[36mSupported ORMs:\\x1b[0m\n  Prisma, Drizzle, TypeORM (auto-detected)\n\n\\x1b[36mExamples:\\x1b[0m\n  buntok db migrate                    # Run pending migrations\n  buntok db migrate add user_table     # Create new migration\n  buntok db seed                       # Seed database\n  buntok db studio                     # Open Prisma Studio / etc\n  buntok db reset --dry-run            # Preview reset command\n`);\n}\n\nconst DESTRUCTIVE_COMMANDS: DbCommand[] = [\"reset\"];\n\n/**\n * A project-level seed configuration takes precedence (e.g. Prisma's\n * `prisma.seed` key in package.json). Otherwise, if the local seeders\n * directory exists, `buntok db seed` runs them via the built-in runner.\n */\nfunction hasOrmSeedConfig(orm: ORM): boolean {\n\tif (orm !== \"prisma\" || !existsSync(\"package.json\")) return false;\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(\"package.json\", \"utf-8\")) as {\n\t\t\tprisma?: { seed?: unknown };\n\t\t};\n\t\treturn Boolean(pkg.prisma?.seed);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport async function dbCommand(args: string[]): Promise<void> {\n\tconst subcommand = args[0] as DbCommand | undefined;\n\n\tif (!subcommand) {\n\t\tprintUsage();\n\t\treturn;\n\t}\n\n\tconst validCommands: DbCommand[] = [\"migrate\", \"seed\", \"reset\", \"generate\", \"studio\", \"status\"];\n\tif (!validCommands.includes(subcommand)) {\n\t\tconsole.error(`\\x1b[31mUnknown command: ${subcommand}\\x1b[0m`);\n\t\tprintUsage();\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tconst orm = detectORMOrNull();\n\tif (!orm) {\n\t\tconsole.error(\"\\x1b[31mCould not detect ORM.\\x1b[0m\");\n\t\tconsole.error(\"Supported: Prisma, Drizzle, TypeORM\");\n\t\tconsole.error(\"Make sure you have one installed in package.json.\");\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tconst dryRun = args.includes(\"--dry-run\");\n\tconst subArgs = args.slice(1).filter((a) => a !== \"--dry-run\");\n\n\tconsole.log(`\\x1b[36mDetected ORM: ${orm}\\x1b[0m`);\n\n\t// Local seeders fallback: `buntok db seed` runs src/db/seeders directly\n\t// when the ORM has no seed configuration of its own.\n\tif (subcommand === \"seed\" && !hasOrmSeedConfig(orm)) {\n\t\tconst localSeeders = join(\"src\", \"db\", \"seeders\");\n\t\tif (existsSync(localSeeders)) {\n\t\t\tif (dryRun) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`\\x1b[90m[dry-run] Would run local seeders from ${localSeeders} (orm: ${orm})\\x1b[0m`,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait dbSeedCommand(orm);\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// Confirmation for destructive commands\n\tif (DESTRUCTIVE_COMMANDS.includes(subcommand) && !dryRun) {\n\t\tconst confirmed = await confirm(\n\t\t\t`\\x1b[31mWARNING: This will ${subcommand} the database. Continue?\\x1b[0m`,\n\t\t);\n\t\tif (!confirmed) {\n\t\t\tconsole.log(\"\\x1b[90mAborted.\\x1b[0m\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tswitch (orm) {\n\t\tcase \"prisma\":\n\t\t\tprismaCommand(subcommand, subArgs, dryRun);\n\t\t\tbreak;\n\t\tcase \"drizzle\":\n\t\t\tdrizzleCommand(subcommand, subArgs, dryRun);\n\t\t\tbreak;\n\t\tcase \"typeorm\":\n\t\t\ttypeormCommand(subcommand, subArgs, dryRun);\n\t\t\tbreak;\n\t}\n}\n","import { existsSync } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { detectORMOrNull, type ORM } from \"../project.js\";\n\nexport async function dbSeedCommand(orm?: ORM) {\n\tconst detectedOrm = orm ?? detectORMOrNull() ?? \"prisma\";\n\tconsole.log(\"\\x1b[36mRunning Database Seeders...\\x1b[0m\\n\");\n\n\tconst seederDir = resolve(process.cwd(), \"src/db/seeders\");\n\n\tif (!existsSync(seederDir)) {\n\t\tconsole.warn(\n\t\t\t`\\x1b[33mWarning: No seeders directory found at ${seederDir}\\x1b[0m`,\n\t\t);\n\t\treturn;\n\t}\n\n\tconst files = await fs.readdir(seederDir);\n\tconst seederFiles = files.filter(\n\t\t(f) => f.endsWith(\".ts\") && !f.endsWith(\".d.ts\") && !f.startsWith(\".\"),\n\t);\n\n\tif (seederFiles.length === 0) {\n\t\tconsole.log(\"\\x1b[90mNo seeders to run.\\x1b[0m\");\n\t\treturn;\n\t}\n\n\t// TypeORM needs an initialized DataSource; Prisma/Drizzle connect lazily\n\tconst preamble =\n\t\tdetectedOrm === \"typeorm\"\n\t\t\t? `import { AppDataSource } from \"@/lib/data-source\";\\nawait AppDataSource.initialize();\\n`\n\t\t\t: \"\";\n\tconst epilogue =\n\t\tdetectedOrm === \"typeorm\" ? `\\n  await AppDataSource.destroy();` : \"\";\n\n\t// Create a temporary runner script to execute all seeders in the user's app context\n\tconst runnerContent = `${seederFiles.map((file, i) => `import * as seeder${i} from \"./${file.replace(\".ts\", \"\")}\";`).join(\"\\n\")}\n${preamble}\nasync function runSeeders() {\n  console.log(\"🌱 Starting database seeding...\");\n  \n  try {\n    ${seederFiles\n\t\t\t.map(\n\t\t\t\t(_file, i) => `\n    for (const key of Object.keys(seeder${i})) {\n      if (typeof (seeder${i} as any)[key] === 'function') {\n        await (seeder${i} as any)[key]();\n      }\n    }`,\n\t\t\t)\n\t\t\t.join(\"\\n    \")}\n    \n    console.log(\"✅ All seeders executed successfully!\");${epilogue}\n    process.exit(0);\n  } catch (error) {\n    console.error(\"❌ Seeding failed:\", error);\n    process.exit(1);\n  }\n}\n\nrunSeeders();\n`;\n\n\tconst runnerPath = join(seederDir, \".runner.ts\");\n\tawait fs.writeFile(runnerPath, runnerContent);\n\n\ttry {\n\t\tconst proc = Bun.spawnSync([\"bun\", \"run\", runnerPath], {\n\t\t\tcwd: process.cwd(),\n\t\t\tstdio: [\"inherit\", \"inherit\", \"inherit\"],\n\t\t});\n\n\t\tif (proc.exitCode !== 0) {\n\t\t\tconsole.error(\"\\x1b[31mSeeding process failed.\\x1b[0m\");\n\t\t}\n\t} finally {\n\t\t// Cleanup temporary runner\n\t\tif (existsSync(runnerPath)) {\n\t\t\tawait fs.unlink(runnerPath);\n\t\t}\n\t}\n}\n","import { resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { findEntryFiles } from \"../project.js\";\n\nfunction getMethodColor(method: string): string {\n\tswitch (method) {\n\t\tcase \"GET\": return \"\\x1b[32m\";    // green\n\t\tcase \"POST\": return \"\\x1b[33m\";   // yellow\n\t\tcase \"PUT\": return \"\\x1b[34m\";    // blue\n\t\tcase \"PATCH\": return \"\\x1b[35m\";  // magenta\n\t\tcase \"DELETE\": return \"\\x1b[31m\"; // red\n\t\tcase \"OPTIONS\": return \"\\x1b[36m\"; // cyan\n\t\tcase \"HEAD\": return \"\\x1b[37m\";   // white\n\t\tdefault: return \"\\x1b[37m\";      // white\n\t}\n}\n\nfunction printRouteTable(routes: any[]): void {\n\tif (routes.length === 0) {\n\t\tconsole.log(\"\\x1b[33mNo routes found.\\x1b[0m\");\n\t\treturn;\n\t}\n\n\t// Calculate column widths\n\tconst methodWidth = Math.max(6, ...routes.map((r: any) => r.method.length));\n\tconst pathWidth = Math.max(4, ...routes.map((r: any) => r.path.length));\n\tconst mwWidth = Math.max(11, ...routes.map((r: any) => {\n\t\tconst mw = (r.middlewares || []).join(\", \");\n\t\treturn mw.length > 40 ? 40 : mw.length;\n\t}));\n\tconst handlerWidth = Math.max(7, ...routes.map((r: any) => (r.handler || \"anonymous\").length));\n\n\t// Print header\n\tconst header = [\n\t\t\"Method\".padEnd(methodWidth),\n\t\t\"Path\".padEnd(pathWidth),\n\t\t\"Middlewares\".padEnd(mwWidth),\n\t\t\"Handler\".padEnd(handlerWidth),\n\t].join(\" │ \");\n\n\tconst separator = [\n\t\t\"─\".repeat(methodWidth),\n\t\t\"─\".repeat(pathWidth),\n\t\t\"─\".repeat(mwWidth),\n\t\t\"─\".repeat(handlerWidth),\n\t].join(\"─┼─\");\n\n\tconsole.log(`\\x1b[1m${header}\\x1b[0m`);\n\tconsole.log(separator);\n\n\t// Print rows\n\tfor (const route of routes) {\n\t\tconst methodColor = getMethodColor(route.method);\n\t\tconst mwStr = (route.middlewares || []).length > 0 ? route.middlewares.join(\", \") : \"-\";\n\t\tconst truncatedMw = mwStr.length > 40 ? mwStr.slice(0, 37) + \"...\" : mwStr;\n\n\t\tconst row = [\n\t\t\t`${methodColor}${route.method.padEnd(methodWidth)}\\x1b[0m`,\n\t\t\troute.path.padEnd(pathWidth),\n\t\t\ttruncatedMw.padEnd(mwWidth),\n\t\t\t(route.handler || \"anonymous\").padEnd(handlerWidth),\n\t\t].join(\" │ \");\n\n\t\tconsole.log(row);\n\t}\n\n\t// Print summary\n\tconsole.log(`\\n\\x1b[36m${routes.length} routes registered\\x1b[0m`);\n}\n\nfunction printSourceSummary(routes: any[]): void {\n\tconst bySource: Record<string, any[]> = {};\n\tfor (const route of routes) {\n\t\tconst key = route.controller || route.group || route.source || \"direct\";\n\t\tif (!bySource[key]) bySource[key] = [];\n\t\tbySource[key].push(route);\n\t}\n\n\tconst sources = Object.keys(bySource);\n\tif (sources.length <= 1) return;\n\n\tconsole.log(`\\n\\x1b[36mBy source:\\x1b[0m`);\n\tfor (const source of sources) {\n\t\tconst count = bySource[source]?.length ?? 0;\n\t\tconsole.log(`  ${source}: ${count} routes`);\n\t}\n}\n\nexport async function debugRoutesCommand(flags: string[]): Promise<void> {\n\tconst isJson = flags.includes(\"--json\");\n\tconst targetDir = process.cwd();\n\n\t// Importing the entry must not boot the HTTP server (app.listen in\n\t// server.ts would otherwise keep the process alive forever)\n\tprocess.env.BUNTOK_CLI_NO_LISTEN = \"1\";\n\n\t// Find the user's app entry point (shared candidate list + content probe)\n\tconst candidates = findEntryFiles(targetDir);\n\n\tif (candidates.length === 0) {\n\t\tconsole.error(\"\\x1b[31mError: Could not find app entry point (server.ts, src/index.ts, src/main.ts, src/app.ts, or src/server.ts)\\x1b[0m\");\n\t\tprocess.exit(1);\n\t}\n\n\t// Import candidates in preference order until one exports the app.\n\t// A listener-only entry (old server.ts without `export { app }`) falls\n\t// through to the next candidate instead of failing outright.\n\tlet app: any = null;\n\tlet importError: { entry: string; err: any } | null = null;\n\n\tfor (const entry of candidates) {\n\t\tconst entryFile = resolve(targetDir, entry);\n\t\tlet mod: any;\n\t\ttry {\n\t\t\tconst fileUrl = pathToFileURL(entryFile).href;\n\t\t\tmod = await import(fileUrl);\n\t\t} catch (err: any) {\n\t\t\tif (!importError) importError = { entry, err };\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (mod.default && mod.default.routeDebugInfo) {\n\t\t\tapp = mod.default;\n\t\t\tbreak;\n\t\t}\n\t\tfor (const key of Object.keys(mod)) {\n\t\t\tif (mod[key] && mod[key].routeDebugInfo) {\n\t\t\t\tapp = mod[key];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (app) break;\n\t}\n\n\ttry {\n\t\tif (!app) {\n\t\t\tif (importError) {\n\t\t\t\tconsole.error(`\\x1b[31mError importing ${importError.entry}: ${importError.err.message}\\x1b[0m`);\n\t\t\t\tif (importError.err.stack) {\n\t\t\t\t\tconsole.error(`\\x1b[90m${importError.err.stack}\\x1b[0m`);\n\t\t\t\t}\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t\tconsole.error(`\\x1b[31mError: Could not find Buntok instance with routeDebugInfo (checked: ${candidates.join(\", \")}). Make sure your entry point exports a Buntok app instance.\\x1b[0m`);\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\tconst routes = app.routeDebugInfo || [];\n\n\t\tif (isJson) {\n\t\t\tconsole.log(JSON.stringify(routes, null, 2));\n\t\t} else {\n\t\t\tprintRouteTable(routes);\n\t\t\tprintSourceSummary(routes);\n\t\t}\n\t\tprocess.exit(0);\n\t} catch (err: any) {\n\t\tconsole.error(`\\x1b[31mError importing app: ${err.message}\\x1b[0m`);\n\t\tif (err.stack) {\n\t\t\tconsole.error(`\\x1b[90m${err.stack}\\x1b[0m`);\n\t\t}\n\t\tprocess.exit(1);\n\t}\n}\n","import { resolve } from \"node:path\";\nimport { buildChildEnv, loadEnvFiles, watchEnvFiles, type LoadedEnv } from \"../env.js\";\nimport { findEntryFile } from \"../project.js\";\n\nexport async function devCommand(flags: string[]): Promise<void> {\n\tconst isExpose = flags.includes(\"--expose\");\n\tconst port = flags.find(f => f.startsWith(\"--port=\"))?.split(\"=\")[1] || \"1212\";\n\n\tconst targetDir = process.cwd();\n\n\t// Find the user's entry point (shared candidate list + content probe)\n\tconst entry = findEntryFile(targetDir);\n\n\tif (!entry) {\n\t\tconsole.error(\"\\x1b[31mError: Could not find app entry point (server.ts, src/index.ts, src/main.ts, src/app.ts, or src/server.ts)\\x1b[0m\");\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\tconst entryFile = resolve(targetDir, entry);\n\n\t// Build dev command\n\tconst args = [\"--watch\", entryFile];\n\n\t// Resolve the tunnel before spawning so a missing localtunnel never leaves\n\t// an orphan server process behind.\n\tlet tunnel: { url: string; close: () => Promise<unknown> } | undefined;\n\tif (isExpose) {\n\t\tconsole.log(\"\\x1b[36mStarting development server with tunnel...\\x1b[0m\\n\");\n\t\ttry {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: optional dynamic import\n\t\t\tconst lt = await import(\"localtunnel\" as string);\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: optional dynamic import\n\t\t\ttunnel = await (lt as any).default({\n\t\t\t\tport: Number.parseInt(port),\n\t\t\t\tsubdomain: `buntok-${Date.now()}`,\n\t\t\t});\n\t\t} catch (err: any) {\n\t\t\tif (err.message?.includes(\"Cannot find module\") || err.code === \"ERR_MODULE_NOT_FOUND\") {\n\t\t\t\tconsole.error(\"\\x1b[31mError: localtunnel is required for --expose flag\\x1b[0m\");\n\t\t\t\tconsole.error(\"Install it with: \\x1b[36mbun add -d localtunnel\\x1b[0m\");\n\t\t\t\tprocess.exitCode = 1;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t// Environment for the child: parent values minus stale .env keys, overlaid\n\t// with the freshly parsed .env files. Bun lets the environment win over\n\t// .env files, so the merge has to be explicit whenever .env changes.\n\tlet envKeys = new Set<string>();\n\tconst nextEnv = (fresh: LoadedEnv): Record<string, string> => {\n\t\tconst env = buildChildEnv(process.env, envKeys, fresh, {\n\t\t\tNODE_ENV: \"development\",\n\t\t\tPORT: port,\n\t\t});\n\t\tenvKeys = new Set(fresh.keys);\n\t\treturn env;\n\t};\n\n\tconst spawnServer = (env: Record<string, string>) =>\n\t\tBun.spawn([\"bun\", \"run\", ...args], {\n\t\t\tstdio: [\"inherit\", \"inherit\", \"inherit\"],\n\t\t\tenv,\n\t\t\tcwd: targetDir,\n\t\t});\n\n\tlet proc = spawnServer(nextEnv(loadEnvFiles(targetDir)));\n\n\t// Restart when any .env file changes - Bun's own --watch ignores .env.\n\tlet restartRequested = false;\n\tconst stopEnvWatch = watchEnvFiles(targetDir, (file) => {\n\t\tif (restartRequested) return;\n\t\trestartRequested = true;\n\t\tconsole.log(`\\x1b[33m  🔁 ${file} changed - restarting dev server...\\x1b[0m`);\n\t\tproc.kill();\n\t});\n\n\tconst runServer = async (): Promise<void> => {\n\t\tfor (;;) {\n\t\t\tawait proc.exited;\n\t\t\tif (!restartRequested) return;\n\t\t\trestartRequested = false;\n\t\t\tconst fresh = loadEnvFiles(targetDir);\n\t\t\tconsole.log(`\\x1b[32m  ✅ Dev server restarted\\x1b[0m`);\n\t\t\tproc = spawnServer(nextEnv(fresh));\n\t\t}\n\t};\n\n\t// Kill the child whenever this process goes away - SIGTERM to the parent\n\t// alone would otherwise leave an orphaned `bun --watch` behind.\n\tconst shutdown = async () => {\n\t\tstopEnvWatch();\n\t\tproc.kill();\n\t\tif (tunnel) await tunnel.close();\n\t\tprocess.exit(0);\n\t};\n\tprocess.on(\"SIGINT\", () => {\n\t\tvoid shutdown();\n\t});\n\tprocess.on(\"SIGTERM\", () => {\n\t\tvoid shutdown();\n\t});\n\tprocess.on(\"exit\", () => {\n\t\tstopEnvWatch();\n\t\tproc.kill();\n\t});\n\n\tif (tunnel) {\n\t\tconsole.log(`\\x1b[32m  Server running at http://localhost:${port}\\x1b[0m`);\n\t\tconsole.log(`\\x1b[36m  Tunnel: ${tunnel.url} → http://localhost:${port}\\x1b[0m`);\n\t\tconsole.log(`\\n  Press Ctrl+C to stop\\n`);\n\n\t\tawait runServer();\n\t\tawait tunnel.close();\n\t} else {\n\t\tconsole.log(`\\x1b[36mStarting development server...\\x1b[0m\\n`);\n\t\tconsole.log(`\\x1b[32m  Server running at http://localhost:${port}\\x1b[0m`);\n\t\tconsole.log(`\\n  Press Ctrl+C to stop\\n`);\n\n\t\tawait runServer();\n\t\tstopEnvWatch();\n\t}\n}\n","import { existsSync, readFileSync, statSync, watch, type FSWatcher } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/** Files loaded by Bun, in ascending precedence order (development mode). */\nconst ENV_FILES = [\".env\", \".env.development\", \".env.local\"] as const;\n\n/** Parse a single .env-style file (simple dotenv format, zero-dep). */\nexport function parseEnvFile(path: string): Record<string, string> {\n\tif (!existsSync(path)) return {};\n\tconst values: Record<string, string> = {};\n\tfor (const rawLine of readFileSync(path, \"utf8\").split(\"\\n\")) {\n\t\tconst line = rawLine.trim();\n\t\tif (!line || line.startsWith(\"#\")) continue;\n\t\tconst withoutExport = line.startsWith(\"export \") ? line.slice(7).trim() : line;\n\t\tconst eq = withoutExport.indexOf(\"=\");\n\t\tif (eq <= 0) continue;\n\t\tconst key = withoutExport.slice(0, eq).trim();\n\t\tif (!key) continue;\n\t\tlet value = withoutExport.slice(eq + 1).trim();\n\t\tif (\n\t\t\tvalue.length >= 2 &&\n\t\t\t((value.startsWith('\"') && value.endsWith('\"')) ||\n\t\t\t\t(value.startsWith(\"'\") && value.endsWith(\"'\")))\n\t\t) {\n\t\t\tvalue = value.slice(1, -1);\n\t\t}\n\t\tvalues[key] = value;\n\t}\n\treturn values;\n}\n\nexport interface LoadedEnv {\n\tvalues: Record<string, string>;\n\tkeys: string[];\n}\n\n/** Load every supported .env file in Bun's precedence order. */\nexport function loadEnvFiles(dir: string): LoadedEnv {\n\tconst values: Record<string, string> = {};\n\tfor (const name of ENV_FILES) {\n\t\tObject.assign(values, parseEnvFile(join(dir, name)));\n\t}\n\treturn { values, keys: Object.keys(values) };\n}\n\n/**\n * Build the child environment for the dev server: strip keys that came from\n * the previous .env generation (so deletions propagate), then overlay the\n * freshly parsed values. Shell/process values always win over files in Bun,\n * which is why the merge must be explicit on restart.\n */\nexport function buildChildEnv(\n\tparentEnv: Record<string, string | undefined>,\n\toldKeys: ReadonlySet<string>,\n\tfresh: LoadedEnv,\n\toverrides: Record<string, string>,\n): Record<string, string> {\n\tconst out: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(parentEnv)) {\n\t\tif (value !== undefined) out[key] = value;\n\t}\n\tfor (const key of oldKeys) delete out[key];\n\tObject.assign(out, fresh.values, overrides);\n\treturn out;\n}\n\nfunction envFileSignature(dir: string): string {\n\treturn ENV_FILES.map((name) => {\n\t\ttry {\n\t\t\treturn statSync(join(dir, name)).mtimeMs;\n\t\t} catch {\n\t\t\treturn 0;\n\t\t}\n\t}).join(\",\");\n}\n\n/**\n * Watch a directory for .env file changes (created, modified, renamed, or\n * deleted) and invoke `onChange` (debounced). Returns a stop function.\n *\n * The directory itself is watched instead of each file so editors that save\n * via atomic rename keep triggering events. If the event has no filename\n * (some platforms), an mtime signature of the .env files is used instead.\n */\nexport function watchEnvFiles(dir: string, onChange: (file: string) => void): () => void {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet signature = envFileSignature(dir);\n\n\tconst schedule = (file: string) => {\n\t\tif (timer) clearTimeout(timer);\n\t\ttimer = setTimeout(() => {\n\t\t\ttimer = undefined;\n\t\t\tsignature = envFileSignature(dir);\n\t\t\tonChange(file);\n\t\t}, 150);\n\t};\n\n\tlet watcher: FSWatcher;\n\ttry {\n\t\twatcher = watch(dir, (_event, filename) => {\n\t\t\tconst name = filename ? filename.toString() : \"\";\n\t\t\tif (name && !/^\\.env(\\..+)?$/.test(name)) return;\n\t\t\tif (!name) {\n\t\t\t\tconst next = envFileSignature(dir);\n\t\t\t\tif (next === signature) return;\n\t\t\t\tsignature = next;\n\t\t\t\tschedule(\".env\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tschedule(name);\n\t\t});\n\t} catch {\n\t\t// Directory unreadable / no permission - env watching stays disabled.\n\t\treturn () => {};\n\t}\n\n\treturn () => {\n\t\tif (timer) clearTimeout(timer);\n\t\twatcher.close();\n\t};\n}\n","import { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { execSync } from \"node:child_process\";\nimport { createInterface } from \"node:readline\";\nimport {\n\tBIOME_CONFIG,\n\tDOCKERFILE_TEMPLATE,\n\tDOCKERIGNORE_CONTENT,\n\tENV_CONTENT,\n\tENV_EXAMPLE_CONTENT,\n\tENV_TS_TEMPLATE,\n\tGITIGNORE_CONTENT,\n\tINDEX_TEMPLATE,\n\tSERVER_TS_TEMPLATE,\n\tTSCONFIG_TEMPLATE,\n\tVSCODE_SETTINGS,\n\tVERCEL_JSON_TEMPLATE,\n\tcopySkillMd,\n} from \"../templates.js\";\n\nlet pipedAnswers: string[] | null = null;\n\nfunction askQuestion(question: string, defaultValue = true): Promise<boolean> {\n\tconst finish = (raw: string): boolean => {\n\t\tconst normalized = raw.trim().toLowerCase();\n\t\tif (normalized === \"\") return defaultValue;\n\t\treturn normalized === \"y\" || normalized === \"yes\";\n\t};\n\n\t// Piped stdin: consume all answers up-front so a fast writer cannot\n\t// outrun per-question readline buffering (later prompts would hang or\n\t// silently swallow the rest of the setup).\n\tif (!process.stdin.isTTY) {\n\t\tif (pipedAnswers === null) {\n\t\t\ttry {\n\t\t\t\tpipedAnswers = readFileSync(0, \"utf-8\").split(\"\\n\");\n\t\t\t} catch {\n\t\t\t\tpipedAnswers = [];\n\t\t\t}\n\t\t}\n\t\tprocess.stdout.write(question);\n\t\tconst answer = pipedAnswers.shift() ?? \"\";\n\t\treturn Promise.resolve(finish(answer));\n\t}\n\n\treturn new Promise((resolve) => {\n\t\tconst rl = createInterface({\n\t\t\tinput: process.stdin,\n\t\t\toutput: process.stdout,\n\t\t});\n\n\t\trl.question(question, (answer) => {\n\t\t\trl.close();\n\t\t\tresolve(finish(answer));\n\t\t});\n\t});\n}\n\nconst REQUIRED_SCRIPTS: Record<string, string> = {\n\tdev: \"bun --watch server.ts\",\n\tbuild: \"bunx buntok build\",\n\tstart: \"bun buntok/server.js\",\n\tcheck: \"bunx @biomejs/biome check --write .\",\n\tformat: \"bunx @biomejs/biome format --write .\",\n\tlint: \"bunx @biomejs/biome lint .\",\n};\n\nfunction updatePackageJson(projectRoot: string): boolean {\n\tconst pkgPath = join(projectRoot, \"package.json\");\n\n\tlet pkg: { name?: string; scripts?: Record<string, string>; devDependencies?: Record<string, string> };\n\tif (existsSync(pkgPath)) {\n\t\tpkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n\t} else {\n\t\tconst folderName = projectRoot.split(\"/\").pop() || \"my-app\";\n\t\tpkg = { name: folderName, scripts: {}, devDependencies: {} };\n\t}\n\n\tif (!pkg.scripts) pkg.scripts = {};\n\n\tlet added = 0;\n\tfor (const [key, value] of Object.entries(REQUIRED_SCRIPTS)) {\n\t\tif (!pkg.scripts[key]) {\n\t\t\tpkg.scripts[key] = value;\n\t\t\tadded++;\n\t\t}\n\t}\n\n\tif (added === 0) {\n\t\tconsole.log(\"\\x1b[90m• package.json: all scripts already present\\x1b[0m\");\n\t\treturn false;\n\t}\n\n\twriteFileSync(pkgPath, JSON.stringify(pkg, null, 2) + \"\\n\", \"utf-8\");\n\tconsole.log(`\\x1b[32m✓ Updated\\x1b[0m package.json (added ${added} scripts)`);\n\treturn true;\n}\n\nfunction ensureProjectDependencies(projectRoot: string): void {\n\tif (process.env.BUNTOK_NO_AUTO_INSTALL === \"1\") return;\n\n\tconst pkgPath = join(projectRoot, \"package.json\");\n\tif (!existsSync(pkgPath)) return;\n\n\tlet allDeps: Record<string, unknown> = {};\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n\t\tallDeps = { ...pkg.dependencies, ...pkg.devDependencies };\n\t} catch {\n\t\treturn;\n\t}\n\n\tif (!allDeps[\"@buntok/core\"]) {\n\t\tconsole.log(\"\\x1b[90m• Installing @buntok/core...\\x1b[0m\");\n\t\ttry {\n\t\t\texecSync(\"bun add @buntok/core\", {\n\t\t\t\tcwd: projectRoot,\n\t\t\t\tstdio: \"ignore\",\n\t\t\t});\n\t\t\tconsole.log(\"\\x1b[32m✓ Installed\\x1b[0m @buntok/core\");\n\t\t} catch {\n\t\t\tconsole.warn(\n\t\t\t\t\"\\x1b[33m⚠ Failed to install @buntok/core, run: bun add @buntok/core\\x1b[0m\",\n\t\t\t);\n\t\t}\n\t}\n\n\tconst missingTypes = [\"@types/bun\", \"@types/node\", \"typescript\"].filter(\n\t\t(pkg) => !allDeps[pkg],\n\t);\n\tif (missingTypes.length > 0) {\n\t\tconsole.log(\n\t\t\t\"\\x1b[90m• Installing TypeScript toolchain (@types/bun, @types/node, typescript)...\\x1b[0m\",\n\t\t);\n\t\ttry {\n\t\t\texecSync(`bun add -d ${missingTypes.join(\" \")}`, {\n\t\t\t\tcwd: projectRoot,\n\t\t\t\tstdio: \"ignore\",\n\t\t\t});\n\t\t\tconsole.log(`\\x1b[32m✓ Installed\\x1b[0m ${missingTypes.join(\", \")}`);\n\t\t} catch {\n\t\t\tconsole.warn(\n\t\t\t\t\"\\x1b[33m⚠ Failed to install TypeScript toolchain, run: bun add -d @types/bun @types/node typescript\\x1b[0m\",\n\t\t\t);\n\t\t}\n\t}\n}\n\nfunction stripJsonComments(text: string): string {\n\t// Remove single-line comments (// ...) but not inside strings\n\treturn text.replace(/(?<![\"':].*)\\/\\/.*$/gm, \"\");\n}\n\nfunction setupTsconfig(projectRoot: string): boolean {\n\tconst tsconfigPath = join(projectRoot, \"tsconfig.json\");\n\n\tif (existsSync(tsconfigPath)) {\n\t\t// Merge paths into existing tsconfig\n\t\tconst raw = readFileSync(tsconfigPath, \"utf-8\");\n\t\tconst existing = JSON.parse(stripJsonComments(raw));\n\t\tif (!existing.compilerOptions) existing.compilerOptions = {};\n\t\tif (!existing.compilerOptions.paths) {\n\t\t\texisting.compilerOptions.paths = { \"@/*\": [\"./src/*\"] };\n\t\t\twriteFileSync(tsconfigPath, JSON.stringify(existing, null, 2) + \"\\n\", \"utf-8\");\n\t\t\tconsole.log(\"\\x1b[32m✓ Updated\\x1b[0m tsconfig.json (added path alias)\");\n\t\t\treturn true;\n\t\t}\n\t\tif (!existing.compilerOptions.paths[\"@/*\"]) {\n\t\t\texisting.compilerOptions.paths[\"@/*\"] = [\"./src/*\"];\n\t\t\twriteFileSync(tsconfigPath, JSON.stringify(existing, null, 2) + \"\\n\", \"utf-8\");\n\t\t\tconsole.log(\"\\x1b[32m✓ Updated\\x1b[0m tsconfig.json (added path alias)\");\n\t\t\treturn true;\n\t\t}\n\t\tconsole.log(\"\\x1b[90m• tsconfig.json: path alias already present\\x1b[0m\");\n\t\treturn false;\n\t}\n\n\t// Create new tsconfig\n\twriteFileSync(tsconfigPath, JSON.stringify(TSCONFIG_TEMPLATE, null, 2) + \"\\n\", \"utf-8\");\n\tconsole.log(\"\\x1b[32m✓ Created\\x1b[0m tsconfig.json\");\n\treturn true;\n}\n\nfunction setupBiome(projectRoot: string): boolean {\n\tconst biomePath = join(projectRoot, \"biome.json\");\n\n\tif (existsSync(biomePath)) {\n\t\tconsole.log(\"\\x1b[90m• biome.json: already exists, skipping\\x1b[0m\");\n\t\treturn false;\n\t}\n\n\t// Check if @biomejs/biome is installed\n\tconst pkgPath = join(projectRoot, \"package.json\");\n\tlet needsInstall = false;\n\tif (process.env.BUNTOK_NO_AUTO_INSTALL !== \"1\" && existsSync(pkgPath)) {\n\t\tconst pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n\t\tconst allDeps = {\n\t\t\t...pkg.dependencies,\n\t\t\t...pkg.devDependencies,\n\t\t};\n\t\tneedsInstall = !allDeps[\"@biomejs/biome\"];\n\t}\n\n\tif (needsInstall) {\n\t\tconsole.log(\"\\x1b[90m• Installing @biomejs/biome...\\x1b[0m\");\n\t\ttry {\n\t\t\texecSync(\"bun add -d @biomejs/biome\", {\n\t\t\t\tcwd: projectRoot,\n\t\t\t\tstdio: \"ignore\",\n\t\t\t});\n\t\t\tconsole.log(\"\\x1b[32m✓ Installed\\x1b[0m @biomejs/biome\");\n\t\t} catch {\n\t\t\tconsole.warn(\n\t\t\t\t\"\\x1b[33m⚠ Failed to install @biomejs/biome, please install manually\\x1b[0m\",\n\t\t\t);\n\t\t}\n\t}\n\n\twriteFileSync(biomePath, JSON.stringify(BIOME_CONFIG, null, 2) + \"\\n\", \"utf-8\");\n\tconsole.log(\"\\x1b[32m✓ Created\\x1b[0m biome.json\");\n\treturn true;\n}\n\nfunction createIndexFile(projectRoot: string): boolean {\n\tconst srcDir = join(projectRoot, \"src\");\n\tconst indexPath = join(srcDir, \"index.ts\");\n\n\tif (existsSync(indexPath)) {\n\t\tconsole.log(\"\\x1b[90m• src/index.ts: already exists, skipping\\x1b[0m\");\n\t\treturn false;\n\t}\n\n\tif (!existsSync(srcDir)) {\n\t\tmkdirSync(srcDir, { recursive: true });\n\t}\n\n\twriteFileSync(indexPath, INDEX_TEMPLATE, \"utf-8\");\n\tconsole.log(\"\\x1b[32m✓ Created\\x1b[0m src/index.ts\");\n\treturn true;\n}\n\nfunction createEnvTsFile(projectRoot: string): boolean {\n\tconst srcDir = join(projectRoot, \"src\");\n\tconst envTsPath = join(srcDir, \"env.ts\");\n\n\tif (existsSync(envTsPath)) {\n\t\tconsole.log(\"\\x1b[90m• src/env.ts: already exists, skipping\\x1b[0m\");\n\t\treturn false;\n\t}\n\n\tif (!existsSync(srcDir)) {\n\t\tmkdirSync(srcDir, { recursive: true });\n\t}\n\n\twriteFileSync(envTsPath, ENV_TS_TEMPLATE, \"utf-8\");\n\tconsole.log(\"\\x1b[32m✓ Created\\x1b[0m src/env.ts\");\n\treturn true;\n}\n\nfunction setupVscode(projectRoot: string): boolean {\n\tconst vscodeDir = join(projectRoot, \".vscode\");\n\tconst settingsPath = join(vscodeDir, \"settings.json\");\n\n\tif (existsSync(settingsPath)) {\n\t\tconsole.log(\"\\x1b[90m• .vscode/settings.json: already exists, skipping\\x1b[0m\");\n\t\treturn false;\n\t}\n\n\tif (!existsSync(vscodeDir)) {\n\t\tmkdirSync(vscodeDir, { recursive: true });\n\t}\n\n\twriteFileSync(settingsPath, JSON.stringify(VSCODE_SETTINGS, null, 2) + \"\\n\", \"utf-8\");\n\tconsole.log(\"\\x1b[32m✓ Created\\x1b[0m .vscode/settings.json\");\n\treturn true;\n}\n\nfunction createEnvFiles(projectRoot: string): boolean {\n\tconst envPath = join(projectRoot, \".env\");\n\tconst envExamplePath = join(projectRoot, \".env.example\");\n\tlet created = false;\n\n\tif (!existsSync(envPath)) {\n\t\twriteFileSync(envPath, ENV_CONTENT, \"utf-8\");\n\t\tconsole.log(\"\\x1b[32m✓ Created\\x1b[0m .env\");\n\t\tcreated = true;\n\t} else {\n\t\tconsole.log(\"\\x1b[90m• .env: already exists, skipping\\x1b[0m\");\n\t}\n\n\tif (!existsSync(envExamplePath)) {\n\t\twriteFileSync(envExamplePath, ENV_EXAMPLE_CONTENT, \"utf-8\");\n\t\tconsole.log(\"\\x1b[32m✓ Created\\x1b[0m .env.example\");\n\t\tcreated = true;\n\t} else {\n\t\tconsole.log(\"\\x1b[90m• .env.example: already exists, skipping\\x1b[0m\");\n\t}\n\n\treturn created;\n}\n\nfunction createGitignore(projectRoot: string): boolean {\n\tconst gitignorePath = join(projectRoot, \".gitignore\");\n\n\tif (existsSync(gitignorePath)) {\n\t\tconsole.log(\"\\x1b[90m• .gitignore: already exists, skipping\\x1b[0m\");\n\t\treturn false;\n\t}\n\n\twriteFileSync(gitignorePath, GITIGNORE_CONTENT, \"utf-8\");\n\tconsole.log(\"\\x1b[32m✓ Created\\x1b[0m .gitignore\");\n\treturn true;\n}\n\nfunction createVercelJson(projectRoot: string): boolean {\n\tconst vercelPath = join(projectRoot, \"vercel.json\");\n\n\tif (existsSync(vercelPath)) {\n\t\tconsole.log(\"\\x1b[90m• vercel.json: already exists, skipping\\x1b[0m\");\n\t\treturn false;\n\t}\n\n\twriteFileSync(vercelPath, JSON.stringify(VERCEL_JSON_TEMPLATE, null, 2) + \"\\n\", \"utf-8\");\n\tconsole.log(\"\\x1b[32m✓ Created\\x1b[0m vercel.json\");\n\n\t// Create empty public/ directory (Vercel requires it for static files)\n\tconst publicDir = join(projectRoot, \"public\");\n\tif (!existsSync(publicDir)) {\n\t\tmkdirSync(publicDir, { recursive: true });\n\t\twriteFileSync(join(publicDir, \".gitkeep\"), \"\");\n\t}\n\n\treturn true;\n}\n\nfunction createServerTsFile(projectRoot: string): boolean {\n\tconst serverPath = join(projectRoot, \"server.ts\");\n\n\tif (existsSync(serverPath)) {\n\t\tconsole.log(\"\\x1b[90m• server.ts: already exists, skipping\\x1b[0m\");\n\t\treturn false;\n\t}\n\n\twriteFileSync(serverPath, SERVER_TS_TEMPLATE, \"utf-8\");\n\tconsole.log(\"\\x1b[32m✓ Created\\x1b[0m server.ts\");\n\treturn true;\n}\n\nfunction updateDevScript(projectRoot: string): boolean {\n\tconst pkgPath = join(projectRoot, \"package.json\");\n\tif (!existsSync(pkgPath)) return false;\n\n\tconst pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n\tif (!pkg.scripts) return false;\n\n\tif (pkg.scripts.dev === \"bun --watch src/index.ts\") {\n\t\tpkg.scripts.dev = \"bun --watch server.ts\";\n\t\twriteFileSync(pkgPath, JSON.stringify(pkg, null, 2) + \"\\n\", \"utf-8\");\n\t\tconsole.log(\"\\x1b[32m✓ Updated\\x1b[0m package.json (dev → server.ts)\");\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nfunction createDockerfile(projectRoot: string): boolean {\n\tconst dockerfilePath = join(projectRoot, \"Dockerfile\");\n\tconst dockerignorePath = join(projectRoot, \".dockerignore\");\n\n\tlet created = false;\n\n\tif (!existsSync(dockerfilePath)) {\n\t\twriteFileSync(dockerfilePath, DOCKERFILE_TEMPLATE, \"utf-8\");\n\t\tconsole.log(\"\\x1b[32m✓ Created\\x1b[0m Dockerfile\");\n\t\tcreated = true;\n\t} else {\n\t\tconsole.log(\"\\x1b[90m• Dockerfile: already exists, skipping\\x1b[0m\");\n\t}\n\n\tif (!existsSync(dockerignorePath)) {\n\t\twriteFileSync(dockerignorePath, DOCKERIGNORE_CONTENT, \"utf-8\");\n\t\tconsole.log(\"\\x1b[32m✓ Created\\x1b[0m .dockerignore\");\n\t\tcreated = true;\n\t} else {\n\t\tconsole.log(\"\\x1b[90m• .dockerignore: already exists, skipping\\x1b[0m\");\n\t}\n\n\treturn created;\n}\n\nexport async function initCommand() {\n\tconst projectRoot = process.cwd();\n\n\tcopySkillMd(projectRoot);\n\tupdatePackageJson(projectRoot);\n\tensureProjectDependencies(projectRoot);\n\tsetupTsconfig(projectRoot);\n\tsetupBiome(projectRoot);\n\tsetupVscode(projectRoot);\n\tcreateEnvTsFile(projectRoot);\n\tcreateIndexFile(projectRoot);\n\tcreateEnvFiles(projectRoot);\n\tcreateGitignore(projectRoot);\n\tupdateDevScript(projectRoot);\n\n\tconst vercelPath = join(projectRoot, \"vercel.json\");\n\tif (!existsSync(vercelPath)) {\n\t\tconsole.log(\"\");\n\t\tconst useVercel = await askQuestion(\n\t\t\t\"\\x1b[36mDo you want to deploy to Vercel? (y/N): \\x1b[0m\",\n\t\t\tfalse,\n\t\t);\n\t\tif (useVercel) {\n\t\t\tcreateVercelJson(projectRoot);\n\t\t}\n\t} else {\n\t\tconsole.log(\"\\x1b[90m• vercel.json: already exists, skipping\\x1b[0m\");\n\t}\n\n\tcreateServerTsFile(projectRoot);\n\n\tconst dockerfilePath = join(projectRoot, \"Dockerfile\");\n\tif (!existsSync(dockerfilePath)) {\n\t\tconsole.log(\"\");\n\t\tconst useDocker = await askQuestion(\n\t\t\t\"\\x1b[36mDo you want to add Docker support? (Y/n): \\x1b[0m\",\n\t\t\ttrue,\n\t\t);\n\t\tif (useDocker) {\n\t\t\tcreateDockerfile(projectRoot);\n\t\t}\n\t} else {\n\t\tconsole.log(\"\\x1b[90m• Dockerfile: already exists, skipping\\x1b[0m\");\n\t}\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { generateOpenApiDocument } from \"../../helpers/openapi\";\n\nexport async function makeDocsCommand() {\n\tconsole.log(\"\\x1b[36mGenerating OpenAPI documentation...\\x1b[0m\");\n\n\t// Prevent the user's app from starting its HTTP server during import\n\tprocess.env.BUNTOK_DOCS_BUILD = \"1\";\n\n\tconst entryPath = resolve(process.cwd(), \"src/index.ts\");\n\n\ttry {\n\t\tconsole.log(`\\x1b[90mLoading app from ${entryPath}...\\x1b[0m`);\n\n\t\tconst userApp = await import(entryPath);\n\t\tconst appInstance = userApp.app || userApp.default;\n\n\t\tif (!appInstance?.openApiDocs) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Could not find an exported 'app' instance in src/index.ts. Make sure you export your app: `export const app = new Buntok();`\",\n\t\t\t);\n\t\t}\n\n\t\tconst docsConfig = appInstance._apiDocsConfig;\n\n\t\tconst document = generateOpenApiDocument({\n\t\t\topenApiDocs: appInstance.openApiDocs,\n\t\t\ttitle: docsConfig?.title,\n\t\t\tversion: docsConfig?.version,\n\t\t\tdescription: docsConfig?.description,\n\t\t});\n\n\t\tif (!document) {\n\t\t\tconsole.warn(\n\t\t\t\t\"\\x1b[33m  ⚠ No routes with OpenAPI metadata found. Add zValidator() or zResponse() to your routes.\\x1b[0m\",\n\t\t\t);\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\t// Output directory: public/docs/\n\t\tconst docsDir = resolve(process.cwd(), \"public/docs\");\n\t\tmkdirSync(docsDir, { recursive: true });\n\n\t\tconst swaggerPath = resolve(docsDir, \"swagger.json\");\n\t\twriteFileSync(swaggerPath, JSON.stringify(document, null, 2));\n\t\tconsole.log(\n\t\t\t`\\x1b[32m✔ swagger.json generated at public/docs/swagger.json\\x1b[0m`,\n\t\t);\n\n\t\tprocess.exit(0);\n\t} catch (error) {\n\t\tconsole.error(\"\\x1b[31mFailed to generate docs:\\x1b[0m\");\n\t\tconsole.error(error);\n\t\tprocess.exit(1);\n\t}\n}\n","import { execSync } from \"node:child_process\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport {\n\tfakerLinesForFields,\n\tgetEntityModel,\n\tloadPrismaSchema,\n\tparseFieldsFlag,\n\ttsLinesForFields,\n\ttype FieldDef,\n\ttype PrismaSchema,\n} from \"../generators/model.js\";\nimport { detectORMOrNull, formatWithBiome } from \"../project.js\";\nimport { toPascalCase, toSnakeCase } from \"../utils.js\";\n\ninterface FactoryShape {\n\ttypeImport: string;\n\ttypeDecl: string;\n\tbody: string;\n}\n\nfunction generateFactoryTemplate(\n\tentityName: string,\n\torm: string | null,\n\tfields: FieldDef[] | null,\n\tschema: PrismaSchema | null,\n): FactoryShape {\n\tconst interfaceName = toPascalCase(entityName);\n\tconst fakerLines = fields ? fakerLinesForFields(fields, schema) : [];\n\tconst body =\n\t\tfakerLines.length > 0\n\t\t\t? fakerLines.map((l) => `  ${l}`).join(\"\\n\")\n\t\t\t: `  // TODO: Define your factory fields here\n  // Example:\n  // id: faker.number.int({ max: 10000 }),\n  // name: faker.person.fullName(),\n  // email: faker.internet.email(),`;\n\n\t// Prisma projects get the generated model type; everyone else gets an\n\t// inline type derived from the schema fields when available.\n\tif (orm === \"prisma\") {\n\t\tconst typeImport = `import type { ${interfaceName} } from \"@prisma/client\";`;\n\t\treturn {\n\t\t\ttypeImport,\n\t\t\ttypeDecl: \"\",\n\t\t\tbody,\n\t\t};\n\t}\n\n\tconst tsLines = fields ? tsLinesForFields(fields, schema) : [];\n\tconst typeDecl =\n\t\ttsLines.length > 0\n\t\t\t? `type ${interfaceName} = {\\n${tsLines.map((l) => `  ${l}`).join(\"\\n\")}\\n};`\n\t\t\t: `type ${interfaceName} = Record<string, unknown>;`;\n\treturn {\n\t\ttypeImport: \"\",\n\t\ttypeDecl,\n\t\tbody,\n\t};\n}\n\nfunction ensureFakerDependency(targetDir: string): void {\n\tif (process.env.BUNTOK_NO_AUTO_INSTALL === \"1\") return;\n\n\tconst pkgPath = join(targetDir, \"package.json\");\n\tif (!existsSync(pkgPath)) return;\n\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n\t\tconst allDeps = { ...pkg.dependencies, ...pkg.devDependencies };\n\t\tif (allDeps[\"@faker-js/faker\"]) return;\n\t} catch {\n\t\treturn;\n\t}\n\n\tconsole.log(\"\\x1b[90m• Installing @faker-js/faker...\\x1b[0m\");\n\ttry {\n\t\texecSync(\"bun add -d @faker-js/faker\", {\n\t\t\tcwd: targetDir,\n\t\t\tstdio: \"ignore\",\n\t\t});\n\t\tconsole.log(\"\\x1b[32m✓ Installed\\x1b[0m @faker-js/faker\");\n\t} catch {\n\t\tconsole.warn(\n\t\t\t\"\\x1b[33m⚠ Failed to install @faker-js/faker, run: bun add -d @faker-js/faker\\x1b[0m\",\n\t\t);\n\t}\n}\n\nexport async function makeFactoryCommand(\n\tentityName: string,\n\tflags: string[],\n): Promise<void> {\n\tconst targetDir = process.cwd();\n\tconst isDryRun = flags.includes(\"--dry-run\");\n\tconst force = flags.includes(\"--force\");\n\tconst fieldsFlagValue = (() => {\n\t\tconst i = flags.indexOf(\"--fields\");\n\t\treturn i !== -1 ? flags[i + 1] : undefined;\n\t})();\n\n\t// Resolve paths\n\tconst factoryDir = resolve(targetDir, \"src\", \"factories\");\n\n\t// Ensure factories directory exists\n\tif (!existsSync(factoryDir)) {\n\t\tif (!isDryRun) {\n\t\t\tmkdirSync(factoryDir, { recursive: true });\n\t\t}\n\t\tconsole.log(`Created directory: src/factories/`);\n\t}\n\n\t// Model awareness: Prisma schema first, `--fields` overrides\n\tconst schema = fieldsFlagValue ? null : loadPrismaSchema();\n\tconst model = schema ? getEntityModel(entityName, schema) : null;\n\tconst fields: FieldDef[] | null = fieldsFlagValue\n\t\t? parseFieldsFlag(fieldsFlagValue)\n\t\t: (model?.fields ?? null);\n\tconst orm = detectORMOrNull();\n\tconst shape = generateFactoryTemplate(entityName, orm, fields, schema);\n\n\tconst className = toPascalCase(entityName) + \"Factory\";\n\tconst importLines = [\n\t\t`import { Factory } from \"@buntok/core\";`,\n\t\t`import { faker } from \"@faker-js/faker\";`,\n\t\tshape.typeImport,\n\t].filter((l) => l.length > 0);\n\tconst content = `${importLines.join(\"\\n\")}\n\nexport const ${className} = Factory.define<${toPascalCase(entityName)}>(() => ({\n${shape.body}\n}));\n${shape.typeDecl ? `\\n${shape.typeDecl}\\n` : \"\"}`;\n\n\tconst fileName = `${toSnakeCase(entityName)}.factory.ts`;\n\tconst filePath = join(factoryDir, fileName);\n\n\tif (isDryRun) {\n\t\tconsole.log(`\\n\\x1b[36m[DRY RUN] Would create:\\x1b[0m`);\n\t\tconsole.log(`  ${filePath}`);\n\t\tconsole.log(`\\x1b[36mContent:\\x1b[0m`);\n\t\tconsole.log(content);\n\t\treturn;\n\t}\n\n\t// Check if file already exists\n\tif (existsSync(filePath) && !force) {\n\t\tconsole.error(\n\t\t\t`\\x1b[31mError: Factory file already exists: ${filePath} (use --force to overwrite)\\x1b[0m`,\n\t\t);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\t// Write factory file\n\twriteFileSync(filePath, content, \"utf-8\");\n\tensureFakerDependency(targetDir);\n\tif (formatWithBiome([filePath])) {\n\t\tconsole.log(\n\t\t\t\"\\x1b[90m✨ Auto-formatted generated factory file with Biome\\x1b[0m\",\n\t\t);\n\t}\n\tconsole.log(`Created: src/factories/${fileName}`);\n\n\t// Print usage hint\n\tconsole.log(`\\n\\x1b[36mUsage:\\x1b[0m`);\n\tconsole.log(\n\t\t`  import { ${className} } from \"@/factories/${fileName.replace(\".ts\", \"\")}\";`,\n\t);\n\tconsole.log(``);\n\tconsole.log(`  const user = await ${className}.create();`);\n\tconsole.log(`  const users = await ${className}.createMany(10);`);\n\tconsole.log(`  const admin = await ${className}.create({ role: \"admin\" });`);\n}\n","import { existsSync } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { formatWithBiome } from \"../project.js\";\nimport { toPascalCase } from \"../utils.js\";\n\nfunction generateMiddleware(_name: string, pascalName: string): string {\n\treturn `import type { Context, Middleware } from \"@buntok/core\";\n\nexport const ${pascalName}Middleware: Middleware = async (ctx: Context, next: () => Promise<Response> | Response) => {\n  const start = performance.now();\n\n  const response = await next();\n\n  const end = performance.now();\n  const url = new URL(ctx.request.url);\n  console.log(\\`[\\${ctx.request.method}] \\${url.pathname} - \\${Math.round(end - start)}ms\\`);\n\n  return response;\n}\n`;\n}\n\nexport async function makeMiddlewareCommand(name: string, flags: string[] = []) {\n\tconst pascalName = toPascalCase(name);\n\tconst dryRun = flags.includes(\"--dry-run\");\n\tconst force = flags.includes(\"--force\");\n\tconsole.log(`\\n\\x1b[36mCreating ${pascalName} Middleware${dryRun ? \" (dry-run)\" : \"\"}...\\x1b[0m\\n`);\n\n\tconst middlewaresDir = \"src/middlewares\";\n\n\tif (!existsSync(middlewaresDir)) {\n\t\tif (dryRun) {\n\t\t\tconsole.log(`\\x1b[90mWould create directory: ${middlewaresDir}\\x1b[0m`);\n\t\t} else {\n\t\t\tawait fs.mkdir(middlewaresDir, { recursive: true });\n\t\t}\n\t}\n\n\tconst filePath = join(middlewaresDir, `${name}.middleware.ts`);\n\n\tif (existsSync(filePath) && !force) {\n\t\tconsole.error(\n\t\t\t`\\x1b[31mError: Middleware file already exists at ${filePath} (use --force to overwrite)\\x1b[0m`,\n\t\t);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tconst content = generateMiddleware(name, pascalName);\n\n\tif (dryRun) {\n\t\tconsole.log(`\\x1b[90mWould create file: ${filePath}\\x1b[0m`);\n\t\tconsole.log(`\\n\\x1b[36m--- Generated content ---\\x1b[0m\\n`);\n\t\tconsole.log(content);\n\t\treturn;\n\t}\n\n\tawait fs.writeFile(filePath, content);\n\n\tif (formatWithBiome([filePath])) {\n\t\tconsole.log(\"\\x1b[90m✨ Auto-formatted generated file with Biome\\x1b[0m\");\n\t}\n\n\tconsole.log(`\\x1b[32m✓ Generated middleware:\\x1b[0m ${filePath}`);\n\n\tconsole.log(`\n\\x1b[36mUsage:\\x1b[0m\n  Import and apply it globally in src/index.ts:\n     \\x1b[32mapp.use(${pascalName}Middleware);\\x1b[0m\n\n  Or apply it to a specific controller:\n     \\x1b[32m@Use(${pascalName}Middleware)\\x1b[0m\n     \\x1b[32mexport class MyController { ... }\\x1b[0m\n`);\n}\n","import { existsSync } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { ORM } from \"../generators/repository.js\";\nimport { detectORMOrNull, formatWithBiome } from \"../project.js\";\nimport { toCamelCase, toPascalCase, toSnakeCase } from \"../utils.js\";\nimport { makeFactoryCommand } from \"./make-factory.js\";\n\nfunction generatePrismaSeeder(name: string, pascalName: string, useFactory: boolean): string {\n\tconst delegate = toCamelCase(name);\n\tconst factoryFile = toSnakeCase(name);\n\tif (useFactory) {\n\t\treturn `import { prisma } from \"@/lib/prisma\";\nimport { ${pascalName}Factory } from \"@/factories/${factoryFile}.factory\";\n\nexport async function seed${pascalName}() {\n  console.log(\"Seeding ${pascalName}...\");\n\n  const items = ${pascalName}Factory.buildMany(100);\n  await prisma.${delegate}.createMany({ data: items });\n\n  console.log(\"✓ ${pascalName} seeded successfully (100 records)\");\n}\n`;\n\t}\n\treturn `import { prisma } from \"@/lib/prisma\";\n\nexport async function seed${pascalName}() {\n  console.log(\"Seeding ${pascalName}...\");\n\n  // TODO: Insert your dummy data here\n  // await prisma.${delegate}.createMany({\n  //   data: [\n  //     { name: \"Dummy 1\" },\n  //     { name: \"Dummy 2\" },\n  //   ],\n  // });\n\n  console.log(\"✓ ${pascalName} seeded successfully\");\n}\n`;\n}\n\nfunction generateDrizzleSeeder(name: string, pascalName: string, useFactory: boolean): string {\n\tif (useFactory) {\n\t\treturn `import { db } from \"@/lib/db\";\nimport { ${name} } from \"@/lib/db/schema\";\nimport { ${pascalName}Factory } from \"@/factories/${toSnakeCase(name)}.factory\";\n\nexport async function seed${pascalName}() {\n  console.log(\"Seeding ${pascalName}...\");\n\n  const items = ${pascalName}Factory.buildMany(100);\n  await db.insert(${name}).values(items);\n\n  console.log(\"✓ ${pascalName} seeded successfully (100 records)\");\n}\n`;\n\t}\n\treturn `import { db } from \"@/lib/db\";\nimport { ${name} } from \"@/lib/db/schema\";\n\nexport async function seed${pascalName}() {\n  console.log(\"Seeding ${pascalName}...\");\n\n  // TODO: Insert your dummy data here\n  // await db.insert(${name}).values([\n  //   { name: \"Dummy 1\" },\n  //   { name: \"Dummy 2\" },\n  // ]);\n\n  console.log(\"✓ ${pascalName} seeded successfully\");\n}\n`;\n}\n\nfunction generateTypeORMSeeder(name: string, pascalName: string, useFactory: boolean): string {\n\tif (useFactory) {\n\t\treturn `import { AppDataSource } from \"@/lib/data-source\";\nimport { ${pascalName} } from \"@/lib/entities/${pascalName}\";\nimport { ${pascalName}Factory } from \"@/factories/${toSnakeCase(name)}.factory\";\n\nexport async function seed${pascalName}() {\n  console.log(\"Seeding ${pascalName}...\");\n\n  const repo = AppDataSource.getRepository(${pascalName});\n  const items = ${pascalName}Factory.buildMany(100);\n  await repo.save(items.map(item => repo.create(item)));\n\n  console.log(\"✓ ${pascalName} seeded successfully (100 records)\");\n}\n`;\n\t}\n\treturn `import { AppDataSource } from \"@/lib/data-source\";\nimport { ${pascalName} } from \"@/lib/entities/${pascalName}\";\n\nexport async function seed${pascalName}() {\n  console.log(\"Seeding ${pascalName}...\");\n\n  const repo = AppDataSource.getRepository(${pascalName});\n\n  // TODO: Insert your dummy data here\n  // await repo.save(repo.create({ name: \"Dummy 1\" }));\n  // await repo.save(repo.create({ name: \"Dummy 2\" }));\n\n  console.log(\"✓ ${pascalName} seeded successfully\");\n}\n`;\n}\n\nfunction generatePlainSeeder(\n\tname: string,\n\tpascalName: string,\n\tuseFactory: boolean,\n): string {\n\tconst factoryFile = toSnakeCase(name);\n\tif (useFactory) {\n\t\treturn `import { ${pascalName}Factory } from \"@/factories/${factoryFile}.factory\";\n\nexport async function seed${pascalName}() {\n  console.log(\"Seeding ${pascalName}...\");\n\n  const items = ${pascalName}Factory.buildMany(100);\n  console.log(\\`Generated \\${items.length} ${name} records (no ORM detected - persist them in your own data layer)\\`);\n\n  console.log(\"✓ ${pascalName} seeded successfully\");\n}\n`;\n\t}\n\treturn `export async function seed${pascalName}() {\n  console.log(\"Seeding ${pascalName}...\");\n\n  // TODO: Insert your dummy data here\n  // console.log([{ name: \"Dummy 1\" }, { name: \"Dummy 2\" }]);\n\n  console.log(\"✓ ${pascalName} seeded successfully\");\n}\n`;\n}\n\nfunction generateSeeder(\n\tname: string,\n\tpascalName: string,\n\torm: ORM | null,\n\tuseFactory: boolean,\n): string {\n\tif (orm === null) {\n\t\treturn generatePlainSeeder(name, pascalName, useFactory);\n\t}\n\tswitch (orm) {\n\t\tcase \"drizzle\":\n\t\t\treturn generateDrizzleSeeder(name, pascalName, useFactory);\n\t\tcase \"typeorm\":\n\t\t\treturn generateTypeORMSeeder(name, pascalName, useFactory);\n\t\tcase \"prisma\":\n\t\tdefault:\n\t\t\treturn generatePrismaSeeder(name, pascalName, useFactory);\n\t}\n}\n\nexport async function makeSeederCommand(name: string, flags: string[] = []) {\n\tconst pascalName = toPascalCase(name);\n\tconst orm = detectORMOrNull();\n\tconst useFactory = flags.includes(\"--factory\");\n\tconst dryRun = flags.includes(\"--dry-run\");\n\tconst force = flags.includes(\"--force\");\n\tconsole.log(`\\n\\x1b[36mScaffolding Seeder for ${pascalName} (orm: ${orm ?? \"plain\"}${useFactory ? \", using factory\" : \"\"}${dryRun ? \", dry-run\" : \"\"})...\\x1b[0m\\n`);\n\n\tconst seederDir = \"src/db/seeders\";\n\n\tif (!existsSync(seederDir)) {\n\t\tif (dryRun) {\n\t\t\tconsole.log(`\\x1b[90mWould create directory: ${seederDir}\\x1b[0m`);\n\t\t} else {\n\t\t\tawait fs.mkdir(seederDir, { recursive: true });\n\t\t}\n\t}\n\n\tconst filePath = join(seederDir, `${name}.seeder.ts`);\n\n\tif (existsSync(filePath) && !force) {\n\t\tconsole.error(\n\t\t\t`\\x1b[31mError: Seeder file already exists at ${filePath} (use --force to overwrite)\\x1b[0m`,\n\t\t);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tconst content = generateSeeder(name, pascalName, orm, useFactory);\n\n\tif (dryRun) {\n\t\tconsole.log(`\\x1b[90mWould create file: ${filePath}\\x1b[0m`);\n\t\tconsole.log(`\\n\\x1b[36m--- Generated content ---\\x1b[0m\\n`);\n\t\tconsole.log(content);\n\t\treturn;\n\t}\n\n\tawait fs.writeFile(filePath, content);\n\n\tif (formatWithBiome([filePath])) {\n\t\tconsole.log(\n\t\t\t\"\\x1b[90m✨ Auto-formatted generated seeder file with Biome\\x1b[0m\",\n\t\t);\n\t}\n\n\tconsole.log(`\\x1b[32m✓ Generated seeder:\\x1b[0m ${filePath}`);\n\n\tif (useFactory) {\n\t\t// Generate the factory on demand instead of just warning about it\n\t\tconst factoryPath = `src/factories/${toSnakeCase(name)}.factory.ts`;\n\t\tif (!existsSync(factoryPath)) {\n\t\t\tconsole.log(\n\t\t\t\t`\\n\\x1b[90mFactory not found at ${factoryPath} - generating it...\\x1b[0m`,\n\t\t\t);\n\t\t\tawait makeFactoryCommand(name, []);\n\t\t}\n\t}\n}\n","import { existsSync } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { formatWithBiome } from \"../project.js\";\nimport { toPascalCase } from \"../utils.js\";\n\ninterface TestShape {\n\t/** Service constructor takes the repository as a dependency. */\n\tinjectsRepo: boolean;\n\thasRepoFile: boolean;\n}\n\nfunction generateTest(\n\tname: string,\n\tpascalName: string,\n\tshape: TestShape,\n): string {\n\tconst repoImport = shape.hasRepoFile\n\t\t? `import { ${pascalName}Repository } from \"@/modules/${name}/${name}.repository\";\\n`\n\t\t: \"\";\n\tconst repoCast = shape.hasRepoFile\n\t\t? `mockRepo as unknown as ${pascalName}Repository`\n\t\t: `mockRepo as any`;\n\n\tif (!shape.injectsRepo) {\n\t\treturn `import { describe, it, expect, beforeEach } from \"bun:test\";\nimport { ${pascalName}Service } from \"@/modules/${name}/${name}.service\";\n\ndescribe(\"${pascalName}Service\", () => {\n  let service: ${pascalName}Service;\n\n  beforeEach(() => {\n    service = new ${pascalName}Service();\n  });\n\n  it(\"should be defined\", () => {\n    expect(service).toBeDefined();\n  });\n\n  it(\"should return an array of items\", async () => {\n    const result = await service.getAll();\n    expect(result).toBeInstanceOf(Array);\n  });\n\n  it(\"should return a single item by id\", async () => {\n    const result = await service.getById(1);\n    expect(result).toHaveProperty(\"id\", 1);\n  });\n\n  // TODO: Add more business logic tests here\n});\n`;\n\t}\n\n\treturn `import { describe, it, expect, beforeEach, mock } from \"bun:test\";\nimport { ${pascalName}Service } from \"@/modules/${name}/${name}.service\";\n${repoImport}\ndescribe(\"${pascalName}Service\", () => {\n  let service: ${pascalName}Service;\n  let mockRepo: any;\n\n  beforeEach(() => {\n    // 1. Mock the repository layer\n    mockRepo = {\n      findAll: mock(() => Promise.resolve([])),\n      findById: mock((id: number) => Promise.resolve({ id, name: \"Test ${pascalName}\" })),\n      create: mock((data: any) => Promise.resolve({ id: 1, ...data })),\n      update: mock((id: number, data: any) => Promise.resolve({ id, ...data })),\n      delete: mock((id: number) => Promise.resolve({ id })),\n      count: mock(() => Promise.resolve(0)),\n    };\n\n    // 2. Inject mock repo into the service\n    service = new ${pascalName}Service(${repoCast});\n  });\n\n  it(\"should be defined\", () => {\n    expect(service).toBeDefined();\n  });\n\n  it(\"should return an array of items\", async () => {\n    const result = await service.getAll();\n    expect(result).toBeInstanceOf(Array);\n    expect(mockRepo.findAll).toHaveBeenCalledTimes(1);\n  });\n\n  it(\"should return a single item by id\", async () => {\n    const result = await service.getById(1);\n    expect(result).toHaveProperty(\"id\", 1);\n    expect(result).toHaveProperty(\"name\", \"Test ${pascalName}\");\n    expect(mockRepo.findById).toHaveBeenCalledWith(1);\n  });\n\n  // TODO: Add more business logic tests here\n});\n`;\n}\n\nexport async function makeTestCommand(name: string, flags: string[] = []) {\n\tconst pascalName = toPascalCase(name);\n\tconst dryRun = flags.includes(\"--dry-run\");\n\tconst force = flags.includes(\"--force\");\n\tconsole.log(`\\n\\x1b[36mScaffolding Unit Test for ${pascalName}${dryRun ? \" (dry-run)\" : \"\"}...\\x1b[0m\\n`);\n\n\t// Stack awareness: the test must match what actually exists on disk\n\tconst servicePath = join(\"src\", \"modules\", name, `${name}.service.ts`);\n\tconst repoPath = join(\"src\", \"modules\", name, `${name}.repository.ts`);\n\tconst hasService = existsSync(servicePath);\n\tconst hasRepo = existsSync(repoPath);\n\n\tif (!hasService) {\n\t\tconsole.error(\n\t\t\t`\\x1b[31mError: No service found at ${servicePath} - the unit test would import a file that does not exist.\\x1b[0m`,\n\t\t);\n\t\tconsole.error(\n\t\t\t`  Generate it first: \\x1b[36mbuntok create ${name} --service\\x1b[0m`,\n\t\t);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tconst serviceSource = await fs.readFile(servicePath, \"utf-8\");\n\tconst shape: TestShape = {\n\t\tinjectsRepo: /constructor\\s*\\(/.test(serviceSource),\n\t\thasRepoFile: hasRepo,\n\t};\n\n\tconst testsDir = \"tests\";\n\n\tif (!existsSync(testsDir)) {\n\t\tif (dryRun) {\n\t\t\tconsole.log(`\\x1b[90mWould create directory: ${testsDir}\\x1b[0m`);\n\t\t} else {\n\t\t\tawait fs.mkdir(testsDir, { recursive: true });\n\t\t}\n\t}\n\n\tconst filePath = join(testsDir, `${name}.spec.ts`);\n\n\tif (existsSync(filePath) && !force) {\n\t\tconsole.error(\n\t\t\t`\\x1b[31mError: Test file already exists at ${filePath} (use --force to overwrite)\\x1b[0m`,\n\t\t);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tconst content = generateTest(name, pascalName, shape);\n\n\tif (dryRun) {\n\t\tconsole.log(`\\x1b[90mWould create file: ${filePath}\\x1b[0m`);\n\t\tconsole.log(`\\n\\x1b[36m--- Generated content ---\\x1b[0m\\n`);\n\t\tconsole.log(content);\n\t\treturn;\n\t}\n\n\tawait fs.writeFile(filePath, content);\n\n\tif (formatWithBiome([filePath])) {\n\t\tconsole.log(\n\t\t\t\"\\x1b[90m✨ Auto-formatted generated test file with Biome\\x1b[0m\",\n\t\t);\n\t}\n\n\tconsole.log(`\\x1b[32m✓ Generated test suite:\\x1b[0m ${filePath}`);\n\n\tconsole.log(`\n\\x1b[36mNext steps:\\x1b[0m\n  Run your test suite instantly using Bun's native test runner:\n     \\x1b[33mbun test\\x1b[0m\n     \\x1b[33mbun test --watch\\x1b[0m  (For TDD / Live Reload)\n`);\n}\n","import { existsSync } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { formatWithBiome } from \"../project.js\";\nimport { resolveNames, toPascalCase } from \"../utils.js\";\n\nfunction generateE2ETest(name: string, pascalName: string): string {\n\tconst route = resolveNames(name).route;\n\treturn `import { describe, it, expect } from \"bun:test\";\nimport { app } from \"@/index\";\n\ndescribe(\"${pascalName} API (E2E)\", () => {\n  it(\"should return a list of items (GET /${route})\", async () => {\n    const response = await app.request(\"/${route}\", {\n      method: \"GET\",\n    });\n\n    expect(response.status).toBe(200);\n\n    // const body = await response.json();\n    // expect(Array.isArray(body)).toBe(true);\n  });\n\n  it(\"should create a new item (POST /${route})\", async () => {\n    const response = await app.request(\"/${route}\", {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify({\n        // TODO: add payload here\n      }),\n    });\n\n    // expect(response.status).toBe(201);\n  });\n\n  it(\"should handle not found items (GET /${route}/999999)\", async () => {\n    const response = await app.request(\"/${route}/999999\", {\n      method: \"GET\",\n    });\n\n    // expect(response.status).toBe(404);\n  });\n});\n`;\n}\n\nexport async function makeTestE2ECommand(name: string, flags: string[] = []) {\n\tconst pascalName = toPascalCase(name);\n\tconst dryRun = flags.includes(\"--dry-run\");\n\tconst force = flags.includes(\"--force\");\n\tconsole.log(\n\t\t`\\n\\x1b[36mScaffolding E2E Test for ${pascalName} API${dryRun ? \" (dry-run)\" : \"\"}...\\x1b[0m\\n`,\n\t);\n\n\tconst testsDir = \"tests/e2e\";\n\n\tif (!existsSync(testsDir)) {\n\t\tif (dryRun) {\n\t\t\tconsole.log(`\\x1b[90mWould create directory: ${testsDir}\\x1b[0m`);\n\t\t} else {\n\t\t\tawait fs.mkdir(testsDir, { recursive: true });\n\t\t}\n\t}\n\n\tconst filePath = join(testsDir, `${name}.e2e.spec.ts`);\n\n\tif (existsSync(filePath) && !force) {\n\t\tconsole.error(\n\t\t\t`\\x1b[31mError: E2E Test file already exists at ${filePath} (use --force to overwrite)\\x1b[0m`,\n\t\t);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tconst content = generateE2ETest(name, pascalName);\n\n\tif (dryRun) {\n\t\tconsole.log(`\\x1b[90mWould create file: ${filePath}\\x1b[0m`);\n\t\tconsole.log(`\\n\\x1b[36m--- Generated content ---\\x1b[0m\\n`);\n\t\tconsole.log(content);\n\t\treturn;\n\t}\n\n\tawait fs.writeFile(filePath, content);\n\n\tif (formatWithBiome([filePath])) {\n\t\tconsole.log(\n\t\t\t\"\\x1b[90m✨ Auto-formatted generated E2E test file with Biome\\x1b[0m\",\n\t\t);\n\t}\n\n\tconsole.log(`\\x1b[32m✓ Generated E2E test suite:\\x1b[0m ${filePath}`);\n\n\tconsole.log(`\n\\x1b[36mNext steps:\\x1b[0m\n  Run all E2E tests:\n     \\x1b[33mbun test tests/e2e\\x1b[0m\n`);\n}\n","#!/usr/bin/env bun\n\nimport { buildCommand } from \"./commands/build.js\";\nimport { checkCommand } from \"./commands/check.js\";\nimport { createCommand } from \"./commands/create.js\";\nimport { dbCommand } from \"./commands/db.js\";\nimport { debugRoutesCommand } from \"./commands/debug-routes.js\";\nimport { devCommand } from \"./commands/dev.js\";\nimport { initCommand } from \"./commands/init.js\";\nimport { makeDocsCommand } from \"./commands/make-docs.js\";\nimport { makeFactoryCommand } from \"./commands/make-factory.js\";\nimport { makeMiddlewareCommand } from \"./commands/make-middleware.js\";\nimport { makeSeederCommand } from \"./commands/make-seeder.js\";\nimport { makeTestCommand } from \"./commands/make-test.js\";\nimport { makeTestE2ECommand } from \"./commands/make-test-e2e.js\";\nimport { VERSION } from \"../core-exports.js\";\n\nfunction printBanner() {\n\tconsole.log(`\n\\x1b[36m  Buntok CLI v${VERSION}\\x1b[0m\n`);\n}\n\nfunction printUsage() {\n\tconsole.log(`\n\\x1b[36mUsage:\\x1b[0m\n  buntok <command> [arguments] [options]\n\n\\x1b[36mCommands:\\x1b[0m\n  init                   Setup project: copy SKILL.md + configure package.json + generate env.ts\n  dev                    Start development server with hot reload\n  build                  Build project for production (output → buntok/)\n  check                  Run TypeScript type check\n  create <entity>        Generate all files for entity (repo, service, controller, schema)\n  db <command>           Database operations (migrate, seed, reset, generate, studio, status)\n  debug:routes           Show all registered routes with middleware chains\n  make:docs              Generate OpenAPI documentation automatically\n  make:factory <entity>  Generate data factory for entity\n  make:test <entity>     Generate unit test for entity\n  make:test:e2e <entity> Generate E2E test for entity\n  make:seeder <entity>   Generate database seeder for entity\n  make:middleware <name>  Generate middleware file\n\n\\x1b[36mGlobal options:\\x1b[0m\n  --help, -h             Show this help\n  --version, -v          Show CLI version\n\n\\x1b[36mOptions (for dev):\\x1b[0m\n  --port=PORT            Dev server port (default: env PORT or 1212)\n  --expose               Public tunnel URL via localtunnel (requires: bun add -d localtunnel)\n\n\\x1b[36mOptions (for create):\\x1b[0m\n  --repo                 Generate only repository\n  --service              Generate only service\n  --controller           Generate only controller\n  --schema               Generate only schema\n  --prisma               Use Prisma ORM (default: auto-detect, falls back to plain code)\n  --drizzle              Use Drizzle ORM\n  --typeorm              Use TypeORM\n  --dry-run              Preview files without writing\n  --force                Overwrite files that already exist\n  --base                 Generate code extending BaseRepository/BaseService/BaseController (default: plain code)\n  --fields \"a:string,b:int?\"  Field list for the schema when there is no Prisma model\n  --app <instance>       Buntok instance to register the controller into (default: auto-detect, e.g. --app apiV1)\n\n\\x1b[36mOptions (for check command):\\x1b[0m\n  --json                 Output results as JSON\n  --plain                Plain text output without colors\n\n\\x1b[36mOptions (for make:* commands):\\x1b[0m\n  --dry-run              Preview generated content without writing\n  --force                Overwrite file that already exists\n  --factory              (make:seeder) Use factory pattern data\n\n\\x1b[36mAliases:\\x1b[0m\n  g, gen, generate       Shortcut for create\n\n\\x1b[36mExamples:\\x1b[0m\n  buntok init                             # Initialize project setup\n  buntok dev                              # Start development server\n  buntok dev --expose                     # Start with public tunnel URL\n  buntok dev --expose --port=3000         # Start with tunnel on port 3000\n  buntok build                            # Build project for production\n  buntok check                            # Run TypeScript type check\n  buntok create user                      # Generate all files for user entity\n  buntok g user --repo --service          # Generate repository and service only\n  buntok g user --drizzle                 # Generate with Drizzle ORM\n  buntok g user --dry-run                # Preview what would be generated\n  buntok g user --app apiV1              # Register into the apiV1 Buntok instance\n  buntok db migrate                       # Run pending migrations\n  buntok db seed                          # Seed database\n  buntok debug:routes                     # Show all registered routes\n  buntok debug:routes --json              # Output routes as JSON\n  buntok make:factory user                # Generate user data factory\n  buntok make:docs                        # Generate OpenAPI documentation\n  buntok make:test user                   # Generate unit test for user\n  buntok make:test:e2e user               # Generate E2E test for user API\n  buntok make:seeder user                 # Generate user seeder\n  buntok make:middleware auth             # Generate auth middleware\n`);\n}\n\nexport async function main() {\n\tconst args = process.argv.slice(2);\n\tconst command = args[0];\n\tconst arg1 = args[1];\n\n\tif (!command) {\n\t\tprintBanner();\n\t\tprintUsage();\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tif (command === \"--help\" || command === \"-h\" || command === \"help\") {\n\t\tprintUsage();\n\t\treturn;\n\t}\n\tif (command === \"--version\" || command === \"-v\" || command === \"version\") {\n\t\tprintBanner();\n\t\treturn;\n\t}\n\n\tswitch (command) {\n\t\tcase \"init\":\n\t\t\tawait initCommand();\n\t\t\tbreak;\n\t\tcase \"build\":\n\t\t\tawait buildCommand();\n\t\t\tbreak;\n\t\tcase \"check\":\n\t\t\tawait checkCommand(args.slice(1));\n\t\t\tbreak;\n\t\tcase \"dev\":\n\t\t\tawait devCommand(args.slice(1));\n\t\t\tbreak;\n\t\tcase \"g\":\n\t\tcase \"gen\":\n\t\tcase \"generate\":\n\t\tcase \"create\":\n\t\t\tif (!arg1) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`\\x1b[31mError: entity name is required for ${command} command\\x1b[0m`,\n\t\t\t\t);\n\t\t\t\tprocess.exitCode = 1;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait createCommand(arg1, args.slice(2));\n\t\t\tbreak;\n\t\tcase \"db\":\n\t\t\tawait dbCommand(args.slice(1));\n\t\t\tbreak;\n\t\tcase \"debug:routes\":\n\t\t\tawait debugRoutesCommand(args.slice(1));\n\t\t\tbreak;\n\t\tcase \"make:docs\":\n\t\t\tawait makeDocsCommand();\n\t\t\tbreak;\n\t\tcase \"make:factory\":\n\t\t\tif (!arg1) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"\\x1b[31mError: entity name is required for make:factory command\\x1b[0m\",\n\t\t\t\t);\n\t\t\t\tprocess.exitCode = 1;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait makeFactoryCommand(arg1, args.slice(2));\n\t\t\tbreak;\n\t\tcase \"make:test\":\n\t\t\tif (!arg1) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"\\x1b[31mError: entity name is required for make:test command\\x1b[0m\",\n\t\t\t\t);\n\t\t\t\tprocess.exitCode = 1;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait makeTestCommand(arg1, args.slice(2));\n\t\t\tbreak;\n\t\tcase \"make:test:e2e\":\n\t\t\tif (!arg1) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"\\x1b[31mError: entity name is required for make:test:e2e command\\x1b[0m\",\n\t\t\t\t);\n\t\t\t\tprocess.exitCode = 1;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait makeTestE2ECommand(arg1, args.slice(2));\n\t\t\tbreak;\n\t\tcase \"make:seeder\":\n\t\t\tif (!arg1) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"\\x1b[31mError: entity name is required for make:seeder command\\x1b[0m\",\n\t\t\t\t);\n\t\t\t\tprocess.exitCode = 1;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait makeSeederCommand(arg1, args.slice(2));\n\t\t\tbreak;\n\t\tcase \"make:middleware\":\n\t\t\tif (!arg1) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"\\x1b[31mError: middleware name is required for make:middleware command\\x1b[0m\",\n\t\t\t\t);\n\t\t\t\tprocess.exitCode = 1;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait makeMiddlewareCommand(arg1, args.slice(2));\n\t\t\tbreak;\n\t\tdefault: {\n\t\t\tconst commands = [\n\t\t\t\t\"init\",\n\t\t\t\t\"dev\",\n\t\t\t\t\"build\",\n\t\t\t\t\"check\",\n\t\t\t\t\"create\",\n\t\t\t\t\"g\",\n\t\t\t\t\"db\",\n\t\t\t\t\"debug:routes\",\n\t\t\t\t\"make:docs\",\n\t\t\t\t\"make:factory\",\n\t\t\t\t\"make:test\",\n\t\t\t\t\"make:test:e2e\",\n\t\t\t\t\"make:seeder\",\n\t\t\t\t\"make:middleware\",\n\t\t\t];\n\t\t\t// Simple fuzzy match: prefix or contains\n\t\t\tconst closest = commands.find(\n\t\t\t\t(c) =>\n\t\t\t\t\tc.startsWith(command) ||\n\t\t\t\t\tcommand.startsWith(c) ||\n\t\t\t\t\tc.includes(command) ||\n\t\t\t\t\tcommand.includes(c),\n\t\t\t);\n\t\t\tconst hint = closest ? `\\n  Did you mean: buntok ${closest}?` : \"\";\n\t\t\tconsole.error(`\\x1b[31mUnknown command: ${command}\\x1b[0m${hint}`);\n\t\t\tprintUsage();\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nmain();\n"]}