@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.
@@ -0,0 +1,234 @@
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
+ schemaPath: 'prisma/schema.prisma',
31
+ validateCompose: true,
32
+ compose: { maxDepth: 6, parallel: true, setCache: true },
33
+ autoRegisterModels: true,
34
+ queryLog: {
35
+ slowThreshold: 500,
36
+ onSlowQuery: (e) => {
37
+ console.warn(`Slow ${e.model}.${e.method}: ${e.durationMs}ms`);
38
+ },
39
+ },
40
+ }),
41
+ }),
42
+ UserModule,
43
+ ],
44
+ })
45
+ export class AppModule {}
46
+ ```
47
+
48
+ Prisma 5/6: pass `dmmf: Prisma.dmmf` instead of (or in addition to skipping) `schemaPath`. Prisma 7: `schemaPath` only.
49
+
50
+ ## 2. TypeMap binder
51
+
52
+ ```typescript
53
+ // src/infrastructure/prisma/define-repo.ts
54
+ import { createDefineRepo } from '@prismakit/nestjs';
55
+ import type { Prisma } from '@prisma/client';
56
+
57
+ export const defineRepo = createDefineRepo<Prisma.TypeMap>();
58
+ ```
59
+
60
+ ## 3. Feature repository + select presets
61
+
62
+ ```typescript
63
+ // src/modules/users/repositories/user.repository.ts
64
+ import { Prisma } from '@prisma/client';
65
+ import { defineRepo } from '../../../infrastructure/prisma/define-repo';
66
+
67
+ const DAY = 86_400;
68
+
69
+ export const userSelectPresets = {
70
+ minimal: { id: true } satisfies Prisma.UserSelect,
71
+ general: {
72
+ id: true,
73
+ email: true,
74
+ name: true,
75
+ } satisfies Prisma.UserSelect,
76
+ withPassword: {
77
+ id: true,
78
+ email: true,
79
+ password: true,
80
+ } satisfies Prisma.UserSelect,
81
+ };
82
+
83
+ export const UserRepository = defineRepo({
84
+ model: 'user',
85
+ scalarFields: Prisma.UserScalarFieldEnum,
86
+ cache: {
87
+ ttl: DAY,
88
+ nullTtl: 60,
89
+ sensitiveFields: ['password'],
90
+ methods: { getFirst: { enabled: false } },
91
+ },
92
+ lock: true,
93
+ });
94
+ export interface UserRepository extends InstanceType<typeof UserRepository> {}
95
+ ```
96
+
97
+ ## 4. Feature module + thin controller + service
98
+
99
+ ```typescript
100
+ // src/modules/users/user.module.ts
101
+ import { Module } from '@nestjs/common';
102
+ import { UserController } from './user.controller';
103
+ import { UserService } from './user.service';
104
+ import { UserRepository } from './repositories/user.repository';
105
+
106
+ @Module({
107
+ controllers: [UserController],
108
+ providers: [UserService, UserRepository],
109
+ exports: [UserService, UserRepository],
110
+ })
111
+ export class UserModule {}
112
+ ```
113
+
114
+ ```typescript
115
+ // src/modules/users/user.controller.ts
116
+ import { Controller, Get, Param } from '@nestjs/common';
117
+ import { UserService } from './user.service';
118
+
119
+ @Controller('users')
120
+ export class UserController {
121
+ constructor(private readonly users: UserService) {}
122
+
123
+ @Get(':id')
124
+ getOne(@Param('id') id: string) {
125
+ return this.users.getProfile(id);
126
+ }
127
+ }
128
+ ```
129
+
130
+ ```typescript
131
+ // src/modules/users/user.service.ts
132
+ import { Injectable } from '@nestjs/common';
133
+ import { UserRepository, userSelectPresets } from './repositories/user.repository';
134
+
135
+ @Injectable()
136
+ export class UserService {
137
+ constructor(private readonly users: UserRepository) {}
138
+
139
+ getProfile(id: string) {
140
+ return this.users.getThrowById({
141
+ id,
142
+ select: userSelectPresets.general,
143
+ setCache: true,
144
+ });
145
+ }
146
+
147
+ async assertEmailFree(email: string) {
148
+ const hit = await this.users.getFirst({
149
+ where: { email },
150
+ select: userSelectPresets.minimal,
151
+ });
152
+ if (hit) throw new Error('email taken');
153
+ }
154
+ }
155
+ ```
156
+
157
+ ## 5. Checkout — multi-repo `execTx`
158
+
159
+ ```typescript
160
+ @Injectable()
161
+ export class CheckoutService {
162
+ constructor(
163
+ private readonly tx: TransactionService,
164
+ private readonly orders: OrderRepository,
165
+ private readonly stocks: StockRepository,
166
+ ) {}
167
+
168
+ handleCheckout(input: { stockId: string; qty: number; userId: string }) {
169
+ return this.tx.execTx(
170
+ async (tx) => {
171
+ const order = await this.orders.create({
172
+ tx,
173
+ data: { userId: input.userId, stockId: input.stockId, qty: input.qty },
174
+ select: { id: true },
175
+ invalidate: 'none',
176
+ });
177
+ await this.stocks.updateById({
178
+ tx,
179
+ id: input.stockId,
180
+ data: { qty: { decrement: input.qty } },
181
+ invalidate: 'none',
182
+ });
183
+ return order;
184
+ },
185
+ async () => {
186
+ await this.orders.invalidateCache({});
187
+ await this.stocks.invalidateCache({ id: input.stockId });
188
+ },
189
+ );
190
+ }
191
+ }
192
+ ```
193
+
194
+ Register `OrderRepository` and `StockRepository` in the feature module `providers`. Both need `cache` config for `invalidate: 'none'` / `invalidateCache` to exist on the type.
195
+
196
+ ## 6. Wallet lock
197
+
198
+ ```typescript
199
+ @Injectable()
200
+ export class WalletService {
201
+ constructor(
202
+ private readonly tx: TransactionService,
203
+ private readonly wallets: WalletRepository,
204
+ ) {}
205
+
206
+ debit(id: string, amount: number) {
207
+ return this.tx.execTx(
208
+ async (tx) => {
209
+ const wallet = await this.wallets.getById({
210
+ tx,
211
+ id,
212
+ select: { id: true, balance: true },
213
+ lock: { mode: 'update' },
214
+ });
215
+ if (!wallet || wallet.balance < amount) {
216
+ throw new Error('insufficient funds');
217
+ }
218
+ return this.wallets.updateById({
219
+ tx,
220
+ id,
221
+ data: { balance: wallet.balance - amount },
222
+ select: { id: true, balance: true },
223
+ invalidate: 'none',
224
+ });
225
+ },
226
+ async () => {
227
+ await this.wallets.invalidateCache({ id });
228
+ },
229
+ );
230
+ }
231
+ }
232
+ ```
233
+
234
+ `WalletRepository` must set `lock: true` (or a table/client key). Never call `lock` outside `execTx`.
@@ -0,0 +1,145 @@
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 | Optional extra allowlist. Omit — repo `cache` is the source of truth. |
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
+ schemaPath: 'prisma/schema.prisma',
43
+ }),
44
+ });
45
+ ```
46
+
47
+ ## DI tokens
48
+
49
+ | Token | Type | Allowed injectors |
50
+ |-------|------|-------------------|
51
+ | `PRISMAKIT_PRISMA` | `PrismaClientLike` | Injectable repositories / kit internals only |
52
+ | `PRISMAKIT_CACHE` | `CacheAdapter` | Repositories / kit internals |
53
+ | `PRISMAKIT_OPTIONS` | `PrismaKitModuleOptions` | Kit internals |
54
+
55
+ `TransactionService`, `RepositoryRegistry`, and `AutoComposer` are Nest providers from `PrismaKitModule`.
56
+
57
+ ## `TransactionService`
58
+
59
+ ```typescript
60
+ execTx<T, TClient = unknown>(
61
+ fn: (tx: TClient) => Promise<T>,
62
+ afterCommit?: () => Promise<void>,
63
+ options?: TransactionOptions,
64
+ ): Promise<T>
65
+ ```
66
+
67
+ `TransactionOptions`: `{ maxWait?: number; timeout?: number; isolationLevel?: 'ReadUncommitted' | 'ReadCommitted' | 'RepeatableRead' | 'Serializable' | string }`.
68
+
69
+ Type the client when useful:
70
+
71
+ ```typescript
72
+ await this.tx.execTx<User, Prisma.TransactionClient>(async (tx) => { /* ... */ });
73
+ ```
74
+
75
+ `afterCommit` runs only after `prisma.$transaction` resolves successfully.
76
+
77
+ ## Repository factories
78
+
79
+ | Factory | When |
80
+ |---------|------|
81
+ | `createDefineRepo<Prisma.TypeMap>()` then local `defineRepo({ model, ... })` | **Default** for apps. Zero phantoms; `model` + `scalarFields` + `cache` / `lock`. |
82
+ | `defineInjectableRepository({ model, select, create, update, where, orderBy, payload, ... })` | TypeMap unavailable. Package aliases: `defineRepository`. |
83
+ | `createInjectableRepository({ model, ... })` | Thin / untyped. Results `unknown` unless `toPayload` is supplied. Alias: `createPrismaRepository`. |
84
+
85
+ `createDefineRepo` runtime options: `model`, `scalarFields?`, `primaryKey?`, `cache?`, `lock?`, `schemaPath?`.
86
+
87
+ When `cache` is set, the returned API includes `setCache` / `cacheTags` / `invalidate` / `tags` / `invalidateCache`. Otherwise those fields are omitted from the type (`HasCacheFromOptions`).
88
+
89
+ `createDefineRepo` / `RepositoryApiFromTypeMap` includes the full runtime surface: `createMany`, `updateMany`, `upsert`, `deleteMany`, `lock` + `orderBy` on `getFirst`, `lock` on `getMany`, and composite-PK `id` on `*ById`. `primaryKey` is optional — composite `@@id` is read from schema meta.
90
+
91
+ Export the instance type with interface merging so `cache` on options gates `setCache` (a same-name `type` alias collapses to `any`):
92
+
93
+ ```typescript
94
+ export interface UserRepository extends InstanceType<typeof UserRepository> {}
95
+ ```
96
+
97
+ ## `defineInjectableRepository` shape (escape hatch)
98
+
99
+ ```typescript
100
+ import { Prisma } from '@prisma/client';
101
+ import { defineInjectableRepository } from '@prismakit/nestjs';
102
+
103
+ type Of<S> = S extends Prisma.UserSelect
104
+ ? Prisma.UserGetPayload<{ select: S }>
105
+ : never;
106
+
107
+ export const UserRepository = defineInjectableRepository({
108
+ model: 'user',
109
+ scalarFields: Prisma.UserScalarFieldEnum,
110
+ select: null! as Prisma.UserSelect,
111
+ create: null! as Prisma.UserCreateInput,
112
+ update: null! as Prisma.UserUpdateInput,
113
+ where: null! as Prisma.UserWhereInput,
114
+ orderBy: null! as Prisma.UserOrderByWithRelationInput,
115
+ payload: class {
116
+ declare readonly _select: unknown;
117
+ declare type: () => Of<this['_select']>;
118
+ },
119
+ cache: { ttl: 86_400, sensitiveFields: ['password'] },
120
+ lock: true,
121
+ });
122
+ ```
123
+
124
+ ## Re-exports from core
125
+
126
+ `@prismakit/nestjs` re-exports: `AutoComposer`, `RepositoryRegistry`, `CacheAdapter`, repository option/instance types, `RepoPayloadHKT`, `ComposeOptions`, `TelemetryOptions`, `TelemetryEvent`, `loadPrismaMetaFromDmmf`, `loadPrismaMetaFromSchema`, `setComposeOptions`, `setTelemetry`.
127
+
128
+ Prefer importing Nest-only APIs from `@prismakit/nestjs` and core-only helpers from `@prismakit/core`.
129
+
130
+ ## Layout
131
+
132
+ ```
133
+ src/
134
+ app.module.ts # PrismaKitModule.forRootAsync
135
+ infrastructure/prisma/
136
+ define-repo.ts # createDefineRepo<Prisma.TypeMap>()
137
+ prisma.service.ts # client construction only
138
+ modules/<feature>/
139
+ <feature>.module.ts # providers: [Service, XRepository]
140
+ <feature>.service.ts # inject repos + TransactionService
141
+ <feature>.controller.ts # HTTP only
142
+ repositories/<feature>.repository.ts
143
+ ```
144
+
145
+ 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` is omitted (repo `cache` is source of truth), or lists every cached model if an allowlist is used
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 interface XRepository extends InstanceType<typeof XRepository> {}` (infers cache from options; do not use a same-name `type` alias)
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
  }