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,435 @@
1
+ ---
2
+ name: typescript
3
+ description: TypeScript 개발 가이드라인
4
+ user-invocable: false
5
+ ---
6
+
7
+ <skill name="typescript">
8
+ <purpose>타입 안전한 코드 작성을 위한 TypeScript 가이드라인</purpose>
9
+
10
+ <philosophy>
11
+ <principle>타입은 문서다 - 코드만 봐도 의도 파악</principle>
12
+ <principle>컴파일 타임에 버그를 잡는다</principle>
13
+ <principle>any는 타입 시스템 포기 선언</principle>
14
+ </philosophy>
15
+
16
+ <strict-config>
17
+ <description>tsconfig.json 필수 설정</description>
18
+ <config>
19
+ <![CDATA[
20
+ {
21
+ "compilerOptions": {
22
+ "strict": true,
23
+ "noImplicitAny": true,
24
+ "strictNullChecks": true,
25
+ "noImplicitReturns": true,
26
+ "noFallthroughCasesInSwitch": true,
27
+ "noUncheckedIndexedAccess": true,
28
+ "exactOptionalPropertyTypes": true
29
+ }
30
+ }
31
+ ]]>
32
+ </config>
33
+ </strict-config>
34
+
35
+ <type-patterns>
36
+ <pattern name="Discriminated Union (상태 표현의 정석)">
37
+ <description>status 필드로 타입을 구분하여 불가능한 상태를 불가능하게</description>
38
+ <example type="bad">
39
+ <![CDATA[
40
+ // Bad: 불가능한 상태가 가능함
41
+ interface ApiResponse {
42
+ data?: User;
43
+ error?: string;
44
+ loading: boolean;
45
+ }
46
+ // { data: undefined, error: "fail", loading: true } 가능 - 의미 없음
47
+ ]]>
48
+ </example>
49
+ <example type="good">
50
+ <![CDATA[
51
+ // Good: 불가능한 상태는 타입으로 불가능
52
+ type ApiResponse<T> =
53
+ | { status: 'idle' }
54
+ | { status: 'loading' }
55
+ | { status: 'success'; data: T }
56
+ | { status: 'error'; error: Error };
57
+
58
+ // 사용 - exhaustive check
59
+ function renderUser(state: ApiResponse<User>) {
60
+ switch (state.status) {
61
+ case 'idle':
62
+ return null;
63
+ case 'loading':
64
+ return <Spinner />;
65
+ case 'success':
66
+ return <UserCard user={state.data} />; // data 접근 안전
67
+ case 'error':
68
+ return <ErrorMessage error={state.error} />; // error 접근 안전
69
+ // 새 상태 추가 시 컴파일 에러로 알려줌
70
+ }
71
+ }
72
+ ]]>
73
+ </example>
74
+ </pattern>
75
+
76
+ <pattern name="Type Narrowing (타입 좁히기)">
77
+ <description>조건문으로 타입을 좁혀서 안전하게 사용</description>
78
+ <example>
79
+ <![CDATA[
80
+ // 1. typeof narrowing
81
+ function process(value: string | number) {
82
+ if (typeof value === 'string') {
83
+ return value.toUpperCase(); // string 메서드 안전
84
+ }
85
+ return value.toFixed(2); // number 메서드 안전
86
+ }
87
+
88
+ // 2. in narrowing
89
+ type Fish = { swim: () => void };
90
+ type Bird = { fly: () => void };
91
+
92
+ function move(animal: Fish | Bird) {
93
+ if ('swim' in animal) {
94
+ animal.swim();
95
+ } else {
96
+ animal.fly();
97
+ }
98
+ }
99
+
100
+ // 3. Custom Type Guard
101
+ function isError(value: unknown): value is Error {
102
+ return value instanceof Error;
103
+ }
104
+
105
+ function handleResult(result: unknown) {
106
+ if (isError(result)) {
107
+ console.error(result.message); // Error 타입으로 좁혀짐
108
+ }
109
+ }
110
+
111
+ // 4. Assertion Function
112
+ function assertDefined<T>(value: T | null | undefined): asserts value is T {
113
+ if (value === null || value === undefined) {
114
+ throw new Error('Value is not defined');
115
+ }
116
+ }
117
+
118
+ function processUser(user: User | null) {
119
+ assertDefined(user);
120
+ console.log(user.name); // user는 이제 User 타입
121
+ }
122
+ ]]>
123
+ </example>
124
+ </pattern>
125
+
126
+ <pattern name="Branded Types (타입 브랜딩)">
127
+ <description>같은 원시 타입이지만 의미가 다른 값을 구분</description>
128
+ <example>
129
+ <![CDATA[
130
+ // 문제: userId와 orderId 둘 다 string이라 실수하기 쉬움
131
+ function getUser(id: string) { ... }
132
+ function getOrder(id: string) { ... }
133
+
134
+ const userId = 'user-123';
135
+ const orderId = 'order-456';
136
+ getUser(orderId); // 컴파일 에러 없음! 버그!
137
+
138
+ // 해결: Branded Types
139
+ type UserId = string & { readonly __brand: 'UserId' };
140
+ type OrderId = string & { readonly __brand: 'OrderId' };
141
+
142
+ // 생성 함수
143
+ function createUserId(id: string): UserId {
144
+ // 검증 로직 추가 가능
145
+ if (!id.startsWith('user-')) {
146
+ throw new Error('Invalid user ID format');
147
+ }
148
+ return id as UserId;
149
+ }
150
+
151
+ function createOrderId(id: string): OrderId {
152
+ if (!id.startsWith('order-')) {
153
+ throw new Error('Invalid order ID format');
154
+ }
155
+ return id as OrderId;
156
+ }
157
+
158
+ // 사용
159
+ function getUser(id: UserId): Promise<User> { ... }
160
+ function getOrder(id: OrderId): Promise<Order> { ... }
161
+
162
+ const userId = createUserId('user-123');
163
+ const orderId = createOrderId('order-456');
164
+
165
+ getUser(userId); // ✅ OK
166
+ getUser(orderId); // ❌ Type Error!
167
+
168
+ // 실전 응용: 금액, 이메일 등
169
+ type Money = number & { readonly __brand: 'Money' };
170
+ type Email = string & { readonly __brand: 'Email' };
171
+ type Password = string & { readonly __brand: 'Password' };
172
+ ]]>
173
+ </example>
174
+ </pattern>
175
+
176
+ <pattern name="Template Literal Types">
177
+ <description>문자열 패턴을 타입으로 강제</description>
178
+ <example>
179
+ <![CDATA[
180
+ // API 경로 타입
181
+ type ApiPath = `/api/${string}`;
182
+
183
+ function fetchApi(path: ApiPath) { ... }
184
+
185
+ fetchApi('/api/users'); // ✅ OK
186
+ fetchApi('/users'); // ❌ Type Error
187
+
188
+ // 이벤트 이름 타입
189
+ type EventName = `on${Capitalize<string>}`;
190
+ // 'onClick', 'onSubmit', 'onChange' 등
191
+
192
+ // CSS 단위 타입
193
+ type CSSUnit = `${number}${'px' | 'rem' | 'em' | '%'}`;
194
+ const width: CSSUnit = '100px'; // ✅
195
+ const height: CSSUnit = '100'; // ❌
196
+
197
+ // 환경변수 타입
198
+ type EnvVar = `${Uppercase<string>}_${Uppercase<string>}`;
199
+ // DATABASE_URL, API_KEY 등
200
+ ]]>
201
+ </example>
202
+ </pattern>
203
+
204
+ <pattern name="Infer로 타입 추출">
205
+ <description>조건부 타입에서 타입 추출</description>
206
+ <example>
207
+ <![CDATA[
208
+ // Promise에서 내부 타입 추출
209
+ type Awaited<T> = T extends Promise<infer U> ? U : T;
210
+
211
+ type A = Awaited<Promise<string>>; // string
212
+ type B = Awaited<Promise<Promise<number>>>; // Promise<number>
213
+
214
+ // 배열 요소 타입 추출
215
+ type ArrayElement<T> = T extends (infer U)[] ? U : never;
216
+
217
+ type C = ArrayElement<string[]>; // string
218
+ type D = ArrayElement<[number, string]>; // number | string
219
+
220
+ // 함수 첫 번째 인자 타입 추출
221
+ type FirstArg<T> = T extends (first: infer F, ...args: any[]) => any ? F : never;
222
+
223
+ function greet(name: string, age: number) {}
224
+ type E = FirstArg<typeof greet>; // string
225
+
226
+ // 실전: API 응답에서 data 타입 추출
227
+ type ApiResponse<T> = { success: true; data: T } | { success: false; error: string };
228
+ type ExtractData<T> = T extends { success: true; data: infer D } ? D : never;
229
+
230
+ type UserResponse = ApiResponse<User>;
231
+ type UserData = ExtractData<UserResponse>; // User
232
+ ]]>
233
+ </example>
234
+ </pattern>
235
+
236
+ <pattern name="Zod로 런타임 검증">
237
+ <description>외부 입력은 타입만으로 부족, 런타임 검증 필요</description>
238
+ <example>
239
+ <![CDATA[
240
+ import { z } from 'zod';
241
+
242
+ // 스키마 정의 = 타입 + 검증 규칙
243
+ const userSchema = z.object({
244
+ id: z.string().uuid(),
245
+ email: z.string().email(),
246
+ name: z.string().min(2).max(50),
247
+ age: z.number().int().min(0).max(150).optional(),
248
+ role: z.enum(['admin', 'user', 'guest']),
249
+ createdAt: z.string().datetime(),
250
+ });
251
+
252
+ // 타입 자동 추출
253
+ type User = z.infer<typeof userSchema>;
254
+ // {
255
+ // id: string;
256
+ // email: string;
257
+ // name: string;
258
+ // age?: number;
259
+ // role: 'admin' | 'user' | 'guest';
260
+ // createdAt: string;
261
+ // }
262
+
263
+ // API 핸들러에서 사용
264
+ async function createUser(req: Request) {
265
+ // 런타임 검증 + 타입 안전
266
+ const result = userSchema.safeParse(req.body);
267
+
268
+ if (!result.success) {
269
+ return { error: result.error.format() };
270
+ }
271
+
272
+ const user = result.data; // User 타입으로 안전하게 사용
273
+ await db.user.create({ data: user });
274
+ }
275
+
276
+ // 환경변수 검증
277
+ const envSchema = z.object({
278
+ DATABASE_URL: z.string().url(),
279
+ JWT_SECRET: z.string().min(32),
280
+ PORT: z.coerce.number().default(3000),
281
+ });
282
+
283
+ export const env = envSchema.parse(process.env);
284
+ // env.PORT는 number 타입 (coerce로 변환됨)
285
+ ]]>
286
+ </example>
287
+ </pattern>
288
+ </type-patterns>
289
+
290
+ <utility-types-advanced>
291
+ <pattern name="DTO 타입 조합">
292
+ <example>
293
+ <![CDATA[
294
+ interface User {
295
+ id: string;
296
+ email: string;
297
+ name: string;
298
+ password: string;
299
+ createdAt: Date;
300
+ updatedAt: Date;
301
+ }
302
+
303
+ // Create DTO - id, timestamps 제외
304
+ type CreateUserDto = Omit<User, 'id' | 'createdAt' | 'updatedAt'>;
305
+ // { email: string; name: string; password: string; }
306
+
307
+ // Update DTO - id 제외, 나머지 선택적
308
+ type UpdateUserDto = Partial<Omit<User, 'id' | 'createdAt' | 'updatedAt'>>;
309
+ // { email?: string; name?: string; password?: string; }
310
+
311
+ // Response DTO - password 제외
312
+ type UserResponseDto = Omit<User, 'password'>;
313
+ // { id: string; email: string; name: string; createdAt: Date; updatedAt: Date; }
314
+
315
+ // List Response
316
+ type UsersListDto = {
317
+ users: UserResponseDto[];
318
+ total: number;
319
+ page: number;
320
+ limit: number;
321
+ };
322
+ ]]>
323
+ </example>
324
+ </pattern>
325
+
326
+ <pattern name="DeepPartial / DeepReadonly">
327
+ <example>
328
+ <![CDATA[
329
+ // 깊은 곳까지 선택적으로
330
+ type DeepPartial<T> = {
331
+ [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
332
+ };
333
+
334
+ interface Config {
335
+ database: {
336
+ host: string;
337
+ port: number;
338
+ credentials: {
339
+ user: string;
340
+ password: string;
341
+ };
342
+ };
343
+ }
344
+
345
+ // 부분 설정 가능
346
+ const partialConfig: DeepPartial<Config> = {
347
+ database: {
348
+ host: 'localhost',
349
+ // port, credentials 생략 가능
350
+ },
351
+ };
352
+
353
+ // 깊은 곳까지 읽기 전용
354
+ type DeepReadonly<T> = {
355
+ readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
356
+ };
357
+
358
+ const frozenConfig: DeepReadonly<Config> = { ... };
359
+ frozenConfig.database.host = 'new'; // ❌ Error
360
+ ]]>
361
+ </example>
362
+ </pattern>
363
+ </utility-types-advanced>
364
+
365
+ <real-world-example>
366
+ <title>타입 안전한 API 클라이언트</title>
367
+ <example>
368
+ <![CDATA[
369
+ // types/api.ts
370
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
371
+
372
+ type ApiEndpoint = {
373
+ '/users': {
374
+ GET: { response: User[]; query?: { page?: number; limit?: number } };
375
+ POST: { response: User; body: CreateUserDto };
376
+ };
377
+ '/users/:id': {
378
+ GET: { response: User; params: { id: string } };
379
+ PUT: { response: User; params: { id: string }; body: UpdateUserDto };
380
+ DELETE: { response: void; params: { id: string } };
381
+ };
382
+ };
383
+
384
+ // 타입 안전한 fetch 래퍼
385
+ async function api<
386
+ Path extends keyof ApiEndpoint,
387
+ Method extends keyof ApiEndpoint[Path]
388
+ >(
389
+ path: Path,
390
+ method: Method,
391
+ options?: ApiEndpoint[Path][Method] extends { body: infer B } ? { body: B } : never
392
+ ): Promise<ApiEndpoint[Path][Method] extends { response: infer R } ? R : never> {
393
+ // 구현
394
+ }
395
+
396
+ // 사용 - 자동완성 + 타입 체크
397
+ const users = await api('/users', 'GET'); // User[]
398
+ const user = await api('/users', 'POST', {
399
+ body: { email: 'a@b.com', name: 'Test', password: '...' }
400
+ }); // User
401
+
402
+ // 잘못된 사용은 컴파일 에러
403
+ await api('/users', 'DELETE'); // ❌ DELETE는 /users에 없음
404
+ await api('/users', 'POST', { body: { email: 'a@b.com' } }); // ❌ name, password 누락
405
+ ]]>
406
+ </example>
407
+ </real-world-example>
408
+
409
+ <rules>
410
+ <category name="필수">
411
+ <must>strict 모드 사용</must>
412
+ <must>함수 반환 타입 명시</must>
413
+ <must>외부 입력(API, 사용자)은 Zod로 런타임 검증</must>
414
+ <must>Discriminated Union으로 상태 표현</must>
415
+ <must>Branded Types로 ID 타입 구분</must>
416
+ </category>
417
+ <category name="금지">
418
+ <must-not>any 사용 (unknown + type guard 대신)</must-not>
419
+ <must-not>타입 단언 남용 (as Type)</must-not>
420
+ <must-not>@ts-ignore, @ts-expect-error 남용</must-not>
421
+ <must-not>! non-null assertion 남용</must-not>
422
+ </category>
423
+ </rules>
424
+
425
+ <checklist>
426
+ <item priority="critical">strict 모드 활성화</item>
427
+ <item priority="critical">any 타입 없음</item>
428
+ <item priority="critical">함수 반환 타입 명시</item>
429
+ <item priority="critical">외부 입력 Zod 검증</item>
430
+ <item priority="high">Discriminated Union으로 상태 표현</item>
431
+ <item priority="high">Branded Types로 ID 구분</item>
432
+ <item priority="medium">Utility Types 활용</item>
433
+ <item priority="medium">Template Literal Types 활용</item>
434
+ </checklist>
435
+ </skill>
@@ -0,0 +1,131 @@
1
+ # TimSquad Project Configuration
2
+ # 이 파일을 .timsquad/config.yaml로 복사하여 사용
3
+
4
+ # ============================================================
5
+ # 프로젝트 기본 정보
6
+ # ============================================================
7
+ project:
8
+ name: my-project
9
+ type: web-service # web-service | api-backend | platform | fintech | infra
10
+ level: 2 # 1=MVP | 2=Standard | 3=Enterprise
11
+ description: ""
12
+
13
+ # ============================================================
14
+ # 개발 방법론
15
+ # ============================================================
16
+ methodology:
17
+ development: tdd # tdd | bdd | none
18
+ process: agile # agile | waterfall | kanban
19
+ branching: github-flow # gitflow | trunk-based | github-flow
20
+ review: required # required | optional | none
21
+
22
+ # ============================================================
23
+ # 기술 스택
24
+ # ============================================================
25
+ stack:
26
+ # 공통
27
+ language: typescript # typescript | javascript
28
+
29
+ # 프론트엔드
30
+ frontend:
31
+ enabled: true
32
+ framework: nextjs # nextjs | react | vue | svelte | none
33
+ state: zustand # zustand | redux | recoil | context | none
34
+ styling: tailwind # tailwind | styled-components | css-modules | none
35
+ testing: vitest # vitest | jest | none
36
+
37
+ # 백엔드
38
+ backend:
39
+ enabled: true
40
+ runtime: node # node | python | go | java
41
+ framework: hono # hono | express | fastify | nest | none
42
+ testing: vitest # vitest | jest | pytest | none
43
+
44
+ # 데이터베이스
45
+ database:
46
+ primary: postgresql # postgresql | mysql | mongodb | sqlite
47
+ orm: prisma # prisma | typeorm | drizzle | none
48
+ cache: none # redis | memcached | none
49
+
50
+ # 인프라
51
+ infrastructure:
52
+ hosting: vercel # vercel | aws | gcp | self-hosted
53
+ container: false # Docker 사용 여부
54
+ ci_cd: github # github | gitlab | none
55
+
56
+ # ============================================================
57
+ # 에이전트 스킬 설정 (자동 로딩)
58
+ # ============================================================
59
+ skills:
60
+ # 기본 스킬 (항상 로드)
61
+ base:
62
+ - coding
63
+ - testing
64
+ - security
65
+
66
+ # 방법론에 따라 자동 로드
67
+ methodology:
68
+ tdd: [methodology/tdd]
69
+ bdd: [methodology/bdd]
70
+
71
+ # 스택에 따라 자동 로드
72
+ stack:
73
+ typescript: [typescript]
74
+ react: [frontend/react]
75
+ nextjs: [frontend/nextjs, frontend/react]
76
+ vue: [frontend/vue]
77
+ node: [backend/node]
78
+ python: [backend/python]
79
+ prisma: [database/prisma]
80
+ typeorm: [database/typeorm]
81
+
82
+ # ============================================================
83
+ # 에이전트별 커스텀 설정
84
+ # ============================================================
85
+ agents:
86
+ tsq-planner:
87
+ model: opus # opus | sonnet | haiku
88
+ additional-skills: []
89
+
90
+ tsq-developer:
91
+ model: sonnet
92
+ additional-skills: []
93
+ # 프론트엔드/백엔드 분리 시
94
+ # variants:
95
+ # frontend:
96
+ # skills: [frontend/react, frontend/nextjs]
97
+ # backend:
98
+ # skills: [backend/node, database/prisma]
99
+
100
+ tsq-qa:
101
+ model: sonnet
102
+ additional-skills: []
103
+
104
+ # ============================================================
105
+ # 품질 기준
106
+ # ============================================================
107
+ quality:
108
+ test_coverage:
109
+ minimum: 80
110
+ recommended: 90
111
+
112
+ code_review:
113
+ required: true
114
+ approvers: 1
115
+
116
+ lint:
117
+ enabled: true
118
+ fix_on_save: true
119
+
120
+ # ============================================================
121
+ # SSOT 설정
122
+ # ============================================================
123
+ ssot:
124
+ # 프로젝트 레벨에 따른 필수 문서는 자동 결정
125
+ # 추가로 필요한 문서 지정
126
+ additional: []
127
+
128
+ # 문서 템플릿 커스터마이징
129
+ templates:
130
+ # 기본 템플릿 사용
131
+ use_default: true
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: clean-architecture
3
+ description: Uncle Bob's Clean Architecture
4
+ aliases: [onion, ports-and-adapters-lite]
5
+ suitable-for: [web-service, api-backend, platform]
6
+ ---
7
+
8
+ # Clean Architecture
9
+
10
+ <architecture name="clean">
11
+ <philosophy>
12
+ <principle>의존성은 항상 안쪽(도메인)으로 향한다</principle>
13
+ <principle>비즈니스 규칙은 프레임워크에 독립적</principle>
14
+ <principle>외부 세계(DB, UI, API)는 세부사항</principle>
15
+ <principle>테스트 가능성이 설계의 핵심</principle>
16
+ </philosophy>
17
+
18
+ <layers>
19
+ <layer name="entities" level="core">
20
+ <description>엔터프라이즈 비즈니스 규칙. 가장 안정적인 레이어</description>
21
+ <contains>도메인 모델, 비즈니스 규칙, Value Objects</contains>
22
+ <depends-on>없음 (의존성 없음)</depends-on>
23
+ </layer>
24
+
25
+ <layer name="use-cases" level="application">
26
+ <description>애플리케이션 비즈니스 규칙. 유스케이스 구현</description>
27
+ <contains>서비스, 유스케이스, 포트(인터페이스)</contains>
28
+ <depends-on>entities</depends-on>
29
+ </layer>
30
+
31
+ <layer name="adapters" level="interface">
32
+ <description>외부와 내부를 연결하는 어댑터</description>
33
+ <contains>컨트롤러, 리포지토리 구현, DTO 변환</contains>
34
+ <depends-on>use-cases, entities</depends-on>
35
+ </layer>
36
+
37
+ <layer name="infrastructure" level="external">
38
+ <description>프레임워크와 드라이버</description>
39
+ <contains>DB, 웹 프레임워크, 외부 API 클라이언트</contains>
40
+ <depends-on>adapters</depends-on>
41
+ </layer>
42
+ </layers>
43
+
44
+ <dependency-rule>
45
+ <rule>안쪽 레이어는 바깥쪽 레이어를 알지 못한다</rule>
46
+ <rule>의존성 역전 원칙(DIP)으로 방향 제어</rule>
47
+ <rule>인터페이스는 안쪽에, 구현은 바깥쪽에</rule>
48
+ </dependency-rule>
49
+ </architecture>