muno-claude-plugin 1.2.0 → 1.4.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muno-claude-plugin",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Unleash Claude Code's full power - Complete development workflow with expert personas, TDD-based agents, and automated documentation",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {
@@ -2,129 +2,41 @@
2
2
 
3
3
  AI 기반 개발 워크플로우 스킬 체계입니다.
4
4
 
5
- ## 스킬 목록
6
-
7
- | 스킬 | 설명 | 입력 | 출력 |
8
- |------|------|------|------|
9
- | `/prd-generator` | PRD 생성 | 요구사항 | PRD 문서 |
10
- | `/epic-story-generator` | Epic & Story 생성 | PRD | Epic, Story (AC 포함) |
11
- | `/hld-generator` | High-Level Design | Story | 아키텍처 설계 |
12
- | `/lld-generator` | Low-Level Design | Story, HLD | 상세 설계 (ERD, API) |
13
- | `/task-generator` | Implementation Task | Story, LLD | 구현 태스크 |
14
- | `/tc-generator` | Test Case 생성 | Story (AC) | TC 문서 |
5
+ > **Note**: 워크플로우 순서는 `workflow-navigator` agent가 상황에 맞게 제안합니다.
6
+ > 모든 단계가 항상 필요한 것은 아닙니다.
15
7
 
16
- ## 자동 호출 Subagent
8
+ ---
17
9
 
18
- | Subagent | 설명 | 자동 호출 시점 |
19
- |----------|------|---------------|
20
- | `tc-reviewer` | TC 품질 리뷰 및 보완 | TC 생성 **직후** |
21
- | `acceptance-test-generator` | TC 기반 인수 테스트 생성 | TC 리뷰 **완료 후** (테스트 먼저) |
22
- | `unit-test-generator` | TDD 기반 Unit Test 생성 | Task 구현 **시작 전** (테스트 먼저) |
23
- | `task-tracker` | Epic/Story/Task 상태 추적 | Task 완료, TC 통과, 리뷰 승인 시 |
10
+ ## 스킬 목록
24
11
 
25
- > **2-Level TDD 워크플로우**:
26
- >
27
- > **Story Level (인수 테스트)**:
28
- > 1. tc-generator TC 명세 생성
29
- > 2. tc-reviewer가 TC 품질 리뷰 보완
30
- > 3. acceptance-test-generator 인수 테스트 생성 (Red)
31
- > 4. 모든 Task 완료 인수 테스트 통과 (Green)
32
- >
33
- > **Task Level (단위 테스트)**:
34
- > 1. unit-test-generator가 Task 완료 조건 기반 Unit Test 생성 (Red)
35
- > 2. 코딩 agent가 테스트 통과하도록 구현 (Green)
36
- > 3. 리팩토링 (Refactor)
12
+ | 스킬 | 설명 | 주요 입력 |
13
+ |------|------|----------|
14
+ | `/prd-generator` | PRD 생성 | 요구사항 |
15
+ | `/epic-story-generator` | Epic & Story 생성 | PRD (선택) |
16
+ | `/hld-generator` | High-Level Design | PRD + Epic/Story |
17
+ | `/lld-generator` | Low-Level Design | HLD |
18
+ | `/task-generator` | Implementation Task | Story + LLD |
19
+ | `/tc-generator` | Test Case 생성 | Story AC |
37
20
 
38
21
  ---
39
22
 
40
- ## 전체 워크플로우
23
+ ## Subagent 목록
41
24
 
42
- ```
43
- ┌─────────────────────────────────────────────────────────────────────────────┐
44
- │ Development Workflow │
45
- └─────────────────────────────────────────────────────────────────────────────┘
46
-
47
- [1. 기획]
48
- 요구사항
49
-
50
-
51
- /prd-generator ──────────────────────────────► PRD 문서
52
-
53
-
54
- [2. 백로그] /epic-story-generator
55
-
56
- ┌─────────────────┴─────────────────┐
57
- ▼ ▼
58
- Epic Story
59
- │ │
60
- │ ┌──────────────┤
61
- │ │ │
62
- │ ▼ │
63
- [3. 테스트 설계] │ /tc-generator │
64
- │ │ │
65
- │ ▼ │
66
- │ TC 문서 │
67
- │ │ │
68
- │ ▼ │
69
- │ tc-reviewer (subagent) │
70
- │ │ │
71
- │ ▼ │
72
- │ TC 리뷰 및 보완 │
73
- │ │ │
74
- │ ▼ │
75
- │ acceptance-test-generator │
76
- │ (subagent) │
77
- │ │ │
78
- │ ▼ │
79
- │ 인수 테스트 생성 (Red) │
80
- │ │
81
- ▼ ▼
82
- [4. 설계] /hld-generator /lld-generator
83
- │ │
84
- ▼ ▼
85
- HLD 문서 LLD 문서
86
-
87
-
88
- [5. 구현] /task-generator
89
-
90
-
91
- Implementation Tasks
92
-
93
- ┌───────────────────────────────────┤
94
- ▼ │
95
- [5-1. TDD] unit-test-generator (subagent) │
96
- │ │
97
- ▼ │
98
- Unit Test 생성 (Red) │
99
- │ │
100
- ▼ ▼
101
- [5-2. 구현] 코드 구현 ◄──────────────────── Task 기반 구현
102
-
103
- │ Unit Test 통과 (Green)
104
-
105
- [6. 인수 테스트] 인수 테스트 실행
106
-
107
-
108
- 모든 TC passed 확인
109
-
110
-
111
- [7. 자동 추적] task-tracker (subagent)
112
-
113
- ┌──────────┴──────────┐
114
- ▼ ▼
115
- Task 상태 자동 업데이트 Story/Epic 완료 체크
116
- │ │
117
- ▼ ▼
118
- todo → inprogress 모든 Task close
119
- → resolve → review + 모든 TC passed
120
- → close │
121
-
122
- Story close
123
- ```
25
+ | Subagent | 설명 | 호출 시점 |
26
+ |----------|------|----------|
27
+ | `workflow-navigator` | 다음 워크플로우 제안 | 작업 시작 시 |
28
+ | `prd-reviewer` | PRD 품질 리뷰 | PRD 생성 후 |
29
+ | `epic-story-reviewer` | Epic/Story 리뷰 | Epic/Story 생성 후 |
30
+ | `hld-reviewer` | HLD 아키텍처 리뷰 | HLD 생성 후 |
31
+ | `lld-reviewer` | LLD 상세 설계 리뷰 | LLD 생성 후 |
32
+ | `tc-reviewer` | TC 품질 리뷰 | TC 생성 후 |
33
+ | `acceptance-test-generator` | 인수 테스트 생성 | TC 리뷰 후 |
34
+ | `unit-test-generator` | Unit Test 생성 (TDD) | Task 구현 전 |
35
+ | `task-tracker` | 상태 추적 | Task 완료 시 |
124
36
 
125
37
  ---
126
38
 
127
- ## 복잡도별 워크플로우
39
+ ## 복잡도별 권장 워크플로우
128
40
 
129
41
  ### L1~L2: 단순 작업
130
42
  ```
@@ -133,14 +45,28 @@ AI 기반 개발 워크플로우 스킬 체계입니다.
133
45
 
134
46
  ### L3: 중간 복잡도
135
47
  ```
136
- 요구사항 → Story → Task + LLD → 구현 → TC → 테스트
48
+ 요구사항 → Story → Task → 구현
49
+ └→ LLD (선택)
137
50
  ```
138
51
 
139
52
  ### L4~L5: 높은 복잡도
140
53
  ```
141
- 요구사항 → PRD → Epic/Story → HLD → LLD → Task → 구현 → TC → 테스트
54
+ 요구사항 → PRD → Epic/Story → HLD (선택) → LLD → Task → 구현
142
55
  ```
143
56
 
57
+ > 복잡도 판단은 `workflow-navigator`에게 문의하세요.
58
+
59
+ ---
60
+
61
+ ## 건너뛰기 가능한 단계
62
+
63
+ | 단계 | 건너뛰기 조건 |
64
+ |------|-------------|
65
+ | PRD | 이해관계자 합의 불필요, 단순 기능 |
66
+ | Epic/Story | 사용자 가치 없음 (기술 부채, 리팩토링) |
67
+ | HLD | 아키텍처 변경 없음, 기존 패턴 내 |
68
+ | LLD | 서빙 전략/장애 대응 변경 없음 |
69
+
144
70
  ---
145
71
 
146
72
  ## 문서 저장 구조
@@ -148,113 +74,33 @@ AI 기반 개발 워크플로우 스킬 체계입니다.
148
74
  ```
149
75
  project/
150
76
  ├── documents/
151
- │ ├── prd/
152
- │ └── feature-prd.md
153
- ├── hld/
154
- │ │ └── feature-hld.md
155
- │ └── lld/
156
- │ └── feature-lld.md
77
+ │ ├── prd/ # PRD 문서
78
+ ├── hld/ # HLD 문서
79
+ └── lld/ # LLD 문서
157
80
 
158
- ├── backlogs/
81
+ ├── backlogs/ # Epic/Story
159
82
  │ └── feature-name/
160
83
  │ ├── index.md
161
- ├── EPIC-001-xxx.md
162
- │ ├── STORY-001-xxx.md
163
- │ └── STORY-002-xxx.md
84
+ └── STORY-xxx.md
164
85
 
165
- ├── .tasks/
166
- │ └── STORY-001/
167
- ├── index.md
168
- │ ├── TASK-001-entity.md
169
- │ ├── TASK-002-repository.md
170
- │ └── ...
86
+ ├── .tasks/ # Task
87
+ │ └── STORY-xxx/
88
+ └── TASK-xxx.md
171
89
 
172
- ├── test-cases/
173
- └── STORY-001/
174
- │ ├── index.md
175
- │ ├── TC-001-01-happy-path.md
176
- │ └── TC-001-02-error-case.md
177
-
178
- └── src/test/
179
- └── ... (테스트 코드)
180
- ```
181
-
182
- ---
183
-
184
- ## 상태 관리
185
-
186
- ### 상태 라이프사이클
187
-
188
- **공통 상태 흐름**: `todo → inprogress → resolve → review → close`
189
-
190
- ```
191
- todo → inprogress → resolve → review → close
192
- │ │ │ │ │
193
- │ │ │ │ └─ 최종 완료
194
- │ │ │ └─ 코드 리뷰 중
195
- │ │ └─ 구현 완료, 리뷰 대기
196
- │ └─ 작업 진행 중
197
- └─ 대기 중 (백로그)
198
-
199
- blocked ← 어느 단계에서든 차단 가능
200
- ```
201
-
202
- ### 2-Level 상태 관리
203
-
204
- | Level | 담당 | 위치 | 설명 |
205
- |-------|------|------|------|
206
- | 개별 문서 | 코딩 Agent | `TASK-*.md`, `TC-*.md` | 개별 작업 상태 직접 관리 |
207
- | 중앙 문서 | task-tracker | `.status/current.yaml` | 전체 상태 집계 및 동기화 |
208
-
209
- ### TC 상태
210
-
211
- - `ready` → `passed` / `failed`
212
- - `blocked` (구현 미완료)
213
-
214
- ### 완료 조건
215
-
216
- ```
217
- Story close = 모든 Task close + 모든 TC passed
218
- Epic close = 모든 Story close
90
+ └── test-cases/ # Test Cases
91
+ └── STORY-xxx/
92
+ └── TC-xxx.md
219
93
  ```
220
94
 
221
95
  ---
222
96
 
223
- ## 스킬 호출 예시
224
-
225
- ```bash
226
- # PRD 생성
227
- /prd-generator
228
-
229
- # Story 생성
230
- /epic-story-generator
231
-
232
- # 설계
233
- /hld-generator
234
- /lld-generator
235
-
236
- # 구현 태스크
237
- /task-generator
238
-
239
- # 테스트
240
- /tc-generator # TC 문서 생성
241
- # → tc-reviewer 자동 호출 (TC 품질 리뷰)
242
- # → acceptance-test-generator 자동 호출 (인수 테스트 생성)
243
- ```
244
-
245
- > **Note**:
246
- > - TC 생성 시 `tc-reviewer` → `acceptance-test-generator` 순서로 자동 호출
247
- > - Unit Test는 `unit-test-generator` subagent가 Task 구현 전에 자동 생성 (TDD)
248
- > - 상태 추적은 `task-tracker` subagent가 자동 수행
249
-
250
- ---
251
-
252
- ## 관련 페르소나
97
+ ## 페르소나
253
98
 
254
- | 페르소나 | 역할 | 주요 스킬/에이전트 |
255
- |----------|------|-------------------|
256
- | `/po` | Product Owner | prd-generator, epic-story-generator |
99
+ | 페르소나 | 역할 | 주요 스킬 |
100
+ |----------|------|----------|
101
+ | `/po` | Product Owner | prd-generator |
257
102
  | `/sm` | Scrum Master | epic-story-generator |
258
103
  | `/principal-engineer` | 아키텍처 | hld-generator |
259
- | `/dev` | 개발자 | lld-generator, task-generator |
260
- | `/qa` | QA 엔지니어 | tc-generator (→ tc-reviewer, acceptance-test-generator 자동 호출) |
104
+ | `/staff-engineer` | 상세 설계 | lld-generator |
105
+ | `/dev` | 개발자 | task-generator |
106
+ | `/qa` | QA 엔지니어 | tc-generator |
@@ -169,37 +169,33 @@ status: ready
169
169
 
170
170
  ## 출력: 인수 테스트 코드
171
171
 
172
- ### Integration Test 패턴
172
+ ### 프로젝트 언어 감지
173
+
174
+ **테스트 코드 생성 전 반드시 프로젝트의 기술 스택을 파악합니다:**
175
+
176
+ ```
177
+ 1. build.gradle.kts / build.gradle → Kotlin/Java + Spring
178
+ 2. pom.xml → Java + Spring
179
+ 3. package.json → TypeScript/JavaScript + Node.js
180
+ 4. requirements.txt / pyproject.toml → Python
181
+ 5. go.mod → Go
182
+ 6. 기존 테스트 코드 패턴 확인 → 프로젝트 컨벤션 따르기
183
+ ```
184
+
185
+ ---
186
+
187
+ ## 언어별 Integration Test 패턴
188
+
189
+ ### Kotlin + Spring Boot
173
190
 
174
191
  ```kotlin
175
- package com.example.integration.chat
176
-
177
- import org.junit.jupiter.api.*
178
- import org.springframework.beans.factory.annotation.Autowired
179
- import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
180
- import org.springframework.boot.test.context.SpringBootTest
181
- import org.springframework.http.MediaType
182
- import org.springframework.test.web.servlet.MockMvc
183
- import org.springframework.test.web.servlet.post
184
- import org.springframework.transaction.annotation.Transactional
185
-
186
- /**
187
- * STORY-001: 채팅방 관리
188
- *
189
- * TC 기반 인수 테스트 - Story 완료 검증
190
- * TC 생성 직후 작성 (Test First)
191
- */
192
192
  @SpringBootTest
193
193
  @AutoConfigureMockMvc
194
194
  @Transactional
195
195
  @DisplayName("STORY-001: 채팅방 관리 인수 테스트")
196
196
  class ChatRoomAcceptanceTest {
197
197
 
198
- @Autowired
199
- lateinit var mockMvc: MockMvc
200
-
201
- @Autowired
202
- lateinit var chatRoomRepository: ChatRoomRepository
198
+ @Autowired lateinit var mockMvc: MockMvc
203
199
 
204
200
  @Nested
205
201
  @DisplayName("TC-001-01: 채팅방 생성 - Happy Path")
@@ -207,50 +203,10 @@ class ChatRoomAcceptanceTest {
207
203
 
208
204
  @Test
209
205
  @DisplayName("인증된 사용자가 유효한 제목으로 채팅방 생성 시 성공")
210
- fun `POST api_chat-rooms - 201 Created with chatRoomId`() {
211
- // Arrange (사전 조건)
212
- val token = createTestUserToken(userId = 1L)
213
- val requestBody = """
214
- {
215
- "title": "새 채팅방"
216
- }
217
- """.trimIndent()
218
-
219
- // Act (실행 단계)
220
- val result = mockMvc.post("/api/chat-rooms") {
221
- contentType = MediaType.APPLICATION_JSON
222
- content = requestBody
223
- header("Authorization", "Bearer $token")
224
- }
225
-
226
- // Assert (기대 결과)
227
- result.andExpect {
228
- status { isCreated() }
229
- jsonPath("$.chatRoomId") { exists() }
230
- jsonPath("$.title") { value("새 채팅방") }
231
- }
232
-
233
- // DB 검증
234
- val chatRooms = chatRoomRepository.findAll()
235
- assertThat(chatRooms).hasSize(1)
236
- assertThat(chatRooms[0].title).isEqualTo("새 채팅방")
237
- }
238
- }
239
-
240
- @Nested
241
- @DisplayName("TC-001-02: 채팅방 생성 - 빈 제목 오류")
242
- inner class TC_001_02_EmptyTitle {
243
-
244
- @Test
245
- @DisplayName("빈 제목으로 채팅방 생성 시 400 Bad Request")
246
- fun `POST api_chat-rooms with empty title - 400 Bad Request`() {
206
+ fun `POST api_chat-rooms - 201 Created`() {
247
207
  // Arrange
248
208
  val token = createTestUserToken(userId = 1L)
249
- val requestBody = """
250
- {
251
- "title": ""
252
- }
253
- """.trimIndent()
209
+ val requestBody = """{"title": "새 채팅방"}"""
254
210
 
255
211
  // Act & Assert
256
212
  mockMvc.post("/api/chat-rooms") {
@@ -258,86 +214,135 @@ class ChatRoomAcceptanceTest {
258
214
  content = requestBody
259
215
  header("Authorization", "Bearer $token")
260
216
  }.andExpect {
261
- status { isBadRequest() }
262
- jsonPath("$.error") { exists() }
217
+ status { isCreated() }
218
+ jsonPath("$.chatRoomId") { exists() }
263
219
  }
264
220
  }
265
221
  }
222
+ }
223
+ ```
224
+
225
+ ### Java + Spring Boot
226
+
227
+ ```java
228
+ @SpringBootTest
229
+ @AutoConfigureMockMvc
230
+ @Transactional
231
+ @DisplayName("STORY-001: 채팅방 관리 인수 테스트")
232
+ class ChatRoomAcceptanceTest {
233
+
234
+ @Autowired
235
+ private MockMvc mockMvc;
266
236
 
267
237
  @Nested
268
- @DisplayName("TC-001-03: 인증 없이 채팅방 생성 시도")
269
- inner class TC_001_03_Unauthorized {
238
+ @DisplayName("TC-001-01: 채팅방 생성 - Happy Path")
239
+ class TC_001_01_CreateChatRoom {
270
240
 
271
241
  @Test
272
- @DisplayName("인증 토큰 없이 채팅방 생성 시 401 Unauthorized")
273
- fun `POST api_chat-rooms without token - 401 Unauthorized`() {
242
+ @DisplayName("인증된 사용자가 유효한 제목으로 채팅방 생성 시 성공")
243
+ void createChatRoom_withValidTitle_returns201() throws Exception {
274
244
  // Arrange
275
- val requestBody = """
276
- {
277
- "title": "새 채팅방"
278
- }
279
- """.trimIndent()
245
+ String requestBody = """{"title": "새 채팅방"}""";
280
246
 
281
247
  // Act & Assert
282
- mockMvc.post("/api/chat-rooms") {
283
- contentType = MediaType.APPLICATION_JSON
284
- content = requestBody
285
- }.andExpect {
286
- status { isUnauthorized() }
287
- }
248
+ mockMvc.perform(post("/api/chat-rooms")
249
+ .contentType(MediaType.APPLICATION_JSON)
250
+ .content(requestBody)
251
+ .header("Authorization", "Bearer " + testToken))
252
+ .andExpect(status().isCreated())
253
+ .andExpect(jsonPath("$.chatRoomId").exists());
288
254
  }
289
255
  }
290
-
291
- private fun createTestUserToken(userId: Long): String {
292
- // 테스트용 JWT 토큰 생성
293
- return "test-token-for-user-$userId"
294
- }
295
256
  }
296
257
  ```
297
258
 
298
- ---
259
+ ### TypeScript + Jest + Supertest
260
+
261
+ ```typescript
262
+ describe('STORY-001: 채팅방 관리 인수 테스트', () => {
263
+ describe('TC-001-01: 채팅방 생성 - Happy Path', () => {
264
+ it('인증된 사용자가 유효한 제목으로 채팅방 생성 시 성공', async () => {
265
+ // Arrange
266
+ const token = await createTestUserToken(1);
267
+ const requestBody = { title: '새 채팅방' };
268
+
269
+ // Act
270
+ const response = await request(app)
271
+ .post('/api/chat-rooms')
272
+ .set('Authorization', `Bearer ${token}`)
273
+ .send(requestBody);
274
+
275
+ // Assert
276
+ expect(response.status).toBe(201);
277
+ expect(response.body.chatRoomId).toBeDefined();
278
+ });
279
+ });
280
+ });
281
+ ```
299
282
 
300
- ## 테스트 유형별 패턴
283
+ ### Python + pytest + FastAPI/Django
284
+
285
+ ```python
286
+ import pytest
287
+ from httpx import AsyncClient
288
+
289
+ class TestChatRoomAcceptance:
290
+ """STORY-001: 채팅방 관리 인수 테스트"""
291
+
292
+ class TestTC_001_01_CreateChatRoom:
293
+ """TC-001-01: 채팅방 생성 - Happy Path"""
294
+
295
+ @pytest.mark.asyncio
296
+ async def test_create_chatroom_with_valid_title_returns_201(
297
+ self, client: AsyncClient, auth_token: str
298
+ ):
299
+ response = await client.post(
300
+ "/api/chat-rooms",
301
+ json={"title": "새 채팅방"},
302
+ headers={"Authorization": f"Bearer {auth_token}"}
303
+ )
304
+ assert response.status_code == 201
305
+ assert "chatRoomId" in response.json()
306
+ ```
301
307
 
302
- ### Integration Test (API 레벨)
308
+ ### Go + testing + httptest
303
309
 
304
- ```kotlin
305
- @SpringBootTest
306
- @AutoConfigureMockMvc
307
- @Transactional
308
- class XxxAcceptanceTest {
309
- @Autowired lateinit var mockMvc: MockMvc
310
+ ```go
311
+ func TestChatRoomAcceptance(t *testing.T) {
312
+ t.Run("TC-001-01: 채팅방 생성 - Happy Path", func(t *testing.T) {
313
+ // Arrange
314
+ token := createTestUserToken(1)
315
+ body := `{"title": "새 채팅방"}`
310
316
 
311
- @Nested
312
- @DisplayName("TC-XXX-XX: 시나리오명")
313
- inner class TC_XXX_XX { ... }
314
- }
315
- ```
317
+ req := httptest.NewRequest("POST", "/api/chat-rooms", strings.NewReader(body))
318
+ req.Header.Set("Authorization", "Bearer "+token)
316
319
 
317
- ### E2E Test (전체 흐름)
320
+ // Act
321
+ rec := httptest.NewRecorder()
322
+ router.ServeHTTP(rec, req)
318
323
 
319
- ```kotlin
320
- @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
321
- @TestMethodOrder(MethodOrderer.OrderAnnotation::class)
322
- class XxxE2ETest {
323
- @Autowired lateinit var restTemplate: TestRestTemplate
324
-
325
- @Test
326
- @Order(1)
327
- fun `Step 1: 사용자 생성`() { ... }
328
-
329
- @Test
330
- @Order(2)
331
- fun `Step 2: 로그인`() { ... }
332
-
333
- @Test
334
- @Order(3)
335
- fun `Step 3: 채팅방 생성`() { ... }
324
+ // Assert
325
+ assert.Equal(t, http.StatusCreated, rec.Code)
326
+ })
336
327
  }
337
328
  ```
338
329
 
339
330
  ---
340
331
 
332
+ ## 테스트 유형별 패턴 (언어 공통)
333
+
334
+ ### Integration Test (API 레벨)
335
+ - HTTP 요청/응답 검증
336
+ - DB 상태 검증
337
+ - 트랜잭션 롤백으로 격리
338
+
339
+ ### E2E Test (전체 흐름)
340
+ - 실제 서버 구동 (랜덤 포트)
341
+ - 순차적 시나리오 실행
342
+ - 외부 의존성 Mock 또는 실제 연동
343
+
344
+ ---
345
+
341
346
  ## 생성 워크플로우
342
347
 
343
348
  ### Step 1: TC 분석
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: epic-story-reviewer
3
3
  description: Epic/Story 문서를 체크리스트 기반으로 검토합니다. 통과하면 OK, 문제 있을 때만 피드백합니다.
4
- model: opus
4
+ model: sonnet
5
5
  color: blue
6
6
  ---
7
7
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: prd-reviewer
3
3
  description: PRD 문서를 체크리스트 기반으로 검토합니다. 통과하면 OK, 문제 있을 때만 피드백합니다.
4
- model: opus
4
+ model: sonnet
5
5
  color: red
6
6
  ---
7
7
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: tc-reviewer
3
3
  description: TC 문서를 체크리스트 기반으로 검토합니다. 통과하면 OK, 문제 있을 때만 피드백합니다.
4
- model: opus
4
+ model: sonnet
5
5
  color: purple
6
6
  ---
7
7
 
@@ -153,21 +153,26 @@ status: todo
153
153
 
154
154
  ## 출력: Unit Test 코드
155
155
 
156
- ### 생성 패턴
156
+ ### 프로젝트 언어 감지
157
+
158
+ **테스트 코드 생성 전 반드시 프로젝트의 기술 스택을 파악합니다:**
159
+
160
+ ```
161
+ 1. build.gradle.kts / build.gradle → Kotlin/Java + Spring
162
+ 2. pom.xml → Java + Spring
163
+ 3. package.json → TypeScript/JavaScript + Node.js
164
+ 4. requirements.txt / pyproject.toml → Python
165
+ 5. go.mod → Go
166
+ 6. 기존 테스트 코드 패턴 확인 → 프로젝트 컨벤션 따르기
167
+ ```
168
+
169
+ ---
170
+
171
+ ## 언어별 Unit Test 패턴
172
+
173
+ ### Kotlin + JUnit5
157
174
 
158
175
  ```kotlin
159
- package com.example.domain.chat.model
160
-
161
- import org.junit.jupiter.api.*
162
- import org.assertj.core.api.Assertions.assertThat
163
- import org.assertj.core.api.Assertions.assertThatThrownBy
164
-
165
- /**
166
- * TASK-001: ChatRoom Entity 생성
167
- *
168
- * TDD 기반 Unit Test - 구현 전 작성
169
- * Task 완료 조건을 검증하는 테스트
170
- */
171
176
  @DisplayName("ChatRoom Entity 테스트")
172
177
  class ChatRoomTest {
173
178
 
@@ -176,8 +181,8 @@ class ChatRoomTest {
176
181
  inner class Creation {
177
182
 
178
183
  @Test
179
- @DisplayName("유효한 값으로 Entity 생성 시 성공한다")
180
- fun `유효한 userId와 title로 ChatRoom 생성 시 externalId가 자동 생성된다`() {
184
+ @DisplayName("유효한 값으로 Entity 생성 시 성공")
185
+ fun `유효한 userId와 title로 ChatRoom 생성 시 성공`() {
181
186
  // Arrange
182
187
  val userId = 1L
183
188
  val title = "테스트 채팅방"
@@ -187,80 +192,150 @@ class ChatRoomTest {
187
192
 
188
193
  // Assert
189
194
  assertThat(chatRoom.externalId).isNotNull()
190
- assertThat(chatRoom.userId).isEqualTo(userId)
191
195
  assertThat(chatRoom.title).isEqualTo(title)
192
- assertThat(chatRoom.status).isEqualTo(ChatRoomStatus.ACTIVE)
193
- }
194
-
195
- @Test
196
- @DisplayName("externalId는 UUIDv7 형식이다")
197
- fun `externalId는 UUIDv7 형식으로 생성된다`() {
198
- // Arrange & Act
199
- val chatRoom = ChatRoom(userId = 1L, title = "테스트")
200
-
201
- // Assert
202
- assertThat(chatRoom.externalId).isNotNull()
203
- assertThat(chatRoom.externalId.toString()).matches(
204
- "^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
205
- )
206
196
  }
207
197
  }
208
198
 
209
199
  @Nested
210
- @DisplayName("Title 유효성 검증")
211
- inner class TitleValidation {
200
+ @DisplayName("유효성 검증")
201
+ inner class Validation {
212
202
 
213
203
  @Test
214
204
  @DisplayName("빈 제목으로 생성 시 예외 발생")
215
- fun `빈 title로 ChatRoom 생성 시 IllegalArgumentException 발생`() {
216
- // Arrange & Act & Assert
217
- assertThatThrownBy {
218
- ChatRoom(userId = 1L, title = "")
219
- }.isInstanceOf(IllegalArgumentException::class.java)
220
- .hasMessageContaining("title")
205
+ fun `빈 title로 ChatRoom 생성 시 예외`() {
206
+ assertThatThrownBy { ChatRoom(userId = 1L, title = "") }
207
+ .isInstanceOf(IllegalArgumentException::class.java)
221
208
  }
209
+ }
210
+ }
211
+ ```
222
212
 
223
- @Test
224
- @DisplayName("100자 초과 제목으로 생성 시 예외 발생")
225
- fun `100자 초과 title로 ChatRoom 생성 시 IllegalArgumentException 발생`() {
226
- // Arrange
227
- val longTitle = "가".repeat(101)
213
+ ### Java + JUnit5
228
214
 
229
- // Act & Assert
230
- assertThatThrownBy {
231
- ChatRoom(userId = 1L, title = longTitle)
232
- }.isInstanceOf(IllegalArgumentException::class.java)
233
- .hasMessageContaining("100")
234
- }
215
+ ```java
216
+ @DisplayName("ChatRoom Entity 테스트")
217
+ class ChatRoomTest {
218
+
219
+ @Nested
220
+ @DisplayName("Entity 생성")
221
+ class Creation {
235
222
 
236
223
  @Test
237
- @DisplayName("100자 제목은 허용")
238
- fun `100자 title로 ChatRoom 생성 시 성공`() {
224
+ @DisplayName("유효한 값으로 Entity 생성 시 성공")
225
+ void createChatRoom_withValidInput_succeeds() {
239
226
  // Arrange
240
- val maxTitle = "가".repeat(100)
227
+ Long userId = 1L;
228
+ String title = "테스트 채팅방";
241
229
 
242
230
  // Act
243
- val chatRoom = ChatRoom(userId = 1L, title = maxTitle)
231
+ ChatRoom chatRoom = new ChatRoom(userId, title);
244
232
 
245
233
  // Assert
246
- assertThat(chatRoom.title).hasSize(100)
234
+ assertThat(chatRoom.getExternalId()).isNotNull();
235
+ assertThat(chatRoom.getTitle()).isEqualTo(title);
247
236
  }
248
237
  }
249
238
 
250
239
  @Nested
251
- @DisplayName("Status 기본값")
252
- inner class DefaultStatus {
240
+ @DisplayName("유효성 검증")
241
+ class Validation {
253
242
 
254
243
  @Test
255
- @DisplayName("생성 시 기본 상태는 ACTIVE")
256
- fun `ChatRoom 생성 시 status 기본값은 ACTIVE`() {
244
+ @DisplayName("빈 제목으로 생성 시 예외 발생")
245
+ void createChatRoom_withEmptyTitle_throwsException() {
246
+ assertThatThrownBy(() -> new ChatRoom(1L, ""))
247
+ .isInstanceOf(IllegalArgumentException.class);
248
+ }
249
+ }
250
+ }
251
+ ```
252
+
253
+ ### TypeScript + Jest
254
+
255
+ ```typescript
256
+ describe('ChatRoom Entity', () => {
257
+ describe('Entity 생성', () => {
258
+ it('유효한 값으로 Entity 생성 시 성공', () => {
259
+ // Arrange
260
+ const userId = 1;
261
+ const title = '테스트 채팅방';
262
+
263
+ // Act
264
+ const chatRoom = new ChatRoom({ userId, title });
265
+
266
+ // Assert
267
+ expect(chatRoom.externalId).toBeDefined();
268
+ expect(chatRoom.title).toBe(title);
269
+ });
270
+ });
271
+
272
+ describe('유효성 검증', () => {
273
+ it('빈 제목으로 생성 시 예외 발생', () => {
274
+ expect(() => new ChatRoom({ userId: 1, title: '' }))
275
+ .toThrow('title');
276
+ });
277
+ });
278
+ });
279
+ ```
280
+
281
+ ### Python + pytest
282
+
283
+ ```python
284
+ import pytest
285
+ from domain.chat.model import ChatRoom
286
+
287
+ class TestChatRoom:
288
+ """ChatRoom Entity 테스트"""
289
+
290
+ class TestCreation:
291
+ """Entity 생성"""
292
+
293
+ def test_create_chatroom_with_valid_input_succeeds(self):
294
+ # Arrange
295
+ user_id = 1
296
+ title = "테스트 채팅방"
297
+
298
+ # Act
299
+ chat_room = ChatRoom(user_id=user_id, title=title)
300
+
301
+ # Assert
302
+ assert chat_room.external_id is not None
303
+ assert chat_room.title == title
304
+
305
+ class TestValidation:
306
+ """유효성 검증"""
307
+
308
+ def test_create_chatroom_with_empty_title_raises_error(self):
309
+ with pytest.raises(ValueError, match="title"):
310
+ ChatRoom(user_id=1, title="")
311
+ ```
312
+
313
+ ### Go + testing
314
+
315
+ ```go
316
+ func TestChatRoom(t *testing.T) {
317
+ t.Run("Entity 생성", func(t *testing.T) {
318
+ t.Run("유효한 값으로 Entity 생성 시 성공", func(t *testing.T) {
319
+ // Arrange
320
+ userID := int64(1)
321
+ title := "테스트 채팅방"
322
+
257
323
  // Act
258
- val chatRoom = ChatRoom(userId = 1L, title = "테스트")
324
+ chatRoom, err := NewChatRoom(userID, title)
259
325
 
260
326
  // Assert
261
- assertThat(chatRoom.status).isEqualTo(ChatRoomStatus.ACTIVE)
262
- }
263
- }
327
+ assert.NoError(t, err)
328
+ assert.NotEmpty(t, chatRoom.ExternalID)
329
+ assert.Equal(t, title, chatRoom.Title)
330
+ })
331
+ })
332
+
333
+ t.Run("유효성 검증", func(t *testing.T) {
334
+ t.Run("빈 제목으로 생성 시 에러", func(t *testing.T) {
335
+ _, err := NewChatRoom(1, "")
336
+ assert.Error(t, err)
337
+ })
338
+ })
264
339
  }
265
340
  ```
266
341
 
@@ -0,0 +1,256 @@
1
+ ---
2
+ name: workflow-navigator
3
+ description: |
4
+ 현재 상황을 분석하고 다음 워크플로우를 제안합니다.
5
+ 복잡도에 따라 불필요한 단계는 건너뛰고, 적절한 다음 스텝을 안내합니다.
6
+ "다음에 뭐 해야 해?", "워크플로우 안내", "다음 단계" 등의 요청에 자동 호출됩니다.
7
+ model: haiku
8
+ color: cyan
9
+ ---
10
+
11
+ # Workflow Navigator
12
+
13
+ ## 페르소나
14
+
15
+ @.claude/personas/sm.md
16
+
17
+ ---
18
+
19
+ ## 역할
20
+
21
+ 현재 프로젝트 상황을 분석하여 **다음 워크플로우를 제안**합니다.
22
+ - 정적인 워크플로우를 강제하지 않음
23
+ - 복잡도와 상황에 따라 유연하게 판단
24
+ - 불필요한 단계는 건너뛰기 제안
25
+
26
+ ---
27
+
28
+ ## 복잡도 레벨 정의
29
+
30
+ | Level | 설명 | 예시 |
31
+ |-------|------|------|
32
+ | **L1** | 단순 수정, 버그 픽스 | 오타 수정, 간단한 버그 |
33
+ | **L2** | 단일 컴포넌트 변경 | 필드 추가, 단일 API 수정 |
34
+ | **L3** | 복수 컴포넌트 연동 | 새 기능 (1-2 Story) |
35
+ | **L4** | 시스템 간 연동 | 복잡한 기능 (여러 Story) |
36
+ | **L5** | 아키텍처 변경 | 신규 시스템, 대규모 리팩토링 |
37
+
38
+ ---
39
+
40
+ ## 레벨별 권장 워크플로우
41
+
42
+ ### L1~L2: 단순 작업
43
+ ```
44
+ 요구사항 → 바로 구현
45
+ ```
46
+ - PRD 불필요
47
+ - Story 불필요
48
+ - 설계 문서 불필요
49
+
50
+ ### L3: 중간 복잡도
51
+ ```
52
+ 요구사항 → Story (AC) → LLD (선택) → Task → 구현
53
+ ```
54
+ - PRD 선택적 (간단한 배경 설명)
55
+ - HLD 불필요
56
+ - LLD는 기술적 복잡도에 따라 선택
57
+
58
+ ### L4: 높은 복잡도
59
+ ```
60
+ 요구사항 → PRD → Epic/Story → HLD (선택) → LLD → Task → 구현
61
+ ```
62
+ - PRD 필수 (이해관계자 합의)
63
+ - HLD는 아키텍처 변경 시에만
64
+
65
+ ### L5: 아키텍처 변경
66
+ ```
67
+ 요구사항 → PRD → Epic/Story → HLD → LLD → Task → 구현
68
+ ```
69
+ - 모든 단계 필수
70
+ - 아키텍처 리뷰 필수
71
+
72
+ ---
73
+
74
+ ## 건너뛰기 판단 기준
75
+
76
+ ### HLD 건너뛰기 조건
77
+ - [ ] 기존 아키텍처 내에서 해결 가능
78
+ - [ ] 새로운 컴포넌트 추가 없음
79
+ - [ ] 시스템 간 연동 방식 변경 없음
80
+ - [ ] 기술 스택 변경 없음
81
+
82
+ **모두 해당 → HLD 건너뛰기**
83
+
84
+ ### LLD 건너뛰기 조건
85
+ - [ ] 데이터 서빙 전략 변경 없음
86
+ - [ ] 새로운 외부 의존성 없음
87
+ - [ ] 장애 대응 전략 변경 없음
88
+ - [ ] 성능 최적화 불필요
89
+
90
+ **모두 해당 → LLD 건너뛰기**
91
+
92
+ ### Story 건너뛰기 조건 (개발 단독 Task)
93
+ - [ ] 사용자 가치(비즈니스 가치) 없음
94
+ - [ ] 기술적 부채 해결
95
+ - [ ] 인프라/DevOps 작업
96
+ - [ ] 리팩토링
97
+
98
+ **해당 시 → Story 없이 Task 직접 생성**
99
+
100
+ ---
101
+
102
+ ## 상황 분석 프로세스
103
+
104
+ ### Step 1: 현재 상태 파악
105
+
106
+ ```
107
+ 1. documents/ 폴더 확인
108
+ - documents/prd/ → PRD 존재 여부
109
+ - documents/hld/ → HLD 존재 여부
110
+ - documents/lld/ → LLD 존재 여부
111
+
112
+ 2. backlogs/ 폴더 확인
113
+ - Epic/Story 존재 여부
114
+ - Story 상태 (open/close)
115
+
116
+ 3. .tasks/ 폴더 확인
117
+ - Task 존재 여부
118
+ - Task 상태 (todo/inprogress/done)
119
+
120
+ 4. test-cases/ 폴더 확인
121
+ - TC 존재 여부
122
+ ```
123
+
124
+ ### Step 2: 복잡도 판단
125
+
126
+ 사용자에게 질문:
127
+ ```
128
+ 이 작업의 복잡도를 판단하기 위해 몇 가지 질문이 있습니다.
129
+
130
+ 1. 이 작업이 영향을 주는 컴포넌트는 몇 개인가요?
131
+ - 1개 → L1~L2
132
+ - 2-3개 → L3
133
+ - 4개 이상 또는 시스템 간 → L4~L5
134
+
135
+ 2. 아키텍처 변경이 필요한가요?
136
+ - 아니오 → L1~L3
137
+ - 예 → L4~L5
138
+
139
+ 3. 이해관계자 합의가 필요한가요?
140
+ - 아니오 → L1~L3
141
+ - 예 → L4~L5
142
+ ```
143
+
144
+ ### Step 3: 다음 스텝 제안
145
+
146
+ ---
147
+
148
+ ## 출력 형식
149
+
150
+ ### 상황 분석 결과
151
+ ```markdown
152
+ # 워크플로우 분석
153
+
154
+ ## 현재 상태
155
+ | 문서 | 상태 |
156
+ |------|------|
157
+ | PRD | ✅ 있음 / ❌ 없음 |
158
+ | Epic/Story | ✅ 있음 / ❌ 없음 |
159
+ | HLD | ✅ 있음 / ❌ 없음 / ⏭️ 건너뛰기 |
160
+ | LLD | ✅ 있음 / ❌ 없음 / ⏭️ 건너뛰기 |
161
+ | Task | ✅ 있음 / ❌ 없음 |
162
+ | TC | ✅ 있음 / ❌ 없음 |
163
+
164
+ ## 복잡도 판단
165
+ **Level: L[N]** - [설명]
166
+
167
+ ## 다음 스텝 제안
168
+ 1. **[권장]** `/skill-name` - [이유]
169
+ 2. [대안] `/skill-name` - [이유]
170
+
171
+ ## 건너뛸 수 있는 단계
172
+ - [단계명]: [건너뛰기 가능 이유]
173
+ ```
174
+
175
+ ---
176
+
177
+ ## 예시
178
+
179
+ ### 예시 1: 단순 버그 수정
180
+ ```
181
+ 사용자: "로그인 버튼 클릭 시 에러 발생 수정"
182
+
183
+ 분석 결과:
184
+ - 복잡도: L1 (단순 버그 수정)
185
+ - PRD: 불필요
186
+ - Story: 불필요
187
+ - 설계: 불필요
188
+
189
+ 제안: 바로 코드 분석 및 수정 진행
190
+ ```
191
+
192
+ ### 예시 2: 새 기능 추가 (중간)
193
+ ```
194
+ 사용자: "사용자 프로필에 프로필 이미지 업로드 기능 추가"
195
+
196
+ 분석 결과:
197
+ - 복잡도: L3 (복수 컴포넌트: 프론트엔드 + API + 스토리지)
198
+ - PRD: 선택적 (간단한 요구사항 정리)
199
+ - Story: 필요 (사용자 가치 있음)
200
+ - HLD: 불필요 (기존 아키텍처 내)
201
+ - LLD: 선택적 (스토리지 전략 복잡도에 따라)
202
+
203
+ 제안:
204
+ 1. [권장] `/epic-story-generator` - Story AC 정의
205
+ 2. [이후] `/task-generator` - 구현 Task 분해
206
+ ```
207
+
208
+ ### 예시 3: 기술 부채 해결
209
+ ```
210
+ 사용자: "레거시 코드 리팩토링 - 서비스 레이어 분리"
211
+
212
+ 분석 결과:
213
+ - 복잡도: L3 (리팩토링)
214
+ - PRD: 불필요 (기술적 작업)
215
+ - Story: 불필요 (사용자 가치 없음, 개발 단독)
216
+ - HLD: 불필요 (아키텍처 변경 없음)
217
+ - LLD: 선택적
218
+
219
+ 제안:
220
+ 1. [권장] `/task-generator` - Task 직접 생성 (Story 건너뛰기)
221
+ ```
222
+
223
+ ### 예시 4: 신규 시스템 구축
224
+ ```
225
+ 사용자: "실시간 알림 시스템 구축"
226
+
227
+ 분석 결과:
228
+ - 복잡도: L5 (신규 시스템)
229
+ - PRD: 필수 (이해관계자 합의 필요)
230
+ - Epic/Story: 필수
231
+ - HLD: 필수 (새 아키텍처)
232
+ - LLD: 필수 (서빙 전략, 장애 대응)
233
+
234
+ 제안:
235
+ 1. [권장] `/prd-generator` - 요구사항 정의
236
+ 2. [이후 순서] Epic/Story → HLD → LLD → Task
237
+ ```
238
+
239
+ ---
240
+
241
+ ## 자동 호출 조건
242
+
243
+ 이 agent는 다음 상황에서 자동 호출됩니다:
244
+ - "다음에 뭐 해야 해?"
245
+ - "워크플로우 안내해줘"
246
+ - "다음 단계가 뭐야?"
247
+ - 작업 시작 시 (스킬 호출 전)
248
+
249
+ ---
250
+
251
+ ## 원칙
252
+
253
+ 1. **유연성**: 정해진 순서를 강요하지 않음
254
+ 2. **효율성**: 불필요한 단계는 건너뛰기 제안
255
+ 3. **맥락 인식**: 현재 문서/상태 기반 판단
256
+ 4. **실용성**: 형식보다 실질적 가치 추구
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: hld-generator
3
3
  description: |
4
- PRD를 기반으로 HLD(High-Level Design)를 생성합니다.
4
+ PRD와 Epic/Story를 기반으로 HLD(High-Level Design)를 생성합니다.
5
5
  HLD는 구현 전에 시스템의 큰 그림을 설계하는 문서로, 방향 정렬, 리스크 조기 발견,
6
6
  의사결정 기록, 시니어/아키텍트의 조기 피드백을 위해 작성합니다.
7
7
  "HLD 작성해줘", "상위 설계 문서 만들어줘", "시스템 설계", "아키텍처 설계" 등의 요청에 사용합니다.