@prismakit/cli 2.2.3 → 3.0.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.
@@ -0,0 +1,235 @@
1
+ # PrismaKit NestJS examples
2
+
3
+ Production-shaped snippets. Contract: skill `prismakit` + [SKILL.md](SKILL.md).
4
+
5
+ ## 1. App module (Redis, allowlist, slow-query log)
6
+
7
+ ```typescript
8
+ // src/app.module.ts
9
+ import { Module } from '@nestjs/common';
10
+ import { ConfigModule, ConfigService } from '@nestjs/config';
11
+ import { PrismaKitModule } from '@prismakit/nestjs';
12
+ import { RedisCacheAdapter } from '@prismakit/redis';
13
+ import { PrismaClientModule } from './infrastructure/prisma/prisma-client.module';
14
+ import { PrismaService } from './infrastructure/prisma/prisma.service';
15
+ import { UserModule } from './modules/users/user.module';
16
+
17
+ @Module({
18
+ imports: [
19
+ ConfigModule.forRoot({ isGlobal: true }),
20
+ PrismaClientModule,
21
+ PrismaKitModule.forRootAsync({
22
+ imports: [PrismaClientModule],
23
+ inject: [PrismaService, ConfigService],
24
+ useFactory: (prisma: PrismaService, config: ConfigService) => ({
25
+ prisma,
26
+ cache: new RedisCacheAdapter({
27
+ url: config.get<string>('REDIS_URL'),
28
+ prefix: config.get<string>('CACHE_PREFIX') ?? 'myapp',
29
+ }),
30
+ cacheModels: ['user', 'product', 'wallet'],
31
+ schemaPath: 'prisma/schema.prisma',
32
+ validateCompose: true,
33
+ compose: { maxDepth: 6, parallel: true, setCache: true },
34
+ autoRegisterModels: true,
35
+ queryLog: {
36
+ slowThreshold: 500,
37
+ onSlowQuery: (e) => {
38
+ console.warn(`Slow ${e.model}.${e.method}: ${e.durationMs}ms`);
39
+ },
40
+ },
41
+ }),
42
+ }),
43
+ UserModule,
44
+ ],
45
+ })
46
+ export class AppModule {}
47
+ ```
48
+
49
+ Prisma 5/6: pass `dmmf: Prisma.dmmf` instead of (or in addition to skipping) `schemaPath`. Prisma 7: `schemaPath` only.
50
+
51
+ ## 2. TypeMap binder
52
+
53
+ ```typescript
54
+ // src/infrastructure/prisma/define-repo.ts
55
+ import { createDefineRepo } from '@prismakit/nestjs';
56
+ import type { Prisma } from '@prisma/client';
57
+
58
+ export const defineRepo = createDefineRepo<Prisma.TypeMap>();
59
+ ```
60
+
61
+ ## 3. Feature repository + select presets
62
+
63
+ ```typescript
64
+ // src/modules/users/repositories/user.repository.ts
65
+ import { Prisma } from '@prisma/client';
66
+ import { defineRepo } from '../../../infrastructure/prisma/define-repo';
67
+
68
+ const DAY = 86_400;
69
+
70
+ export const userSelectPresets = {
71
+ minimal: { id: true } satisfies Prisma.UserSelect,
72
+ general: {
73
+ id: true,
74
+ email: true,
75
+ name: true,
76
+ } satisfies Prisma.UserSelect,
77
+ withPassword: {
78
+ id: true,
79
+ email: true,
80
+ password: true,
81
+ } satisfies Prisma.UserSelect,
82
+ };
83
+
84
+ export const UserRepository = defineRepo({
85
+ model: 'user',
86
+ scalarFields: Prisma.UserScalarFieldEnum,
87
+ cache: {
88
+ ttl: DAY,
89
+ nullTtl: 60,
90
+ sensitiveFields: ['password'],
91
+ methods: { getFirst: { enabled: false } },
92
+ },
93
+ lock: true,
94
+ });
95
+ export type UserRepository = InstanceType<typeof UserRepository>;
96
+ ```
97
+
98
+ ## 4. Feature module + thin controller + service
99
+
100
+ ```typescript
101
+ // src/modules/users/user.module.ts
102
+ import { Module } from '@nestjs/common';
103
+ import { UserController } from './user.controller';
104
+ import { UserService } from './user.service';
105
+ import { UserRepository } from './repositories/user.repository';
106
+
107
+ @Module({
108
+ controllers: [UserController],
109
+ providers: [UserService, UserRepository],
110
+ exports: [UserService, UserRepository],
111
+ })
112
+ export class UserModule {}
113
+ ```
114
+
115
+ ```typescript
116
+ // src/modules/users/user.controller.ts
117
+ import { Controller, Get, Param } from '@nestjs/common';
118
+ import { UserService } from './user.service';
119
+
120
+ @Controller('users')
121
+ export class UserController {
122
+ constructor(private readonly users: UserService) {}
123
+
124
+ @Get(':id')
125
+ getOne(@Param('id') id: string) {
126
+ return this.users.getProfile(id);
127
+ }
128
+ }
129
+ ```
130
+
131
+ ```typescript
132
+ // src/modules/users/user.service.ts
133
+ import { Injectable } from '@nestjs/common';
134
+ import { UserRepository, userSelectPresets } from './repositories/user.repository';
135
+
136
+ @Injectable()
137
+ export class UserService {
138
+ constructor(private readonly users: UserRepository) {}
139
+
140
+ getProfile(id: string) {
141
+ return this.users.getThrowById({
142
+ id,
143
+ select: userSelectPresets.general,
144
+ setCache: true,
145
+ });
146
+ }
147
+
148
+ async assertEmailFree(email: string) {
149
+ const hit = await this.users.getFirst({
150
+ where: { email },
151
+ select: userSelectPresets.minimal,
152
+ });
153
+ if (hit) throw new Error('email taken');
154
+ }
155
+ }
156
+ ```
157
+
158
+ ## 5. Checkout — multi-repo `execTx`
159
+
160
+ ```typescript
161
+ @Injectable()
162
+ export class CheckoutService {
163
+ constructor(
164
+ private readonly tx: TransactionService,
165
+ private readonly orders: OrderRepository,
166
+ private readonly stocks: StockRepository,
167
+ ) {}
168
+
169
+ handleCheckout(input: { stockId: string; qty: number; userId: string }) {
170
+ return this.tx.execTx(
171
+ async (tx) => {
172
+ const order = await this.orders.create({
173
+ tx,
174
+ data: { userId: input.userId, stockId: input.stockId, qty: input.qty },
175
+ select: { id: true },
176
+ invalidate: 'none',
177
+ });
178
+ await this.stocks.updateById({
179
+ tx,
180
+ id: input.stockId,
181
+ data: { qty: { decrement: input.qty } },
182
+ invalidate: 'none',
183
+ });
184
+ return order;
185
+ },
186
+ async () => {
187
+ await this.orders.invalidateCache({});
188
+ await this.stocks.invalidateCache({ id: input.stockId });
189
+ },
190
+ );
191
+ }
192
+ }
193
+ ```
194
+
195
+ Register `OrderRepository` and `StockRepository` in the feature module `providers`. Both need `cache` config for `invalidate: 'none'` / `invalidateCache` to exist on the type.
196
+
197
+ ## 6. Wallet lock
198
+
199
+ ```typescript
200
+ @Injectable()
201
+ export class WalletService {
202
+ constructor(
203
+ private readonly tx: TransactionService,
204
+ private readonly wallets: WalletRepository,
205
+ ) {}
206
+
207
+ debit(id: string, amount: number) {
208
+ return this.tx.execTx(
209
+ async (tx) => {
210
+ const wallet = await this.wallets.getById({
211
+ tx,
212
+ id,
213
+ select: { id: true, balance: true },
214
+ lock: { mode: 'update' },
215
+ });
216
+ if (!wallet || wallet.balance < amount) {
217
+ throw new Error('insufficient funds');
218
+ }
219
+ return this.wallets.updateById({
220
+ tx,
221
+ id,
222
+ data: { balance: wallet.balance - amount },
223
+ select: { id: true, balance: true },
224
+ invalidate: 'none',
225
+ });
226
+ },
227
+ async () => {
228
+ await this.wallets.invalidateCache({ id });
229
+ },
230
+ );
231
+ }
232
+ }
233
+ ```
234
+
235
+ `WalletRepository` must set `lock: true` (or a table/client key). Never call `lock` outside `execTx`.
@@ -0,0 +1,146 @@
1
+ # PrismaKit NestJS reference
2
+
3
+ API surface for `@prismakit/nestjs` 3.x. Repository methods, cache, compose, and locks are documented in skill `prismakit` (`reference.md` in that skill). This file covers the Nest adapter only.
4
+
5
+ ## `PrismaKitModuleOptions`
6
+
7
+ | Option | Required | Description |
8
+ |--------|----------|-------------|
9
+ | `prisma` | yes | `PrismaClient` (or compatible). Provided as `PRISMAKIT_PRISMA` for repositories only. |
10
+ | `cache` | no | `CacheAdapter` (`RedisCacheAdapter` / `MemoryCacheAdapter`). |
11
+ | `dmmf` | no | `Prisma.dmmf` on Prisma 5/6. Skip on Prisma 7 — use `schemaPath`. |
12
+ | `schemaPath` | no | Load meta from `schema.prisma` when `dmmf` is omitted (compose + locks). Defaults to `prisma/schema.prisma`. |
13
+ | `validateCompose` | no | When `true`, `assertSelectComposeValid` on module init. |
14
+ | `cacheModels` | no | Strict allowlist of model keys with `cache` config. Omit = fail-open. |
15
+ | `compose` | no | `ComposeOptions`: `maxDepth` (default 10), `parallel` (default true), `setCache` (default true). `tx` is per-call only. |
16
+ | `telemetry` | no | `{ enabled?: boolean; onEvent?: (event) => void }`. |
17
+ | `queryLog` | no | `{ slowThreshold?: number; onSlowQuery?: (e) => void }`. Default threshold 500ms. Setting this enables telemetry. |
18
+ | `autoRegisterModels` | no | `true` = stub repos for all schema/DMMF models; `string[]` = those client keys only. |
19
+
20
+ `queryLog.onSlowQuery` receives `{ model?, method?, durationMs, thresholdMs }` for `query.complete` events at/above the threshold.
21
+
22
+ ## Async config
23
+
24
+ ```typescript
25
+ export type PrismaKitModuleAsyncOptions = {
26
+ imports?: Array<Type<unknown> | DynamicModule | Promise<DynamicModule>>;
27
+ useFactory: (...args: unknown[]) => Promise<PrismaKitModuleOptions> | PrismaKitModuleOptions;
28
+ inject?: unknown[];
29
+ };
30
+ ```
31
+
32
+ ```typescript
33
+ PrismaKitModule.forRootAsync({
34
+ imports: [ConfigModule],
35
+ inject: [ConfigService],
36
+ useFactory: (config: ConfigService) => ({
37
+ prisma: prismaClient,
38
+ cache: new RedisCacheAdapter({
39
+ url: config.get('REDIS_URL'),
40
+ prefix: config.get('CACHE_PREFIX') ?? 'myapp',
41
+ }),
42
+ cacheModels: ['user', 'product'],
43
+ schemaPath: 'prisma/schema.prisma',
44
+ }),
45
+ });
46
+ ```
47
+
48
+ ## DI tokens
49
+
50
+ | Token | Type | Allowed injectors |
51
+ |-------|------|-------------------|
52
+ | `PRISMAKIT_PRISMA` | `PrismaClientLike` | Injectable repositories / kit internals only |
53
+ | `PRISMAKIT_CACHE` | `CacheAdapter` | Repositories / kit internals |
54
+ | `PRISMAKIT_OPTIONS` | `PrismaKitModuleOptions` | Kit internals |
55
+
56
+ `TransactionService`, `RepositoryRegistry`, and `AutoComposer` are Nest providers from `PrismaKitModule`.
57
+
58
+ ## `TransactionService`
59
+
60
+ ```typescript
61
+ execTx<T, TClient = unknown>(
62
+ fn: (tx: TClient) => Promise<T>,
63
+ afterCommit?: () => Promise<void>,
64
+ options?: TransactionOptions,
65
+ ): Promise<T>
66
+ ```
67
+
68
+ `TransactionOptions`: `{ maxWait?: number; timeout?: number; isolationLevel?: 'ReadUncommitted' | 'ReadCommitted' | 'RepeatableRead' | 'Serializable' | string }`.
69
+
70
+ Type the client when useful:
71
+
72
+ ```typescript
73
+ await this.tx.execTx<User, Prisma.TransactionClient>(async (tx) => { /* ... */ });
74
+ ```
75
+
76
+ `afterCommit` runs only after `prisma.$transaction` resolves successfully.
77
+
78
+ ## Repository factories
79
+
80
+ | Factory | When |
81
+ |---------|------|
82
+ | `createDefineRepo<Prisma.TypeMap>()` then local `defineRepo({ model, ... })` | **Default** for apps. Zero phantoms; `model` + `scalarFields` + `cache` / `lock`. |
83
+ | `defineInjectableRepository({ model, select, create, update, where, orderBy, payload, ... })` | TypeMap unavailable. Package aliases: `defineRepository`. |
84
+ | `createInjectableRepository({ model, ... })` | Thin / untyped. Results `unknown` unless `toPayload` is supplied. Alias: `createPrismaRepository`. |
85
+
86
+ `createDefineRepo` runtime options: `model`, `scalarFields?`, `primaryKey?`, `cache?`, `lock?`, `schemaPath?`.
87
+
88
+ When `cache` is set, the returned API includes `setCache` / `cacheTags` / `invalidate` / `tags` / `invalidateCache`. Otherwise those fields are omitted from the type (`HasCacheFromOptions`).
89
+
90
+ `createDefineRepo` / `RepositoryApiFromTypeMap` includes the full runtime surface: `createMany`, `updateMany`, `upsert`, `deleteMany`, `lock` + `orderBy` on `getFirst`, `lock` on `getMany`, and composite-PK `id` on `*ById`. Derive the instance type from the constructor — do not restate model/cache as a second generic:
91
+
92
+ Export the instance type:
93
+
94
+ ```typescript
95
+ export type UserRepository = InstanceType<typeof UserRepository>;
96
+ ```
97
+
98
+ ## `defineInjectableRepository` shape (escape hatch)
99
+
100
+ ```typescript
101
+ import { Prisma } from '@prisma/client';
102
+ import { defineInjectableRepository } from '@prismakit/nestjs';
103
+
104
+ type Of<S> = S extends Prisma.UserSelect
105
+ ? Prisma.UserGetPayload<{ select: S }>
106
+ : never;
107
+
108
+ export const UserRepository = defineInjectableRepository({
109
+ model: 'user',
110
+ scalarFields: Prisma.UserScalarFieldEnum,
111
+ select: null! as Prisma.UserSelect,
112
+ create: null! as Prisma.UserCreateInput,
113
+ update: null! as Prisma.UserUpdateInput,
114
+ where: null! as Prisma.UserWhereInput,
115
+ orderBy: null! as Prisma.UserOrderByWithRelationInput,
116
+ payload: class {
117
+ declare readonly _select: unknown;
118
+ declare type: () => Of<this['_select']>;
119
+ },
120
+ cache: { ttl: 86_400, sensitiveFields: ['password'] },
121
+ lock: true,
122
+ });
123
+ ```
124
+
125
+ ## Re-exports from core
126
+
127
+ `@prismakit/nestjs` re-exports: `AutoComposer`, `RepositoryRegistry`, `CacheAdapter`, repository option/instance types, `RepoPayloadHKT`, `ComposeOptions`, `TelemetryOptions`, `TelemetryEvent`, `loadPrismaMetaFromDmmf`, `loadPrismaMetaFromSchema`, `setComposeOptions`, `setTelemetry`.
128
+
129
+ Prefer importing Nest-only APIs from `@prismakit/nestjs` and core-only helpers from `@prismakit/core`.
130
+
131
+ ## Layout
132
+
133
+ ```
134
+ src/
135
+ app.module.ts # PrismaKitModule.forRootAsync
136
+ infrastructure/prisma/
137
+ define-repo.ts # createDefineRepo<Prisma.TypeMap>()
138
+ prisma.service.ts # client construction only
139
+ modules/<feature>/
140
+ <feature>.module.ts # providers: [Service, XRepository]
141
+ <feature>.service.ts # inject repos + TransactionService
142
+ <feature>.controller.ts # HTTP only
143
+ repositories/<feature>.repository.ts
144
+ ```
145
+
146
+ Prisma client usage is allowed under `**/repositories/**` and `**/infrastructure/prisma/**` only.
@@ -0,0 +1,57 @@
1
+ # PrismaKit NestJS review checklist
2
+
3
+ Copy this list and tick every item before marking a Nest data-access change done. Also complete skill `prismakit` `review-checklist.md` (layering, cache, compose, locks).
4
+
5
+ ```
6
+ Task Progress:
7
+ - [ ] Core contract (skill prismakit checklist)
8
+ - [ ] Module wiring
9
+ - [ ] Repository factory
10
+ - [ ] DI
11
+ - [ ] Transactions
12
+ - [ ] Feature shape
13
+ ```
14
+
15
+ ## Core contract
16
+
17
+ - [ ] Skill `prismakit` review checklist is complete for this change
18
+
19
+ ## Module wiring
20
+
21
+ - [ ] `PrismaKitModule.forRoot` or `forRootAsync` is imported once at the app root
22
+ - [ ] `prisma` is the shared client instance
23
+ - [ ] Prisma meta is loaded: `dmmf: Prisma.dmmf` (Prisma 5/6) or `schemaPath: 'prisma/schema.prisma'` (Prisma 7)
24
+ - [ ] Production cache uses `RedisCacheAdapter` with a stable `prefix`
25
+ - [ ] `cacheModels` lists every model whose repository sets `cache`
26
+ - [ ] `validateCompose: true` is on for apps that nest relations in `select`
27
+
28
+ ## Repository factory
29
+
30
+ - [ ] App has a single binder: `createDefineRepo<Prisma.TypeMap>()` in `src/infrastructure/prisma/define-repo.ts` (or equivalent)
31
+ - [ ] Feature repos call that binder — not `createInjectableRepository` unless types are intentionally thin
32
+ - [ ] `export type XRepository = InstanceType<typeof XRepository>`
33
+ - [ ] Repo class is in `providers` and `exports` of the feature module
34
+ - [ ] `lock: true` (or table/client key) is set when any call uses `lock`
35
+
36
+ ## DI
37
+
38
+ - [ ] Services inject `*Repository` and `TransactionService` — never `PRISMAKIT_PRISMA`, `PrismaClient`, or `PrismaService`
39
+ - [ ] Controllers inject services — not repositories, not Prisma
40
+ - [ ] `PRISMAKIT_PRISMA` appears only inside `**/repositories/**` or kit internals
41
+
42
+ ## Transactions
43
+
44
+ - [ ] Multi-step writes use `this.tx.execTx(fn, afterCommit)`
45
+ - [ ] No `prisma.$transaction` / `.$transaction` in feature code
46
+ - [ ] Every repo call inside `fn` receives `tx`
47
+ - [ ] Writes use `invalidate: 'none'`
48
+ - [ ] Matching `invalidateCache` calls live in `afterCommit`
49
+ - [ ] Row locks run only inside `execTx`
50
+
51
+ ## Feature shape
52
+
53
+ - [ ] HTTP mapping stays in the controller; orchestration in the service; Prisma in the repository
54
+ - [ ] Select presets live next to the repository (`minimal` / `general` / `withPassword`)
55
+ - [ ] After `prismakit generate --full`, the new `*Module` is imported in `AppModule`
56
+ - [ ] After repo-only generate, the class is registered in feature `providers`
57
+ - [ ] ESLint `prismakit.configs.recommended` passes
@@ -0,0 +1,117 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { afterEach, describe, expect, it } from 'vitest';
6
+
7
+ import { findSkillsRoot, runSkills } from '../commands/skills';
8
+
9
+ const repoSkills = path.resolve(
10
+ path.dirname(fileURLToPath(import.meta.url)),
11
+ '../../../../skills',
12
+ );
13
+
14
+ const tmpDirs: string[] = [];
15
+
16
+ function tmpDir(): string {
17
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'prismakit-skills-'));
18
+ tmpDirs.push(dir);
19
+ return dir;
20
+ }
21
+
22
+ afterEach(() => {
23
+ for (const dir of tmpDirs.splice(0)) {
24
+ fs.rmSync(dir, { recursive: true, force: true });
25
+ }
26
+ });
27
+
28
+ describe('findSkillsRoot', () => {
29
+ it('finds bundled or monorepo skills/ from this test file', () => {
30
+ const found = findSkillsRoot(path.dirname(fileURLToPath(import.meta.url)));
31
+ expect(found).toBeTruthy();
32
+ expect(fs.existsSync(path.join(found!, 'prismakit', 'SKILL.md'))).toBe(
33
+ true,
34
+ );
35
+ expect(
36
+ fs.existsSync(path.join(found!, 'prismakit-nestjs', 'SKILL.md')),
37
+ ).toBe(true);
38
+ });
39
+ });
40
+
41
+ describe('runSkills', () => {
42
+ it('lists bundled skills', () => {
43
+ const result = runSkills({ list: true, skillsRoot: repoSkills });
44
+ expect(result.installed).toEqual([]);
45
+ expect(result.dest).toBe(repoSkills);
46
+ });
47
+
48
+ it('installs both skills into a project .cursor/skills', () => {
49
+ const cwd = tmpDir();
50
+ const result = runSkills({ cwd, skillsRoot: repoSkills });
51
+ expect(result.installed).toEqual(['prismakit', 'prismakit-nestjs']);
52
+ expect(
53
+ fs.existsSync(path.join(cwd, '.cursor/skills/prismakit/SKILL.md')),
54
+ ).toBe(true);
55
+ expect(
56
+ fs.existsSync(
57
+ path.join(cwd, '.cursor/skills/prismakit-nestjs/SKILL.md'),
58
+ ),
59
+ ).toBe(true);
60
+ expect(
61
+ fs.readFileSync(
62
+ path.join(cwd, '.cursor/skills/prismakit/SKILL.md'),
63
+ 'utf-8',
64
+ ),
65
+ ).toContain('name: prismakit');
66
+ });
67
+
68
+ it('installs a single skill and overwrites on re-run', () => {
69
+ const cwd = tmpDir();
70
+ runSkills({ cwd, skillsRoot: repoSkills, skill: ['prismakit'] });
71
+ const skillFile = path.join(cwd, '.cursor/skills/prismakit/SKILL.md');
72
+ fs.appendFileSync(skillFile, '\n# dirty\n');
73
+ runSkills({ cwd, skillsRoot: repoSkills, skill: ['prismakit'] });
74
+ expect(fs.readFileSync(skillFile, 'utf-8')).not.toContain('# dirty');
75
+ expect(
76
+ fs.existsSync(path.join(cwd, '.cursor/skills/prismakit-nestjs')),
77
+ ).toBe(false);
78
+ });
79
+
80
+ it('dry-run does not write files', () => {
81
+ const cwd = tmpDir();
82
+ runSkills({ cwd, skillsRoot: repoSkills, dryRun: true });
83
+ expect(fs.existsSync(path.join(cwd, '.cursor'))).toBe(false);
84
+ });
85
+
86
+ it('copies the data-access rule with --with-rules', () => {
87
+ const cwd = tmpDir();
88
+ const rulesPath = path.resolve(
89
+ repoSkills,
90
+ '../templates/cursor-rules/data-access.mdc',
91
+ );
92
+ const result = runSkills({
93
+ cwd,
94
+ skillsRoot: repoSkills,
95
+ withRules: true,
96
+ rulesPath,
97
+ skill: ['prismakit'],
98
+ });
99
+ expect(result.ruleDest).toBe(
100
+ path.join(cwd, '.cursor/rules/data-access.mdc'),
101
+ );
102
+ expect(fs.existsSync(result.ruleDest!)).toBe(true);
103
+ expect(fs.readFileSync(result.ruleDest!, 'utf-8')).toContain(
104
+ 'repository-only',
105
+ );
106
+ });
107
+
108
+ it('rejects unknown skill names', () => {
109
+ expect(() =>
110
+ runSkills({
111
+ cwd: tmpDir(),
112
+ skillsRoot: repoSkills,
113
+ skill: ['nope'],
114
+ }),
115
+ ).toThrow(/Unknown skill/);
116
+ });
117
+ });
package/src/bin.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  import { runGenerate } from './commands/generate';
2
- import { runCodegen } from './commands/codegen';
3
2
  import { runValidate } from './commands/validate';
3
+ import { runSkills } from './commands/skills';
4
4
 
5
5
  function printHelp(): void {
6
6
  console.log(`prismakit — PrismaKit CLI
7
7
 
8
8
  Usage:
9
9
  prismakit generate <name> [--cache] [--full] [--helpers] [--dto] [--route <path>] [--prisma-import <path>] [--dry-run]
10
- prismakit codegen [--schema <path>] [--write] [--out <file>]
11
- prismakit validate [--no-assert]
10
+ prismakit validate [--schema <path>] [--auto-register] [--no-assert]
11
+ prismakit skills [--global] [--with-rules] [--skill <name>] [--project <path>] [--list] [--dry-run]
12
12
  prismakit help
13
13
 
14
14
  By default, generate writes only the repository file.
@@ -18,8 +18,10 @@ Pass --helpers / --dto with --full for helpers and Swagger DTOs.
18
18
  Examples:
19
19
  prismakit generate product --cache
20
20
  prismakit generate product --cache --full --helpers --dto --route products
21
- prismakit codegen --write
22
- prismakit validate
21
+ prismakit validate --auto-register
22
+ prismakit skills
23
+ prismakit skills --global
24
+ prismakit skills --with-rules
23
25
  `);
24
26
  }
25
27
 
@@ -34,10 +36,12 @@ function parseArgs(argv: string[]): {
34
36
 
35
37
  for (let i = 0; i < rest.length; i++) {
36
38
  const arg = rest[i];
37
- if (arg.startsWith('--')) {
39
+ if (arg === '-g') {
40
+ flags.global = true;
41
+ } else if (arg.startsWith('--')) {
38
42
  const key = arg.slice(2);
39
43
  const next = rest[i + 1];
40
- if (next && !next.startsWith('--')) {
44
+ if (next && !next.startsWith('-')) {
41
45
  flags[key] = next;
42
46
  i++;
43
47
  } else {
@@ -80,18 +84,29 @@ function main(): void {
80
84
  });
81
85
  break;
82
86
  }
83
- case 'codegen': {
84
- runCodegen({
87
+ case 'validate': {
88
+ runValidate({
89
+ assert: !flags['no-assert'],
85
90
  schemaPath:
86
91
  typeof flags.schema === 'string' ? flags.schema : undefined,
87
- write: !!flags.write,
88
- outFile: typeof flags.out === 'string' ? flags.out : undefined,
92
+ autoRegisterModels: !!flags['auto-register'],
89
93
  });
90
94
  break;
91
95
  }
92
- case 'validate': {
93
- runValidate({
94
- assert: !flags['no-assert'],
96
+ case 'skills':
97
+ case 'skill': {
98
+ const skillFlag = flags.skill;
99
+ runSkills({
100
+ global: !!flags.global,
101
+ withRules: !!flags['with-rules'],
102
+ dryRun: !!flags['dry-run'],
103
+ list: !!flags.list,
104
+ projectRoot:
105
+ typeof flags.project === 'string' ? flags.project : undefined,
106
+ skill:
107
+ typeof skillFlag === 'string'
108
+ ? skillFlag.split(',').map((s) => s.trim()).filter(Boolean)
109
+ : undefined,
95
110
  });
96
111
  break;
97
112
  }