@prismakit/cli 2.2.3 → 3.0.2

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.
@@ -59,7 +59,7 @@ export const {{pascal}}Repository = createInjectableRepository({
59
59
  scalarFields: Prisma.{{pascal}}ScalarFieldEnum,
60
60
  {{cacheBlock}}});
61
61
 
62
- export type {{pascal}}Repository = InstanceType<typeof {{pascal}}Repository>;
62
+ export interface {{pascal}}Repository extends InstanceType<typeof {{pascal}}Repository> {}
63
63
  `,
64
64
  names,
65
65
  {
@@ -463,56 +463,6 @@ Scaffolded repository "${names.pascal}Repository". Register it in your feature m
463
463
  }
464
464
  }
465
465
 
466
- // src/commands/codegen.ts
467
- import * as fs2 from "fs";
468
- import * as path2 from "path";
469
- import {
470
- computeRelationAliasesFromSchema,
471
- getSchemaModels
472
- } from "@prismakit/core";
473
- function runCodegen(options = {}) {
474
- const cwd = options.cwd ?? process.cwd();
475
- const schemaPath = options.schemaPath ?? path2.join(cwd, "prisma", "schema.prisma");
476
- if (!fs2.existsSync(schemaPath)) {
477
- throw new Error(`Prisma schema not found at ${schemaPath}`);
478
- }
479
- const models = getSchemaModels(schemaPath);
480
- const aliases = computeRelationAliasesFromSchema(models);
481
- const entries = Object.entries(aliases).sort(
482
- ([a], [b]) => a.localeCompare(b)
483
- );
484
- if (entries.length === 0) {
485
- console.log(
486
- "No additional relation aliases suggested (suffix rules cover all)."
487
- );
488
- return;
489
- }
490
- const lines = [
491
- "// Suggested RELATION_MODEL_ALIASES entries (merge into your resolver config)",
492
- "export const SUGGESTED_RELATION_MODEL_ALIASES = {",
493
- ...entries.map(([k, v]) => ` ${k}: '${v}',`),
494
- "} as const;",
495
- ""
496
- ];
497
- const output = lines.join("\n");
498
- if (options.write) {
499
- const out = options.outFile ?? path2.join(
500
- cwd,
501
- "src",
502
- "infrastructure",
503
- "prisma",
504
- "suggested-relation-aliases.ts"
505
- );
506
- fs2.mkdirSync(path2.dirname(out), { recursive: true });
507
- fs2.writeFileSync(out, output, "utf-8");
508
- console.log(`wrote ${path2.relative(cwd, out)}`);
509
- } else {
510
- console.log(output);
511
- }
512
- console.log(`
513
- ${entries.length} alias suggestion(s).`);
514
- }
515
-
516
466
  // src/commands/validate.ts
517
467
  import {
518
468
  assertSelectComposeValid,
@@ -520,9 +470,13 @@ import {
520
470
  } from "@prismakit/core";
521
471
  function runValidate(options = {}) {
522
472
  const cwd = options.cwd ?? process.cwd();
473
+ const validateOptions = {
474
+ schemaPath: options.schemaPath,
475
+ autoRegisterModels: options.autoRegisterModels
476
+ };
523
477
  if (options.assert !== false) {
524
478
  try {
525
- assertSelectComposeValid(cwd);
479
+ assertSelectComposeValid(cwd, validateOptions);
526
480
  console.log("Select compose validation passed.");
527
481
  } catch (err) {
528
482
  console.error(err.message);
@@ -530,7 +484,7 @@ function runValidate(options = {}) {
530
484
  }
531
485
  return;
532
486
  }
533
- const issues = validateSelectCompose(cwd);
487
+ const issues = validateSelectCompose(cwd, validateOptions);
534
488
  if (issues.length === 0) {
535
489
  console.log("Select compose validation passed.");
536
490
  return;
@@ -541,12 +495,162 @@ function runValidate(options = {}) {
541
495
  process.exitCode = 1;
542
496
  }
543
497
 
498
+ // src/commands/skills.ts
499
+ import * as fs2 from "fs";
500
+ import * as os from "os";
501
+ import * as path2 from "path";
502
+ import { fileURLToPath } from "url";
503
+ var SKILL_NAMES = ["prismakit", "prismakit-nestjs"];
504
+ var RULE_NAME = "data-access.mdc";
505
+ function isSkillsRoot(dir) {
506
+ return fs2.existsSync(path2.join(dir, "prismakit", "SKILL.md"));
507
+ }
508
+ function findSkillsRoot(startDir) {
509
+ let dir = path2.resolve(startDir);
510
+ for (let i = 0; i < 10; i++) {
511
+ for (const candidate of [
512
+ path2.join(dir, "skills"),
513
+ dir,
514
+ path2.join(dir, "templates", "cursor-skills")
515
+ ]) {
516
+ if (isSkillsRoot(candidate)) return candidate;
517
+ }
518
+ const parent = path2.dirname(dir);
519
+ if (parent === dir) break;
520
+ dir = parent;
521
+ }
522
+ return void 0;
523
+ }
524
+ function findRulesPath(skillsRoot) {
525
+ const candidates = [
526
+ path2.join(skillsRoot, "..", "templates", "cursor-rules", RULE_NAME),
527
+ path2.join(skillsRoot, "..", "rules", RULE_NAME),
528
+ path2.join(path2.dirname(skillsRoot), "rules", RULE_NAME)
529
+ ];
530
+ return candidates.find((p) => fs2.existsSync(p));
531
+ }
532
+ function copyDir(src, dest) {
533
+ fs2.mkdirSync(dest, { recursive: true });
534
+ for (const entry of fs2.readdirSync(src, { withFileTypes: true })) {
535
+ if (entry.name === ".git") continue;
536
+ const from = path2.join(src, entry.name);
537
+ const to = path2.join(dest, entry.name);
538
+ if (entry.isDirectory()) {
539
+ copyDir(from, to);
540
+ } else if (entry.isFile()) {
541
+ fs2.copyFileSync(from, to);
542
+ }
543
+ }
544
+ }
545
+ function resolveHome() {
546
+ return process.env.HOME || os.homedir();
547
+ }
548
+ function assertNotBuiltinSkills(dest) {
549
+ const forbidden = path2.join(resolveHome(), ".cursor", "skills-cursor");
550
+ const resolved = path2.resolve(dest);
551
+ if (resolved === forbidden || resolved.startsWith(`${forbidden}${path2.sep}`)) {
552
+ throw new Error(
553
+ `Refusing to install into ${resolved} (reserved for Cursor built-ins).`
554
+ );
555
+ }
556
+ }
557
+ function cliStartDir() {
558
+ try {
559
+ const url = import.meta.url;
560
+ if (typeof url === "string" && url.length > 0) {
561
+ return path2.dirname(fileURLToPath(url));
562
+ }
563
+ } catch {
564
+ }
565
+ return process.cwd();
566
+ }
567
+ function runSkills(options = {}) {
568
+ const skillsRoot = options.skillsRoot ?? findSkillsRoot(cliStartDir()) ?? findSkillsRoot(process.cwd());
569
+ if (!skillsRoot) {
570
+ throw new Error(
571
+ "Could not find PrismaKit skills. Reinstall @prismakit/cli or clone fikiap23/prismakit."
572
+ );
573
+ }
574
+ const available = SKILL_NAMES.filter(
575
+ (name) => fs2.existsSync(path2.join(skillsRoot, name, "SKILL.md"))
576
+ );
577
+ if (available.length === 0) {
578
+ throw new Error(`No skills found in ${skillsRoot}`);
579
+ }
580
+ const requested = options.skill?.length ? options.skill.map((s) => s.trim()).filter(Boolean) : [...available];
581
+ for (const name of requested) {
582
+ if (!available.includes(name)) {
583
+ throw new Error(
584
+ `Unknown skill "${name}". Available: ${available.join(", ")}`
585
+ );
586
+ }
587
+ }
588
+ if (options.list) {
589
+ console.log(`Skills in ${skillsRoot}:`);
590
+ for (const name of available) {
591
+ console.log(` - ${name}`);
592
+ }
593
+ return { dest: skillsRoot, installed: [] };
594
+ }
595
+ const cwd = options.cwd ?? process.cwd();
596
+ const dest = options.global ? path2.join(resolveHome(), ".cursor", "skills") : path2.join(
597
+ path2.resolve(options.projectRoot ?? cwd),
598
+ ".cursor",
599
+ "skills"
600
+ );
601
+ assertNotBuiltinSkills(dest);
602
+ if (!options.dryRun) {
603
+ fs2.mkdirSync(dest, { recursive: true });
604
+ }
605
+ const installed = [];
606
+ for (const name of requested) {
607
+ const src = path2.join(skillsRoot, name);
608
+ const target = path2.join(dest, name);
609
+ if (options.dryRun) {
610
+ console.log(`[dry-run] would install ${name} -> ${target}`);
611
+ } else {
612
+ fs2.rmSync(target, { recursive: true, force: true });
613
+ copyDir(src, target);
614
+ console.log(`Installed ${name} -> ${target}`);
615
+ }
616
+ installed.push(name);
617
+ }
618
+ let ruleDest;
619
+ if (options.withRules) {
620
+ const rulesSrc = options.rulesPath ?? findRulesPath(skillsRoot);
621
+ if (!rulesSrc) {
622
+ throw new Error(`Could not find ${RULE_NAME} next to skills.`);
623
+ }
624
+ const rulesDir = options.global ? path2.join(resolveHome(), ".cursor", "rules") : path2.join(path2.resolve(options.projectRoot ?? cwd), ".cursor", "rules");
625
+ ruleDest = path2.join(rulesDir, RULE_NAME);
626
+ if (options.dryRun) {
627
+ console.log(`[dry-run] would install rule -> ${ruleDest}`);
628
+ } else {
629
+ fs2.mkdirSync(rulesDir, { recursive: true });
630
+ fs2.copyFileSync(rulesSrc, ruleDest);
631
+ console.log(`Installed rule -> ${ruleDest}`);
632
+ }
633
+ }
634
+ const scope = options.global ? "global (~/.cursor/skills)" : "project (.cursor/skills)";
635
+ if (options.dryRun) {
636
+ console.log(`Dry-run complete (${scope}).`);
637
+ } else if (options.global) {
638
+ console.log(`Done. Skills are available in all Cursor projects on this machine (${scope}).`);
639
+ } else {
640
+ console.log(
641
+ `Done. Commit .cursor/skills so the team shares the same agent contract (${scope}).`
642
+ );
643
+ }
644
+ return { dest, installed, ruleDest };
645
+ }
646
+
544
647
  export {
545
648
  assertKebabName,
546
649
  resolveNames,
547
650
  renderModuleFiles,
548
651
  runGenerate,
549
- runCodegen,
550
- runValidate
652
+ runValidate,
653
+ findSkillsRoot,
654
+ runSkills
551
655
  };
552
- //# sourceMappingURL=chunk-7E6MWEJP.js.map
656
+ //# sourceMappingURL=chunk-E2CVH3MX.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 { Prisma } from '{{prismaImport}}';\nimport { createInjectableRepository } from '@prismakit/nestjs';\n\nexport const {{pascal}}Repository = createInjectableRepository({\n model: '{{repoModel}}',\n scalarFields: Prisma.{{pascal}}ScalarFieldEnum,\n{{cacheBlock}}});\n\nexport interface {{pascal}}Repository extends InstanceType<typeof {{pascal}}Repository> {}\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":";AAQA,IAAM,gBAAgB;AAEf,SAAS,gBAAgB,MAAoB;AAClD,MAAI,CAAC,cAAc,KAAK,IAAI,GAAG;AAC7B,UAAM,IAAI;AAAA,MACR,wBAAwB,IAAI;AAAA,IAC9B;AAAA,EACF;AACF;AAEA,SAAS,cAAc,OAAuB;AAC5C,SAAO,MACJ,MAAM,GAAG,EACT,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,EAC1D,KAAK,EAAE;AACZ;AAEA,SAAS,aAAa,OAAuB;AAC3C,QAAM,SAAS,cAAc,KAAK;AAClC,SAAO,OAAO,OAAO,CAAC,EAAE,YAAY,IAAI,OAAO,MAAM,CAAC;AACxD;AAEO,SAAS,aAAa,OAAe,OAA6B;AACvE,kBAAgB,KAAK;AACrB,QAAM,SAAS,cAAc,KAAK;AAClC,QAAM,QAAQ,aAAa,KAAK;AAChC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,OAAO,SAAS;AAAA,EAClB;AACF;;;AC1BA,SAAS,MAAM,UAAkB,OAAoB,QAAwC;AAC3F,QAAM,eAAuC;AAAA,IAC3C,cAAc,MAAM;AAAA,IACpB,aAAa,MAAM;AAAA,IACnB,aAAa,MAAM;AAAA,IACnB,aAAa,MAAM;AAAA,IACnB,iBAAiB,MAAM;AAAA,IACvB,GAAG;AAAA,EACL;AACA,MAAI,SAAS;AACb,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,YAAY,GAAG;AACvD,aAAS,OAAO,MAAM,GAAG,EAAE,KAAK,KAAK;AAAA,EACvC;AACA,SAAO;AACT;AAOA,SAAS,iBACP,OACA,cACA,cACA,MACe;AACf,QAAM,aAAa,eACf;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA;AAEJ,QAAM,UAAU;AAAA,IACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA;AAAA,IACA;AAAA,MACE,kBAAkB;AAAA,MAClB,oBAAoB;AAAA,IACtB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,cAAc,GAAG,IAAI,iBAAiB,MAAM,KAAK;AAAA,IACjD;AAAA,EACF;AACF;AAEO,SAAS,kBAAkB,SAA2C;AAC3E,QAAM,EAAE,OAAO,cAAc,OAAO,OAAO,UAAU,OAAO,MAAM,MAAM,IACtE;AACF,QAAM,eAAe,QAAQ,gBAAgB;AAC7C,QAAM,OAAO,eAAe,MAAM,KAAK;AAEvC,QAAM,aAAa,iBAAiB,OAAO,cAAc,cAAc,IAAI;AAE3E,MAAI,CAAC,MAAM;AACT,WAAO,CAAC,UAAU;AAAA,EACpB;AAEA,QAAM,UAAU;AAAA,IACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAoDA;AAAA,IACA;AAAA,MACE,iBAAiB,MACb;AAAA;AAAA;AAAA;AAAA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA,IACN;AAAA,EACF;AAEA,QAAM,aAAa;AAAA,IACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA0FA;AAAA,IACA;AAAA,MACE,iBAAiB,MACb;AAAA;AAAA;AAAA;AAAA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA,IACN;AAAA,EACF;AAEA,QAAM,aAAa;AAAA,IACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAaA;AAAA,IACA;AAAA,MACE,qBAAqB,UACjB;AAAA;AAAA,IACA;AAAA,MACJ,wBAAwB,UACpB,uDACA;AAAA,IACN;AAAA,EACF;AAEA,QAAM,SAAS;AAAA,IACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBA;AAAA,IACA,EAAE,oBAAoB,aAAa;AAAA,EACrC;AAEA,QAAM,QAAQ;AAAA,IACZ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAcA;AAAA,IACA,EAAE,oBAAoB,aAAa;AAAA,EACrC;AAEA,QAAM,QAAyB;AAAA,IAC7B,EAAE,cAAc,GAAG,IAAI,IAAI,MAAM,KAAK,cAAc,SAAS,WAAW;AAAA,IACxE;AAAA,MACE,cAAc,GAAG,IAAI,gBAAgB,MAAM,KAAK;AAAA,MAChD,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,cAAc,GAAG,IAAI,aAAa,MAAM,KAAK;AAAA,MAC7C,SAAS;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,MACE,cAAc,GAAG,IAAI,iBAAiB,MAAM,KAAK;AAAA,MACjD,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,cAAc,GAAG,IAAI,gBAAgB,MAAM,KAAK;AAAA,MAChD,SAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI,KAAK;AACP,UAAM,KAAK;AAAA,MACT,cAAc,GAAG,IAAI,QAAQ,MAAM,KAAK;AAAA,MACxC,SAAS;AAAA,QACP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QA+BA;AAAA,QACA,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,SAAS;AACX,UAAM;AAAA,MACJ;AAAA,QACE,cAAc,GAAG,IAAI,YAAY,MAAM,KAAK;AAAA,QAC5C,SAAS;AAAA,UACP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAiBA;AAAA,UACA,CAAC;AAAA,QACH;AAAA,MACF;AAAA,MACA;AAAA,QACE,cAAc,GAAG,IAAI,YAAY,MAAM,KAAK;AAAA,QAC5C,SAAS;AAAA,UACP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UASA;AAAA,UACA,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;ACtaA,YAAY,QAAQ;AACpB,YAAY,UAAU;AAqBf,SAAS,YAAY,SAAuC;AACjE,QAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;AACvC,QAAM,QAAQ,aAAa,QAAQ,MAAM,QAAQ,KAAK;AACtD,QAAM,OAAO,CAAC,CAAC,QAAQ;AACvB,QAAM,QAAQ,kBAAkB;AAAA,IAC9B;AAAA,IACA,cAAc,CAAC,CAAC,QAAQ;AAAA,IACxB;AAAA,IACA,SAAS,CAAC,CAAC,QAAQ;AAAA,IACnB,KAAK,CAAC,CAAC,QAAQ;AAAA,IACf,cAAc,QAAQ;AAAA,EACxB,CAAC;AAED,aAAW,QAAQ,OAAO;AACxB,UAAM,WAAgB,UAAK,KAAK,KAAK,YAAY;AACjD,QAAI,QAAQ,QAAQ;AAClB,cAAQ,IAAI,yBAAyB,KAAK,YAAY,EAAE;AACxD;AAAA,IACF;AACA,QAAO,cAAW,QAAQ,GAAG;AAC3B,cAAQ,KAAK,kBAAkB,KAAK,YAAY,EAAE;AAClD;AAAA,IACF;AACA,IAAG,aAAe,aAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AACxD,UAAM,UAAU,KAAK,QAAQ,SAAS,IAAI,IACtC,KAAK,UACL,GAAG,KAAK,OAAO;AAAA;AACnB,IAAG,iBAAc,UAAU,SAAS,OAAO;AAC3C,YAAQ,IAAI,WAAW,KAAK,YAAY,EAAE;AAAA,EAC5C;AAEA,MAAI,MAAM;AACR,YAAQ;AAAA,MACN;AAAA,qBAAwB,MAAM,KAAK,eAAe,MAAM,MAAM;AAAA,IAChE;AAAA,EACF,OAAO;AACL,YAAQ;AAAA,MACN;AAAA,yBAA4B,MAAM,MAAM;AAAA,IAC1C;AAAA,EACF;AACF;;;AC9DA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAYA,SAAS,YAAY,UAAkC,CAAC,GAAS;AACtE,QAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;AACvC,QAAM,kBAAkB;AAAA,IACtB,YAAY,QAAQ;AAAA,IACpB,oBAAoB,QAAQ;AAAA,EAC9B;AAEA,MAAI,QAAQ,WAAW,OAAO;AAC5B,QAAI;AACF,+BAAyB,KAAK,eAAe;AAC7C,cAAQ,IAAI,mCAAmC;AAAA,IACjD,SAAS,KAAK;AACZ,cAAQ,MAAO,IAAc,OAAO;AACpC,cAAQ,WAAW;AAAA,IACrB;AACA;AAAA,EACF;AAEA,QAAM,SAAS,sBAAsB,KAAK,eAAe;AACzD,MAAI,OAAO,WAAW,GAAG;AACvB,YAAQ,IAAI,mCAAmC;AAC/C;AAAA,EACF;AAEA,aAAW,SAAS,QAAQ;AAC1B,YAAQ,MAAM,OAAO,MAAM,IAAI,KAAK,MAAM,OAAO,EAAE;AAAA,EACrD;AACA,UAAQ,WAAW;AACrB;;;AC3CA,YAAYA,SAAQ;AACpB,YAAY,QAAQ;AACpB,YAAYC,WAAU;AACtB,SAAS,qBAAqB;AAE9B,IAAM,cAAc,CAAC,aAAa,kBAAkB;AACpD,IAAM,YAAY;AA0BlB,SAAS,aAAa,KAAsB;AAC1C,SAAU,eAAgB,WAAK,KAAK,aAAa,UAAU,CAAC;AAC9D;AAMO,SAAS,eAAe,UAAsC;AACnE,MAAI,MAAW,cAAQ,QAAQ;AAC/B,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,eAAW,aAAa;AAAA,MACjB,WAAK,KAAK,QAAQ;AAAA,MACvB;AAAA,MACK,WAAK,KAAK,aAAa,eAAe;AAAA,IAC7C,GAAG;AACD,UAAI,aAAa,SAAS,EAAG,QAAO;AAAA,IACtC;AACA,UAAM,SAAc,cAAQ,GAAG;AAC/B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAEO,SAAS,cAAc,YAAwC;AACpE,QAAM,aAAa;AAAA,IACZ,WAAK,YAAY,MAAM,aAAa,gBAAgB,SAAS;AAAA,IAC7D,WAAK,YAAY,MAAM,SAAS,SAAS;AAAA,IACzC,WAAU,cAAQ,UAAU,GAAG,SAAS,SAAS;AAAA,EACxD;AACA,SAAO,WAAW,KAAK,CAAC,MAAS,eAAW,CAAC,CAAC;AAChD;AAEA,SAAS,QAAQ,KAAa,MAAoB;AAChD,EAAG,cAAU,MAAM,EAAE,WAAW,KAAK,CAAC;AACtC,aAAW,SAAY,gBAAY,KAAK,EAAE,eAAe,KAAK,CAAC,GAAG;AAChE,QAAI,MAAM,SAAS,OAAQ;AAC3B,UAAM,OAAY,WAAK,KAAK,MAAM,IAAI;AACtC,UAAM,KAAU,WAAK,MAAM,MAAM,IAAI;AACrC,QAAI,MAAM,YAAY,GAAG;AACvB,cAAQ,MAAM,EAAE;AAAA,IAClB,WAAW,MAAM,OAAO,GAAG;AACzB,MAAG,iBAAa,MAAM,EAAE;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,SAAS,cAAsB;AAC7B,SAAO,QAAQ,IAAI,QAAW,WAAQ;AACxC;AAEA,SAAS,uBAAuB,MAAoB;AAClD,QAAM,YAAiB,WAAK,YAAY,GAAG,WAAW,eAAe;AACrE,QAAM,WAAgB,cAAQ,IAAI;AAClC,MAAI,aAAa,aAAa,SAAS,WAAW,GAAG,SAAS,GAAQ,SAAG,EAAE,GAAG;AAC5E,UAAM,IAAI;AAAA,MACR,4BAA4B,QAAQ;AAAA,IACtC;AAAA,EACF;AACF;AAEA,SAAS,cAAsB;AAC7B,MAAI;AACF,UAAM,MAAM,YAAY;AACxB,QAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,GAAG;AAC7C,aAAY,cAAQ,cAAc,GAAG,CAAC;AAAA,IACxC;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO,QAAQ,IAAI;AACrB;AAEO,SAAS,UAAU,UAAgC,CAAC,GAAwB;AACjF,QAAM,aACJ,QAAQ,cACR,eAAe,YAAY,CAAC,KAC5B,eAAe,QAAQ,IAAI,CAAC;AAC9B,MAAI,CAAC,YAAY;AACf,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,YAAY,YAAY;AAAA,IAAO,CAAC,SACjC,eAAgB,WAAK,YAAY,MAAM,UAAU,CAAC;AAAA,EACvD;AACA,MAAI,UAAU,WAAW,GAAG;AAC1B,UAAM,IAAI,MAAM,sBAAsB,UAAU,EAAE;AAAA,EACpD;AAEA,QAAM,YAAY,QAAQ,OAAO,SAC7B,QAAQ,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,IACjD,CAAC,GAAG,SAAS;AAEjB,aAAW,QAAQ,WAAW;AAC5B,QAAI,CAAC,UAAU,SAAS,IAAoC,GAAG;AAC7D,YAAM,IAAI;AAAA,QACR,kBAAkB,IAAI,iBAAiB,UAAU,KAAK,IAAI,CAAC;AAAA,MAC7D;AAAA,IACF;AAAA,EACF;AAEA,MAAI,QAAQ,MAAM;AAChB,YAAQ,IAAI,aAAa,UAAU,GAAG;AACtC,eAAW,QAAQ,WAAW;AAC5B,cAAQ,IAAI,OAAO,IAAI,EAAE;AAAA,IAC3B;AACA,WAAO,EAAE,MAAM,YAAY,WAAW,CAAC,EAAE;AAAA,EAC3C;AAEA,QAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;AACvC,QAAM,OAAO,QAAQ,SACZ,WAAK,YAAY,GAAG,WAAW,QAAQ,IACvC;AAAA,IACE,cAAQ,QAAQ,eAAe,GAAG;AAAA,IACvC;AAAA,IACA;AAAA,EACF;AAEJ,yBAAuB,IAAI;AAE3B,MAAI,CAAC,QAAQ,QAAQ;AACnB,IAAG,cAAU,MAAM,EAAE,WAAW,KAAK,CAAC;AAAA,EACxC;AAEA,QAAM,YAAsB,CAAC;AAC7B,aAAW,QAAQ,WAAW;AAC5B,UAAM,MAAW,WAAK,YAAY,IAAI;AACtC,UAAM,SAAc,WAAK,MAAM,IAAI;AACnC,QAAI,QAAQ,QAAQ;AAClB,cAAQ,IAAI,2BAA2B,IAAI,OAAO,MAAM,EAAE;AAAA,IAC5D,OAAO;AACL,MAAG,WAAO,QAAQ,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAClD,cAAQ,KAAK,MAAM;AACnB,cAAQ,IAAI,aAAa,IAAI,OAAO,MAAM,EAAE;AAAA,IAC9C;AACA,cAAU,KAAK,IAAI;AAAA,EACrB;AAEA,MAAI;AACJ,MAAI,QAAQ,WAAW;AACrB,UAAM,WAAW,QAAQ,aAAa,cAAc,UAAU;AAC9D,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,kBAAkB,SAAS,kBAAkB;AAAA,IAC/D;AACA,UAAM,WAAW,QAAQ,SAChB,WAAK,YAAY,GAAG,WAAW,OAAO,IACtC,WAAU,cAAQ,QAAQ,eAAe,GAAG,GAAG,WAAW,OAAO;AAC1E,eAAgB,WAAK,UAAU,SAAS;AACxC,QAAI,QAAQ,QAAQ;AAClB,cAAQ,IAAI,mCAAmC,QAAQ,EAAE;AAAA,IAC3D,OAAO;AACL,MAAG,cAAU,UAAU,EAAE,WAAW,KAAK,CAAC;AAC1C,MAAG,iBAAa,UAAU,QAAQ;AAClC,cAAQ,IAAI,qBAAqB,QAAQ,EAAE;AAAA,IAC7C;AAAA,EACF;AAEA,QAAM,QAAQ,QAAQ,SAClB,8BACA;AACJ,MAAI,QAAQ,QAAQ;AAClB,YAAQ,IAAI,qBAAqB,KAAK,IAAI;AAAA,EAC5C,WAAW,QAAQ,QAAQ;AACzB,YAAQ,IAAI,sEAAsE,KAAK,IAAI;AAAA,EAC7F,OAAO;AACL,YAAQ;AAAA,MACN,2EAA2E,KAAK;AAAA,IAClF;AAAA,EACF;AAEA,SAAO,EAAE,MAAM,WAAW,SAAS;AACrC;","names":["fs","path"]}
package/dist/index.cjs CHANGED
@@ -31,10 +31,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  assertKebabName: () => assertKebabName,
34
+ findSkillsRoot: () => findSkillsRoot,
34
35
  renderModuleFiles: () => renderModuleFiles,
35
36
  resolveNames: () => resolveNames,
36
- runCodegen: () => runCodegen,
37
37
  runGenerate: () => runGenerate,
38
+ runSkills: () => runSkills,
38
39
  runValidate: () => runValidate
39
40
  });
40
41
  module.exports = __toCommonJS(src_exports);
@@ -104,7 +105,7 @@ export const {{pascal}}Repository = createInjectableRepository({
104
105
  scalarFields: Prisma.{{pascal}}ScalarFieldEnum,
105
106
  {{cacheBlock}}});
106
107
 
107
- export type {{pascal}}Repository = InstanceType<typeof {{pascal}}Repository>;
108
+ export interface {{pascal}}Repository extends InstanceType<typeof {{pascal}}Repository> {}
108
109
  `,
109
110
  names,
110
111
  {
@@ -506,60 +507,17 @@ Scaffolded repository "${names.pascal}Repository". Register it in your feature m
506
507
  }
507
508
  }
508
509
 
509
- // src/commands/codegen.ts
510
- var fs2 = __toESM(require("fs"), 1);
511
- var path2 = __toESM(require("path"), 1);
512
- var import_core = require("@prismakit/core");
513
- function runCodegen(options = {}) {
514
- const cwd = options.cwd ?? process.cwd();
515
- const schemaPath = options.schemaPath ?? path2.join(cwd, "prisma", "schema.prisma");
516
- if (!fs2.existsSync(schemaPath)) {
517
- throw new Error(`Prisma schema not found at ${schemaPath}`);
518
- }
519
- const models = (0, import_core.getSchemaModels)(schemaPath);
520
- const aliases = (0, import_core.computeRelationAliasesFromSchema)(models);
521
- const entries = Object.entries(aliases).sort(
522
- ([a], [b]) => a.localeCompare(b)
523
- );
524
- if (entries.length === 0) {
525
- console.log(
526
- "No additional relation aliases suggested (suffix rules cover all)."
527
- );
528
- return;
529
- }
530
- const lines = [
531
- "// Suggested RELATION_MODEL_ALIASES entries (merge into your resolver config)",
532
- "export const SUGGESTED_RELATION_MODEL_ALIASES = {",
533
- ...entries.map(([k, v]) => ` ${k}: '${v}',`),
534
- "} as const;",
535
- ""
536
- ];
537
- const output = lines.join("\n");
538
- if (options.write) {
539
- const out = options.outFile ?? path2.join(
540
- cwd,
541
- "src",
542
- "infrastructure",
543
- "prisma",
544
- "suggested-relation-aliases.ts"
545
- );
546
- fs2.mkdirSync(path2.dirname(out), { recursive: true });
547
- fs2.writeFileSync(out, output, "utf-8");
548
- console.log(`wrote ${path2.relative(cwd, out)}`);
549
- } else {
550
- console.log(output);
551
- }
552
- console.log(`
553
- ${entries.length} alias suggestion(s).`);
554
- }
555
-
556
510
  // src/commands/validate.ts
557
- var import_core2 = require("@prismakit/core");
511
+ var import_core = require("@prismakit/core");
558
512
  function runValidate(options = {}) {
559
513
  const cwd = options.cwd ?? process.cwd();
514
+ const validateOptions = {
515
+ schemaPath: options.schemaPath,
516
+ autoRegisterModels: options.autoRegisterModels
517
+ };
560
518
  if (options.assert !== false) {
561
519
  try {
562
- (0, import_core2.assertSelectComposeValid)(cwd);
520
+ (0, import_core.assertSelectComposeValid)(cwd, validateOptions);
563
521
  console.log("Select compose validation passed.");
564
522
  } catch (err) {
565
523
  console.error(err.message);
@@ -567,7 +525,7 @@ function runValidate(options = {}) {
567
525
  }
568
526
  return;
569
527
  }
570
- const issues = (0, import_core2.validateSelectCompose)(cwd);
528
+ const issues = (0, import_core.validateSelectCompose)(cwd, validateOptions);
571
529
  if (issues.length === 0) {
572
530
  console.log("Select compose validation passed.");
573
531
  return;
@@ -577,13 +535,164 @@ function runValidate(options = {}) {
577
535
  }
578
536
  process.exitCode = 1;
579
537
  }
538
+
539
+ // src/commands/skills.ts
540
+ var fs2 = __toESM(require("fs"), 1);
541
+ var os = __toESM(require("os"), 1);
542
+ var path2 = __toESM(require("path"), 1);
543
+ var import_node_url = require("url");
544
+ var import_meta = {};
545
+ var SKILL_NAMES = ["prismakit", "prismakit-nestjs"];
546
+ var RULE_NAME = "data-access.mdc";
547
+ function isSkillsRoot(dir) {
548
+ return fs2.existsSync(path2.join(dir, "prismakit", "SKILL.md"));
549
+ }
550
+ function findSkillsRoot(startDir) {
551
+ let dir = path2.resolve(startDir);
552
+ for (let i = 0; i < 10; i++) {
553
+ for (const candidate of [
554
+ path2.join(dir, "skills"),
555
+ dir,
556
+ path2.join(dir, "templates", "cursor-skills")
557
+ ]) {
558
+ if (isSkillsRoot(candidate)) return candidate;
559
+ }
560
+ const parent = path2.dirname(dir);
561
+ if (parent === dir) break;
562
+ dir = parent;
563
+ }
564
+ return void 0;
565
+ }
566
+ function findRulesPath(skillsRoot) {
567
+ const candidates = [
568
+ path2.join(skillsRoot, "..", "templates", "cursor-rules", RULE_NAME),
569
+ path2.join(skillsRoot, "..", "rules", RULE_NAME),
570
+ path2.join(path2.dirname(skillsRoot), "rules", RULE_NAME)
571
+ ];
572
+ return candidates.find((p) => fs2.existsSync(p));
573
+ }
574
+ function copyDir(src, dest) {
575
+ fs2.mkdirSync(dest, { recursive: true });
576
+ for (const entry of fs2.readdirSync(src, { withFileTypes: true })) {
577
+ if (entry.name === ".git") continue;
578
+ const from = path2.join(src, entry.name);
579
+ const to = path2.join(dest, entry.name);
580
+ if (entry.isDirectory()) {
581
+ copyDir(from, to);
582
+ } else if (entry.isFile()) {
583
+ fs2.copyFileSync(from, to);
584
+ }
585
+ }
586
+ }
587
+ function resolveHome() {
588
+ return process.env.HOME || os.homedir();
589
+ }
590
+ function assertNotBuiltinSkills(dest) {
591
+ const forbidden = path2.join(resolveHome(), ".cursor", "skills-cursor");
592
+ const resolved = path2.resolve(dest);
593
+ if (resolved === forbidden || resolved.startsWith(`${forbidden}${path2.sep}`)) {
594
+ throw new Error(
595
+ `Refusing to install into ${resolved} (reserved for Cursor built-ins).`
596
+ );
597
+ }
598
+ }
599
+ function cliStartDir() {
600
+ try {
601
+ const url = import_meta.url;
602
+ if (typeof url === "string" && url.length > 0) {
603
+ return path2.dirname((0, import_node_url.fileURLToPath)(url));
604
+ }
605
+ } catch {
606
+ }
607
+ return process.cwd();
608
+ }
609
+ function runSkills(options = {}) {
610
+ const skillsRoot = options.skillsRoot ?? findSkillsRoot(cliStartDir()) ?? findSkillsRoot(process.cwd());
611
+ if (!skillsRoot) {
612
+ throw new Error(
613
+ "Could not find PrismaKit skills. Reinstall @prismakit/cli or clone fikiap23/prismakit."
614
+ );
615
+ }
616
+ const available = SKILL_NAMES.filter(
617
+ (name) => fs2.existsSync(path2.join(skillsRoot, name, "SKILL.md"))
618
+ );
619
+ if (available.length === 0) {
620
+ throw new Error(`No skills found in ${skillsRoot}`);
621
+ }
622
+ const requested = options.skill?.length ? options.skill.map((s) => s.trim()).filter(Boolean) : [...available];
623
+ for (const name of requested) {
624
+ if (!available.includes(name)) {
625
+ throw new Error(
626
+ `Unknown skill "${name}". Available: ${available.join(", ")}`
627
+ );
628
+ }
629
+ }
630
+ if (options.list) {
631
+ console.log(`Skills in ${skillsRoot}:`);
632
+ for (const name of available) {
633
+ console.log(` - ${name}`);
634
+ }
635
+ return { dest: skillsRoot, installed: [] };
636
+ }
637
+ const cwd = options.cwd ?? process.cwd();
638
+ const dest = options.global ? path2.join(resolveHome(), ".cursor", "skills") : path2.join(
639
+ path2.resolve(options.projectRoot ?? cwd),
640
+ ".cursor",
641
+ "skills"
642
+ );
643
+ assertNotBuiltinSkills(dest);
644
+ if (!options.dryRun) {
645
+ fs2.mkdirSync(dest, { recursive: true });
646
+ }
647
+ const installed = [];
648
+ for (const name of requested) {
649
+ const src = path2.join(skillsRoot, name);
650
+ const target = path2.join(dest, name);
651
+ if (options.dryRun) {
652
+ console.log(`[dry-run] would install ${name} -> ${target}`);
653
+ } else {
654
+ fs2.rmSync(target, { recursive: true, force: true });
655
+ copyDir(src, target);
656
+ console.log(`Installed ${name} -> ${target}`);
657
+ }
658
+ installed.push(name);
659
+ }
660
+ let ruleDest;
661
+ if (options.withRules) {
662
+ const rulesSrc = options.rulesPath ?? findRulesPath(skillsRoot);
663
+ if (!rulesSrc) {
664
+ throw new Error(`Could not find ${RULE_NAME} next to skills.`);
665
+ }
666
+ const rulesDir = options.global ? path2.join(resolveHome(), ".cursor", "rules") : path2.join(path2.resolve(options.projectRoot ?? cwd), ".cursor", "rules");
667
+ ruleDest = path2.join(rulesDir, RULE_NAME);
668
+ if (options.dryRun) {
669
+ console.log(`[dry-run] would install rule -> ${ruleDest}`);
670
+ } else {
671
+ fs2.mkdirSync(rulesDir, { recursive: true });
672
+ fs2.copyFileSync(rulesSrc, ruleDest);
673
+ console.log(`Installed rule -> ${ruleDest}`);
674
+ }
675
+ }
676
+ const scope = options.global ? "global (~/.cursor/skills)" : "project (.cursor/skills)";
677
+ if (options.dryRun) {
678
+ console.log(`Dry-run complete (${scope}).`);
679
+ } else if (options.global) {
680
+ console.log(`Done. Skills are available in all Cursor projects on this machine (${scope}).`);
681
+ } else {
682
+ console.log(
683
+ `Done. Commit .cursor/skills so the team shares the same agent contract (${scope}).`
684
+ );
685
+ }
686
+ return { dest, installed, ruleDest };
687
+ }
580
688
  // Annotate the CommonJS export names for ESM import in node:
581
689
  0 && (module.exports = {
582
690
  assertKebabName,
691
+ findSkillsRoot,
583
692
  renderModuleFiles,
584
693
  resolveNames,
585
- runCodegen,
586
694
  runGenerate,
695
+ runSkills,
587
696
  runValidate
588
697
  });
589
698
  //# sourceMappingURL=index.cjs.map