@prismakit/cli 3.0.3 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.cjs CHANGED
@@ -79,12 +79,10 @@ function renderRepository(names, cacheEnabled, prismaImport, base) {
79
79
  },
80
80
  ` : "";
81
81
  const content = apply(
82
- `import { Prisma } from '{{prismaImport}}';
83
- import { createInjectableRepository } from '@prismakit/nestjs';
82
+ `import { createInjectableRepository } from '@prismakit/nestjs';
84
83
 
85
84
  export class {{pascal}}Repository extends createInjectableRepository({
86
85
  model: '{{repoModel}}',
87
- scalarFields: Prisma.{{pascal}}ScalarFieldEnum,
88
86
  {{cacheBlock}}}) {}
89
87
  `,
90
88
  names,
package/dist/bin.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/generate.ts","../src/naming.ts","../src/templates.ts","../src/commands/validate.ts","../src/commands/skills.ts","../src/bin.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { resolveNames } from '../naming';\nimport { renderModuleFiles } from '../templates';\n\nexport type GenerateCommandOptions = {\n name: string;\n cache?: boolean;\n route?: string;\n cwd?: string;\n dryRun?: boolean;\n /** Emit full Nest module (controller/service/types). Default: repo-only. */\n full?: boolean;\n /** Emit validate + mapper helpers. */\n helpers?: boolean;\n /** Emit class-validator DTOs. */\n dto?: boolean;\n /** Prisma client import path (default `@prisma/client`). */\n prismaImport?: string;\n};\n\nexport function runGenerate(options: GenerateCommandOptions): void {\n const cwd = options.cwd ?? process.cwd();\n const names = resolveNames(options.name, options.route);\n const full = !!options.full;\n const files = renderModuleFiles({\n names,\n cacheEnabled: !!options.cache,\n full,\n helpers: !!options.helpers,\n dto: !!options.dto,\n prismaImport: options.prismaImport,\n });\n\n for (const file of files) {\n const fullPath = path.join(cwd, file.relativePath);\n if (options.dryRun) {\n console.log(`[dry-run] would write ${file.relativePath}`);\n continue;\n }\n if (fs.existsSync(fullPath)) {\n console.warn(`skip (exists): ${file.relativePath}`);\n continue;\n }\n fs.mkdirSync(path.dirname(fullPath), { recursive: true });\n const content = file.content.endsWith('\\n')\n ? file.content\n : `${file.content}\\n`;\n fs.writeFileSync(fullPath, content, 'utf-8');\n console.log(`created ${file.relativePath}`);\n }\n\n if (full) {\n console.log(\n `\\nScaffolded module \"${names.kebab}\". Register ${names.pascal}Module in app.module.ts.`,\n );\n } else {\n console.log(\n `\\nScaffolded repository \"${names.pascal}Repository\". Register it in your feature module providers.`,\n );\n }\n}\n","export interface ModuleNames {\n kebab: string;\n camel: string;\n pascal: string;\n repoModel: string;\n route: string;\n}\n\nconst KEBAB_NAME_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;\n\nexport function assertKebabName(name: string): void {\n if (!KEBAB_NAME_RE.test(name)) {\n throw new Error(\n `Invalid module name \"${name}\". Use kebab-case (e.g. product, blog-post).`,\n );\n }\n}\n\nfunction kebabToPascal(kebab: string): string {\n return kebab\n .split('-')\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\nfunction kebabToCamel(kebab: string): string {\n const pascal = kebabToPascal(kebab);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\nexport function resolveNames(kebab: string, route?: string): ModuleNames {\n assertKebabName(kebab);\n const pascal = kebabToPascal(kebab);\n const camel = kebabToCamel(kebab);\n return {\n kebab,\n camel,\n pascal,\n repoModel: camel,\n route: route ?? kebab,\n };\n}\n","import type { ModuleNames } from './naming';\n\nexport type GenerateOptions = {\n names: ModuleNames;\n cacheEnabled: boolean;\n /** When false (default), only emit the repository file. */\n full?: boolean;\n /** Emit validate + mapper helpers. */\n helpers?: boolean;\n /** Emit class-validator DTOs with @ApiProperty. */\n dto?: boolean;\n /** Prisma client import path (default `@prisma/client`). */\n prismaImport?: string;\n};\n\nfunction apply(template: string, names: ModuleNames, extras: Record<string, string>): string {\n const replacements: Record<string, string> = {\n '{{pascal}}': names.pascal,\n '{{camel}}': names.camel,\n '{{kebab}}': names.kebab,\n '{{route}}': names.route,\n '{{repoModel}}': names.repoModel,\n ...extras,\n };\n let result = template;\n for (const [key, value] of Object.entries(replacements)) {\n result = result.split(key).join(value);\n }\n return result;\n}\n\nexport type GeneratedFile = {\n relativePath: string;\n content: string;\n};\n\nfunction renderRepository(\n names: ModuleNames,\n cacheEnabled: boolean,\n prismaImport: string,\n base: string,\n): GeneratedFile {\n const cacheBlock = cacheEnabled\n ? ` cache: {\n ttl: 86400,\n sensitiveFields: ['password'],\n defaultSetCache: true,\n },\n`\n : '';\n\n const content = apply(\n `import { Prisma } from '{{prismaImport}}';\nimport { createInjectableRepository } from '@prismakit/nestjs';\n\nexport class {{pascal}}Repository extends createInjectableRepository({\n model: '{{repoModel}}',\n scalarFields: Prisma.{{pascal}}ScalarFieldEnum,\n{{cacheBlock}}}) {}\n`,\n names,\n {\n '{{cacheBlock}}': cacheBlock,\n '{{prismaImport}}': prismaImport,\n },\n );\n\n return {\n relativePath: `${base}/repositories/${names.kebab}.repository.ts`,\n content,\n };\n}\n\nexport function renderModuleFiles(options: GenerateOptions): GeneratedFile[] {\n const { names, cacheEnabled, full = false, helpers = false, dto = false } =\n options;\n const prismaImport = options.prismaImport ?? '@prisma/client';\n const base = `src/modules/${names.kebab}`;\n\n const repository = renderRepository(names, cacheEnabled, prismaImport, base);\n\n if (!full) {\n return [repository];\n }\n\n const service = apply(\n `import { Injectable } from '@nestjs/common';\n\nimport { {{pascal}}Repository } from '../repositories/{{kebab}}.repository';\nimport { get{{pascal}}Select } from '../types/select-{{kebab}}.type';\nimport { where{{pascal}}GetManyPaginate } from '../types/where-{{kebab}}.type';\n{{dtoImport}}\n@Injectable()\nexport class {{pascal}}Service {\n constructor(private readonly {{camel}}Repository: {{pascal}}Repository) {}\n\n async handleCreate(dto: Create{{pascal}}Dto) {\n return await this.{{camel}}Repository.create({\n data: { ...dto },\n select: get{{pascal}}Select('general'),\n });\n }\n\n async handleGetById(id: string) {\n return await this.{{camel}}Repository.getThrowById({\n id,\n select: get{{pascal}}Select('general'),\n setCache: true,\n });\n }\n\n async handleGetManyPaginate(filter: Filter{{pascal}}Dto) {\n const { where } = where{{pascal}}GetManyPaginate(filter);\n return await this.{{camel}}Repository.getManyPaginate({\n where,\n select: get{{pascal}}Select('general'),\n page: filter.page,\n pageSize: filter.pageSize,\n setCache: true,\n });\n }\n\n async handleUpdateById(id: string, dto: Update{{pascal}}Dto) {\n return await this.{{camel}}Repository.updateById({\n id,\n data: { ...dto },\n select: get{{pascal}}Select('general'),\n });\n }\n\n async handleDeleteById(id: string) {\n return await this.{{camel}}Repository.deleteById({\n id,\n select: get{{pascal}}Select('minimal'),\n });\n }\n}\n`,\n names,\n {\n '{{dtoImport}}': dto\n ? `import type {\\n Create{{pascal}}Dto,\\n Update{{pascal}}Dto,\\n Filter{{pascal}}Dto,\\n} from '../dto/{{kebab}}.dto';\\n`\n : `type Create{{pascal}}Dto = Record<string, unknown>;\\ntype Update{{pascal}}Dto = Record<string, unknown>;\\ntype Filter{{pascal}}Dto = { page?: number; pageSize?: number; q?: string };\\n`,\n },\n );\n\n const controller = apply(\n `import {\n Body,\n Controller,\n Delete,\n Get,\n HttpStatus,\n Param,\n Patch,\n Post,\n Query,\n Res,\n} from '@nestjs/common';\nimport type { Response } from 'express';\n\nimport { {{pascal}}Service } from '../services/{{kebab}}.service';\n{{dtoImport}}\n@Controller('{{route}}')\nexport class {{pascal}}Controller {\n constructor(private readonly {{camel}}Service: {{pascal}}Service) {}\n\n @Post()\n async create(@Body() dto: Create{{pascal}}Dto, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleCreate(dto);\n return res.status(HttpStatus.CREATED).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Get()\n async getMany(@Query() filter: Filter{{pascal}}Dto, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleGetManyPaginate(filter);\n return res.status(HttpStatus.OK).json({ data: result.data, meta: result.meta });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Get(':id')\n async getById(@Param('id') id: string, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleGetById(id);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Patch(':id')\n async update(\n @Param('id') id: string,\n @Body() dto: Update{{pascal}}Dto,\n @Res() res: Response,\n ) {\n try {\n const result = await this.{{camel}}Service.handleUpdateById(id, dto);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Delete(':id')\n async delete(@Param('id') id: string, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleDeleteById(id);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n}\n`,\n names,\n {\n '{{dtoImport}}': dto\n ? `import type {\\n Create{{pascal}}Dto,\\n Update{{pascal}}Dto,\\n Filter{{pascal}}Dto,\\n} from '../dto/{{kebab}}.dto';\\n`\n : `type Create{{pascal}}Dto = Record<string, unknown>;\\ntype Update{{pascal}}Dto = Record<string, unknown>;\\ntype Filter{{pascal}}Dto = { page?: number; pageSize?: number; q?: string };\\n`,\n },\n );\n\n const moduleFile = apply(\n `import { Module } from '@nestjs/common';\n\nimport { {{pascal}}Controller } from './controllers/{{kebab}}.controller';\nimport { {{pascal}}Service } from './services/{{kebab}}.service';\nimport { {{pascal}}Repository } from './repositories/{{kebab}}.repository';\n{{helpersImport}}\n@Module({\n controllers: [{{pascal}}Controller],\n providers: [{{pascal}}Service, {{pascal}}Repository{{helpersProviders}}],\n exports: [{{pascal}}Service, {{pascal}}Repository],\n})\nexport class {{pascal}}Module {}\n`,\n names,\n {\n '{{helpersImport}}': helpers\n ? `import { {{pascal}}ValidateHelper } from './helpers/{{kebab}}-validate.helper';\\nimport { {{pascal}}MapperHelper } from './helpers/{{kebab}}-mapper.helper';\\n`\n : '',\n '{{helpersProviders}}': helpers\n ? `, {{pascal}}ValidateHelper, {{pascal}}MapperHelper`\n : '',\n },\n );\n\n const select = apply(\n `import { Prisma } from '{{prismaImport}}';\n\ntype {{pascal}}SelectPresetKey = keyof typeof {{camel}}SelectPresets;\n\nexport function get{{pascal}}Select<K extends {{pascal}}SelectPresetKey>(key: K) {\n return {{camel}}SelectPresets[key];\n}\n\nexport const {{camel}}SelectPresets = {\n minimal: {\n id: true,\n } satisfies Prisma.{{pascal}}Select,\n\n general: {\n id: true,\n } satisfies Prisma.{{pascal}}Select,\n};\n`,\n names,\n { '{{prismaImport}}': prismaImport },\n );\n\n const where = apply(\n `import { Prisma } from '{{prismaImport}}';\n\nexport function where{{pascal}}GetManyPaginate(filter: {\n q?: string;\n}): {\n where: Prisma.{{pascal}}WhereInput;\n} {\n const { q } = filter;\n const where: Prisma.{{pascal}}WhereInput = {\n ...(q ? { /* add searchable fields */ } : {}),\n };\n return { where };\n}\n`,\n names,\n { '{{prismaImport}}': prismaImport },\n );\n\n const files: GeneratedFile[] = [\n { relativePath: `${base}/${names.kebab}.module.ts`, content: moduleFile },\n {\n relativePath: `${base}/controllers/${names.kebab}.controller.ts`,\n content: controller,\n },\n {\n relativePath: `${base}/services/${names.kebab}.service.ts`,\n content: service,\n },\n repository,\n {\n relativePath: `${base}/types/select-${names.kebab}.type.ts`,\n content: select,\n },\n {\n relativePath: `${base}/types/where-${names.kebab}.type.ts`,\n content: where,\n },\n ];\n\n if (dto) {\n files.push({\n relativePath: `${base}/dto/${names.kebab}.dto.ts`,\n content: apply(\n `import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';\nimport { IsOptional, IsString } from 'class-validator';\n\nexport class Create{{pascal}}Dto {\n @ApiProperty({ example: 'name' })\n @IsString()\n name!: string;\n}\n\nexport class Update{{pascal}}Dto {\n @ApiPropertyOptional({ example: 'name' })\n @IsOptional()\n @IsString()\n name?: string;\n}\n\nexport class Filter{{pascal}}Dto {\n @ApiPropertyOptional()\n @IsOptional()\n page?: number;\n\n @ApiPropertyOptional()\n @IsOptional()\n pageSize?: number;\n\n @ApiPropertyOptional()\n @IsOptional()\n @IsString()\n q?: string;\n}\n`,\n names,\n {},\n ),\n });\n }\n\n if (helpers) {\n files.push(\n {\n relativePath: `${base}/helpers/${names.kebab}-validate.helper.ts`,\n content: apply(\n `import { Injectable } from '@nestjs/common';\n\nimport { {{pascal}}Repository } from '../repositories/{{kebab}}.repository';\nimport { get{{pascal}}Select } from '../types/select-{{kebab}}.type';\n\n@Injectable()\nexport class {{pascal}}ValidateHelper {\n constructor(private readonly {{camel}}Repository: {{pascal}}Repository) {}\n\n async assertExists(id: string) {\n return this.{{camel}}Repository.getThrowById({\n id,\n select: get{{pascal}}Select('minimal'),\n });\n }\n}\n`,\n names,\n {},\n ),\n },\n {\n relativePath: `${base}/helpers/${names.kebab}-mapper.helper.ts`,\n content: apply(\n `import { Injectable } from '@nestjs/common';\n\n@Injectable()\nexport class {{pascal}}MapperHelper {\n toResponse(entity: Record<string, unknown>) {\n return entity;\n }\n}\n`,\n names,\n {},\n ),\n },\n );\n }\n\n return files;\n}\n","import {\n assertSelectComposeValid,\n validateSelectCompose,\n} from '@prismakit/core';\n\nexport type ValidateCommandOptions = {\n cwd?: string;\n assert?: boolean;\n schemaPath?: string;\n autoRegisterModels?: boolean;\n};\n\n/**\n * Run select-compose validation from @prismakit/core.\n */\nexport function runValidate(options: ValidateCommandOptions = {}): void {\n const cwd = options.cwd ?? process.cwd();\n const validateOptions = {\n schemaPath: options.schemaPath,\n autoRegisterModels: options.autoRegisterModels,\n };\n\n if (options.assert !== false) {\n try {\n assertSelectComposeValid(cwd, validateOptions);\n console.log('Select compose validation passed.');\n } catch (err) {\n console.error((err as Error).message);\n process.exitCode = 1;\n }\n return;\n }\n\n const issues = validateSelectCompose(cwd, validateOptions);\n if (issues.length === 0) {\n console.log('Select compose validation passed.');\n return;\n }\n\n for (const issue of issues) {\n console.error(` - ${issue.file}: ${issue.message}`);\n }\n process.exitCode = 1;\n}\n","import * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst SKILL_NAMES = ['prismakit', 'prismakit-nestjs'] as const;\nconst RULE_NAME = 'data-access.mdc';\n\nexport type SkillsCommandOptions = {\n cwd?: string;\n /** Install into ~/.cursor/skills (all projects). Default: <cwd>/.cursor/skills */\n global?: boolean;\n /** Override project root when not using --global. */\n projectRoot?: string;\n /** Subset of skill folder names. Default: both. */\n skill?: string[];\n /** Also copy templates/cursor-rules/data-access.mdc into .cursor/rules. */\n withRules?: boolean;\n dryRun?: boolean;\n list?: boolean;\n /** Test override: directory that contains prismakit/ and prismakit-nestjs/. */\n skillsRoot?: string;\n /** Test override: path to data-access.mdc. */\n rulesPath?: string;\n};\n\nexport type SkillsInstallResult = {\n dest: string;\n installed: string[];\n ruleDest?: string;\n};\n\nfunction isSkillsRoot(dir: string): boolean {\n return fs.existsSync(path.join(dir, 'prismakit', 'SKILL.md'));\n}\n\n/**\n * Walk up from the CLI entrypoint looking for bundled `skills/` (published\n * package) or the monorepo `skills/` directory.\n */\nexport function findSkillsRoot(startDir: string): string | undefined {\n let dir = path.resolve(startDir);\n for (let i = 0; i < 10; i++) {\n for (const candidate of [\n path.join(dir, 'skills'),\n dir,\n path.join(dir, 'templates', 'cursor-skills'),\n ]) {\n if (isSkillsRoot(candidate)) return candidate;\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return undefined;\n}\n\nexport function findRulesPath(skillsRoot: string): string | undefined {\n const candidates = [\n path.join(skillsRoot, '..', 'templates', 'cursor-rules', RULE_NAME),\n path.join(skillsRoot, '..', 'rules', RULE_NAME),\n path.join(path.dirname(skillsRoot), 'rules', RULE_NAME),\n ];\n return candidates.find((p) => fs.existsSync(p));\n}\n\nfunction copyDir(src: string, dest: string): void {\n fs.mkdirSync(dest, { recursive: true });\n for (const entry of fs.readdirSync(src, { withFileTypes: true })) {\n if (entry.name === '.git') continue;\n const from = path.join(src, entry.name);\n const to = path.join(dest, entry.name);\n if (entry.isDirectory()) {\n copyDir(from, to);\n } else if (entry.isFile()) {\n fs.copyFileSync(from, to);\n }\n }\n}\n\nfunction resolveHome(): string {\n return process.env.HOME || os.homedir();\n}\n\nfunction assertNotBuiltinSkills(dest: string): void {\n const forbidden = path.join(resolveHome(), '.cursor', 'skills-cursor');\n const resolved = path.resolve(dest);\n if (resolved === forbidden || resolved.startsWith(`${forbidden}${path.sep}`)) {\n throw new Error(\n `Refusing to install into ${resolved} (reserved for Cursor built-ins).`,\n );\n }\n}\n\nfunction cliStartDir(): string {\n try {\n const url = import.meta.url;\n if (typeof url === 'string' && url.length > 0) {\n return path.dirname(fileURLToPath(url));\n }\n } catch {\n // CJS bundle has an empty import.meta\n }\n return process.cwd();\n}\n\nexport function runSkills(options: SkillsCommandOptions = {}): SkillsInstallResult {\n const skillsRoot =\n options.skillsRoot ??\n findSkillsRoot(cliStartDir()) ??\n findSkillsRoot(process.cwd());\n if (!skillsRoot) {\n throw new Error(\n 'Could not find PrismaKit skills. Reinstall @prismakit/cli or clone fikiap23/prismakit.',\n );\n }\n\n const available = SKILL_NAMES.filter((name) =>\n fs.existsSync(path.join(skillsRoot, name, 'SKILL.md')),\n );\n if (available.length === 0) {\n throw new Error(`No skills found in ${skillsRoot}`);\n }\n\n const requested = options.skill?.length\n ? options.skill.map((s) => s.trim()).filter(Boolean)\n : [...available];\n\n for (const name of requested) {\n if (!available.includes(name as (typeof SKILL_NAMES)[number])) {\n throw new Error(\n `Unknown skill \"${name}\". Available: ${available.join(', ')}`,\n );\n }\n }\n\n if (options.list) {\n console.log(`Skills in ${skillsRoot}:`);\n for (const name of available) {\n console.log(` - ${name}`);\n }\n return { dest: skillsRoot, installed: [] };\n }\n\n const cwd = options.cwd ?? process.cwd();\n const dest = options.global\n ? path.join(resolveHome(), '.cursor', 'skills')\n : path.join(\n path.resolve(options.projectRoot ?? cwd),\n '.cursor',\n 'skills',\n );\n\n assertNotBuiltinSkills(dest);\n\n if (!options.dryRun) {\n fs.mkdirSync(dest, { recursive: true });\n }\n\n const installed: string[] = [];\n for (const name of requested) {\n const src = path.join(skillsRoot, name);\n const target = path.join(dest, name);\n if (options.dryRun) {\n console.log(`[dry-run] would install ${name} -> ${target}`);\n } else {\n fs.rmSync(target, { recursive: true, force: true });\n copyDir(src, target);\n console.log(`Installed ${name} -> ${target}`);\n }\n installed.push(name);\n }\n\n let ruleDest: string | undefined;\n if (options.withRules) {\n const rulesSrc = options.rulesPath ?? findRulesPath(skillsRoot);\n if (!rulesSrc) {\n throw new Error(`Could not find ${RULE_NAME} next to skills.`);\n }\n const rulesDir = options.global\n ? path.join(resolveHome(), '.cursor', 'rules')\n : path.join(path.resolve(options.projectRoot ?? cwd), '.cursor', 'rules');\n ruleDest = path.join(rulesDir, RULE_NAME);\n if (options.dryRun) {\n console.log(`[dry-run] would install rule -> ${ruleDest}`);\n } else {\n fs.mkdirSync(rulesDir, { recursive: true });\n fs.copyFileSync(rulesSrc, ruleDest);\n console.log(`Installed rule -> ${ruleDest}`);\n }\n }\n\n const scope = options.global\n ? 'global (~/.cursor/skills)'\n : 'project (.cursor/skills)';\n if (options.dryRun) {\n console.log(`Dry-run complete (${scope}).`);\n } else if (options.global) {\n console.log(`Done. Skills are available in all Cursor projects on this machine (${scope}).`);\n } else {\n console.log(\n `Done. Commit .cursor/skills so the team shares the same agent contract (${scope}).`,\n );\n }\n\n return { dest, installed, ruleDest };\n}\n","import { runGenerate } from './commands/generate';\nimport { runValidate } from './commands/validate';\nimport { runSkills } from './commands/skills';\n\nfunction printHelp(): void {\n console.log(`prismakit — PrismaKit CLI\n\nUsage:\n prismakit generate <name> [--cache] [--full] [--helpers] [--dto] [--route <path>] [--prisma-import <path>] [--dry-run]\n prismakit validate [--schema <path>] [--auto-register] [--no-assert]\n prismakit skills [--global] [--with-rules] [--skill <name>] [--project <path>] [--list] [--dry-run]\n prismakit help\n\nBy default, generate writes only the repository file.\nPass --full for a Nest module (controller, service, types).\nPass --helpers / --dto with --full for helpers and Swagger DTOs.\n\nExamples:\n prismakit generate product --cache\n prismakit generate product --cache --full --helpers --dto --route products\n prismakit validate --auto-register\n prismakit skills\n prismakit skills --global\n prismakit skills --with-rules\n`);\n}\n\nfunction parseArgs(argv: string[]): {\n command: string;\n positional: string[];\n flags: Record<string, string | boolean>;\n} {\n const [command = 'help', ...rest] = argv;\n const positional: string[] = [];\n const flags: Record<string, string | boolean> = {};\n\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i];\n if (arg === '-g') {\n flags.global = true;\n } else if (arg.startsWith('--')) {\n const key = arg.slice(2);\n const next = rest[i + 1];\n if (next && !next.startsWith('-')) {\n flags[key] = next;\n i++;\n } else {\n flags[key] = true;\n }\n } else {\n positional.push(arg);\n }\n }\n\n return { command, positional, flags };\n}\n\nfunction main(): void {\n const { command, positional, flags } = parseArgs(process.argv.slice(2));\n\n try {\n switch (command) {\n case 'generate':\n case 'gen': {\n const name = positional[0];\n if (!name) {\n console.error('Missing module name. Usage: prismakit generate <name>');\n process.exitCode = 1;\n return;\n }\n runGenerate({\n name,\n cache: !!flags.cache,\n full: !!flags.full,\n helpers: !!flags.helpers,\n dto: !!flags.dto,\n route:\n typeof flags.route === 'string' ? flags.route : undefined,\n prismaImport:\n typeof flags['prisma-import'] === 'string'\n ? flags['prisma-import']\n : undefined,\n dryRun: !!flags['dry-run'],\n });\n break;\n }\n case 'validate': {\n runValidate({\n assert: !flags['no-assert'],\n schemaPath:\n typeof flags.schema === 'string' ? flags.schema : undefined,\n autoRegisterModels: !!flags['auto-register'],\n });\n break;\n }\n case 'skills':\n case 'skill': {\n const skillFlag = flags.skill;\n runSkills({\n global: !!flags.global,\n withRules: !!flags['with-rules'],\n dryRun: !!flags['dry-run'],\n list: !!flags.list,\n projectRoot:\n typeof flags.project === 'string' ? flags.project : undefined,\n skill:\n typeof skillFlag === 'string'\n ? skillFlag.split(',').map((s) => s.trim()).filter(Boolean)\n : undefined,\n });\n break;\n }\n case 'help':\n case '--help':\n case '-h':\n printHelp();\n break;\n default:\n console.error(`Unknown command: ${command}`);\n printHelp();\n process.exitCode = 1;\n }\n } catch (err) {\n console.error((err as Error).message);\n process.exitCode = 1;\n }\n}\n\nmain();\n"],"mappings":"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","names":["fs","path"]}
1
+ {"version":3,"sources":["../src/commands/generate.ts","../src/naming.ts","../src/templates.ts","../src/commands/validate.ts","../src/commands/skills.ts","../src/bin.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { resolveNames } from '../naming';\nimport { renderModuleFiles } from '../templates';\n\nexport type GenerateCommandOptions = {\n name: string;\n cache?: boolean;\n route?: string;\n cwd?: string;\n dryRun?: boolean;\n /** Emit full Nest module (controller/service/types). Default: repo-only. */\n full?: boolean;\n /** Emit validate + mapper helpers. */\n helpers?: boolean;\n /** Emit class-validator DTOs. */\n dto?: boolean;\n /** Prisma client import path (default `@prisma/client`). */\n prismaImport?: string;\n};\n\nexport function runGenerate(options: GenerateCommandOptions): void {\n const cwd = options.cwd ?? process.cwd();\n const names = resolveNames(options.name, options.route);\n const full = !!options.full;\n const files = renderModuleFiles({\n names,\n cacheEnabled: !!options.cache,\n full,\n helpers: !!options.helpers,\n dto: !!options.dto,\n prismaImport: options.prismaImport,\n });\n\n for (const file of files) {\n const fullPath = path.join(cwd, file.relativePath);\n if (options.dryRun) {\n console.log(`[dry-run] would write ${file.relativePath}`);\n continue;\n }\n if (fs.existsSync(fullPath)) {\n console.warn(`skip (exists): ${file.relativePath}`);\n continue;\n }\n fs.mkdirSync(path.dirname(fullPath), { recursive: true });\n const content = file.content.endsWith('\\n')\n ? file.content\n : `${file.content}\\n`;\n fs.writeFileSync(fullPath, content, 'utf-8');\n console.log(`created ${file.relativePath}`);\n }\n\n if (full) {\n console.log(\n `\\nScaffolded module \"${names.kebab}\". Register ${names.pascal}Module in app.module.ts.`,\n );\n } else {\n console.log(\n `\\nScaffolded repository \"${names.pascal}Repository\". Register it in your feature module providers.`,\n );\n }\n}\n","export interface ModuleNames {\n kebab: string;\n camel: string;\n pascal: string;\n repoModel: string;\n route: string;\n}\n\nconst KEBAB_NAME_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;\n\nexport function assertKebabName(name: string): void {\n if (!KEBAB_NAME_RE.test(name)) {\n throw new Error(\n `Invalid module name \"${name}\". Use kebab-case (e.g. product, blog-post).`,\n );\n }\n}\n\nfunction kebabToPascal(kebab: string): string {\n return kebab\n .split('-')\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\nfunction kebabToCamel(kebab: string): string {\n const pascal = kebabToPascal(kebab);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\nexport function resolveNames(kebab: string, route?: string): ModuleNames {\n assertKebabName(kebab);\n const pascal = kebabToPascal(kebab);\n const camel = kebabToCamel(kebab);\n return {\n kebab,\n camel,\n pascal,\n repoModel: camel,\n route: route ?? kebab,\n };\n}\n","import type { ModuleNames } from './naming';\n\nexport type GenerateOptions = {\n names: ModuleNames;\n cacheEnabled: boolean;\n /** When false (default), only emit the repository file. */\n full?: boolean;\n /** Emit validate + mapper helpers. */\n helpers?: boolean;\n /** Emit class-validator DTOs with @ApiProperty. */\n dto?: boolean;\n /** Prisma client import path (default `@prisma/client`). */\n prismaImport?: string;\n};\n\nfunction apply(template: string, names: ModuleNames, extras: Record<string, string>): string {\n const replacements: Record<string, string> = {\n '{{pascal}}': names.pascal,\n '{{camel}}': names.camel,\n '{{kebab}}': names.kebab,\n '{{route}}': names.route,\n '{{repoModel}}': names.repoModel,\n ...extras,\n };\n let result = template;\n for (const [key, value] of Object.entries(replacements)) {\n result = result.split(key).join(value);\n }\n return result;\n}\n\nexport type GeneratedFile = {\n relativePath: string;\n content: string;\n};\n\nfunction renderRepository(\n names: ModuleNames,\n cacheEnabled: boolean,\n prismaImport: string,\n base: string,\n): GeneratedFile {\n const cacheBlock = cacheEnabled\n ? ` cache: {\n ttl: 86400,\n sensitiveFields: ['password'],\n defaultSetCache: true,\n },\n`\n : '';\n\n const content = apply(\n `import { createInjectableRepository } from '@prismakit/nestjs';\n\nexport class {{pascal}}Repository extends createInjectableRepository({\n model: '{{repoModel}}',\n{{cacheBlock}}}) {}\n`,\n names,\n {\n '{{cacheBlock}}': cacheBlock,\n '{{prismaImport}}': prismaImport,\n },\n );\n\n return {\n relativePath: `${base}/repositories/${names.kebab}.repository.ts`,\n content,\n };\n}\n\nexport function renderModuleFiles(options: GenerateOptions): GeneratedFile[] {\n const { names, cacheEnabled, full = false, helpers = false, dto = false } =\n options;\n const prismaImport = options.prismaImport ?? '@prisma/client';\n const base = `src/modules/${names.kebab}`;\n\n const repository = renderRepository(names, cacheEnabled, prismaImport, base);\n\n if (!full) {\n return [repository];\n }\n\n const service = apply(\n `import { Injectable } from '@nestjs/common';\n\nimport { {{pascal}}Repository } from '../repositories/{{kebab}}.repository';\nimport { get{{pascal}}Select } from '../types/select-{{kebab}}.type';\nimport { where{{pascal}}GetManyPaginate } from '../types/where-{{kebab}}.type';\n{{dtoImport}}\n@Injectable()\nexport class {{pascal}}Service {\n constructor(private readonly {{camel}}Repository: {{pascal}}Repository) {}\n\n async handleCreate(dto: Create{{pascal}}Dto) {\n return await this.{{camel}}Repository.create({\n data: { ...dto },\n select: get{{pascal}}Select('general'),\n });\n }\n\n async handleGetById(id: string) {\n return await this.{{camel}}Repository.getThrowById({\n id,\n select: get{{pascal}}Select('general'),\n setCache: true,\n });\n }\n\n async handleGetManyPaginate(filter: Filter{{pascal}}Dto) {\n const { where } = where{{pascal}}GetManyPaginate(filter);\n return await this.{{camel}}Repository.getManyPaginate({\n where,\n select: get{{pascal}}Select('general'),\n page: filter.page,\n pageSize: filter.pageSize,\n setCache: true,\n });\n }\n\n async handleUpdateById(id: string, dto: Update{{pascal}}Dto) {\n return await this.{{camel}}Repository.updateById({\n id,\n data: { ...dto },\n select: get{{pascal}}Select('general'),\n });\n }\n\n async handleDeleteById(id: string) {\n return await this.{{camel}}Repository.deleteById({\n id,\n select: get{{pascal}}Select('minimal'),\n });\n }\n}\n`,\n names,\n {\n '{{dtoImport}}': dto\n ? `import type {\\n Create{{pascal}}Dto,\\n Update{{pascal}}Dto,\\n Filter{{pascal}}Dto,\\n} from '../dto/{{kebab}}.dto';\\n`\n : `type Create{{pascal}}Dto = Record<string, unknown>;\\ntype Update{{pascal}}Dto = Record<string, unknown>;\\ntype Filter{{pascal}}Dto = { page?: number; pageSize?: number; q?: string };\\n`,\n },\n );\n\n const controller = apply(\n `import {\n Body,\n Controller,\n Delete,\n Get,\n HttpStatus,\n Param,\n Patch,\n Post,\n Query,\n Res,\n} from '@nestjs/common';\nimport type { Response } from 'express';\n\nimport { {{pascal}}Service } from '../services/{{kebab}}.service';\n{{dtoImport}}\n@Controller('{{route}}')\nexport class {{pascal}}Controller {\n constructor(private readonly {{camel}}Service: {{pascal}}Service) {}\n\n @Post()\n async create(@Body() dto: Create{{pascal}}Dto, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleCreate(dto);\n return res.status(HttpStatus.CREATED).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Get()\n async getMany(@Query() filter: Filter{{pascal}}Dto, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleGetManyPaginate(filter);\n return res.status(HttpStatus.OK).json({ data: result.data, meta: result.meta });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Get(':id')\n async getById(@Param('id') id: string, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleGetById(id);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Patch(':id')\n async update(\n @Param('id') id: string,\n @Body() dto: Update{{pascal}}Dto,\n @Res() res: Response,\n ) {\n try {\n const result = await this.{{camel}}Service.handleUpdateById(id, dto);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Delete(':id')\n async delete(@Param('id') id: string, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleDeleteById(id);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n}\n`,\n names,\n {\n '{{dtoImport}}': dto\n ? `import type {\\n Create{{pascal}}Dto,\\n Update{{pascal}}Dto,\\n Filter{{pascal}}Dto,\\n} from '../dto/{{kebab}}.dto';\\n`\n : `type Create{{pascal}}Dto = Record<string, unknown>;\\ntype Update{{pascal}}Dto = Record<string, unknown>;\\ntype Filter{{pascal}}Dto = { page?: number; pageSize?: number; q?: string };\\n`,\n },\n );\n\n const moduleFile = apply(\n `import { Module } from '@nestjs/common';\n\nimport { {{pascal}}Controller } from './controllers/{{kebab}}.controller';\nimport { {{pascal}}Service } from './services/{{kebab}}.service';\nimport { {{pascal}}Repository } from './repositories/{{kebab}}.repository';\n{{helpersImport}}\n@Module({\n controllers: [{{pascal}}Controller],\n providers: [{{pascal}}Service, {{pascal}}Repository{{helpersProviders}}],\n exports: [{{pascal}}Service, {{pascal}}Repository],\n})\nexport class {{pascal}}Module {}\n`,\n names,\n {\n '{{helpersImport}}': helpers\n ? `import { {{pascal}}ValidateHelper } from './helpers/{{kebab}}-validate.helper';\\nimport { {{pascal}}MapperHelper } from './helpers/{{kebab}}-mapper.helper';\\n`\n : '',\n '{{helpersProviders}}': helpers\n ? `, {{pascal}}ValidateHelper, {{pascal}}MapperHelper`\n : '',\n },\n );\n\n const select = apply(\n `import { Prisma } from '{{prismaImport}}';\n\ntype {{pascal}}SelectPresetKey = keyof typeof {{camel}}SelectPresets;\n\nexport function get{{pascal}}Select<K extends {{pascal}}SelectPresetKey>(key: K) {\n return {{camel}}SelectPresets[key];\n}\n\nexport const {{camel}}SelectPresets = {\n minimal: {\n id: true,\n } satisfies Prisma.{{pascal}}Select,\n\n general: {\n id: true,\n } satisfies Prisma.{{pascal}}Select,\n};\n`,\n names,\n { '{{prismaImport}}': prismaImport },\n );\n\n const where = apply(\n `import { Prisma } from '{{prismaImport}}';\n\nexport function where{{pascal}}GetManyPaginate(filter: {\n q?: string;\n}): {\n where: Prisma.{{pascal}}WhereInput;\n} {\n const { q } = filter;\n const where: Prisma.{{pascal}}WhereInput = {\n ...(q ? { /* add searchable fields */ } : {}),\n };\n return { where };\n}\n`,\n names,\n { '{{prismaImport}}': prismaImport },\n );\n\n const files: GeneratedFile[] = [\n { relativePath: `${base}/${names.kebab}.module.ts`, content: moduleFile },\n {\n relativePath: `${base}/controllers/${names.kebab}.controller.ts`,\n content: controller,\n },\n {\n relativePath: `${base}/services/${names.kebab}.service.ts`,\n content: service,\n },\n repository,\n {\n relativePath: `${base}/types/select-${names.kebab}.type.ts`,\n content: select,\n },\n {\n relativePath: `${base}/types/where-${names.kebab}.type.ts`,\n content: where,\n },\n ];\n\n if (dto) {\n files.push({\n relativePath: `${base}/dto/${names.kebab}.dto.ts`,\n content: apply(\n `import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';\nimport { IsOptional, IsString } from 'class-validator';\n\nexport class Create{{pascal}}Dto {\n @ApiProperty({ example: 'name' })\n @IsString()\n name!: string;\n}\n\nexport class Update{{pascal}}Dto {\n @ApiPropertyOptional({ example: 'name' })\n @IsOptional()\n @IsString()\n name?: string;\n}\n\nexport class Filter{{pascal}}Dto {\n @ApiPropertyOptional()\n @IsOptional()\n page?: number;\n\n @ApiPropertyOptional()\n @IsOptional()\n pageSize?: number;\n\n @ApiPropertyOptional()\n @IsOptional()\n @IsString()\n q?: string;\n}\n`,\n names,\n {},\n ),\n });\n }\n\n if (helpers) {\n files.push(\n {\n relativePath: `${base}/helpers/${names.kebab}-validate.helper.ts`,\n content: apply(\n `import { Injectable } from '@nestjs/common';\n\nimport { {{pascal}}Repository } from '../repositories/{{kebab}}.repository';\nimport { get{{pascal}}Select } from '../types/select-{{kebab}}.type';\n\n@Injectable()\nexport class {{pascal}}ValidateHelper {\n constructor(private readonly {{camel}}Repository: {{pascal}}Repository) {}\n\n async assertExists(id: string) {\n return this.{{camel}}Repository.getThrowById({\n id,\n select: get{{pascal}}Select('minimal'),\n });\n }\n}\n`,\n names,\n {},\n ),\n },\n {\n relativePath: `${base}/helpers/${names.kebab}-mapper.helper.ts`,\n content: apply(\n `import { Injectable } from '@nestjs/common';\n\n@Injectable()\nexport class {{pascal}}MapperHelper {\n toResponse(entity: Record<string, unknown>) {\n return entity;\n }\n}\n`,\n names,\n {},\n ),\n },\n );\n }\n\n return files;\n}\n","import {\n assertSelectComposeValid,\n validateSelectCompose,\n} from '@prismakit/core';\n\nexport type ValidateCommandOptions = {\n cwd?: string;\n assert?: boolean;\n schemaPath?: string;\n autoRegisterModels?: boolean;\n};\n\n/**\n * Run select-compose validation from @prismakit/core.\n */\nexport function runValidate(options: ValidateCommandOptions = {}): void {\n const cwd = options.cwd ?? process.cwd();\n const validateOptions = {\n schemaPath: options.schemaPath,\n autoRegisterModels: options.autoRegisterModels,\n };\n\n if (options.assert !== false) {\n try {\n assertSelectComposeValid(cwd, validateOptions);\n console.log('Select compose validation passed.');\n } catch (err) {\n console.error((err as Error).message);\n process.exitCode = 1;\n }\n return;\n }\n\n const issues = validateSelectCompose(cwd, validateOptions);\n if (issues.length === 0) {\n console.log('Select compose validation passed.');\n return;\n }\n\n for (const issue of issues) {\n console.error(` - ${issue.file}: ${issue.message}`);\n }\n process.exitCode = 1;\n}\n","import * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst SKILL_NAMES = ['prismakit', 'prismakit-nestjs'] as const;\nconst RULE_NAME = 'data-access.mdc';\n\nexport type SkillsCommandOptions = {\n cwd?: string;\n /** Install into ~/.cursor/skills (all projects). Default: <cwd>/.cursor/skills */\n global?: boolean;\n /** Override project root when not using --global. */\n projectRoot?: string;\n /** Subset of skill folder names. Default: both. */\n skill?: string[];\n /** Also copy templates/cursor-rules/data-access.mdc into .cursor/rules. */\n withRules?: boolean;\n dryRun?: boolean;\n list?: boolean;\n /** Test override: directory that contains prismakit/ and prismakit-nestjs/. */\n skillsRoot?: string;\n /** Test override: path to data-access.mdc. */\n rulesPath?: string;\n};\n\nexport type SkillsInstallResult = {\n dest: string;\n installed: string[];\n ruleDest?: string;\n};\n\nfunction isSkillsRoot(dir: string): boolean {\n return fs.existsSync(path.join(dir, 'prismakit', 'SKILL.md'));\n}\n\n/**\n * Walk up from the CLI entrypoint looking for bundled `skills/` (published\n * package) or the monorepo `skills/` directory.\n */\nexport function findSkillsRoot(startDir: string): string | undefined {\n let dir = path.resolve(startDir);\n for (let i = 0; i < 10; i++) {\n for (const candidate of [\n path.join(dir, 'skills'),\n dir,\n path.join(dir, 'templates', 'cursor-skills'),\n ]) {\n if (isSkillsRoot(candidate)) return candidate;\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return undefined;\n}\n\nexport function findRulesPath(skillsRoot: string): string | undefined {\n const candidates = [\n path.join(skillsRoot, '..', 'templates', 'cursor-rules', RULE_NAME),\n path.join(skillsRoot, '..', 'rules', RULE_NAME),\n path.join(path.dirname(skillsRoot), 'rules', RULE_NAME),\n ];\n return candidates.find((p) => fs.existsSync(p));\n}\n\nfunction copyDir(src: string, dest: string): void {\n fs.mkdirSync(dest, { recursive: true });\n for (const entry of fs.readdirSync(src, { withFileTypes: true })) {\n if (entry.name === '.git') continue;\n const from = path.join(src, entry.name);\n const to = path.join(dest, entry.name);\n if (entry.isDirectory()) {\n copyDir(from, to);\n } else if (entry.isFile()) {\n fs.copyFileSync(from, to);\n }\n }\n}\n\nfunction resolveHome(): string {\n return process.env.HOME || os.homedir();\n}\n\nfunction assertNotBuiltinSkills(dest: string): void {\n const forbidden = path.join(resolveHome(), '.cursor', 'skills-cursor');\n const resolved = path.resolve(dest);\n if (resolved === forbidden || resolved.startsWith(`${forbidden}${path.sep}`)) {\n throw new Error(\n `Refusing to install into ${resolved} (reserved for Cursor built-ins).`,\n );\n }\n}\n\nfunction cliStartDir(): string {\n try {\n const url = import.meta.url;\n if (typeof url === 'string' && url.length > 0) {\n return path.dirname(fileURLToPath(url));\n }\n } catch {\n // CJS bundle has an empty import.meta\n }\n return process.cwd();\n}\n\nexport function runSkills(options: SkillsCommandOptions = {}): SkillsInstallResult {\n const skillsRoot =\n options.skillsRoot ??\n findSkillsRoot(cliStartDir()) ??\n findSkillsRoot(process.cwd());\n if (!skillsRoot) {\n throw new Error(\n 'Could not find PrismaKit skills. Reinstall @prismakit/cli or clone fikiap23/prismakit.',\n );\n }\n\n const available = SKILL_NAMES.filter((name) =>\n fs.existsSync(path.join(skillsRoot, name, 'SKILL.md')),\n );\n if (available.length === 0) {\n throw new Error(`No skills found in ${skillsRoot}`);\n }\n\n const requested = options.skill?.length\n ? options.skill.map((s) => s.trim()).filter(Boolean)\n : [...available];\n\n for (const name of requested) {\n if (!available.includes(name as (typeof SKILL_NAMES)[number])) {\n throw new Error(\n `Unknown skill \"${name}\". Available: ${available.join(', ')}`,\n );\n }\n }\n\n if (options.list) {\n console.log(`Skills in ${skillsRoot}:`);\n for (const name of available) {\n console.log(` - ${name}`);\n }\n return { dest: skillsRoot, installed: [] };\n }\n\n const cwd = options.cwd ?? process.cwd();\n const dest = options.global\n ? path.join(resolveHome(), '.cursor', 'skills')\n : path.join(\n path.resolve(options.projectRoot ?? cwd),\n '.cursor',\n 'skills',\n );\n\n assertNotBuiltinSkills(dest);\n\n if (!options.dryRun) {\n fs.mkdirSync(dest, { recursive: true });\n }\n\n const installed: string[] = [];\n for (const name of requested) {\n const src = path.join(skillsRoot, name);\n const target = path.join(dest, name);\n if (options.dryRun) {\n console.log(`[dry-run] would install ${name} -> ${target}`);\n } else {\n fs.rmSync(target, { recursive: true, force: true });\n copyDir(src, target);\n console.log(`Installed ${name} -> ${target}`);\n }\n installed.push(name);\n }\n\n let ruleDest: string | undefined;\n if (options.withRules) {\n const rulesSrc = options.rulesPath ?? findRulesPath(skillsRoot);\n if (!rulesSrc) {\n throw new Error(`Could not find ${RULE_NAME} next to skills.`);\n }\n const rulesDir = options.global\n ? path.join(resolveHome(), '.cursor', 'rules')\n : path.join(path.resolve(options.projectRoot ?? cwd), '.cursor', 'rules');\n ruleDest = path.join(rulesDir, RULE_NAME);\n if (options.dryRun) {\n console.log(`[dry-run] would install rule -> ${ruleDest}`);\n } else {\n fs.mkdirSync(rulesDir, { recursive: true });\n fs.copyFileSync(rulesSrc, ruleDest);\n console.log(`Installed rule -> ${ruleDest}`);\n }\n }\n\n const scope = options.global\n ? 'global (~/.cursor/skills)'\n : 'project (.cursor/skills)';\n if (options.dryRun) {\n console.log(`Dry-run complete (${scope}).`);\n } else if (options.global) {\n console.log(`Done. Skills are available in all Cursor projects on this machine (${scope}).`);\n } else {\n console.log(\n `Done. Commit .cursor/skills so the team shares the same agent contract (${scope}).`,\n );\n }\n\n return { dest, installed, ruleDest };\n}\n","import { runGenerate } from './commands/generate';\nimport { runValidate } from './commands/validate';\nimport { runSkills } from './commands/skills';\n\nfunction printHelp(): void {\n console.log(`prismakit — PrismaKit CLI\n\nUsage:\n prismakit generate <name> [--cache] [--full] [--helpers] [--dto] [--route <path>] [--prisma-import <path>] [--dry-run]\n prismakit validate [--schema <path>] [--auto-register] [--no-assert]\n prismakit skills [--global] [--with-rules] [--skill <name>] [--project <path>] [--list] [--dry-run]\n prismakit help\n\nBy default, generate writes only the repository file.\nPass --full for a Nest module (controller, service, types).\nPass --helpers / --dto with --full for helpers and Swagger DTOs.\n\nExamples:\n prismakit generate product --cache\n prismakit generate product --cache --full --helpers --dto --route products\n prismakit validate --auto-register\n prismakit skills\n prismakit skills --global\n prismakit skills --with-rules\n`);\n}\n\nfunction parseArgs(argv: string[]): {\n command: string;\n positional: string[];\n flags: Record<string, string | boolean>;\n} {\n const [command = 'help', ...rest] = argv;\n const positional: string[] = [];\n const flags: Record<string, string | boolean> = {};\n\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i];\n if (arg === '-g') {\n flags.global = true;\n } else if (arg.startsWith('--')) {\n const key = arg.slice(2);\n const next = rest[i + 1];\n if (next && !next.startsWith('-')) {\n flags[key] = next;\n i++;\n } else {\n flags[key] = true;\n }\n } else {\n positional.push(arg);\n }\n }\n\n return { command, positional, flags };\n}\n\nfunction main(): void {\n const { command, positional, flags } = parseArgs(process.argv.slice(2));\n\n try {\n switch (command) {\n case 'generate':\n case 'gen': {\n const name = positional[0];\n if (!name) {\n console.error('Missing module name. Usage: prismakit generate <name>');\n process.exitCode = 1;\n return;\n }\n runGenerate({\n name,\n cache: !!flags.cache,\n full: !!flags.full,\n helpers: !!flags.helpers,\n dto: !!flags.dto,\n route:\n typeof flags.route === 'string' ? flags.route : undefined,\n prismaImport:\n typeof flags['prisma-import'] === 'string'\n ? flags['prisma-import']\n : undefined,\n dryRun: !!flags['dry-run'],\n });\n break;\n }\n case 'validate': {\n runValidate({\n assert: !flags['no-assert'],\n schemaPath:\n typeof flags.schema === 'string' ? flags.schema : undefined,\n autoRegisterModels: !!flags['auto-register'],\n });\n break;\n }\n case 'skills':\n case 'skill': {\n const skillFlag = flags.skill;\n runSkills({\n global: !!flags.global,\n withRules: !!flags['with-rules'],\n dryRun: !!flags['dry-run'],\n list: !!flags.list,\n projectRoot:\n typeof flags.project === 'string' ? flags.project : undefined,\n skill:\n typeof skillFlag === 'string'\n ? skillFlag.split(',').map((s) => s.trim()).filter(Boolean)\n : undefined,\n });\n break;\n }\n case 'help':\n case '--help':\n case '-h':\n printHelp();\n break;\n default:\n console.error(`Unknown command: ${command}`);\n printHelp();\n process.exitCode = 1;\n }\n } catch (err) {\n console.error((err as Error).message);\n process.exitCode = 1;\n }\n}\n\nmain();\n"],"mappings":"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","names":["fs","path"]}
package/dist/bin.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  runGenerate,
4
4
  runSkills,
5
5
  runValidate
6
- } from "./chunk-7D4JHNVX.js";
6
+ } from "./chunk-MY5VVL5C.js";
7
7
 
8
8
  // src/bin.ts
9
9
  function printHelp() {
@@ -51,12 +51,10 @@ function renderRepository(names, cacheEnabled, prismaImport, base) {
51
51
  },
52
52
  ` : "";
53
53
  const content = apply(
54
- `import { Prisma } from '{{prismaImport}}';
55
- import { createInjectableRepository } from '@prismakit/nestjs';
54
+ `import { createInjectableRepository } from '@prismakit/nestjs';
56
55
 
57
56
  export class {{pascal}}Repository extends createInjectableRepository({
58
57
  model: '{{repoModel}}',
59
- scalarFields: Prisma.{{pascal}}ScalarFieldEnum,
60
58
  {{cacheBlock}}}) {}
61
59
  `,
62
60
  names,
@@ -651,4 +649,4 @@ export {
651
649
  findSkillsRoot,
652
650
  runSkills
653
651
  };
654
- //# sourceMappingURL=chunk-7D4JHNVX.js.map
652
+ //# sourceMappingURL=chunk-MY5VVL5C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/naming.ts","../src/templates.ts","../src/commands/generate.ts","../src/commands/validate.ts","../src/commands/skills.ts"],"sourcesContent":["export interface ModuleNames {\n kebab: string;\n camel: string;\n pascal: string;\n repoModel: string;\n route: string;\n}\n\nconst KEBAB_NAME_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;\n\nexport function assertKebabName(name: string): void {\n if (!KEBAB_NAME_RE.test(name)) {\n throw new Error(\n `Invalid module name \"${name}\". Use kebab-case (e.g. product, blog-post).`,\n );\n }\n}\n\nfunction kebabToPascal(kebab: string): string {\n return kebab\n .split('-')\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\nfunction kebabToCamel(kebab: string): string {\n const pascal = kebabToPascal(kebab);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\nexport function resolveNames(kebab: string, route?: string): ModuleNames {\n assertKebabName(kebab);\n const pascal = kebabToPascal(kebab);\n const camel = kebabToCamel(kebab);\n return {\n kebab,\n camel,\n pascal,\n repoModel: camel,\n route: route ?? kebab,\n };\n}\n","import type { ModuleNames } from './naming';\n\nexport type GenerateOptions = {\n names: ModuleNames;\n cacheEnabled: boolean;\n /** When false (default), only emit the repository file. */\n full?: boolean;\n /** Emit validate + mapper helpers. */\n helpers?: boolean;\n /** Emit class-validator DTOs with @ApiProperty. */\n dto?: boolean;\n /** Prisma client import path (default `@prisma/client`). */\n prismaImport?: string;\n};\n\nfunction apply(template: string, names: ModuleNames, extras: Record<string, string>): string {\n const replacements: Record<string, string> = {\n '{{pascal}}': names.pascal,\n '{{camel}}': names.camel,\n '{{kebab}}': names.kebab,\n '{{route}}': names.route,\n '{{repoModel}}': names.repoModel,\n ...extras,\n };\n let result = template;\n for (const [key, value] of Object.entries(replacements)) {\n result = result.split(key).join(value);\n }\n return result;\n}\n\nexport type GeneratedFile = {\n relativePath: string;\n content: string;\n};\n\nfunction renderRepository(\n names: ModuleNames,\n cacheEnabled: boolean,\n prismaImport: string,\n base: string,\n): GeneratedFile {\n const cacheBlock = cacheEnabled\n ? ` cache: {\n ttl: 86400,\n sensitiveFields: ['password'],\n defaultSetCache: true,\n },\n`\n : '';\n\n const content = apply(\n `import { createInjectableRepository } from '@prismakit/nestjs';\n\nexport class {{pascal}}Repository extends createInjectableRepository({\n model: '{{repoModel}}',\n{{cacheBlock}}}) {}\n`,\n names,\n {\n '{{cacheBlock}}': cacheBlock,\n '{{prismaImport}}': prismaImport,\n },\n );\n\n return {\n relativePath: `${base}/repositories/${names.kebab}.repository.ts`,\n content,\n };\n}\n\nexport function renderModuleFiles(options: GenerateOptions): GeneratedFile[] {\n const { names, cacheEnabled, full = false, helpers = false, dto = false } =\n options;\n const prismaImport = options.prismaImport ?? '@prisma/client';\n const base = `src/modules/${names.kebab}`;\n\n const repository = renderRepository(names, cacheEnabled, prismaImport, base);\n\n if (!full) {\n return [repository];\n }\n\n const service = apply(\n `import { Injectable } from '@nestjs/common';\n\nimport { {{pascal}}Repository } from '../repositories/{{kebab}}.repository';\nimport { get{{pascal}}Select } from '../types/select-{{kebab}}.type';\nimport { where{{pascal}}GetManyPaginate } from '../types/where-{{kebab}}.type';\n{{dtoImport}}\n@Injectable()\nexport class {{pascal}}Service {\n constructor(private readonly {{camel}}Repository: {{pascal}}Repository) {}\n\n async handleCreate(dto: Create{{pascal}}Dto) {\n return await this.{{camel}}Repository.create({\n data: { ...dto },\n select: get{{pascal}}Select('general'),\n });\n }\n\n async handleGetById(id: string) {\n return await this.{{camel}}Repository.getThrowById({\n id,\n select: get{{pascal}}Select('general'),\n setCache: true,\n });\n }\n\n async handleGetManyPaginate(filter: Filter{{pascal}}Dto) {\n const { where } = where{{pascal}}GetManyPaginate(filter);\n return await this.{{camel}}Repository.getManyPaginate({\n where,\n select: get{{pascal}}Select('general'),\n page: filter.page,\n pageSize: filter.pageSize,\n setCache: true,\n });\n }\n\n async handleUpdateById(id: string, dto: Update{{pascal}}Dto) {\n return await this.{{camel}}Repository.updateById({\n id,\n data: { ...dto },\n select: get{{pascal}}Select('general'),\n });\n }\n\n async handleDeleteById(id: string) {\n return await this.{{camel}}Repository.deleteById({\n id,\n select: get{{pascal}}Select('minimal'),\n });\n }\n}\n`,\n names,\n {\n '{{dtoImport}}': dto\n ? `import type {\\n Create{{pascal}}Dto,\\n Update{{pascal}}Dto,\\n Filter{{pascal}}Dto,\\n} from '../dto/{{kebab}}.dto';\\n`\n : `type Create{{pascal}}Dto = Record<string, unknown>;\\ntype Update{{pascal}}Dto = Record<string, unknown>;\\ntype Filter{{pascal}}Dto = { page?: number; pageSize?: number; q?: string };\\n`,\n },\n );\n\n const controller = apply(\n `import {\n Body,\n Controller,\n Delete,\n Get,\n HttpStatus,\n Param,\n Patch,\n Post,\n Query,\n Res,\n} from '@nestjs/common';\nimport type { Response } from 'express';\n\nimport { {{pascal}}Service } from '../services/{{kebab}}.service';\n{{dtoImport}}\n@Controller('{{route}}')\nexport class {{pascal}}Controller {\n constructor(private readonly {{camel}}Service: {{pascal}}Service) {}\n\n @Post()\n async create(@Body() dto: Create{{pascal}}Dto, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleCreate(dto);\n return res.status(HttpStatus.CREATED).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Get()\n async getMany(@Query() filter: Filter{{pascal}}Dto, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleGetManyPaginate(filter);\n return res.status(HttpStatus.OK).json({ data: result.data, meta: result.meta });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Get(':id')\n async getById(@Param('id') id: string, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleGetById(id);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Patch(':id')\n async update(\n @Param('id') id: string,\n @Body() dto: Update{{pascal}}Dto,\n @Res() res: Response,\n ) {\n try {\n const result = await this.{{camel}}Service.handleUpdateById(id, dto);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Delete(':id')\n async delete(@Param('id') id: string, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleDeleteById(id);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n}\n`,\n names,\n {\n '{{dtoImport}}': dto\n ? `import type {\\n Create{{pascal}}Dto,\\n Update{{pascal}}Dto,\\n Filter{{pascal}}Dto,\\n} from '../dto/{{kebab}}.dto';\\n`\n : `type Create{{pascal}}Dto = Record<string, unknown>;\\ntype Update{{pascal}}Dto = Record<string, unknown>;\\ntype Filter{{pascal}}Dto = { page?: number; pageSize?: number; q?: string };\\n`,\n },\n );\n\n const moduleFile = apply(\n `import { Module } from '@nestjs/common';\n\nimport { {{pascal}}Controller } from './controllers/{{kebab}}.controller';\nimport { {{pascal}}Service } from './services/{{kebab}}.service';\nimport { {{pascal}}Repository } from './repositories/{{kebab}}.repository';\n{{helpersImport}}\n@Module({\n controllers: [{{pascal}}Controller],\n providers: [{{pascal}}Service, {{pascal}}Repository{{helpersProviders}}],\n exports: [{{pascal}}Service, {{pascal}}Repository],\n})\nexport class {{pascal}}Module {}\n`,\n names,\n {\n '{{helpersImport}}': helpers\n ? `import { {{pascal}}ValidateHelper } from './helpers/{{kebab}}-validate.helper';\\nimport { {{pascal}}MapperHelper } from './helpers/{{kebab}}-mapper.helper';\\n`\n : '',\n '{{helpersProviders}}': helpers\n ? `, {{pascal}}ValidateHelper, {{pascal}}MapperHelper`\n : '',\n },\n );\n\n const select = apply(\n `import { Prisma } from '{{prismaImport}}';\n\ntype {{pascal}}SelectPresetKey = keyof typeof {{camel}}SelectPresets;\n\nexport function get{{pascal}}Select<K extends {{pascal}}SelectPresetKey>(key: K) {\n return {{camel}}SelectPresets[key];\n}\n\nexport const {{camel}}SelectPresets = {\n minimal: {\n id: true,\n } satisfies Prisma.{{pascal}}Select,\n\n general: {\n id: true,\n } satisfies Prisma.{{pascal}}Select,\n};\n`,\n names,\n { '{{prismaImport}}': prismaImport },\n );\n\n const where = apply(\n `import { Prisma } from '{{prismaImport}}';\n\nexport function where{{pascal}}GetManyPaginate(filter: {\n q?: string;\n}): {\n where: Prisma.{{pascal}}WhereInput;\n} {\n const { q } = filter;\n const where: Prisma.{{pascal}}WhereInput = {\n ...(q ? { /* add searchable fields */ } : {}),\n };\n return { where };\n}\n`,\n names,\n { '{{prismaImport}}': prismaImport },\n );\n\n const files: GeneratedFile[] = [\n { relativePath: `${base}/${names.kebab}.module.ts`, content: moduleFile },\n {\n relativePath: `${base}/controllers/${names.kebab}.controller.ts`,\n content: controller,\n },\n {\n relativePath: `${base}/services/${names.kebab}.service.ts`,\n content: service,\n },\n repository,\n {\n relativePath: `${base}/types/select-${names.kebab}.type.ts`,\n content: select,\n },\n {\n relativePath: `${base}/types/where-${names.kebab}.type.ts`,\n content: where,\n },\n ];\n\n if (dto) {\n files.push({\n relativePath: `${base}/dto/${names.kebab}.dto.ts`,\n content: apply(\n `import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';\nimport { IsOptional, IsString } from 'class-validator';\n\nexport class Create{{pascal}}Dto {\n @ApiProperty({ example: 'name' })\n @IsString()\n name!: string;\n}\n\nexport class Update{{pascal}}Dto {\n @ApiPropertyOptional({ example: 'name' })\n @IsOptional()\n @IsString()\n name?: string;\n}\n\nexport class Filter{{pascal}}Dto {\n @ApiPropertyOptional()\n @IsOptional()\n page?: number;\n\n @ApiPropertyOptional()\n @IsOptional()\n pageSize?: number;\n\n @ApiPropertyOptional()\n @IsOptional()\n @IsString()\n q?: string;\n}\n`,\n names,\n {},\n ),\n });\n }\n\n if (helpers) {\n files.push(\n {\n relativePath: `${base}/helpers/${names.kebab}-validate.helper.ts`,\n content: apply(\n `import { Injectable } from '@nestjs/common';\n\nimport { {{pascal}}Repository } from '../repositories/{{kebab}}.repository';\nimport { get{{pascal}}Select } from '../types/select-{{kebab}}.type';\n\n@Injectable()\nexport class {{pascal}}ValidateHelper {\n constructor(private readonly {{camel}}Repository: {{pascal}}Repository) {}\n\n async assertExists(id: string) {\n return this.{{camel}}Repository.getThrowById({\n id,\n select: get{{pascal}}Select('minimal'),\n });\n }\n}\n`,\n names,\n {},\n ),\n },\n {\n relativePath: `${base}/helpers/${names.kebab}-mapper.helper.ts`,\n content: apply(\n `import { Injectable } from '@nestjs/common';\n\n@Injectable()\nexport class {{pascal}}MapperHelper {\n toResponse(entity: Record<string, unknown>) {\n return entity;\n }\n}\n`,\n names,\n {},\n ),\n },\n );\n }\n\n return files;\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { resolveNames } from '../naming';\nimport { renderModuleFiles } from '../templates';\n\nexport type GenerateCommandOptions = {\n name: string;\n cache?: boolean;\n route?: string;\n cwd?: string;\n dryRun?: boolean;\n /** Emit full Nest module (controller/service/types). Default: repo-only. */\n full?: boolean;\n /** Emit validate + mapper helpers. */\n helpers?: boolean;\n /** Emit class-validator DTOs. */\n dto?: boolean;\n /** Prisma client import path (default `@prisma/client`). */\n prismaImport?: string;\n};\n\nexport function runGenerate(options: GenerateCommandOptions): void {\n const cwd = options.cwd ?? process.cwd();\n const names = resolveNames(options.name, options.route);\n const full = !!options.full;\n const files = renderModuleFiles({\n names,\n cacheEnabled: !!options.cache,\n full,\n helpers: !!options.helpers,\n dto: !!options.dto,\n prismaImport: options.prismaImport,\n });\n\n for (const file of files) {\n const fullPath = path.join(cwd, file.relativePath);\n if (options.dryRun) {\n console.log(`[dry-run] would write ${file.relativePath}`);\n continue;\n }\n if (fs.existsSync(fullPath)) {\n console.warn(`skip (exists): ${file.relativePath}`);\n continue;\n }\n fs.mkdirSync(path.dirname(fullPath), { recursive: true });\n const content = file.content.endsWith('\\n')\n ? file.content\n : `${file.content}\\n`;\n fs.writeFileSync(fullPath, content, 'utf-8');\n console.log(`created ${file.relativePath}`);\n }\n\n if (full) {\n console.log(\n `\\nScaffolded module \"${names.kebab}\". Register ${names.pascal}Module in app.module.ts.`,\n );\n } else {\n console.log(\n `\\nScaffolded repository \"${names.pascal}Repository\". Register it in your feature module providers.`,\n );\n }\n}\n","import {\n assertSelectComposeValid,\n validateSelectCompose,\n} from '@prismakit/core';\n\nexport type ValidateCommandOptions = {\n cwd?: string;\n assert?: boolean;\n schemaPath?: string;\n autoRegisterModels?: boolean;\n};\n\n/**\n * Run select-compose validation from @prismakit/core.\n */\nexport function runValidate(options: ValidateCommandOptions = {}): void {\n const cwd = options.cwd ?? process.cwd();\n const validateOptions = {\n schemaPath: options.schemaPath,\n autoRegisterModels: options.autoRegisterModels,\n };\n\n if (options.assert !== false) {\n try {\n assertSelectComposeValid(cwd, validateOptions);\n console.log('Select compose validation passed.');\n } catch (err) {\n console.error((err as Error).message);\n process.exitCode = 1;\n }\n return;\n }\n\n const issues = validateSelectCompose(cwd, validateOptions);\n if (issues.length === 0) {\n console.log('Select compose validation passed.');\n return;\n }\n\n for (const issue of issues) {\n console.error(` - ${issue.file}: ${issue.message}`);\n }\n process.exitCode = 1;\n}\n","import * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst SKILL_NAMES = ['prismakit', 'prismakit-nestjs'] as const;\nconst RULE_NAME = 'data-access.mdc';\n\nexport type SkillsCommandOptions = {\n cwd?: string;\n /** Install into ~/.cursor/skills (all projects). Default: <cwd>/.cursor/skills */\n global?: boolean;\n /** Override project root when not using --global. */\n projectRoot?: string;\n /** Subset of skill folder names. Default: both. */\n skill?: string[];\n /** Also copy templates/cursor-rules/data-access.mdc into .cursor/rules. */\n withRules?: boolean;\n dryRun?: boolean;\n list?: boolean;\n /** Test override: directory that contains prismakit/ and prismakit-nestjs/. */\n skillsRoot?: string;\n /** Test override: path to data-access.mdc. */\n rulesPath?: string;\n};\n\nexport type SkillsInstallResult = {\n dest: string;\n installed: string[];\n ruleDest?: string;\n};\n\nfunction isSkillsRoot(dir: string): boolean {\n return fs.existsSync(path.join(dir, 'prismakit', 'SKILL.md'));\n}\n\n/**\n * Walk up from the CLI entrypoint looking for bundled `skills/` (published\n * package) or the monorepo `skills/` directory.\n */\nexport function findSkillsRoot(startDir: string): string | undefined {\n let dir = path.resolve(startDir);\n for (let i = 0; i < 10; i++) {\n for (const candidate of [\n path.join(dir, 'skills'),\n dir,\n path.join(dir, 'templates', 'cursor-skills'),\n ]) {\n if (isSkillsRoot(candidate)) return candidate;\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return undefined;\n}\n\nexport function findRulesPath(skillsRoot: string): string | undefined {\n const candidates = [\n path.join(skillsRoot, '..', 'templates', 'cursor-rules', RULE_NAME),\n path.join(skillsRoot, '..', 'rules', RULE_NAME),\n path.join(path.dirname(skillsRoot), 'rules', RULE_NAME),\n ];\n return candidates.find((p) => fs.existsSync(p));\n}\n\nfunction copyDir(src: string, dest: string): void {\n fs.mkdirSync(dest, { recursive: true });\n for (const entry of fs.readdirSync(src, { withFileTypes: true })) {\n if (entry.name === '.git') continue;\n const from = path.join(src, entry.name);\n const to = path.join(dest, entry.name);\n if (entry.isDirectory()) {\n copyDir(from, to);\n } else if (entry.isFile()) {\n fs.copyFileSync(from, to);\n }\n }\n}\n\nfunction resolveHome(): string {\n return process.env.HOME || os.homedir();\n}\n\nfunction assertNotBuiltinSkills(dest: string): void {\n const forbidden = path.join(resolveHome(), '.cursor', 'skills-cursor');\n const resolved = path.resolve(dest);\n if (resolved === forbidden || resolved.startsWith(`${forbidden}${path.sep}`)) {\n throw new Error(\n `Refusing to install into ${resolved} (reserved for Cursor built-ins).`,\n );\n }\n}\n\nfunction cliStartDir(): string {\n try {\n const url = import.meta.url;\n if (typeof url === 'string' && url.length > 0) {\n return path.dirname(fileURLToPath(url));\n }\n } catch {\n // CJS bundle has an empty import.meta\n }\n return process.cwd();\n}\n\nexport function runSkills(options: SkillsCommandOptions = {}): SkillsInstallResult {\n const skillsRoot =\n options.skillsRoot ??\n findSkillsRoot(cliStartDir()) ??\n findSkillsRoot(process.cwd());\n if (!skillsRoot) {\n throw new Error(\n 'Could not find PrismaKit skills. Reinstall @prismakit/cli or clone fikiap23/prismakit.',\n );\n }\n\n const available = SKILL_NAMES.filter((name) =>\n fs.existsSync(path.join(skillsRoot, name, 'SKILL.md')),\n );\n if (available.length === 0) {\n throw new Error(`No skills found in ${skillsRoot}`);\n }\n\n const requested = options.skill?.length\n ? options.skill.map((s) => s.trim()).filter(Boolean)\n : [...available];\n\n for (const name of requested) {\n if (!available.includes(name as (typeof SKILL_NAMES)[number])) {\n throw new Error(\n `Unknown skill \"${name}\". Available: ${available.join(', ')}`,\n );\n }\n }\n\n if (options.list) {\n console.log(`Skills in ${skillsRoot}:`);\n for (const name of available) {\n console.log(` - ${name}`);\n }\n return { dest: skillsRoot, installed: [] };\n }\n\n const cwd = options.cwd ?? process.cwd();\n const dest = options.global\n ? path.join(resolveHome(), '.cursor', 'skills')\n : path.join(\n path.resolve(options.projectRoot ?? cwd),\n '.cursor',\n 'skills',\n );\n\n assertNotBuiltinSkills(dest);\n\n if (!options.dryRun) {\n fs.mkdirSync(dest, { recursive: true });\n }\n\n const installed: string[] = [];\n for (const name of requested) {\n const src = path.join(skillsRoot, name);\n const target = path.join(dest, name);\n if (options.dryRun) {\n console.log(`[dry-run] would install ${name} -> ${target}`);\n } else {\n fs.rmSync(target, { recursive: true, force: true });\n copyDir(src, target);\n console.log(`Installed ${name} -> ${target}`);\n }\n installed.push(name);\n }\n\n let ruleDest: string | undefined;\n if (options.withRules) {\n const rulesSrc = options.rulesPath ?? findRulesPath(skillsRoot);\n if (!rulesSrc) {\n throw new Error(`Could not find ${RULE_NAME} next to skills.`);\n }\n const rulesDir = options.global\n ? path.join(resolveHome(), '.cursor', 'rules')\n : path.join(path.resolve(options.projectRoot ?? cwd), '.cursor', 'rules');\n ruleDest = path.join(rulesDir, RULE_NAME);\n if (options.dryRun) {\n console.log(`[dry-run] would install rule -> ${ruleDest}`);\n } else {\n fs.mkdirSync(rulesDir, { recursive: true });\n fs.copyFileSync(rulesSrc, ruleDest);\n console.log(`Installed rule -> ${ruleDest}`);\n }\n }\n\n const scope = options.global\n ? 'global (~/.cursor/skills)'\n : 'project (.cursor/skills)';\n if (options.dryRun) {\n console.log(`Dry-run complete (${scope}).`);\n } else if (options.global) {\n console.log(`Done. Skills are available in all Cursor projects on this machine (${scope}).`);\n } else {\n console.log(\n `Done. Commit .cursor/skills so the team shares the same agent contract (${scope}).`,\n );\n }\n\n return { dest, installed, ruleDest };\n}\n"],"mappings":"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","names":["fs","path"]}
package/dist/index.cjs CHANGED
@@ -97,12 +97,10 @@ function renderRepository(names, cacheEnabled, prismaImport, base) {
97
97
  },
98
98
  ` : "";
99
99
  const content = apply(
100
- `import { Prisma } from '{{prismaImport}}';
101
- import { createInjectableRepository } from '@prismakit/nestjs';
100
+ `import { createInjectableRepository } from '@prismakit/nestjs';
102
101
 
103
102
  export class {{pascal}}Repository extends createInjectableRepository({
104
103
  model: '{{repoModel}}',
105
- scalarFields: Prisma.{{pascal}}ScalarFieldEnum,
106
104
  {{cacheBlock}}}) {}
107
105
  `,
108
106
  names,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands/generate.ts","../src/naming.ts","../src/templates.ts","../src/commands/validate.ts","../src/commands/skills.ts"],"sourcesContent":["export { runGenerate, type GenerateCommandOptions } from './commands/generate';\nexport { runValidate, type ValidateCommandOptions } from './commands/validate';\nexport {\n runSkills,\n findSkillsRoot,\n type SkillsCommandOptions,\n type SkillsInstallResult,\n} from './commands/skills';\nexport { resolveNames, assertKebabName, type ModuleNames } from './naming';\nexport { renderModuleFiles, type GeneratedFile, type GenerateOptions } from './templates';\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { resolveNames } from '../naming';\nimport { renderModuleFiles } from '../templates';\n\nexport type GenerateCommandOptions = {\n name: string;\n cache?: boolean;\n route?: string;\n cwd?: string;\n dryRun?: boolean;\n /** Emit full Nest module (controller/service/types). Default: repo-only. */\n full?: boolean;\n /** Emit validate + mapper helpers. */\n helpers?: boolean;\n /** Emit class-validator DTOs. */\n dto?: boolean;\n /** Prisma client import path (default `@prisma/client`). */\n prismaImport?: string;\n};\n\nexport function runGenerate(options: GenerateCommandOptions): void {\n const cwd = options.cwd ?? process.cwd();\n const names = resolveNames(options.name, options.route);\n const full = !!options.full;\n const files = renderModuleFiles({\n names,\n cacheEnabled: !!options.cache,\n full,\n helpers: !!options.helpers,\n dto: !!options.dto,\n prismaImport: options.prismaImport,\n });\n\n for (const file of files) {\n const fullPath = path.join(cwd, file.relativePath);\n if (options.dryRun) {\n console.log(`[dry-run] would write ${file.relativePath}`);\n continue;\n }\n if (fs.existsSync(fullPath)) {\n console.warn(`skip (exists): ${file.relativePath}`);\n continue;\n }\n fs.mkdirSync(path.dirname(fullPath), { recursive: true });\n const content = file.content.endsWith('\\n')\n ? file.content\n : `${file.content}\\n`;\n fs.writeFileSync(fullPath, content, 'utf-8');\n console.log(`created ${file.relativePath}`);\n }\n\n if (full) {\n console.log(\n `\\nScaffolded module \"${names.kebab}\". Register ${names.pascal}Module in app.module.ts.`,\n );\n } else {\n console.log(\n `\\nScaffolded repository \"${names.pascal}Repository\". Register it in your feature module providers.`,\n );\n }\n}\n","export interface ModuleNames {\n kebab: string;\n camel: string;\n pascal: string;\n repoModel: string;\n route: string;\n}\n\nconst KEBAB_NAME_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;\n\nexport function assertKebabName(name: string): void {\n if (!KEBAB_NAME_RE.test(name)) {\n throw new Error(\n `Invalid module name \"${name}\". Use kebab-case (e.g. product, blog-post).`,\n );\n }\n}\n\nfunction kebabToPascal(kebab: string): string {\n return kebab\n .split('-')\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\nfunction kebabToCamel(kebab: string): string {\n const pascal = kebabToPascal(kebab);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\nexport function resolveNames(kebab: string, route?: string): ModuleNames {\n assertKebabName(kebab);\n const pascal = kebabToPascal(kebab);\n const camel = kebabToCamel(kebab);\n return {\n kebab,\n camel,\n pascal,\n repoModel: camel,\n route: route ?? kebab,\n };\n}\n","import type { ModuleNames } from './naming';\n\nexport type GenerateOptions = {\n names: ModuleNames;\n cacheEnabled: boolean;\n /** When false (default), only emit the repository file. */\n full?: boolean;\n /** Emit validate + mapper helpers. */\n helpers?: boolean;\n /** Emit class-validator DTOs with @ApiProperty. */\n dto?: boolean;\n /** Prisma client import path (default `@prisma/client`). */\n prismaImport?: string;\n};\n\nfunction apply(template: string, names: ModuleNames, extras: Record<string, string>): string {\n const replacements: Record<string, string> = {\n '{{pascal}}': names.pascal,\n '{{camel}}': names.camel,\n '{{kebab}}': names.kebab,\n '{{route}}': names.route,\n '{{repoModel}}': names.repoModel,\n ...extras,\n };\n let result = template;\n for (const [key, value] of Object.entries(replacements)) {\n result = result.split(key).join(value);\n }\n return result;\n}\n\nexport type GeneratedFile = {\n relativePath: string;\n content: string;\n};\n\nfunction renderRepository(\n names: ModuleNames,\n cacheEnabled: boolean,\n prismaImport: string,\n base: string,\n): GeneratedFile {\n const cacheBlock = cacheEnabled\n ? ` cache: {\n ttl: 86400,\n sensitiveFields: ['password'],\n defaultSetCache: true,\n },\n`\n : '';\n\n const content = apply(\n `import { Prisma } from '{{prismaImport}}';\nimport { createInjectableRepository } from '@prismakit/nestjs';\n\nexport class {{pascal}}Repository extends createInjectableRepository({\n model: '{{repoModel}}',\n scalarFields: Prisma.{{pascal}}ScalarFieldEnum,\n{{cacheBlock}}}) {}\n`,\n names,\n {\n '{{cacheBlock}}': cacheBlock,\n '{{prismaImport}}': prismaImport,\n },\n );\n\n return {\n relativePath: `${base}/repositories/${names.kebab}.repository.ts`,\n content,\n };\n}\n\nexport function renderModuleFiles(options: GenerateOptions): GeneratedFile[] {\n const { names, cacheEnabled, full = false, helpers = false, dto = false } =\n options;\n const prismaImport = options.prismaImport ?? '@prisma/client';\n const base = `src/modules/${names.kebab}`;\n\n const repository = renderRepository(names, cacheEnabled, prismaImport, base);\n\n if (!full) {\n return [repository];\n }\n\n const service = apply(\n `import { Injectable } from '@nestjs/common';\n\nimport { {{pascal}}Repository } from '../repositories/{{kebab}}.repository';\nimport { get{{pascal}}Select } from '../types/select-{{kebab}}.type';\nimport { where{{pascal}}GetManyPaginate } from '../types/where-{{kebab}}.type';\n{{dtoImport}}\n@Injectable()\nexport class {{pascal}}Service {\n constructor(private readonly {{camel}}Repository: {{pascal}}Repository) {}\n\n async handleCreate(dto: Create{{pascal}}Dto) {\n return await this.{{camel}}Repository.create({\n data: { ...dto },\n select: get{{pascal}}Select('general'),\n });\n }\n\n async handleGetById(id: string) {\n return await this.{{camel}}Repository.getThrowById({\n id,\n select: get{{pascal}}Select('general'),\n setCache: true,\n });\n }\n\n async handleGetManyPaginate(filter: Filter{{pascal}}Dto) {\n const { where } = where{{pascal}}GetManyPaginate(filter);\n return await this.{{camel}}Repository.getManyPaginate({\n where,\n select: get{{pascal}}Select('general'),\n page: filter.page,\n pageSize: filter.pageSize,\n setCache: true,\n });\n }\n\n async handleUpdateById(id: string, dto: Update{{pascal}}Dto) {\n return await this.{{camel}}Repository.updateById({\n id,\n data: { ...dto },\n select: get{{pascal}}Select('general'),\n });\n }\n\n async handleDeleteById(id: string) {\n return await this.{{camel}}Repository.deleteById({\n id,\n select: get{{pascal}}Select('minimal'),\n });\n }\n}\n`,\n names,\n {\n '{{dtoImport}}': dto\n ? `import type {\\n Create{{pascal}}Dto,\\n Update{{pascal}}Dto,\\n Filter{{pascal}}Dto,\\n} from '../dto/{{kebab}}.dto';\\n`\n : `type Create{{pascal}}Dto = Record<string, unknown>;\\ntype Update{{pascal}}Dto = Record<string, unknown>;\\ntype Filter{{pascal}}Dto = { page?: number; pageSize?: number; q?: string };\\n`,\n },\n );\n\n const controller = apply(\n `import {\n Body,\n Controller,\n Delete,\n Get,\n HttpStatus,\n Param,\n Patch,\n Post,\n Query,\n Res,\n} from '@nestjs/common';\nimport type { Response } from 'express';\n\nimport { {{pascal}}Service } from '../services/{{kebab}}.service';\n{{dtoImport}}\n@Controller('{{route}}')\nexport class {{pascal}}Controller {\n constructor(private readonly {{camel}}Service: {{pascal}}Service) {}\n\n @Post()\n async create(@Body() dto: Create{{pascal}}Dto, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleCreate(dto);\n return res.status(HttpStatus.CREATED).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Get()\n async getMany(@Query() filter: Filter{{pascal}}Dto, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleGetManyPaginate(filter);\n return res.status(HttpStatus.OK).json({ data: result.data, meta: result.meta });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Get(':id')\n async getById(@Param('id') id: string, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleGetById(id);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Patch(':id')\n async update(\n @Param('id') id: string,\n @Body() dto: Update{{pascal}}Dto,\n @Res() res: Response,\n ) {\n try {\n const result = await this.{{camel}}Service.handleUpdateById(id, dto);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Delete(':id')\n async delete(@Param('id') id: string, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleDeleteById(id);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n}\n`,\n names,\n {\n '{{dtoImport}}': dto\n ? `import type {\\n Create{{pascal}}Dto,\\n Update{{pascal}}Dto,\\n Filter{{pascal}}Dto,\\n} from '../dto/{{kebab}}.dto';\\n`\n : `type Create{{pascal}}Dto = Record<string, unknown>;\\ntype Update{{pascal}}Dto = Record<string, unknown>;\\ntype Filter{{pascal}}Dto = { page?: number; pageSize?: number; q?: string };\\n`,\n },\n );\n\n const moduleFile = apply(\n `import { Module } from '@nestjs/common';\n\nimport { {{pascal}}Controller } from './controllers/{{kebab}}.controller';\nimport { {{pascal}}Service } from './services/{{kebab}}.service';\nimport { {{pascal}}Repository } from './repositories/{{kebab}}.repository';\n{{helpersImport}}\n@Module({\n controllers: [{{pascal}}Controller],\n providers: [{{pascal}}Service, {{pascal}}Repository{{helpersProviders}}],\n exports: [{{pascal}}Service, {{pascal}}Repository],\n})\nexport class {{pascal}}Module {}\n`,\n names,\n {\n '{{helpersImport}}': helpers\n ? `import { {{pascal}}ValidateHelper } from './helpers/{{kebab}}-validate.helper';\\nimport { {{pascal}}MapperHelper } from './helpers/{{kebab}}-mapper.helper';\\n`\n : '',\n '{{helpersProviders}}': helpers\n ? `, {{pascal}}ValidateHelper, {{pascal}}MapperHelper`\n : '',\n },\n );\n\n const select = apply(\n `import { Prisma } from '{{prismaImport}}';\n\ntype {{pascal}}SelectPresetKey = keyof typeof {{camel}}SelectPresets;\n\nexport function get{{pascal}}Select<K extends {{pascal}}SelectPresetKey>(key: K) {\n return {{camel}}SelectPresets[key];\n}\n\nexport const {{camel}}SelectPresets = {\n minimal: {\n id: true,\n } satisfies Prisma.{{pascal}}Select,\n\n general: {\n id: true,\n } satisfies Prisma.{{pascal}}Select,\n};\n`,\n names,\n { '{{prismaImport}}': prismaImport },\n );\n\n const where = apply(\n `import { Prisma } from '{{prismaImport}}';\n\nexport function where{{pascal}}GetManyPaginate(filter: {\n q?: string;\n}): {\n where: Prisma.{{pascal}}WhereInput;\n} {\n const { q } = filter;\n const where: Prisma.{{pascal}}WhereInput = {\n ...(q ? { /* add searchable fields */ } : {}),\n };\n return { where };\n}\n`,\n names,\n { '{{prismaImport}}': prismaImport },\n );\n\n const files: GeneratedFile[] = [\n { relativePath: `${base}/${names.kebab}.module.ts`, content: moduleFile },\n {\n relativePath: `${base}/controllers/${names.kebab}.controller.ts`,\n content: controller,\n },\n {\n relativePath: `${base}/services/${names.kebab}.service.ts`,\n content: service,\n },\n repository,\n {\n relativePath: `${base}/types/select-${names.kebab}.type.ts`,\n content: select,\n },\n {\n relativePath: `${base}/types/where-${names.kebab}.type.ts`,\n content: where,\n },\n ];\n\n if (dto) {\n files.push({\n relativePath: `${base}/dto/${names.kebab}.dto.ts`,\n content: apply(\n `import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';\nimport { IsOptional, IsString } from 'class-validator';\n\nexport class Create{{pascal}}Dto {\n @ApiProperty({ example: 'name' })\n @IsString()\n name!: string;\n}\n\nexport class Update{{pascal}}Dto {\n @ApiPropertyOptional({ example: 'name' })\n @IsOptional()\n @IsString()\n name?: string;\n}\n\nexport class Filter{{pascal}}Dto {\n @ApiPropertyOptional()\n @IsOptional()\n page?: number;\n\n @ApiPropertyOptional()\n @IsOptional()\n pageSize?: number;\n\n @ApiPropertyOptional()\n @IsOptional()\n @IsString()\n q?: string;\n}\n`,\n names,\n {},\n ),\n });\n }\n\n if (helpers) {\n files.push(\n {\n relativePath: `${base}/helpers/${names.kebab}-validate.helper.ts`,\n content: apply(\n `import { Injectable } from '@nestjs/common';\n\nimport { {{pascal}}Repository } from '../repositories/{{kebab}}.repository';\nimport { get{{pascal}}Select } from '../types/select-{{kebab}}.type';\n\n@Injectable()\nexport class {{pascal}}ValidateHelper {\n constructor(private readonly {{camel}}Repository: {{pascal}}Repository) {}\n\n async assertExists(id: string) {\n return this.{{camel}}Repository.getThrowById({\n id,\n select: get{{pascal}}Select('minimal'),\n });\n }\n}\n`,\n names,\n {},\n ),\n },\n {\n relativePath: `${base}/helpers/${names.kebab}-mapper.helper.ts`,\n content: apply(\n `import { Injectable } from '@nestjs/common';\n\n@Injectable()\nexport class {{pascal}}MapperHelper {\n toResponse(entity: Record<string, unknown>) {\n return entity;\n }\n}\n`,\n names,\n {},\n ),\n },\n );\n }\n\n return files;\n}\n","import {\n assertSelectComposeValid,\n validateSelectCompose,\n} from '@prismakit/core';\n\nexport type ValidateCommandOptions = {\n cwd?: string;\n assert?: boolean;\n schemaPath?: string;\n autoRegisterModels?: boolean;\n};\n\n/**\n * Run select-compose validation from @prismakit/core.\n */\nexport function runValidate(options: ValidateCommandOptions = {}): void {\n const cwd = options.cwd ?? process.cwd();\n const validateOptions = {\n schemaPath: options.schemaPath,\n autoRegisterModels: options.autoRegisterModels,\n };\n\n if (options.assert !== false) {\n try {\n assertSelectComposeValid(cwd, validateOptions);\n console.log('Select compose validation passed.');\n } catch (err) {\n console.error((err as Error).message);\n process.exitCode = 1;\n }\n return;\n }\n\n const issues = validateSelectCompose(cwd, validateOptions);\n if (issues.length === 0) {\n console.log('Select compose validation passed.');\n return;\n }\n\n for (const issue of issues) {\n console.error(` - ${issue.file}: ${issue.message}`);\n }\n process.exitCode = 1;\n}\n","import * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst SKILL_NAMES = ['prismakit', 'prismakit-nestjs'] as const;\nconst RULE_NAME = 'data-access.mdc';\n\nexport type SkillsCommandOptions = {\n cwd?: string;\n /** Install into ~/.cursor/skills (all projects). Default: <cwd>/.cursor/skills */\n global?: boolean;\n /** Override project root when not using --global. */\n projectRoot?: string;\n /** Subset of skill folder names. Default: both. */\n skill?: string[];\n /** Also copy templates/cursor-rules/data-access.mdc into .cursor/rules. */\n withRules?: boolean;\n dryRun?: boolean;\n list?: boolean;\n /** Test override: directory that contains prismakit/ and prismakit-nestjs/. */\n skillsRoot?: string;\n /** Test override: path to data-access.mdc. */\n rulesPath?: string;\n};\n\nexport type SkillsInstallResult = {\n dest: string;\n installed: string[];\n ruleDest?: string;\n};\n\nfunction isSkillsRoot(dir: string): boolean {\n return fs.existsSync(path.join(dir, 'prismakit', 'SKILL.md'));\n}\n\n/**\n * Walk up from the CLI entrypoint looking for bundled `skills/` (published\n * package) or the monorepo `skills/` directory.\n */\nexport function findSkillsRoot(startDir: string): string | undefined {\n let dir = path.resolve(startDir);\n for (let i = 0; i < 10; i++) {\n for (const candidate of [\n path.join(dir, 'skills'),\n dir,\n path.join(dir, 'templates', 'cursor-skills'),\n ]) {\n if (isSkillsRoot(candidate)) return candidate;\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return undefined;\n}\n\nexport function findRulesPath(skillsRoot: string): string | undefined {\n const candidates = [\n path.join(skillsRoot, '..', 'templates', 'cursor-rules', RULE_NAME),\n path.join(skillsRoot, '..', 'rules', RULE_NAME),\n path.join(path.dirname(skillsRoot), 'rules', RULE_NAME),\n ];\n return candidates.find((p) => fs.existsSync(p));\n}\n\nfunction copyDir(src: string, dest: string): void {\n fs.mkdirSync(dest, { recursive: true });\n for (const entry of fs.readdirSync(src, { withFileTypes: true })) {\n if (entry.name === '.git') continue;\n const from = path.join(src, entry.name);\n const to = path.join(dest, entry.name);\n if (entry.isDirectory()) {\n copyDir(from, to);\n } else if (entry.isFile()) {\n fs.copyFileSync(from, to);\n }\n }\n}\n\nfunction resolveHome(): string {\n return process.env.HOME || os.homedir();\n}\n\nfunction assertNotBuiltinSkills(dest: string): void {\n const forbidden = path.join(resolveHome(), '.cursor', 'skills-cursor');\n const resolved = path.resolve(dest);\n if (resolved === forbidden || resolved.startsWith(`${forbidden}${path.sep}`)) {\n throw new Error(\n `Refusing to install into ${resolved} (reserved for Cursor built-ins).`,\n );\n }\n}\n\nfunction cliStartDir(): string {\n try {\n const url = import.meta.url;\n if (typeof url === 'string' && url.length > 0) {\n return path.dirname(fileURLToPath(url));\n }\n } catch {\n // CJS bundle has an empty import.meta\n }\n return process.cwd();\n}\n\nexport function runSkills(options: SkillsCommandOptions = {}): SkillsInstallResult {\n const skillsRoot =\n options.skillsRoot ??\n findSkillsRoot(cliStartDir()) ??\n findSkillsRoot(process.cwd());\n if (!skillsRoot) {\n throw new Error(\n 'Could not find PrismaKit skills. Reinstall @prismakit/cli or clone fikiap23/prismakit.',\n );\n }\n\n const available = SKILL_NAMES.filter((name) =>\n fs.existsSync(path.join(skillsRoot, name, 'SKILL.md')),\n );\n if (available.length === 0) {\n throw new Error(`No skills found in ${skillsRoot}`);\n }\n\n const requested = options.skill?.length\n ? options.skill.map((s) => s.trim()).filter(Boolean)\n : [...available];\n\n for (const name of requested) {\n if (!available.includes(name as (typeof SKILL_NAMES)[number])) {\n throw new Error(\n `Unknown skill \"${name}\". Available: ${available.join(', ')}`,\n );\n }\n }\n\n if (options.list) {\n console.log(`Skills in ${skillsRoot}:`);\n for (const name of available) {\n console.log(` - ${name}`);\n }\n return { dest: skillsRoot, installed: [] };\n }\n\n const cwd = options.cwd ?? process.cwd();\n const dest = options.global\n ? path.join(resolveHome(), '.cursor', 'skills')\n : path.join(\n path.resolve(options.projectRoot ?? cwd),\n '.cursor',\n 'skills',\n );\n\n assertNotBuiltinSkills(dest);\n\n if (!options.dryRun) {\n fs.mkdirSync(dest, { recursive: true });\n }\n\n const installed: string[] = [];\n for (const name of requested) {\n const src = path.join(skillsRoot, name);\n const target = path.join(dest, name);\n if (options.dryRun) {\n console.log(`[dry-run] would install ${name} -> ${target}`);\n } else {\n fs.rmSync(target, { recursive: true, force: true });\n copyDir(src, target);\n console.log(`Installed ${name} -> ${target}`);\n }\n installed.push(name);\n }\n\n let ruleDest: string | undefined;\n if (options.withRules) {\n const rulesSrc = options.rulesPath ?? findRulesPath(skillsRoot);\n if (!rulesSrc) {\n throw new Error(`Could not find ${RULE_NAME} next to skills.`);\n }\n const rulesDir = options.global\n ? path.join(resolveHome(), '.cursor', 'rules')\n : path.join(path.resolve(options.projectRoot ?? cwd), '.cursor', 'rules');\n ruleDest = path.join(rulesDir, RULE_NAME);\n if (options.dryRun) {\n console.log(`[dry-run] would install rule -> ${ruleDest}`);\n } else {\n fs.mkdirSync(rulesDir, { recursive: true });\n fs.copyFileSync(rulesSrc, ruleDest);\n console.log(`Installed rule -> ${ruleDest}`);\n }\n }\n\n const scope = options.global\n ? 'global (~/.cursor/skills)'\n : 'project (.cursor/skills)';\n if (options.dryRun) {\n console.log(`Dry-run complete (${scope}).`);\n } else if (options.global) {\n console.log(`Done. Skills are available in all Cursor projects on this machine (${scope}).`);\n } else {\n console.log(\n `Done. Commit .cursor/skills so the team shares the same agent contract (${scope}).`,\n );\n }\n\n return { dest, installed, ruleDest };\n}\n"],"mappings":"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","names":["fs","path"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/generate.ts","../src/naming.ts","../src/templates.ts","../src/commands/validate.ts","../src/commands/skills.ts"],"sourcesContent":["export { runGenerate, type GenerateCommandOptions } from './commands/generate';\nexport { runValidate, type ValidateCommandOptions } from './commands/validate';\nexport {\n runSkills,\n findSkillsRoot,\n type SkillsCommandOptions,\n type SkillsInstallResult,\n} from './commands/skills';\nexport { resolveNames, assertKebabName, type ModuleNames } from './naming';\nexport { renderModuleFiles, type GeneratedFile, type GenerateOptions } from './templates';\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { resolveNames } from '../naming';\nimport { renderModuleFiles } from '../templates';\n\nexport type GenerateCommandOptions = {\n name: string;\n cache?: boolean;\n route?: string;\n cwd?: string;\n dryRun?: boolean;\n /** Emit full Nest module (controller/service/types). Default: repo-only. */\n full?: boolean;\n /** Emit validate + mapper helpers. */\n helpers?: boolean;\n /** Emit class-validator DTOs. */\n dto?: boolean;\n /** Prisma client import path (default `@prisma/client`). */\n prismaImport?: string;\n};\n\nexport function runGenerate(options: GenerateCommandOptions): void {\n const cwd = options.cwd ?? process.cwd();\n const names = resolveNames(options.name, options.route);\n const full = !!options.full;\n const files = renderModuleFiles({\n names,\n cacheEnabled: !!options.cache,\n full,\n helpers: !!options.helpers,\n dto: !!options.dto,\n prismaImport: options.prismaImport,\n });\n\n for (const file of files) {\n const fullPath = path.join(cwd, file.relativePath);\n if (options.dryRun) {\n console.log(`[dry-run] would write ${file.relativePath}`);\n continue;\n }\n if (fs.existsSync(fullPath)) {\n console.warn(`skip (exists): ${file.relativePath}`);\n continue;\n }\n fs.mkdirSync(path.dirname(fullPath), { recursive: true });\n const content = file.content.endsWith('\\n')\n ? file.content\n : `${file.content}\\n`;\n fs.writeFileSync(fullPath, content, 'utf-8');\n console.log(`created ${file.relativePath}`);\n }\n\n if (full) {\n console.log(\n `\\nScaffolded module \"${names.kebab}\". Register ${names.pascal}Module in app.module.ts.`,\n );\n } else {\n console.log(\n `\\nScaffolded repository \"${names.pascal}Repository\". Register it in your feature module providers.`,\n );\n }\n}\n","export interface ModuleNames {\n kebab: string;\n camel: string;\n pascal: string;\n repoModel: string;\n route: string;\n}\n\nconst KEBAB_NAME_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;\n\nexport function assertKebabName(name: string): void {\n if (!KEBAB_NAME_RE.test(name)) {\n throw new Error(\n `Invalid module name \"${name}\". Use kebab-case (e.g. product, blog-post).`,\n );\n }\n}\n\nfunction kebabToPascal(kebab: string): string {\n return kebab\n .split('-')\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\nfunction kebabToCamel(kebab: string): string {\n const pascal = kebabToPascal(kebab);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\nexport function resolveNames(kebab: string, route?: string): ModuleNames {\n assertKebabName(kebab);\n const pascal = kebabToPascal(kebab);\n const camel = kebabToCamel(kebab);\n return {\n kebab,\n camel,\n pascal,\n repoModel: camel,\n route: route ?? kebab,\n };\n}\n","import type { ModuleNames } from './naming';\n\nexport type GenerateOptions = {\n names: ModuleNames;\n cacheEnabled: boolean;\n /** When false (default), only emit the repository file. */\n full?: boolean;\n /** Emit validate + mapper helpers. */\n helpers?: boolean;\n /** Emit class-validator DTOs with @ApiProperty. */\n dto?: boolean;\n /** Prisma client import path (default `@prisma/client`). */\n prismaImport?: string;\n};\n\nfunction apply(template: string, names: ModuleNames, extras: Record<string, string>): string {\n const replacements: Record<string, string> = {\n '{{pascal}}': names.pascal,\n '{{camel}}': names.camel,\n '{{kebab}}': names.kebab,\n '{{route}}': names.route,\n '{{repoModel}}': names.repoModel,\n ...extras,\n };\n let result = template;\n for (const [key, value] of Object.entries(replacements)) {\n result = result.split(key).join(value);\n }\n return result;\n}\n\nexport type GeneratedFile = {\n relativePath: string;\n content: string;\n};\n\nfunction renderRepository(\n names: ModuleNames,\n cacheEnabled: boolean,\n prismaImport: string,\n base: string,\n): GeneratedFile {\n const cacheBlock = cacheEnabled\n ? ` cache: {\n ttl: 86400,\n sensitiveFields: ['password'],\n defaultSetCache: true,\n },\n`\n : '';\n\n const content = apply(\n `import { createInjectableRepository } from '@prismakit/nestjs';\n\nexport class {{pascal}}Repository extends createInjectableRepository({\n model: '{{repoModel}}',\n{{cacheBlock}}}) {}\n`,\n names,\n {\n '{{cacheBlock}}': cacheBlock,\n '{{prismaImport}}': prismaImport,\n },\n );\n\n return {\n relativePath: `${base}/repositories/${names.kebab}.repository.ts`,\n content,\n };\n}\n\nexport function renderModuleFiles(options: GenerateOptions): GeneratedFile[] {\n const { names, cacheEnabled, full = false, helpers = false, dto = false } =\n options;\n const prismaImport = options.prismaImport ?? '@prisma/client';\n const base = `src/modules/${names.kebab}`;\n\n const repository = renderRepository(names, cacheEnabled, prismaImport, base);\n\n if (!full) {\n return [repository];\n }\n\n const service = apply(\n `import { Injectable } from '@nestjs/common';\n\nimport { {{pascal}}Repository } from '../repositories/{{kebab}}.repository';\nimport { get{{pascal}}Select } from '../types/select-{{kebab}}.type';\nimport { where{{pascal}}GetManyPaginate } from '../types/where-{{kebab}}.type';\n{{dtoImport}}\n@Injectable()\nexport class {{pascal}}Service {\n constructor(private readonly {{camel}}Repository: {{pascal}}Repository) {}\n\n async handleCreate(dto: Create{{pascal}}Dto) {\n return await this.{{camel}}Repository.create({\n data: { ...dto },\n select: get{{pascal}}Select('general'),\n });\n }\n\n async handleGetById(id: string) {\n return await this.{{camel}}Repository.getThrowById({\n id,\n select: get{{pascal}}Select('general'),\n setCache: true,\n });\n }\n\n async handleGetManyPaginate(filter: Filter{{pascal}}Dto) {\n const { where } = where{{pascal}}GetManyPaginate(filter);\n return await this.{{camel}}Repository.getManyPaginate({\n where,\n select: get{{pascal}}Select('general'),\n page: filter.page,\n pageSize: filter.pageSize,\n setCache: true,\n });\n }\n\n async handleUpdateById(id: string, dto: Update{{pascal}}Dto) {\n return await this.{{camel}}Repository.updateById({\n id,\n data: { ...dto },\n select: get{{pascal}}Select('general'),\n });\n }\n\n async handleDeleteById(id: string) {\n return await this.{{camel}}Repository.deleteById({\n id,\n select: get{{pascal}}Select('minimal'),\n });\n }\n}\n`,\n names,\n {\n '{{dtoImport}}': dto\n ? `import type {\\n Create{{pascal}}Dto,\\n Update{{pascal}}Dto,\\n Filter{{pascal}}Dto,\\n} from '../dto/{{kebab}}.dto';\\n`\n : `type Create{{pascal}}Dto = Record<string, unknown>;\\ntype Update{{pascal}}Dto = Record<string, unknown>;\\ntype Filter{{pascal}}Dto = { page?: number; pageSize?: number; q?: string };\\n`,\n },\n );\n\n const controller = apply(\n `import {\n Body,\n Controller,\n Delete,\n Get,\n HttpStatus,\n Param,\n Patch,\n Post,\n Query,\n Res,\n} from '@nestjs/common';\nimport type { Response } from 'express';\n\nimport { {{pascal}}Service } from '../services/{{kebab}}.service';\n{{dtoImport}}\n@Controller('{{route}}')\nexport class {{pascal}}Controller {\n constructor(private readonly {{camel}}Service: {{pascal}}Service) {}\n\n @Post()\n async create(@Body() dto: Create{{pascal}}Dto, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleCreate(dto);\n return res.status(HttpStatus.CREATED).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Get()\n async getMany(@Query() filter: Filter{{pascal}}Dto, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleGetManyPaginate(filter);\n return res.status(HttpStatus.OK).json({ data: result.data, meta: result.meta });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Get(':id')\n async getById(@Param('id') id: string, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleGetById(id);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Patch(':id')\n async update(\n @Param('id') id: string,\n @Body() dto: Update{{pascal}}Dto,\n @Res() res: Response,\n ) {\n try {\n const result = await this.{{camel}}Service.handleUpdateById(id, dto);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Delete(':id')\n async delete(@Param('id') id: string, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleDeleteById(id);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n}\n`,\n names,\n {\n '{{dtoImport}}': dto\n ? `import type {\\n Create{{pascal}}Dto,\\n Update{{pascal}}Dto,\\n Filter{{pascal}}Dto,\\n} from '../dto/{{kebab}}.dto';\\n`\n : `type Create{{pascal}}Dto = Record<string, unknown>;\\ntype Update{{pascal}}Dto = Record<string, unknown>;\\ntype Filter{{pascal}}Dto = { page?: number; pageSize?: number; q?: string };\\n`,\n },\n );\n\n const moduleFile = apply(\n `import { Module } from '@nestjs/common';\n\nimport { {{pascal}}Controller } from './controllers/{{kebab}}.controller';\nimport { {{pascal}}Service } from './services/{{kebab}}.service';\nimport { {{pascal}}Repository } from './repositories/{{kebab}}.repository';\n{{helpersImport}}\n@Module({\n controllers: [{{pascal}}Controller],\n providers: [{{pascal}}Service, {{pascal}}Repository{{helpersProviders}}],\n exports: [{{pascal}}Service, {{pascal}}Repository],\n})\nexport class {{pascal}}Module {}\n`,\n names,\n {\n '{{helpersImport}}': helpers\n ? `import { {{pascal}}ValidateHelper } from './helpers/{{kebab}}-validate.helper';\\nimport { {{pascal}}MapperHelper } from './helpers/{{kebab}}-mapper.helper';\\n`\n : '',\n '{{helpersProviders}}': helpers\n ? `, {{pascal}}ValidateHelper, {{pascal}}MapperHelper`\n : '',\n },\n );\n\n const select = apply(\n `import { Prisma } from '{{prismaImport}}';\n\ntype {{pascal}}SelectPresetKey = keyof typeof {{camel}}SelectPresets;\n\nexport function get{{pascal}}Select<K extends {{pascal}}SelectPresetKey>(key: K) {\n return {{camel}}SelectPresets[key];\n}\n\nexport const {{camel}}SelectPresets = {\n minimal: {\n id: true,\n } satisfies Prisma.{{pascal}}Select,\n\n general: {\n id: true,\n } satisfies Prisma.{{pascal}}Select,\n};\n`,\n names,\n { '{{prismaImport}}': prismaImport },\n );\n\n const where = apply(\n `import { Prisma } from '{{prismaImport}}';\n\nexport function where{{pascal}}GetManyPaginate(filter: {\n q?: string;\n}): {\n where: Prisma.{{pascal}}WhereInput;\n} {\n const { q } = filter;\n const where: Prisma.{{pascal}}WhereInput = {\n ...(q ? { /* add searchable fields */ } : {}),\n };\n return { where };\n}\n`,\n names,\n { '{{prismaImport}}': prismaImport },\n );\n\n const files: GeneratedFile[] = [\n { relativePath: `${base}/${names.kebab}.module.ts`, content: moduleFile },\n {\n relativePath: `${base}/controllers/${names.kebab}.controller.ts`,\n content: controller,\n },\n {\n relativePath: `${base}/services/${names.kebab}.service.ts`,\n content: service,\n },\n repository,\n {\n relativePath: `${base}/types/select-${names.kebab}.type.ts`,\n content: select,\n },\n {\n relativePath: `${base}/types/where-${names.kebab}.type.ts`,\n content: where,\n },\n ];\n\n if (dto) {\n files.push({\n relativePath: `${base}/dto/${names.kebab}.dto.ts`,\n content: apply(\n `import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';\nimport { IsOptional, IsString } from 'class-validator';\n\nexport class Create{{pascal}}Dto {\n @ApiProperty({ example: 'name' })\n @IsString()\n name!: string;\n}\n\nexport class Update{{pascal}}Dto {\n @ApiPropertyOptional({ example: 'name' })\n @IsOptional()\n @IsString()\n name?: string;\n}\n\nexport class Filter{{pascal}}Dto {\n @ApiPropertyOptional()\n @IsOptional()\n page?: number;\n\n @ApiPropertyOptional()\n @IsOptional()\n pageSize?: number;\n\n @ApiPropertyOptional()\n @IsOptional()\n @IsString()\n q?: string;\n}\n`,\n names,\n {},\n ),\n });\n }\n\n if (helpers) {\n files.push(\n {\n relativePath: `${base}/helpers/${names.kebab}-validate.helper.ts`,\n content: apply(\n `import { Injectable } from '@nestjs/common';\n\nimport { {{pascal}}Repository } from '../repositories/{{kebab}}.repository';\nimport { get{{pascal}}Select } from '../types/select-{{kebab}}.type';\n\n@Injectable()\nexport class {{pascal}}ValidateHelper {\n constructor(private readonly {{camel}}Repository: {{pascal}}Repository) {}\n\n async assertExists(id: string) {\n return this.{{camel}}Repository.getThrowById({\n id,\n select: get{{pascal}}Select('minimal'),\n });\n }\n}\n`,\n names,\n {},\n ),\n },\n {\n relativePath: `${base}/helpers/${names.kebab}-mapper.helper.ts`,\n content: apply(\n `import { Injectable } from '@nestjs/common';\n\n@Injectable()\nexport class {{pascal}}MapperHelper {\n toResponse(entity: Record<string, unknown>) {\n return entity;\n }\n}\n`,\n names,\n {},\n ),\n },\n );\n }\n\n return files;\n}\n","import {\n assertSelectComposeValid,\n validateSelectCompose,\n} from '@prismakit/core';\n\nexport type ValidateCommandOptions = {\n cwd?: string;\n assert?: boolean;\n schemaPath?: string;\n autoRegisterModels?: boolean;\n};\n\n/**\n * Run select-compose validation from @prismakit/core.\n */\nexport function runValidate(options: ValidateCommandOptions = {}): void {\n const cwd = options.cwd ?? process.cwd();\n const validateOptions = {\n schemaPath: options.schemaPath,\n autoRegisterModels: options.autoRegisterModels,\n };\n\n if (options.assert !== false) {\n try {\n assertSelectComposeValid(cwd, validateOptions);\n console.log('Select compose validation passed.');\n } catch (err) {\n console.error((err as Error).message);\n process.exitCode = 1;\n }\n return;\n }\n\n const issues = validateSelectCompose(cwd, validateOptions);\n if (issues.length === 0) {\n console.log('Select compose validation passed.');\n return;\n }\n\n for (const issue of issues) {\n console.error(` - ${issue.file}: ${issue.message}`);\n }\n process.exitCode = 1;\n}\n","import * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst SKILL_NAMES = ['prismakit', 'prismakit-nestjs'] as const;\nconst RULE_NAME = 'data-access.mdc';\n\nexport type SkillsCommandOptions = {\n cwd?: string;\n /** Install into ~/.cursor/skills (all projects). Default: <cwd>/.cursor/skills */\n global?: boolean;\n /** Override project root when not using --global. */\n projectRoot?: string;\n /** Subset of skill folder names. Default: both. */\n skill?: string[];\n /** Also copy templates/cursor-rules/data-access.mdc into .cursor/rules. */\n withRules?: boolean;\n dryRun?: boolean;\n list?: boolean;\n /** Test override: directory that contains prismakit/ and prismakit-nestjs/. */\n skillsRoot?: string;\n /** Test override: path to data-access.mdc. */\n rulesPath?: string;\n};\n\nexport type SkillsInstallResult = {\n dest: string;\n installed: string[];\n ruleDest?: string;\n};\n\nfunction isSkillsRoot(dir: string): boolean {\n return fs.existsSync(path.join(dir, 'prismakit', 'SKILL.md'));\n}\n\n/**\n * Walk up from the CLI entrypoint looking for bundled `skills/` (published\n * package) or the monorepo `skills/` directory.\n */\nexport function findSkillsRoot(startDir: string): string | undefined {\n let dir = path.resolve(startDir);\n for (let i = 0; i < 10; i++) {\n for (const candidate of [\n path.join(dir, 'skills'),\n dir,\n path.join(dir, 'templates', 'cursor-skills'),\n ]) {\n if (isSkillsRoot(candidate)) return candidate;\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return undefined;\n}\n\nexport function findRulesPath(skillsRoot: string): string | undefined {\n const candidates = [\n path.join(skillsRoot, '..', 'templates', 'cursor-rules', RULE_NAME),\n path.join(skillsRoot, '..', 'rules', RULE_NAME),\n path.join(path.dirname(skillsRoot), 'rules', RULE_NAME),\n ];\n return candidates.find((p) => fs.existsSync(p));\n}\n\nfunction copyDir(src: string, dest: string): void {\n fs.mkdirSync(dest, { recursive: true });\n for (const entry of fs.readdirSync(src, { withFileTypes: true })) {\n if (entry.name === '.git') continue;\n const from = path.join(src, entry.name);\n const to = path.join(dest, entry.name);\n if (entry.isDirectory()) {\n copyDir(from, to);\n } else if (entry.isFile()) {\n fs.copyFileSync(from, to);\n }\n }\n}\n\nfunction resolveHome(): string {\n return process.env.HOME || os.homedir();\n}\n\nfunction assertNotBuiltinSkills(dest: string): void {\n const forbidden = path.join(resolveHome(), '.cursor', 'skills-cursor');\n const resolved = path.resolve(dest);\n if (resolved === forbidden || resolved.startsWith(`${forbidden}${path.sep}`)) {\n throw new Error(\n `Refusing to install into ${resolved} (reserved for Cursor built-ins).`,\n );\n }\n}\n\nfunction cliStartDir(): string {\n try {\n const url = import.meta.url;\n if (typeof url === 'string' && url.length > 0) {\n return path.dirname(fileURLToPath(url));\n }\n } catch {\n // CJS bundle has an empty import.meta\n }\n return process.cwd();\n}\n\nexport function runSkills(options: SkillsCommandOptions = {}): SkillsInstallResult {\n const skillsRoot =\n options.skillsRoot ??\n findSkillsRoot(cliStartDir()) ??\n findSkillsRoot(process.cwd());\n if (!skillsRoot) {\n throw new Error(\n 'Could not find PrismaKit skills. Reinstall @prismakit/cli or clone fikiap23/prismakit.',\n );\n }\n\n const available = SKILL_NAMES.filter((name) =>\n fs.existsSync(path.join(skillsRoot, name, 'SKILL.md')),\n );\n if (available.length === 0) {\n throw new Error(`No skills found in ${skillsRoot}`);\n }\n\n const requested = options.skill?.length\n ? options.skill.map((s) => s.trim()).filter(Boolean)\n : [...available];\n\n for (const name of requested) {\n if (!available.includes(name as (typeof SKILL_NAMES)[number])) {\n throw new Error(\n `Unknown skill \"${name}\". Available: ${available.join(', ')}`,\n );\n }\n }\n\n if (options.list) {\n console.log(`Skills in ${skillsRoot}:`);\n for (const name of available) {\n console.log(` - ${name}`);\n }\n return { dest: skillsRoot, installed: [] };\n }\n\n const cwd = options.cwd ?? process.cwd();\n const dest = options.global\n ? path.join(resolveHome(), '.cursor', 'skills')\n : path.join(\n path.resolve(options.projectRoot ?? cwd),\n '.cursor',\n 'skills',\n );\n\n assertNotBuiltinSkills(dest);\n\n if (!options.dryRun) {\n fs.mkdirSync(dest, { recursive: true });\n }\n\n const installed: string[] = [];\n for (const name of requested) {\n const src = path.join(skillsRoot, name);\n const target = path.join(dest, name);\n if (options.dryRun) {\n console.log(`[dry-run] would install ${name} -> ${target}`);\n } else {\n fs.rmSync(target, { recursive: true, force: true });\n copyDir(src, target);\n console.log(`Installed ${name} -> ${target}`);\n }\n installed.push(name);\n }\n\n let ruleDest: string | undefined;\n if (options.withRules) {\n const rulesSrc = options.rulesPath ?? findRulesPath(skillsRoot);\n if (!rulesSrc) {\n throw new Error(`Could not find ${RULE_NAME} next to skills.`);\n }\n const rulesDir = options.global\n ? path.join(resolveHome(), '.cursor', 'rules')\n : path.join(path.resolve(options.projectRoot ?? cwd), '.cursor', 'rules');\n ruleDest = path.join(rulesDir, RULE_NAME);\n if (options.dryRun) {\n console.log(`[dry-run] would install rule -> ${ruleDest}`);\n } else {\n fs.mkdirSync(rulesDir, { recursive: true });\n fs.copyFileSync(rulesSrc, ruleDest);\n console.log(`Installed rule -> ${ruleDest}`);\n }\n }\n\n const scope = options.global\n ? 'global (~/.cursor/skills)'\n : 'project (.cursor/skills)';\n if (options.dryRun) {\n console.log(`Dry-run complete (${scope}).`);\n } else if (options.global) {\n console.log(`Done. Skills are available in all Cursor projects on this machine (${scope}).`);\n } else {\n console.log(\n `Done. Commit .cursor/skills so the team shares the same agent contract (${scope}).`,\n );\n }\n\n return { dest, installed, ruleDest };\n}\n"],"mappings":"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","names":["fs","path"]}
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  runGenerate,
7
7
  runSkills,
8
8
  runValidate
9
- } from "./chunk-7D4JHNVX.js";
9
+ } from "./chunk-MY5VVL5C.js";
10
10
  export {
11
11
  assertKebabName,
12
12
  findSkillsRoot,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prismakit/cli",
3
- "version": "3.0.3",
3
+ "version": "3.2.0",
4
4
  "description": "CLI for PrismaKit — generate modules, validate compose, install agent skills",
5
5
  "license": "Apache-2.0",
6
6
  "engines": {
@@ -29,10 +29,10 @@
29
29
  "README.md"
30
30
  ],
31
31
  "dependencies": {
32
- "@prismakit/core": "3.0.2"
32
+ "@prismakit/core": "3.2.0"
33
33
  },
34
34
  "peerDependencies": {
35
- "@prismakit/core": ">=3.0.2"
35
+ "@prismakit/core": ">=3.2.0 <4"
36
36
  },
37
37
  "devDependencies": {
38
38
  "typescript": "^5.9.2",
@@ -52,6 +52,10 @@ Helpers may inject repositories — never the Prisma client.
52
52
  | Existence / uniqueness / auth | `getFirst` — **no** `setCache` |
53
53
  | List | `getMany` + `setCache: true` + optional `cacheTags` |
54
54
  | Paginated list | `getManyPaginate` |
55
+ | Large / infinite list | `getManyCursor` (with `cursor` → default `skip: 1`) |
56
+ | Count / exists check | `count` / `exists` (no `setCache` on auth paths) |
57
+ | Composite PK row | `id: { a, b }` object — kit maps to Prisma `a_b: { a, b }` |
58
+ | Aggregations | `aggregate` / `groupBy` |
55
59
  | Create / update / delete | matching mutation; default invalidation is enough outside tx |
56
60
  | Multi-step write | one transaction; pass `tx` into every repo call |
57
61
  | `SELECT … FOR UPDATE` | `lock: { mode: 'update' }` **inside** `tx` |
@@ -117,8 +121,13 @@ await users.getFirst({
117
121
  | `getFirst` | first match or `null` |
118
122
  | `getMany` | array (`take` / `skip` / `orderBy`) |
119
123
  | `getManyPaginate` | `{ data, meta: { page, pageSize, totalItems, totalPages } }` |
124
+ | `getManyCursor` | `{ data, nextCursor, hasMore }` |
125
+ | `count` / `exists` | `{ count }` / `{ exists }` |
126
+ | `aggregate` / `groupBy` | Prisma delegate results |
120
127
 
121
- `id` is `string` or `Record<string, string>` for composite PKs.
128
+ `id` is `string` or `Record<string, string>` for composite PKs (object form for `@@id([a,b])`).
129
+
130
+ `getManyCursor`: when `cursor` is set, default `skip` is `1` so the cursor row is not repeated. Pass `skip: 0` only for inclusive semantics.
122
131
 
123
132
  ## Writes
124
133
 
@@ -226,7 +235,12 @@ import prismakit from '@prismakit/eslint-plugin';
226
235
  export default [prismakit.configs.recommended];
227
236
  ```
228
237
 
229
- Allowed Prisma usage: `**/repositories/**`, `**/infrastructure/prisma/**`. Rules: `no-prisma-service-outside-repos`, `no-direct-prisma-delegate`, `require-transaction-service`.
238
+ Allowed Prisma usage: `**/repositories/**`, `**/infrastructure/prisma/**`. Rules: `no-prisma-service-outside-repos`, `no-direct-prisma-delegate`, `require-transaction-service`, `require-cached-repo-provider`.
239
+
240
+ ## Observability
241
+
242
+ - Core: `setTelemetry({ enabled, onEvent })` or Nest `telemetry` / `queryLog.slowThreshold`.
243
+ - Optional: `@prismakit/opentelemetry` → `createPrismaKitTelemetry({ slowThreshold })`.
230
244
 
231
245
  ## Clean code
232
246
 
@@ -235,6 +249,7 @@ Allowed Prisma usage: `**/repositories/**`, `**/infrastructure/prisma/**`. Rules
235
249
  - Named TTL constants (`const DAY = 86_400`), not magic numbers scattered in call sites.
236
250
  - Pass `tx` into **every** repo call in a unit of work. Do not mix cached reads with half-committed writes.
237
251
  - Tests: `@prismakit/memory` `MemoryCacheAdapter`. Production: `@prismakit/redis`.
252
+ - Library CI proves PG+Redis paths under `FORCE_INTEGRATION=1` (CRUD, compose, locks, stampede, fail-open).
238
253
 
239
254
  ## Anti-patterns (BAD → GOOD)
240
255
 
@@ -9,16 +9,18 @@ API surface for `@prismakit/core` 3.x. Read [SKILL.md](SKILL.md) first.
9
9
  | `@prismakit/core` | `createRepository`, AutoComposer, locks, pagination, `CacheAdapter` |
10
10
  | `@prismakit/redis` | `RedisCacheAdapter` |
11
11
  | `@prismakit/memory` | `MemoryCacheAdapter` (tests / local) |
12
+ | `@prismakit/opentelemetry` | Map telemetry → OTel metrics/spans |
12
13
  | `@prismakit/cli` | `prismakit generate / validate / skills` |
13
14
  | `@prismakit/eslint-plugin` | Repository-only data-access rules |
14
15
 
15
- Node ≥ 20. Install:
16
+ Node ≥ 20. Line **3.2.x**. Install:
16
17
 
17
18
  ```bash
18
19
  pnpm add @prismakit/core
19
20
  pnpm add @prismakit/redis ioredis # optional production cache
20
21
  pnpm add -D @prismakit/eslint-plugin @prismakit/cli
21
22
  # tests: pnpm add -D @prismakit/memory
23
+ # optional: pnpm add @prismakit/opentelemetry @opentelemetry/api
22
24
  ```
23
25
 
24
26
  ## Factory
@@ -40,7 +42,7 @@ Aliases: `defineRepository`, `createPrismaRepository`.
40
42
  | `cache` | `CacheOptions \| true` | `true` → `{ ttl: 86400, sensitiveFields: ['password'] }`. |
41
43
  | `lock` | `true \| string \| RepositoryLockConfig` | `true` / client key / Pascal name / `@@map` table. |
42
44
  | `schemaPath` | `string` | Lock/schema helpers when global meta is missing. |
43
- | `primaryKey` | `string \| string[]` | `*ById` + row locks. Default: meta PK or `id`. |
45
+ | `primaryKey` | `string \| string[]` | `*ById` + row locks. Default: meta PK or `id`. Composite → Prisma `a_b: { a, b }` where. |
44
46
  | `getDelegate` | `(client) => delegate` | Default `(c) => c[model]`. |
45
47
  | `toPayload` | `(data) => payload` | Default identity. Prefer typed factories over this. |
46
48
 
@@ -59,6 +61,11 @@ All methods accept optional `tx`. Cached repos also accept cache fields (see bel
59
61
  | `getFirst` | `where?`, `select?`, `lock?`, `setCache?`, `cacheTags?` | `T \| null` |
60
62
  | `getMany` | `where?`, `select?`, `orderBy?`, `take?`, `skip?`, `lock?`, `setCache?`, `cacheTags?` | `T[]` |
61
63
  | `getManyPaginate` | `where?`, `select?`, `orderBy?`, `page?`, `pageSize?`, `setCache?`, `cacheTags?` | `PaginatedResult<T>` |
64
+ | `getManyCursor` | `where?`, `select?`, `orderBy?`, `cursor?`, `take?`, `skip?`, `setCache?`, `cacheTags?` | `CursorPage<T>`; with `cursor`, default `skip: 1` |
65
+ | `count` | `where?`, `select?`, `setCache?`, `cacheTags?` | `{ count: number }` |
66
+ | `exists` | `where?`, `setCache?`, `cacheTags?` | `{ exists: boolean }` |
67
+ | `aggregate` | Prisma aggregate args + `setCache?`, `cacheTags?` | delegate result |
68
+ | `groupBy` | Prisma groupBy args + `setCache?`, `cacheTags?` | delegate result |
62
69
 
63
70
  `PaginatedResult<T>`:
64
71
 
@@ -127,14 +134,16 @@ await repo.invalidateCache({ id?: string; tags?: string[] });
127
134
  ### Key schema
128
135
 
129
136
  ```
130
- {prefix}:repo:{model}:e:{id}:{method}:{selectHash}
131
- {prefix}:repo:{model}:q:{method}:{queryHash}
132
- {prefix}:repo:{model}:e:{id}:__idx
133
- {prefix}:repo:{model}:e:__idx
134
- {prefix}:repo:{model}:q:__idx
135
- {prefix}:repo:{model}:t:{tag}:__idx
137
+ {prefix}:v2:repo:{model}:e:{id}:{method}:{selectHash}
138
+ {prefix}:v2:repo:{model}:q:{method}:{queryHash}
139
+ {prefix}:v2:repo:{model}:e:{id}:__idx
140
+ {prefix}:v2:repo:{model}:e:__idx
141
+ {prefix}:v2:repo:{model}:q:__idx
142
+ {prefix}:v2:repo:{model}:t:{tag}:__idx
136
143
  ```
137
144
 
145
+ Redis payloads use tagged JSON for `Date`, `BigInt`, `Bytes`, and `Decimal` (`__date`, `__bigint`, …). Bump to v2 keys causes a one-time miss after upgrade.
146
+
138
147
  ### Debug
139
148
 
140
149
  ```bash
@@ -265,6 +274,7 @@ export default [prismakit.configs.recommended];
265
274
  | `prismakit/no-prisma-service-outside-repos` | Inject/reference `PrismaService` / `PrismaClient` outside allowlist |
266
275
  | `prismakit/no-direct-prisma-delegate` | `prisma.<model>.*` outside allowlist |
267
276
  | `prismakit/require-transaction-service` | `.$transaction` in feature code |
277
+ | `prismakit/require-cached-repo-provider` | Cached repo class missing from Nest `providers` |
268
278
 
269
279
  Allowed path patterns (forward slashes):
270
280
 
@@ -63,6 +63,7 @@ Task Progress:
63
63
 
64
64
  - [ ] Relations are nested objects in `select`, not Prisma `include`
65
65
  - [ ] Related model repositories are registered (or `autoRegisterModels` covers them)
66
+ - [ ] Cached repos (`cache` in `defineAppRepo`) are listed in a Nest module `providers` array
66
67
  - [ ] `npx prismakit validate` (or `validateCompose: true`) is clean when selects nest relations
67
68
 
68
69
  ## ESLint / layout
@@ -194,9 +194,10 @@ Repository `cache` is the source of truth. Omit `cacheModels` (fail-open). Pass
194
194
  |--------|-----|
195
195
  | `cacheModels` | Optional extra allowlist (omit — repo `cache` is enough) |
196
196
  | `validateCompose: true` | Assert compose-safe selects on boot |
197
+ | `strictCachedRepos` | Fail boot if a `cache` repo class is not in Nest `providers` (default `true`) |
197
198
  | `compose` | `{ maxDepth, parallel, setCache }` |
198
- | `telemetry` | `{ enabled: true, onEvent }` |
199
- | `queryLog` | `{ slowThreshold, onSlowQuery }` — enables telemetry |
199
+ | `telemetry` | `{ enabled: true, onEvent }` or `createPrismaKitTelemetry()` from `@prismakit/opentelemetry` |
200
+ | `queryLog` | `{ slowThreshold, onSlowQuery }` — enables telemetry / `query.slow` |
200
201
  | `autoRegisterModels` | `true` or `string[]` — stub repos for compose-only models |
201
202
 
202
203
  ## Scaffolding
@@ -204,12 +205,15 @@ Repository `cache` is the source of truth. Omit `cacheModels` (fail-open). Pass
204
205
  ```bash
205
206
  npx prismakit generate product --cache
206
207
  npx prismakit generate product --cache --full --route products
208
+ npx prismakit skills # refresh .cursor/skills after upgrades
207
209
  ```
208
210
 
209
211
  Repo-only: add the class to feature `providers`. `--full`: import `*Module` in `AppModule`. Then `npx prismakit validate`.
210
212
 
211
213
  Enable ESLint `prismakit.configs.recommended` (see skill `prismakit`).
212
214
 
215
+ Reference app: [starter-prismakit-nestjs](https://github.com/fikiap23/starter-prismakit-nestjs) (Nest 11 + Prisma 7 + Redis).
216
+
213
217
  ## Clean code (Nest)
214
218
 
215
219
  - One `defineRepo` binder under `src/infrastructure/prisma/`. Do not call `createDefineRepo` per feature.
@@ -11,6 +11,8 @@ API surface for `@prismakit/nestjs` 3.x. Repository methods, cache, compose, and
11
11
  | `dmmf` | no | `Prisma.dmmf` on Prisma 5/6. Skip on Prisma 7 — use `schemaPath`. |
12
12
  | `schemaPath` | no | Load meta from `schema.prisma` when `dmmf` is omitted (compose + locks). Defaults to `prisma/schema.prisma`. |
13
13
  | `validateCompose` | no | When `true`, `assertSelectComposeValid` on module init. |
14
+ | `strictCachedRepos` | no | Fail boot when a cached repo is missing from `providers`, or listed in two modules. Default `true`. |
15
+ | `modulesRoot` | no | Directory scanned by `strictCachedRepos`. Default `src/modules`. |
14
16
  | `cacheModels` | no | Optional extra allowlist. Omit — repo `cache` is the source of truth. |
15
17
  | `compose` | no | `ComposeOptions`: `maxDepth` (default 10), `parallel` (default true), `setCache` (default true). `tx` is per-call only. |
16
18
  | `telemetry` | no | `{ enabled?: boolean; onEvent?: (event) => void }`. |
@@ -24,6 +24,7 @@ Task Progress:
24
24
  - [ ] Production cache uses `RedisCacheAdapter` with a stable `prefix`
25
25
  - [ ] `cacheModels` is omitted (repo `cache` is source of truth), or lists every cached model if an allowlist is used
26
26
  - [ ] `validateCompose: true` is on for apps that nest relations in `select`
27
+ - [ ] Cached repo classes are in feature `providers` (`strictCachedRepos` fails boot otherwise)
27
28
 
28
29
  ## Repository factory
29
30
 
@@ -22,7 +22,8 @@ describe('cli generate templates', () => {
22
22
  expect(files[0].content).toContain("model: 'product'");
23
23
  expect(files[0].content).toContain('cache: {');
24
24
  expect(files[0].content).not.toContain('getDelegate');
25
- expect(files[0].content).toContain("from '@prisma/client'");
25
+ expect(files[0].content).not.toContain('scalarFields');
26
+ expect(files[0].content).toContain("from '@prismakit/nestjs'");
26
27
  });
27
28
 
28
29
  it('emits full Nest module when full: true', () => {
@@ -0,0 +1,24 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { runValidate } from '../commands/validate';
6
+
7
+ describe('runValidate', () => {
8
+ it('is exported and can be imported', () => {
9
+ expect(typeof runValidate).toBe('function');
10
+ });
11
+
12
+ it('passes on an empty project when assert is false', () => {
13
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'pk-validate-'));
14
+ const prevCwd = process.cwd();
15
+ process.chdir(dir);
16
+ try {
17
+ runValidate({ cwd: dir, assert: false });
18
+ expect(process.exitCode).toBeUndefined();
19
+ } finally {
20
+ process.chdir(prevCwd);
21
+ fs.rmSync(dir, { recursive: true, force: true });
22
+ }
23
+ });
24
+ });
package/src/templates.ts CHANGED
@@ -50,12 +50,10 @@ function renderRepository(
50
50
  : '';
51
51
 
52
52
  const content = apply(
53
- `import { Prisma } from '{{prismaImport}}';
54
- import { createInjectableRepository } from '@prismakit/nestjs';
53
+ `import { createInjectableRepository } from '@prismakit/nestjs';
55
54
 
56
55
  export class {{pascal}}Repository extends createInjectableRepository({
57
56
  model: '{{repoModel}}',
58
- scalarFields: Prisma.{{pascal}}ScalarFieldEnum,
59
57
  {{cacheBlock}}}) {}
60
58
  `,
61
59
  names,
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/naming.ts","../src/templates.ts","../src/commands/generate.ts","../src/commands/validate.ts","../src/commands/skills.ts"],"sourcesContent":["export interface ModuleNames {\n kebab: string;\n camel: string;\n pascal: string;\n repoModel: string;\n route: string;\n}\n\nconst KEBAB_NAME_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;\n\nexport function assertKebabName(name: string): void {\n if (!KEBAB_NAME_RE.test(name)) {\n throw new Error(\n `Invalid module name \"${name}\". Use kebab-case (e.g. product, blog-post).`,\n );\n }\n}\n\nfunction kebabToPascal(kebab: string): string {\n return kebab\n .split('-')\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\nfunction kebabToCamel(kebab: string): string {\n const pascal = kebabToPascal(kebab);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\nexport function resolveNames(kebab: string, route?: string): ModuleNames {\n assertKebabName(kebab);\n const pascal = kebabToPascal(kebab);\n const camel = kebabToCamel(kebab);\n return {\n kebab,\n camel,\n pascal,\n repoModel: camel,\n route: route ?? kebab,\n };\n}\n","import type { ModuleNames } from './naming';\n\nexport type GenerateOptions = {\n names: ModuleNames;\n cacheEnabled: boolean;\n /** When false (default), only emit the repository file. */\n full?: boolean;\n /** Emit validate + mapper helpers. */\n helpers?: boolean;\n /** Emit class-validator DTOs with @ApiProperty. */\n dto?: boolean;\n /** Prisma client import path (default `@prisma/client`). */\n prismaImport?: string;\n};\n\nfunction apply(template: string, names: ModuleNames, extras: Record<string, string>): string {\n const replacements: Record<string, string> = {\n '{{pascal}}': names.pascal,\n '{{camel}}': names.camel,\n '{{kebab}}': names.kebab,\n '{{route}}': names.route,\n '{{repoModel}}': names.repoModel,\n ...extras,\n };\n let result = template;\n for (const [key, value] of Object.entries(replacements)) {\n result = result.split(key).join(value);\n }\n return result;\n}\n\nexport type GeneratedFile = {\n relativePath: string;\n content: string;\n};\n\nfunction renderRepository(\n names: ModuleNames,\n cacheEnabled: boolean,\n prismaImport: string,\n base: string,\n): GeneratedFile {\n const cacheBlock = cacheEnabled\n ? ` cache: {\n ttl: 86400,\n sensitiveFields: ['password'],\n defaultSetCache: true,\n },\n`\n : '';\n\n const content = apply(\n `import { Prisma } from '{{prismaImport}}';\nimport { createInjectableRepository } from '@prismakit/nestjs';\n\nexport class {{pascal}}Repository extends createInjectableRepository({\n model: '{{repoModel}}',\n scalarFields: Prisma.{{pascal}}ScalarFieldEnum,\n{{cacheBlock}}}) {}\n`,\n names,\n {\n '{{cacheBlock}}': cacheBlock,\n '{{prismaImport}}': prismaImport,\n },\n );\n\n return {\n relativePath: `${base}/repositories/${names.kebab}.repository.ts`,\n content,\n };\n}\n\nexport function renderModuleFiles(options: GenerateOptions): GeneratedFile[] {\n const { names, cacheEnabled, full = false, helpers = false, dto = false } =\n options;\n const prismaImport = options.prismaImport ?? '@prisma/client';\n const base = `src/modules/${names.kebab}`;\n\n const repository = renderRepository(names, cacheEnabled, prismaImport, base);\n\n if (!full) {\n return [repository];\n }\n\n const service = apply(\n `import { Injectable } from '@nestjs/common';\n\nimport { {{pascal}}Repository } from '../repositories/{{kebab}}.repository';\nimport { get{{pascal}}Select } from '../types/select-{{kebab}}.type';\nimport { where{{pascal}}GetManyPaginate } from '../types/where-{{kebab}}.type';\n{{dtoImport}}\n@Injectable()\nexport class {{pascal}}Service {\n constructor(private readonly {{camel}}Repository: {{pascal}}Repository) {}\n\n async handleCreate(dto: Create{{pascal}}Dto) {\n return await this.{{camel}}Repository.create({\n data: { ...dto },\n select: get{{pascal}}Select('general'),\n });\n }\n\n async handleGetById(id: string) {\n return await this.{{camel}}Repository.getThrowById({\n id,\n select: get{{pascal}}Select('general'),\n setCache: true,\n });\n }\n\n async handleGetManyPaginate(filter: Filter{{pascal}}Dto) {\n const { where } = where{{pascal}}GetManyPaginate(filter);\n return await this.{{camel}}Repository.getManyPaginate({\n where,\n select: get{{pascal}}Select('general'),\n page: filter.page,\n pageSize: filter.pageSize,\n setCache: true,\n });\n }\n\n async handleUpdateById(id: string, dto: Update{{pascal}}Dto) {\n return await this.{{camel}}Repository.updateById({\n id,\n data: { ...dto },\n select: get{{pascal}}Select('general'),\n });\n }\n\n async handleDeleteById(id: string) {\n return await this.{{camel}}Repository.deleteById({\n id,\n select: get{{pascal}}Select('minimal'),\n });\n }\n}\n`,\n names,\n {\n '{{dtoImport}}': dto\n ? `import type {\\n Create{{pascal}}Dto,\\n Update{{pascal}}Dto,\\n Filter{{pascal}}Dto,\\n} from '../dto/{{kebab}}.dto';\\n`\n : `type Create{{pascal}}Dto = Record<string, unknown>;\\ntype Update{{pascal}}Dto = Record<string, unknown>;\\ntype Filter{{pascal}}Dto = { page?: number; pageSize?: number; q?: string };\\n`,\n },\n );\n\n const controller = apply(\n `import {\n Body,\n Controller,\n Delete,\n Get,\n HttpStatus,\n Param,\n Patch,\n Post,\n Query,\n Res,\n} from '@nestjs/common';\nimport type { Response } from 'express';\n\nimport { {{pascal}}Service } from '../services/{{kebab}}.service';\n{{dtoImport}}\n@Controller('{{route}}')\nexport class {{pascal}}Controller {\n constructor(private readonly {{camel}}Service: {{pascal}}Service) {}\n\n @Post()\n async create(@Body() dto: Create{{pascal}}Dto, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleCreate(dto);\n return res.status(HttpStatus.CREATED).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Get()\n async getMany(@Query() filter: Filter{{pascal}}Dto, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleGetManyPaginate(filter);\n return res.status(HttpStatus.OK).json({ data: result.data, meta: result.meta });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Get(':id')\n async getById(@Param('id') id: string, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleGetById(id);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Patch(':id')\n async update(\n @Param('id') id: string,\n @Body() dto: Update{{pascal}}Dto,\n @Res() res: Response,\n ) {\n try {\n const result = await this.{{camel}}Service.handleUpdateById(id, dto);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n\n @Delete(':id')\n async delete(@Param('id') id: string, @Res() res: Response) {\n try {\n const result = await this.{{camel}}Service.handleDeleteById(id);\n return res.status(HttpStatus.OK).json({ data: result });\n } catch (error) {\n const status = (error as { statusCode?: number })?.statusCode ?? 500;\n return res.status(status).json({\n error: { message: (error as Error).message, httpStatus: status },\n });\n }\n }\n}\n`,\n names,\n {\n '{{dtoImport}}': dto\n ? `import type {\\n Create{{pascal}}Dto,\\n Update{{pascal}}Dto,\\n Filter{{pascal}}Dto,\\n} from '../dto/{{kebab}}.dto';\\n`\n : `type Create{{pascal}}Dto = Record<string, unknown>;\\ntype Update{{pascal}}Dto = Record<string, unknown>;\\ntype Filter{{pascal}}Dto = { page?: number; pageSize?: number; q?: string };\\n`,\n },\n );\n\n const moduleFile = apply(\n `import { Module } from '@nestjs/common';\n\nimport { {{pascal}}Controller } from './controllers/{{kebab}}.controller';\nimport { {{pascal}}Service } from './services/{{kebab}}.service';\nimport { {{pascal}}Repository } from './repositories/{{kebab}}.repository';\n{{helpersImport}}\n@Module({\n controllers: [{{pascal}}Controller],\n providers: [{{pascal}}Service, {{pascal}}Repository{{helpersProviders}}],\n exports: [{{pascal}}Service, {{pascal}}Repository],\n})\nexport class {{pascal}}Module {}\n`,\n names,\n {\n '{{helpersImport}}': helpers\n ? `import { {{pascal}}ValidateHelper } from './helpers/{{kebab}}-validate.helper';\\nimport { {{pascal}}MapperHelper } from './helpers/{{kebab}}-mapper.helper';\\n`\n : '',\n '{{helpersProviders}}': helpers\n ? `, {{pascal}}ValidateHelper, {{pascal}}MapperHelper`\n : '',\n },\n );\n\n const select = apply(\n `import { Prisma } from '{{prismaImport}}';\n\ntype {{pascal}}SelectPresetKey = keyof typeof {{camel}}SelectPresets;\n\nexport function get{{pascal}}Select<K extends {{pascal}}SelectPresetKey>(key: K) {\n return {{camel}}SelectPresets[key];\n}\n\nexport const {{camel}}SelectPresets = {\n minimal: {\n id: true,\n } satisfies Prisma.{{pascal}}Select,\n\n general: {\n id: true,\n } satisfies Prisma.{{pascal}}Select,\n};\n`,\n names,\n { '{{prismaImport}}': prismaImport },\n );\n\n const where = apply(\n `import { Prisma } from '{{prismaImport}}';\n\nexport function where{{pascal}}GetManyPaginate(filter: {\n q?: string;\n}): {\n where: Prisma.{{pascal}}WhereInput;\n} {\n const { q } = filter;\n const where: Prisma.{{pascal}}WhereInput = {\n ...(q ? { /* add searchable fields */ } : {}),\n };\n return { where };\n}\n`,\n names,\n { '{{prismaImport}}': prismaImport },\n );\n\n const files: GeneratedFile[] = [\n { relativePath: `${base}/${names.kebab}.module.ts`, content: moduleFile },\n {\n relativePath: `${base}/controllers/${names.kebab}.controller.ts`,\n content: controller,\n },\n {\n relativePath: `${base}/services/${names.kebab}.service.ts`,\n content: service,\n },\n repository,\n {\n relativePath: `${base}/types/select-${names.kebab}.type.ts`,\n content: select,\n },\n {\n relativePath: `${base}/types/where-${names.kebab}.type.ts`,\n content: where,\n },\n ];\n\n if (dto) {\n files.push({\n relativePath: `${base}/dto/${names.kebab}.dto.ts`,\n content: apply(\n `import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';\nimport { IsOptional, IsString } from 'class-validator';\n\nexport class Create{{pascal}}Dto {\n @ApiProperty({ example: 'name' })\n @IsString()\n name!: string;\n}\n\nexport class Update{{pascal}}Dto {\n @ApiPropertyOptional({ example: 'name' })\n @IsOptional()\n @IsString()\n name?: string;\n}\n\nexport class Filter{{pascal}}Dto {\n @ApiPropertyOptional()\n @IsOptional()\n page?: number;\n\n @ApiPropertyOptional()\n @IsOptional()\n pageSize?: number;\n\n @ApiPropertyOptional()\n @IsOptional()\n @IsString()\n q?: string;\n}\n`,\n names,\n {},\n ),\n });\n }\n\n if (helpers) {\n files.push(\n {\n relativePath: `${base}/helpers/${names.kebab}-validate.helper.ts`,\n content: apply(\n `import { Injectable } from '@nestjs/common';\n\nimport { {{pascal}}Repository } from '../repositories/{{kebab}}.repository';\nimport { get{{pascal}}Select } from '../types/select-{{kebab}}.type';\n\n@Injectable()\nexport class {{pascal}}ValidateHelper {\n constructor(private readonly {{camel}}Repository: {{pascal}}Repository) {}\n\n async assertExists(id: string) {\n return this.{{camel}}Repository.getThrowById({\n id,\n select: get{{pascal}}Select('minimal'),\n });\n }\n}\n`,\n names,\n {},\n ),\n },\n {\n relativePath: `${base}/helpers/${names.kebab}-mapper.helper.ts`,\n content: apply(\n `import { Injectable } from '@nestjs/common';\n\n@Injectable()\nexport class {{pascal}}MapperHelper {\n toResponse(entity: Record<string, unknown>) {\n return entity;\n }\n}\n`,\n names,\n {},\n ),\n },\n );\n }\n\n return files;\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { resolveNames } from '../naming';\nimport { renderModuleFiles } from '../templates';\n\nexport type GenerateCommandOptions = {\n name: string;\n cache?: boolean;\n route?: string;\n cwd?: string;\n dryRun?: boolean;\n /** Emit full Nest module (controller/service/types). Default: repo-only. */\n full?: boolean;\n /** Emit validate + mapper helpers. */\n helpers?: boolean;\n /** Emit class-validator DTOs. */\n dto?: boolean;\n /** Prisma client import path (default `@prisma/client`). */\n prismaImport?: string;\n};\n\nexport function runGenerate(options: GenerateCommandOptions): void {\n const cwd = options.cwd ?? process.cwd();\n const names = resolveNames(options.name, options.route);\n const full = !!options.full;\n const files = renderModuleFiles({\n names,\n cacheEnabled: !!options.cache,\n full,\n helpers: !!options.helpers,\n dto: !!options.dto,\n prismaImport: options.prismaImport,\n });\n\n for (const file of files) {\n const fullPath = path.join(cwd, file.relativePath);\n if (options.dryRun) {\n console.log(`[dry-run] would write ${file.relativePath}`);\n continue;\n }\n if (fs.existsSync(fullPath)) {\n console.warn(`skip (exists): ${file.relativePath}`);\n continue;\n }\n fs.mkdirSync(path.dirname(fullPath), { recursive: true });\n const content = file.content.endsWith('\\n')\n ? file.content\n : `${file.content}\\n`;\n fs.writeFileSync(fullPath, content, 'utf-8');\n console.log(`created ${file.relativePath}`);\n }\n\n if (full) {\n console.log(\n `\\nScaffolded module \"${names.kebab}\". Register ${names.pascal}Module in app.module.ts.`,\n );\n } else {\n console.log(\n `\\nScaffolded repository \"${names.pascal}Repository\". Register it in your feature module providers.`,\n );\n }\n}\n","import {\n assertSelectComposeValid,\n validateSelectCompose,\n} from '@prismakit/core';\n\nexport type ValidateCommandOptions = {\n cwd?: string;\n assert?: boolean;\n schemaPath?: string;\n autoRegisterModels?: boolean;\n};\n\n/**\n * Run select-compose validation from @prismakit/core.\n */\nexport function runValidate(options: ValidateCommandOptions = {}): void {\n const cwd = options.cwd ?? process.cwd();\n const validateOptions = {\n schemaPath: options.schemaPath,\n autoRegisterModels: options.autoRegisterModels,\n };\n\n if (options.assert !== false) {\n try {\n assertSelectComposeValid(cwd, validateOptions);\n console.log('Select compose validation passed.');\n } catch (err) {\n console.error((err as Error).message);\n process.exitCode = 1;\n }\n return;\n }\n\n const issues = validateSelectCompose(cwd, validateOptions);\n if (issues.length === 0) {\n console.log('Select compose validation passed.');\n return;\n }\n\n for (const issue of issues) {\n console.error(` - ${issue.file}: ${issue.message}`);\n }\n process.exitCode = 1;\n}\n","import * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst SKILL_NAMES = ['prismakit', 'prismakit-nestjs'] as const;\nconst RULE_NAME = 'data-access.mdc';\n\nexport type SkillsCommandOptions = {\n cwd?: string;\n /** Install into ~/.cursor/skills (all projects). Default: <cwd>/.cursor/skills */\n global?: boolean;\n /** Override project root when not using --global. */\n projectRoot?: string;\n /** Subset of skill folder names. Default: both. */\n skill?: string[];\n /** Also copy templates/cursor-rules/data-access.mdc into .cursor/rules. */\n withRules?: boolean;\n dryRun?: boolean;\n list?: boolean;\n /** Test override: directory that contains prismakit/ and prismakit-nestjs/. */\n skillsRoot?: string;\n /** Test override: path to data-access.mdc. */\n rulesPath?: string;\n};\n\nexport type SkillsInstallResult = {\n dest: string;\n installed: string[];\n ruleDest?: string;\n};\n\nfunction isSkillsRoot(dir: string): boolean {\n return fs.existsSync(path.join(dir, 'prismakit', 'SKILL.md'));\n}\n\n/**\n * Walk up from the CLI entrypoint looking for bundled `skills/` (published\n * package) or the monorepo `skills/` directory.\n */\nexport function findSkillsRoot(startDir: string): string | undefined {\n let dir = path.resolve(startDir);\n for (let i = 0; i < 10; i++) {\n for (const candidate of [\n path.join(dir, 'skills'),\n dir,\n path.join(dir, 'templates', 'cursor-skills'),\n ]) {\n if (isSkillsRoot(candidate)) return candidate;\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return undefined;\n}\n\nexport function findRulesPath(skillsRoot: string): string | undefined {\n const candidates = [\n path.join(skillsRoot, '..', 'templates', 'cursor-rules', RULE_NAME),\n path.join(skillsRoot, '..', 'rules', RULE_NAME),\n path.join(path.dirname(skillsRoot), 'rules', RULE_NAME),\n ];\n return candidates.find((p) => fs.existsSync(p));\n}\n\nfunction copyDir(src: string, dest: string): void {\n fs.mkdirSync(dest, { recursive: true });\n for (const entry of fs.readdirSync(src, { withFileTypes: true })) {\n if (entry.name === '.git') continue;\n const from = path.join(src, entry.name);\n const to = path.join(dest, entry.name);\n if (entry.isDirectory()) {\n copyDir(from, to);\n } else if (entry.isFile()) {\n fs.copyFileSync(from, to);\n }\n }\n}\n\nfunction resolveHome(): string {\n return process.env.HOME || os.homedir();\n}\n\nfunction assertNotBuiltinSkills(dest: string): void {\n const forbidden = path.join(resolveHome(), '.cursor', 'skills-cursor');\n const resolved = path.resolve(dest);\n if (resolved === forbidden || resolved.startsWith(`${forbidden}${path.sep}`)) {\n throw new Error(\n `Refusing to install into ${resolved} (reserved for Cursor built-ins).`,\n );\n }\n}\n\nfunction cliStartDir(): string {\n try {\n const url = import.meta.url;\n if (typeof url === 'string' && url.length > 0) {\n return path.dirname(fileURLToPath(url));\n }\n } catch {\n // CJS bundle has an empty import.meta\n }\n return process.cwd();\n}\n\nexport function runSkills(options: SkillsCommandOptions = {}): SkillsInstallResult {\n const skillsRoot =\n options.skillsRoot ??\n findSkillsRoot(cliStartDir()) ??\n findSkillsRoot(process.cwd());\n if (!skillsRoot) {\n throw new Error(\n 'Could not find PrismaKit skills. Reinstall @prismakit/cli or clone fikiap23/prismakit.',\n );\n }\n\n const available = SKILL_NAMES.filter((name) =>\n fs.existsSync(path.join(skillsRoot, name, 'SKILL.md')),\n );\n if (available.length === 0) {\n throw new Error(`No skills found in ${skillsRoot}`);\n }\n\n const requested = options.skill?.length\n ? options.skill.map((s) => s.trim()).filter(Boolean)\n : [...available];\n\n for (const name of requested) {\n if (!available.includes(name as (typeof SKILL_NAMES)[number])) {\n throw new Error(\n `Unknown skill \"${name}\". Available: ${available.join(', ')}`,\n );\n }\n }\n\n if (options.list) {\n console.log(`Skills in ${skillsRoot}:`);\n for (const name of available) {\n console.log(` - ${name}`);\n }\n return { dest: skillsRoot, installed: [] };\n }\n\n const cwd = options.cwd ?? process.cwd();\n const dest = options.global\n ? path.join(resolveHome(), '.cursor', 'skills')\n : path.join(\n path.resolve(options.projectRoot ?? cwd),\n '.cursor',\n 'skills',\n );\n\n assertNotBuiltinSkills(dest);\n\n if (!options.dryRun) {\n fs.mkdirSync(dest, { recursive: true });\n }\n\n const installed: string[] = [];\n for (const name of requested) {\n const src = path.join(skillsRoot, name);\n const target = path.join(dest, name);\n if (options.dryRun) {\n console.log(`[dry-run] would install ${name} -> ${target}`);\n } else {\n fs.rmSync(target, { recursive: true, force: true });\n copyDir(src, target);\n console.log(`Installed ${name} -> ${target}`);\n }\n installed.push(name);\n }\n\n let ruleDest: string | undefined;\n if (options.withRules) {\n const rulesSrc = options.rulesPath ?? findRulesPath(skillsRoot);\n if (!rulesSrc) {\n throw new Error(`Could not find ${RULE_NAME} next to skills.`);\n }\n const rulesDir = options.global\n ? path.join(resolveHome(), '.cursor', 'rules')\n : path.join(path.resolve(options.projectRoot ?? cwd), '.cursor', 'rules');\n ruleDest = path.join(rulesDir, RULE_NAME);\n if (options.dryRun) {\n console.log(`[dry-run] would install rule -> ${ruleDest}`);\n } else {\n fs.mkdirSync(rulesDir, { recursive: true });\n fs.copyFileSync(rulesSrc, ruleDest);\n console.log(`Installed rule -> ${ruleDest}`);\n }\n }\n\n const scope = options.global\n ? 'global (~/.cursor/skills)'\n : 'project (.cursor/skills)';\n if (options.dryRun) {\n console.log(`Dry-run complete (${scope}).`);\n } else if (options.global) {\n console.log(`Done. Skills are available in all Cursor projects on this machine (${scope}).`);\n } else {\n console.log(\n `Done. Commit .cursor/skills so the team shares the same agent contract (${scope}).`,\n );\n }\n\n return { dest, installed, ruleDest };\n}\n"],"mappings":"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","names":["fs","path"]}