timsquad 2.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.
Files changed (181) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +347 -0
  3. package/bin/tsq.js +6 -0
  4. package/dist/commands/feedback.d.ts +3 -0
  5. package/dist/commands/feedback.d.ts.map +1 -0
  6. package/dist/commands/feedback.js +142 -0
  7. package/dist/commands/feedback.js.map +1 -0
  8. package/dist/commands/full.d.ts +3 -0
  9. package/dist/commands/full.d.ts.map +1 -0
  10. package/dist/commands/full.js +87 -0
  11. package/dist/commands/full.js.map +1 -0
  12. package/dist/commands/git/commit.d.ts +3 -0
  13. package/dist/commands/git/commit.d.ts.map +1 -0
  14. package/dist/commands/git/commit.js +88 -0
  15. package/dist/commands/git/commit.js.map +1 -0
  16. package/dist/commands/git/index.d.ts +5 -0
  17. package/dist/commands/git/index.d.ts.map +1 -0
  18. package/dist/commands/git/index.js +5 -0
  19. package/dist/commands/git/index.js.map +1 -0
  20. package/dist/commands/git/pr.d.ts +3 -0
  21. package/dist/commands/git/pr.d.ts.map +1 -0
  22. package/dist/commands/git/pr.js +138 -0
  23. package/dist/commands/git/pr.js.map +1 -0
  24. package/dist/commands/git/release.d.ts +3 -0
  25. package/dist/commands/git/release.d.ts.map +1 -0
  26. package/dist/commands/git/release.js +158 -0
  27. package/dist/commands/git/release.js.map +1 -0
  28. package/dist/commands/git/sync.d.ts +3 -0
  29. package/dist/commands/git/sync.d.ts.map +1 -0
  30. package/dist/commands/git/sync.js +132 -0
  31. package/dist/commands/git/sync.js.map +1 -0
  32. package/dist/commands/init.d.ts +3 -0
  33. package/dist/commands/init.d.ts.map +1 -0
  34. package/dist/commands/init.js +150 -0
  35. package/dist/commands/init.js.map +1 -0
  36. package/dist/commands/log.d.ts +3 -0
  37. package/dist/commands/log.d.ts.map +1 -0
  38. package/dist/commands/log.js +271 -0
  39. package/dist/commands/log.js.map +1 -0
  40. package/dist/commands/metrics.d.ts +3 -0
  41. package/dist/commands/metrics.d.ts.map +1 -0
  42. package/dist/commands/metrics.js +299 -0
  43. package/dist/commands/metrics.js.map +1 -0
  44. package/dist/commands/quick.d.ts +3 -0
  45. package/dist/commands/quick.d.ts.map +1 -0
  46. package/dist/commands/quick.js +136 -0
  47. package/dist/commands/quick.js.map +1 -0
  48. package/dist/commands/retro.d.ts +3 -0
  49. package/dist/commands/retro.d.ts.map +1 -0
  50. package/dist/commands/retro.js +280 -0
  51. package/dist/commands/retro.js.map +1 -0
  52. package/dist/commands/status.d.ts +3 -0
  53. package/dist/commands/status.d.ts.map +1 -0
  54. package/dist/commands/status.js +127 -0
  55. package/dist/commands/status.js.map +1 -0
  56. package/dist/commands/watch.d.ts +3 -0
  57. package/dist/commands/watch.d.ts.map +1 -0
  58. package/dist/commands/watch.js +213 -0
  59. package/dist/commands/watch.js.map +1 -0
  60. package/dist/index.d.ts +3 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +50 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/lib/config.d.ts +34 -0
  65. package/dist/lib/config.d.ts.map +1 -0
  66. package/dist/lib/config.js +108 -0
  67. package/dist/lib/config.js.map +1 -0
  68. package/dist/lib/project.d.ts +47 -0
  69. package/dist/lib/project.d.ts.map +1 -0
  70. package/dist/lib/project.js +191 -0
  71. package/dist/lib/project.js.map +1 -0
  72. package/dist/lib/template.d.ts +33 -0
  73. package/dist/lib/template.d.ts.map +1 -0
  74. package/dist/lib/template.js +151 -0
  75. package/dist/lib/template.js.map +1 -0
  76. package/dist/types/config.d.ts +75 -0
  77. package/dist/types/config.d.ts.map +1 -0
  78. package/dist/types/config.js +66 -0
  79. package/dist/types/config.js.map +1 -0
  80. package/dist/types/feedback.d.ts +59 -0
  81. package/dist/types/feedback.d.ts.map +1 -0
  82. package/dist/types/feedback.js +26 -0
  83. package/dist/types/feedback.js.map +1 -0
  84. package/dist/types/index.d.ts +4 -0
  85. package/dist/types/index.d.ts.map +1 -0
  86. package/dist/types/index.js +5 -0
  87. package/dist/types/index.js.map +1 -0
  88. package/dist/types/project.d.ts +89 -0
  89. package/dist/types/project.d.ts.map +1 -0
  90. package/dist/types/project.js +44 -0
  91. package/dist/types/project.js.map +1 -0
  92. package/dist/utils/colors.d.ts +30 -0
  93. package/dist/utils/colors.d.ts.map +1 -0
  94. package/dist/utils/colors.js +54 -0
  95. package/dist/utils/colors.js.map +1 -0
  96. package/dist/utils/date.d.ts +25 -0
  97. package/dist/utils/date.d.ts.map +1 -0
  98. package/dist/utils/date.js +65 -0
  99. package/dist/utils/date.js.map +1 -0
  100. package/dist/utils/fs.d.ts +49 -0
  101. package/dist/utils/fs.d.ts.map +1 -0
  102. package/dist/utils/fs.js +84 -0
  103. package/dist/utils/fs.js.map +1 -0
  104. package/dist/utils/prompts.d.ts +31 -0
  105. package/dist/utils/prompts.d.ts.map +1 -0
  106. package/dist/utils/prompts.js +95 -0
  107. package/dist/utils/prompts.js.map +1 -0
  108. package/dist/utils/yaml.d.ts +21 -0
  109. package/dist/utils/yaml.d.ts.map +1 -0
  110. package/dist/utils/yaml.js +40 -0
  111. package/dist/utils/yaml.js.map +1 -0
  112. package/package.json +71 -0
  113. package/templates/common/CLAUDE.md.template +254 -0
  114. package/templates/common/claude/agents/tsq-dba.md +290 -0
  115. package/templates/common/claude/agents/tsq-designer.md +304 -0
  116. package/templates/common/claude/agents/tsq-developer.md +118 -0
  117. package/templates/common/claude/agents/tsq-planner.md +90 -0
  118. package/templates/common/claude/agents/tsq-prompter.md +336 -0
  119. package/templates/common/claude/agents/tsq-qa.md +134 -0
  120. package/templates/common/claude/agents/tsq-retro.md +168 -0
  121. package/templates/common/claude/agents/tsq-security.md +190 -0
  122. package/templates/common/claude/skills/architecture/SKILL.md +123 -0
  123. package/templates/common/claude/skills/backend/node/SKILL.md +1015 -0
  124. package/templates/common/claude/skills/coding/SKILL.md +171 -0
  125. package/templates/common/claude/skills/database/prisma/SKILL.md +357 -0
  126. package/templates/common/claude/skills/frontend/nextjs/SKILL.md +279 -0
  127. package/templates/common/claude/skills/frontend/react/SKILL.md +1729 -0
  128. package/templates/common/claude/skills/methodology/bdd/SKILL.md +234 -0
  129. package/templates/common/claude/skills/methodology/ddd/SKILL.md +311 -0
  130. package/templates/common/claude/skills/methodology/tdd/SKILL.md +512 -0
  131. package/templates/common/claude/skills/planning/SKILL.md +90 -0
  132. package/templates/common/claude/skills/security/SKILL.md +234 -0
  133. package/templates/common/claude/skills/testing/SKILL.md +146 -0
  134. package/templates/common/claude/skills/typescript/SKILL.md +435 -0
  135. package/templates/common/config.template.yaml +131 -0
  136. package/templates/common/timsquad/architectures/clean/ARCHITECTURE.md +49 -0
  137. package/templates/common/timsquad/architectures/clean/backend.xml +210 -0
  138. package/templates/common/timsquad/architectures/clean/frontend.xml +148 -0
  139. package/templates/common/timsquad/architectures/fsd/ARCHITECTURE.md +67 -0
  140. package/templates/common/timsquad/architectures/fsd/frontend.xml +288 -0
  141. package/templates/common/timsquad/architectures/hexagonal/ARCHITECTURE.md +60 -0
  142. package/templates/common/timsquad/architectures/hexagonal/backend.xml +300 -0
  143. package/templates/common/timsquad/constraints/competency-framework.xml +501 -0
  144. package/templates/common/timsquad/constraints/ssot-schema.xml +433 -0
  145. package/templates/common/timsquad/feedback/feedback-router.sh +341 -0
  146. package/templates/common/timsquad/feedback/routing-rules.yaml +352 -0
  147. package/templates/common/timsquad/generators/data-design.xml +290 -0
  148. package/templates/common/timsquad/generators/prd.xml +280 -0
  149. package/templates/common/timsquad/generators/requirements.xml +220 -0
  150. package/templates/common/timsquad/generators/service-spec.xml +266 -0
  151. package/templates/common/timsquad/logs/_example.md +81 -0
  152. package/templates/common/timsquad/logs/_template.md +46 -0
  153. package/templates/common/timsquad/patterns/cqrs.xml +127 -0
  154. package/templates/common/timsquad/patterns/event-sourcing.xml +85 -0
  155. package/templates/common/timsquad/patterns/repository.xml +64 -0
  156. package/templates/common/timsquad/process/state-machine.xml +343 -0
  157. package/templates/common/timsquad/process/validation-rules.xml +308 -0
  158. package/templates/common/timsquad/process/workflow-base.xml +202 -0
  159. package/templates/common/timsquad/retrospective/cycle-report.template.md +205 -0
  160. package/templates/common/timsquad/retrospective/metrics/metrics-schema.json +203 -0
  161. package/templates/common/timsquad/retrospective/patterns/failure-patterns.md +199 -0
  162. package/templates/common/timsquad/retrospective/patterns/success-patterns.md +262 -0
  163. package/templates/common/timsquad/retrospective/retrospective-config.xml +294 -0
  164. package/templates/common/timsquad/retrospective/retrospective-state.xml +210 -0
  165. package/templates/common/timsquad/ssot/adr/ADR-000-template.md +121 -0
  166. package/templates/common/timsquad/ssot/adr/ADR-001-example.md +115 -0
  167. package/templates/common/timsquad/ssot/data-design.template.md +132 -0
  168. package/templates/common/timsquad/ssot/deployment-spec.template.md +384 -0
  169. package/templates/common/timsquad/ssot/env-config.template.md +346 -0
  170. package/templates/common/timsquad/ssot/error-codes.template.md +114 -0
  171. package/templates/common/timsquad/ssot/functional-spec.template.md +185 -0
  172. package/templates/common/timsquad/ssot/glossary.template.md +148 -0
  173. package/templates/common/timsquad/ssot/integration-spec.template.md +391 -0
  174. package/templates/common/timsquad/ssot/planning.template.md +94 -0
  175. package/templates/common/timsquad/ssot/prd.template.md +102 -0
  176. package/templates/common/timsquad/ssot/requirements.template.md +117 -0
  177. package/templates/common/timsquad/ssot/security-spec.template.md +309 -0
  178. package/templates/common/timsquad/ssot/service-spec.template.md +194 -0
  179. package/templates/common/timsquad/ssot/test-spec.template.md +264 -0
  180. package/templates/common/timsquad/ssot/ui-ux-spec.template.md +262 -0
  181. package/templates/common/timsquad/state/workspace.xml +217 -0
@@ -0,0 +1,171 @@
1
+ ---
2
+ name: coding
3
+ description: 코드 작성 규칙 및 패턴 가이드라인
4
+ user-invocable: false
5
+ ---
6
+
7
+ <skill name="coding">
8
+ <purpose>일관된 고품질 코드 작성을 위한 가이드라인</purpose>
9
+
10
+ <naming-conventions>
11
+ <convention target="클래스" rule="PascalCase">
12
+ <example correct="true">UserService, OrderRepository</example>
13
+ <example correct="false">userService, user_service</example>
14
+ </convention>
15
+ <convention target="함수/메서드" rule="camelCase">
16
+ <example correct="true">getUserById, calculateTotal</example>
17
+ <example correct="false">GetUserById, get_user_by_id</example>
18
+ </convention>
19
+ <convention target="변수" rule="camelCase">
20
+ <example correct="true">userName, totalAmount</example>
21
+ <example correct="false">UserName, user_name</example>
22
+ </convention>
23
+ <convention target="상수" rule="UPPER_SNAKE">
24
+ <example correct="true">MAX_RETRY_COUNT, API_BASE_URL</example>
25
+ <example correct="false">maxRetryCount, MaxRetryCount</example>
26
+ </convention>
27
+ <convention target="파일" rule="kebab-case">
28
+ <example correct="true">user-service.ts, order-repository.ts</example>
29
+ <example correct="false">userService.ts, UserService.ts</example>
30
+ </convention>
31
+ </naming-conventions>
32
+
33
+ <function-principles>
34
+ <principle name="단일 책임">함수는 한 가지 일만 수행</principle>
35
+ <principle name="명확한 이름">함수 이름만으로 동작 파악 가능</principle>
36
+ <principle name="적절한 크기">20줄 이내 권장</principle>
37
+ <examples>
38
+ <example type="good">
39
+ <![CDATA[
40
+ function calculateOrderTotal(items: OrderItem[]): number {
41
+ return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
42
+ }
43
+ ]]>
44
+ <description>단일 책임, 명확한 이름</description>
45
+ </example>
46
+ <example type="bad">
47
+ <![CDATA[
48
+ function processOrder(order: Order): void {
49
+ // 가격 계산 + 재고 확인 + 결제 처리 + 알림 전송...
50
+ }
51
+ ]]>
52
+ <description>여러 책임, 모호한 이름</description>
53
+ </example>
54
+ </examples>
55
+ </function-principles>
56
+
57
+ <error-handling>
58
+ <example type="good">
59
+ <![CDATA[
60
+ async function getUser(id: string): Promise<User> {
61
+ const user = await userRepository.findById(id);
62
+ if (!user) {
63
+ throw new NotFoundError('USER_NOT_FOUND', `User ${id} not found`);
64
+ }
65
+ return user;
66
+ }
67
+ ]]>
68
+ <description>명시적 에러 처리</description>
69
+ </example>
70
+ <example type="bad">
71
+ <![CDATA[
72
+ async function getUser(id: string): Promise<User | null> {
73
+ try {
74
+ return await userRepository.findById(id);
75
+ } catch {
76
+ return null; // 에러 원인 알 수 없음
77
+ }
78
+ }
79
+ ]]>
80
+ <description>에러 무시</description>
81
+ </example>
82
+ </error-handling>
83
+
84
+ <patterns>
85
+ <pattern name="Repository">
86
+ <purpose>데이터 접근 추상화</purpose>
87
+ <example>
88
+ <![CDATA[
89
+ interface UserRepository {
90
+ findById(id: string): Promise<User | null>;
91
+ findByEmail(email: string): Promise<User | null>;
92
+ save(user: User): Promise<User>;
93
+ delete(id: string): Promise<void>;
94
+ }
95
+
96
+ class PostgresUserRepository implements UserRepository {
97
+ async findById(id: string): Promise<User | null> {
98
+ // PostgreSQL 구현
99
+ }
100
+ }
101
+ ]]>
102
+ </example>
103
+ </pattern>
104
+ <pattern name="DTO/Entity 분리">
105
+ <purpose>레이어 간 데이터 전달</purpose>
106
+ <example>
107
+ <![CDATA[
108
+ // Entity (도메인)
109
+ class User {
110
+ constructor(
111
+ public readonly id: string,
112
+ public email: string,
113
+ public passwordHash: string, // 민감 정보
114
+ ) {}
115
+ }
116
+
117
+ // DTO (외부 노출)
118
+ interface UserResponseDto {
119
+ id: string;
120
+ email: string;
121
+ // passwordHash 제외
122
+ }
123
+ ]]>
124
+ </example>
125
+ </pattern>
126
+ <pattern name="의존성 주입">
127
+ <purpose>테스트 용이성, 유연한 구성</purpose>
128
+ <example type="good">
129
+ <![CDATA[
130
+ class UserService {
131
+ constructor(private userRepository: UserRepository) {}
132
+ }
133
+ ]]>
134
+ </example>
135
+ <example type="bad">
136
+ <![CDATA[
137
+ class UserService {
138
+ private userRepository = new PostgresUserRepository();
139
+ }
140
+ ]]>
141
+ </example>
142
+ </pattern>
143
+ </patterns>
144
+
145
+ <anti-patterns>
146
+ <anti-pattern name="any 타입">
147
+ <bad>function process(data: any): any { ... }</bad>
148
+ <good>function process&lt;T&gt;(data: T): ProcessedData&lt;T&gt; { ... }</good>
149
+ </anti-pattern>
150
+ <anti-pattern name="매직 넘버">
151
+ <bad>if (status === 1) { ... }</bad>
152
+ <good>
153
+ const Status = { ACTIVE: 1, INACTIVE: 0 } as const;
154
+ if (status === Status.ACTIVE) { ... }
155
+ </good>
156
+ </anti-pattern>
157
+ <anti-pattern name="콘솔 로그">
158
+ <bad>console.log('user:', user);</bad>
159
+ <good>logger.debug('User fetched', { userId: user.id });</good>
160
+ </anti-pattern>
161
+ </anti-patterns>
162
+
163
+ <checklist>
164
+ <item>함수가 단일 책임을 가지는가</item>
165
+ <item>네이밍이 명확하고 일관적인가</item>
166
+ <item>에러가 명시적으로 처리되는가</item>
167
+ <item>any 타입이 없는가</item>
168
+ <item>매직 넘버/문자열이 없는가</item>
169
+ <item>console.log가 없는가</item>
170
+ </checklist>
171
+ </skill>
@@ -0,0 +1,357 @@
1
+ ---
2
+ name: prisma
3
+ description: Prisma ORM 개발 가이드라인
4
+ user-invocable: false
5
+ ---
6
+
7
+ <skill name="prisma">
8
+ <purpose>Prisma ORM을 활용한 타입 안전 데이터베이스 개발 가이드라인</purpose>
9
+
10
+ <philosophy>
11
+ <principle>Schema as SSOT - 스키마가 진실의 원천</principle>
12
+ <principle>Type Safety - 자동 생성 타입 활용</principle>
13
+ <principle>Migration First - 스키마 변경은 마이그레이션으로</principle>
14
+ </philosophy>
15
+
16
+ <schema-design>
17
+ <pattern name="기본 모델 정의">
18
+ <example>
19
+ <![CDATA[
20
+ // prisma/schema.prisma
21
+
22
+ generator client {
23
+ provider = "prisma-client-js"
24
+ }
25
+
26
+ datasource db {
27
+ provider = "postgresql"
28
+ url = env("DATABASE_URL")
29
+ }
30
+
31
+ model User {
32
+ id String @id @default(cuid())
33
+ email String @unique
34
+ name String
35
+ password String
36
+ role Role @default(USER)
37
+
38
+ createdAt DateTime @default(now())
39
+ updatedAt DateTime @updatedAt
40
+
41
+ // Relations
42
+ posts Post[]
43
+ profile Profile?
44
+
45
+ @@index([email])
46
+ @@map("users") // 테이블명
47
+ }
48
+
49
+ model Post {
50
+ id String @id @default(cuid())
51
+ title String
52
+ content String?
53
+ published Boolean @default(false)
54
+
55
+ createdAt DateTime @default(now())
56
+ updatedAt DateTime @updatedAt
57
+
58
+ // Relations
59
+ author User @relation(fields: [authorId], references: [id])
60
+ authorId String
61
+
62
+ @@index([authorId])
63
+ @@map("posts")
64
+ }
65
+
66
+ enum Role {
67
+ USER
68
+ ADMIN
69
+ }
70
+ ]]>
71
+ </example>
72
+ </pattern>
73
+
74
+ <pattern name="공통 필드 패턴">
75
+ <description>모든 모델에 공통으로 들어가는 필드</description>
76
+ <example>
77
+ <![CDATA[
78
+ // 공통 필드 패턴 (Prisma는 상속 미지원, 컨벤션으로 관리)
79
+ // id String @id @default(cuid())
80
+ // createdAt DateTime @default(now())
81
+ // updatedAt DateTime @updatedAt
82
+
83
+ model BaseFields {
84
+ // 참고용 - 실제 모델에 복사해서 사용
85
+ id String @id @default(cuid())
86
+ createdAt DateTime @default(now())
87
+ updatedAt DateTime @updatedAt
88
+ }
89
+ ]]>
90
+ </example>
91
+ </pattern>
92
+
93
+ <pattern name="소프트 삭제">
94
+ <example>
95
+ <![CDATA[
96
+ model User {
97
+ id String @id @default(cuid())
98
+ // ...
99
+ deletedAt DateTime? // null이면 활성, 값 있으면 삭제됨
100
+
101
+ @@index([deletedAt])
102
+ }
103
+
104
+ // 쿼리 시 필터링
105
+ const activeUsers = await prisma.user.findMany({
106
+ where: { deletedAt: null },
107
+ });
108
+ ]]>
109
+ </example>
110
+ </pattern>
111
+ </schema-design>
112
+
113
+ <client-setup>
114
+ <pattern name="싱글톤 인스턴스">
115
+ <example>
116
+ <![CDATA[
117
+ // lib/prisma.ts
118
+ import { PrismaClient } from '@prisma/client';
119
+
120
+ const globalForPrisma = globalThis as unknown as {
121
+ prisma: PrismaClient | undefined;
122
+ };
123
+
124
+ export const prisma = globalForPrisma.prisma ?? new PrismaClient({
125
+ log: process.env.NODE_ENV === 'development'
126
+ ? ['query', 'error', 'warn']
127
+ : ['error'],
128
+ });
129
+
130
+ if (process.env.NODE_ENV !== 'production') {
131
+ globalForPrisma.prisma = prisma;
132
+ }
133
+ ]]>
134
+ </example>
135
+ </pattern>
136
+
137
+ <pattern name="확장 (Extensions)">
138
+ <example>
139
+ <![CDATA[
140
+ // lib/prisma.ts
141
+ import { PrismaClient } from '@prisma/client';
142
+
143
+ const prismaBase = new PrismaClient();
144
+
145
+ export const prisma = prismaBase.$extends({
146
+ query: {
147
+ // 모든 findMany에 소프트 삭제 필터 자동 적용
148
+ $allModels: {
149
+ async findMany({ model, operation, args, query }) {
150
+ args.where = { ...args.where, deletedAt: null };
151
+ return query(args);
152
+ },
153
+ },
154
+ },
155
+ });
156
+ ]]>
157
+ </example>
158
+ </pattern>
159
+ </client-setup>
160
+
161
+ <query-patterns>
162
+ <pattern name="기본 CRUD">
163
+ <example>
164
+ <![CDATA[
165
+ // Create
166
+ const user = await prisma.user.create({
167
+ data: {
168
+ email: 'test@example.com',
169
+ name: 'Test User',
170
+ password: hashedPassword,
171
+ },
172
+ });
173
+
174
+ // Read
175
+ const user = await prisma.user.findUnique({
176
+ where: { id: userId },
177
+ });
178
+
179
+ const users = await prisma.user.findMany({
180
+ where: { role: 'USER' },
181
+ orderBy: { createdAt: 'desc' },
182
+ take: 10,
183
+ skip: 0,
184
+ });
185
+
186
+ // Update
187
+ const updated = await prisma.user.update({
188
+ where: { id: userId },
189
+ data: { name: 'New Name' },
190
+ });
191
+
192
+ // Delete
193
+ await prisma.user.delete({
194
+ where: { id: userId },
195
+ });
196
+ ]]>
197
+ </example>
198
+ </pattern>
199
+
200
+ <pattern name="Relations 로딩">
201
+ <example>
202
+ <![CDATA[
203
+ // include: 관계 데이터 포함
204
+ const userWithPosts = await prisma.user.findUnique({
205
+ where: { id: userId },
206
+ include: {
207
+ posts: true,
208
+ profile: true,
209
+ },
210
+ });
211
+
212
+ // select: 특정 필드만 선택 (성능 최적화)
213
+ const userWithPosts = await prisma.user.findUnique({
214
+ where: { id: userId },
215
+ select: {
216
+ id: true,
217
+ name: true,
218
+ posts: {
219
+ select: {
220
+ id: true,
221
+ title: true,
222
+ },
223
+ where: { published: true },
224
+ take: 5,
225
+ },
226
+ },
227
+ });
228
+ ]]>
229
+ </example>
230
+ </pattern>
231
+
232
+ <pattern name="트랜잭션">
233
+ <example>
234
+ <![CDATA[
235
+ // Interactive Transaction
236
+ const result = await prisma.$transaction(async (tx) => {
237
+ // 1. 사용자 생성
238
+ const user = await tx.user.create({
239
+ data: { email, name, password },
240
+ });
241
+
242
+ // 2. 프로필 생성
243
+ const profile = await tx.profile.create({
244
+ data: { userId: user.id, bio: '' },
245
+ });
246
+
247
+ // 3. 웰컴 알림 생성
248
+ await tx.notification.create({
249
+ data: { userId: user.id, message: 'Welcome!' },
250
+ });
251
+
252
+ return user;
253
+ });
254
+
255
+ // 실패 시 모두 롤백
256
+ ]]>
257
+ </example>
258
+ </pattern>
259
+
260
+ <pattern name="Upsert">
261
+ <example>
262
+ <![CDATA[
263
+ // 있으면 업데이트, 없으면 생성
264
+ const user = await prisma.user.upsert({
265
+ where: { email: 'test@example.com' },
266
+ update: { name: 'Updated Name' },
267
+ create: { email: 'test@example.com', name: 'New User', password },
268
+ });
269
+ ]]>
270
+ </example>
271
+ </pattern>
272
+ </query-patterns>
273
+
274
+ <migration>
275
+ <commands>
276
+ <command name="npx prisma migrate dev">개발 환경 마이그레이션 (자동 생성 + 적용)</command>
277
+ <command name="npx prisma migrate deploy">프로덕션 마이그레이션 적용</command>
278
+ <command name="npx prisma migrate reset">DB 초기화 + 모든 마이그레이션 재적용</command>
279
+ <command name="npx prisma db push">빠른 프로토타이핑 (마이그레이션 없이)</command>
280
+ <command name="npx prisma generate">클라이언트 재생성</command>
281
+ </commands>
282
+ <workflow>
283
+ <step>1. schema.prisma 수정</step>
284
+ <step>2. npx prisma migrate dev --name descriptive_name</step>
285
+ <step>3. 생성된 SQL 검토</step>
286
+ <step>4. 커밋 (schema.prisma + migrations/)</step>
287
+ </workflow>
288
+ </migration>
289
+
290
+ <repository-pattern>
291
+ <example>
292
+ <![CDATA[
293
+ // domain/user/repository.ts
294
+ export interface UserRepository {
295
+ findById(id: string): Promise<User | null>;
296
+ findByEmail(email: string): Promise<User | null>;
297
+ save(user: User): Promise<User>;
298
+ delete(id: string): Promise<void>;
299
+ }
300
+
301
+ // infrastructure/database/prisma-user-repository.ts
302
+ import { prisma } from '@/lib/prisma';
303
+ import { UserRepository } from '@/domain/user/repository';
304
+
305
+ export class PrismaUserRepository implements UserRepository {
306
+ async findById(id: string) {
307
+ return prisma.user.findUnique({ where: { id } });
308
+ }
309
+
310
+ async findByEmail(email: string) {
311
+ return prisma.user.findUnique({ where: { email } });
312
+ }
313
+
314
+ async save(data: Omit<User, 'id' | 'createdAt' | 'updatedAt'>) {
315
+ return prisma.user.create({ data });
316
+ }
317
+
318
+ async delete(id: string) {
319
+ await prisma.user.delete({ where: { id } });
320
+ }
321
+ }
322
+ ]]>
323
+ </example>
324
+ </repository-pattern>
325
+
326
+ <rules>
327
+ <category name="스키마">
328
+ <must>모든 모델에 id, createdAt, updatedAt</must>
329
+ <must>명시적 인덱스 정의</must>
330
+ <must>@@map으로 테이블명 명시</must>
331
+ <must>관계는 명확히 정의</must>
332
+ </category>
333
+ <category name="쿼리">
334
+ <must>select로 필요한 필드만 조회 (성능)</must>
335
+ <must>페이지네이션 적용 (take, skip)</must>
336
+ <must>복잡한 작업은 트랜잭션 사용</must>
337
+ <must-not>N+1 쿼리 (include/select 사용)</must-not>
338
+ <must-not>무한 결과 조회</must-not>
339
+ </category>
340
+ <category name="마이그레이션">
341
+ <must>migrate dev로 개발</must>
342
+ <must>migrate deploy로 배포</must>
343
+ <must>마이그레이션 파일 커밋</must>
344
+ <must-not>프로덕션에서 db push 사용</must-not>
345
+ </category>
346
+ </rules>
347
+
348
+ <checklist>
349
+ <item priority="critical">싱글톤 인스턴스 사용</item>
350
+ <item priority="critical">마이그레이션으로 스키마 관리</item>
351
+ <item priority="critical">트랜잭션으로 데이터 정합성</item>
352
+ <item priority="high">select로 필요한 필드만 조회</item>
353
+ <item priority="high">인덱스 적절히 설정</item>
354
+ <item priority="high">Repository 패턴 적용</item>
355
+ <item priority="medium">소프트 삭제 고려</item>
356
+ </checklist>
357
+ </skill>