@wooojin/forgen 0.1.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/.claude-plugin/plugin.json +20 -0
- package/CHANGELOG.md +353 -0
- package/CONTRIBUTING.md +98 -0
- package/LICENSE +21 -0
- package/README.ja.md +469 -0
- package/README.ko.md +469 -0
- package/README.md +483 -0
- package/README.zh.md +469 -0
- package/agents/analyst.md +98 -0
- package/agents/architect.md +62 -0
- package/agents/code-reviewer.md +120 -0
- package/agents/code-simplifier.md +197 -0
- package/agents/critic.md +70 -0
- package/agents/debugger.md +117 -0
- package/agents/designer.md +131 -0
- package/agents/executor.md +54 -0
- package/agents/explore.md +145 -0
- package/agents/git-master.md +212 -0
- package/agents/performance-reviewer.md +172 -0
- package/agents/planner.md +29 -0
- package/agents/qa-tester.md +158 -0
- package/agents/refactoring-expert.md +168 -0
- package/agents/scientist.md +144 -0
- package/agents/security-reviewer.md +137 -0
- package/agents/test-engineer.md +153 -0
- package/agents/verifier.md +133 -0
- package/agents/writer.md +184 -0
- package/commands/api-design.md +268 -0
- package/commands/architecture-decision.md +314 -0
- package/commands/ci-cd.md +270 -0
- package/commands/code-review.md +233 -0
- package/commands/compound.md +117 -0
- package/commands/database.md +263 -0
- package/commands/debug-detective.md +99 -0
- package/commands/docker.md +274 -0
- package/commands/documentation.md +276 -0
- package/commands/ecomode.md +51 -0
- package/commands/frontend.md +271 -0
- package/commands/git-master.md +90 -0
- package/commands/incident-response.md +292 -0
- package/commands/migrate.md +101 -0
- package/commands/performance.md +288 -0
- package/commands/refactor.md +105 -0
- package/commands/security-review.md +288 -0
- package/commands/tdd.md +183 -0
- package/commands/testing-strategy.md +265 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +295 -0
- package/dist/core/auto-compound-runner.d.ts +12 -0
- package/dist/core/auto-compound-runner.js +460 -0
- package/dist/core/config-hooks.d.ts +10 -0
- package/dist/core/config-hooks.js +112 -0
- package/dist/core/config-injector.d.ts +50 -0
- package/dist/core/config-injector.js +455 -0
- package/dist/core/doctor.d.ts +1 -0
- package/dist/core/doctor.js +163 -0
- package/dist/core/errors.d.ts +81 -0
- package/dist/core/errors.js +133 -0
- package/dist/core/global-config.d.ts +43 -0
- package/dist/core/global-config.js +25 -0
- package/dist/core/harness.d.ts +24 -0
- package/dist/core/harness.js +621 -0
- package/dist/core/init.d.ts +7 -0
- package/dist/core/init.js +37 -0
- package/dist/core/inspect-cli.d.ts +7 -0
- package/dist/core/inspect-cli.js +47 -0
- package/dist/core/legacy-detector.d.ts +33 -0
- package/dist/core/legacy-detector.js +66 -0
- package/dist/core/logger.d.ts +34 -0
- package/dist/core/logger.js +121 -0
- package/dist/core/mcp-config.d.ts +44 -0
- package/dist/core/mcp-config.js +177 -0
- package/dist/core/notepad.d.ts +31 -0
- package/dist/core/notepad.js +88 -0
- package/dist/core/paths.d.ts +85 -0
- package/dist/core/paths.js +101 -0
- package/dist/core/plugin-detector.d.ts +44 -0
- package/dist/core/plugin-detector.js +226 -0
- package/dist/core/runtime-detector.d.ts +8 -0
- package/dist/core/runtime-detector.js +49 -0
- package/dist/core/scope-resolver.d.ts +8 -0
- package/dist/core/scope-resolver.js +45 -0
- package/dist/core/session-logger.d.ts +6 -0
- package/dist/core/session-logger.js +111 -0
- package/dist/core/session-store.d.ts +28 -0
- package/dist/core/session-store.js +218 -0
- package/dist/core/settings-lock.d.ts +18 -0
- package/dist/core/settings-lock.js +125 -0
- package/dist/core/spawn.d.ts +3 -0
- package/dist/core/spawn.js +135 -0
- package/dist/core/types.d.ts +108 -0
- package/dist/core/types.js +1 -0
- package/dist/core/uninstall.d.ts +4 -0
- package/dist/core/uninstall.js +307 -0
- package/dist/core/v1-bootstrap.d.ts +26 -0
- package/dist/core/v1-bootstrap.js +155 -0
- package/dist/engine/compound-cli.d.ts +24 -0
- package/dist/engine/compound-cli.js +250 -0
- package/dist/engine/compound-extractor.d.ts +68 -0
- package/dist/engine/compound-extractor.js +860 -0
- package/dist/engine/compound-lifecycle.d.ts +32 -0
- package/dist/engine/compound-lifecycle.js +305 -0
- package/dist/engine/compound-loop.d.ts +32 -0
- package/dist/engine/compound-loop.js +511 -0
- package/dist/engine/match-eval-log.d.ts +139 -0
- package/dist/engine/match-eval-log.js +270 -0
- package/dist/engine/phrase-blocklist.d.ts +119 -0
- package/dist/engine/phrase-blocklist.js +208 -0
- package/dist/engine/skill-promoter.d.ts +20 -0
- package/dist/engine/skill-promoter.js +115 -0
- package/dist/engine/solution-format.d.ts +160 -0
- package/dist/engine/solution-format.js +432 -0
- package/dist/engine/solution-index.d.ts +13 -0
- package/dist/engine/solution-index.js +252 -0
- package/dist/engine/solution-matcher.d.ts +364 -0
- package/dist/engine/solution-matcher.js +656 -0
- package/dist/engine/solution-writer.d.ts +76 -0
- package/dist/engine/solution-writer.js +157 -0
- package/dist/engine/term-matcher.d.ts +81 -0
- package/dist/engine/term-matcher.js +268 -0
- package/dist/engine/term-normalizer.d.ts +116 -0
- package/dist/engine/term-normalizer.js +171 -0
- package/dist/fgx.d.ts +6 -0
- package/dist/fgx.js +42 -0
- package/dist/forge/cli.d.ts +11 -0
- package/dist/forge/cli.js +100 -0
- package/dist/forge/evidence-processor.d.ts +21 -0
- package/dist/forge/evidence-processor.js +87 -0
- package/dist/forge/mismatch-detector.d.ts +44 -0
- package/dist/forge/mismatch-detector.js +83 -0
- package/dist/forge/onboarding-cli.d.ts +6 -0
- package/dist/forge/onboarding-cli.js +89 -0
- package/dist/forge/onboarding.d.ts +25 -0
- package/dist/forge/onboarding.js +122 -0
- package/dist/hooks/compound-reflection.d.ts +45 -0
- package/dist/hooks/compound-reflection.js +82 -0
- package/dist/hooks/context-guard.d.ts +24 -0
- package/dist/hooks/context-guard.js +156 -0
- package/dist/hooks/dangerous-patterns.json +18 -0
- package/dist/hooks/db-guard.d.ts +17 -0
- package/dist/hooks/db-guard.js +105 -0
- package/dist/hooks/hook-config.d.ts +29 -0
- package/dist/hooks/hook-config.js +92 -0
- package/dist/hooks/hook-registry.d.ts +43 -0
- package/dist/hooks/hook-registry.js +31 -0
- package/dist/hooks/hooks-generator.d.ts +49 -0
- package/dist/hooks/hooks-generator.js +99 -0
- package/dist/hooks/intent-classifier.d.ts +12 -0
- package/dist/hooks/intent-classifier.js +62 -0
- package/dist/hooks/keyword-detector.d.ts +25 -0
- package/dist/hooks/keyword-detector.js +389 -0
- package/dist/hooks/notepad-injector.d.ts +18 -0
- package/dist/hooks/notepad-injector.js +51 -0
- package/dist/hooks/permission-handler.d.ts +14 -0
- package/dist/hooks/permission-handler.js +114 -0
- package/dist/hooks/post-tool-failure.d.ts +11 -0
- package/dist/hooks/post-tool-failure.js +118 -0
- package/dist/hooks/post-tool-handlers.d.ts +17 -0
- package/dist/hooks/post-tool-handlers.js +115 -0
- package/dist/hooks/post-tool-use.d.ts +29 -0
- package/dist/hooks/post-tool-use.js +151 -0
- package/dist/hooks/pre-compact.d.ts +10 -0
- package/dist/hooks/pre-compact.js +165 -0
- package/dist/hooks/pre-tool-use.d.ts +31 -0
- package/dist/hooks/pre-tool-use.js +325 -0
- package/dist/hooks/prompt-injection-filter.d.ts +56 -0
- package/dist/hooks/prompt-injection-filter.js +287 -0
- package/dist/hooks/rate-limiter.d.ts +21 -0
- package/dist/hooks/rate-limiter.js +86 -0
- package/dist/hooks/secret-filter.d.ts +14 -0
- package/dist/hooks/secret-filter.js +65 -0
- package/dist/hooks/session-recovery.d.ts +27 -0
- package/dist/hooks/session-recovery.js +406 -0
- package/dist/hooks/shared/atomic-write.d.ts +41 -0
- package/dist/hooks/shared/atomic-write.js +148 -0
- package/dist/hooks/shared/context-budget.d.ts +37 -0
- package/dist/hooks/shared/context-budget.js +45 -0
- package/dist/hooks/shared/file-lock.d.ts +56 -0
- package/dist/hooks/shared/file-lock.js +253 -0
- package/dist/hooks/shared/hook-response.d.ts +33 -0
- package/dist/hooks/shared/hook-response.js +62 -0
- package/dist/hooks/shared/injection-caps.d.ts +39 -0
- package/dist/hooks/shared/injection-caps.js +52 -0
- package/dist/hooks/shared/plugin-signal.d.ts +23 -0
- package/dist/hooks/shared/plugin-signal.js +104 -0
- package/dist/hooks/shared/read-stdin.d.ts +8 -0
- package/dist/hooks/shared/read-stdin.js +63 -0
- package/dist/hooks/shared/sanitize-id.d.ts +7 -0
- package/dist/hooks/shared/sanitize-id.js +9 -0
- package/dist/hooks/shared/sanitize.d.ts +7 -0
- package/dist/hooks/shared/sanitize.js +22 -0
- package/dist/hooks/skill-injector.d.ts +38 -0
- package/dist/hooks/skill-injector.js +285 -0
- package/dist/hooks/slop-detector.d.ts +18 -0
- package/dist/hooks/slop-detector.js +93 -0
- package/dist/hooks/solution-injector.d.ts +58 -0
- package/dist/hooks/solution-injector.js +436 -0
- package/dist/hooks/subagent-tracker.d.ts +10 -0
- package/dist/hooks/subagent-tracker.js +90 -0
- package/dist/i18n/index.d.ts +43 -0
- package/dist/i18n/index.js +224 -0
- package/dist/lib.d.ts +14 -0
- package/dist/lib.js +14 -0
- package/dist/mcp/server.d.ts +8 -0
- package/dist/mcp/server.js +40 -0
- package/dist/mcp/solution-reader.d.ts +90 -0
- package/dist/mcp/solution-reader.js +273 -0
- package/dist/mcp/tools.d.ts +16 -0
- package/dist/mcp/tools.js +302 -0
- package/dist/preset/facet-catalog.d.ts +17 -0
- package/dist/preset/facet-catalog.js +46 -0
- package/dist/preset/preset-manager.d.ts +31 -0
- package/dist/preset/preset-manager.js +111 -0
- package/dist/renderer/inspect-renderer.d.ts +11 -0
- package/dist/renderer/inspect-renderer.js +123 -0
- package/dist/renderer/rule-renderer.d.ts +18 -0
- package/dist/renderer/rule-renderer.js +159 -0
- package/dist/store/evidence-store.d.ts +23 -0
- package/dist/store/evidence-store.js +58 -0
- package/dist/store/profile-store.d.ts +12 -0
- package/dist/store/profile-store.js +53 -0
- package/dist/store/recommendation-store.d.ts +22 -0
- package/dist/store/recommendation-store.js +64 -0
- package/dist/store/rule-store.d.ts +22 -0
- package/dist/store/rule-store.js +62 -0
- package/dist/store/session-state-store.d.ts +11 -0
- package/dist/store/session-state-store.js +44 -0
- package/dist/store/types.d.ts +159 -0
- package/dist/store/types.js +7 -0
- package/hooks/hook-registry.json +21 -0
- package/hooks/hooks.json +185 -0
- package/package.json +89 -0
- package/plugin.json +20 -0
- package/scripts/postinstall.js +826 -0
- package/skills/api-design/SKILL.md +262 -0
- package/skills/architecture-decision/SKILL.md +309 -0
- package/skills/ci-cd/SKILL.md +264 -0
- package/skills/code-review/SKILL.md +228 -0
- package/skills/compound/SKILL.md +101 -0
- package/skills/database/SKILL.md +257 -0
- package/skills/debug-detective/SKILL.md +95 -0
- package/skills/docker/SKILL.md +268 -0
- package/skills/documentation/SKILL.md +270 -0
- package/skills/ecomode/SKILL.md +46 -0
- package/skills/frontend/SKILL.md +265 -0
- package/skills/git-master/SKILL.md +86 -0
- package/skills/incident-response/SKILL.md +286 -0
- package/skills/migrate/SKILL.md +96 -0
- package/skills/performance/SKILL.md +282 -0
- package/skills/refactor/SKILL.md +100 -0
- package/skills/security-review/SKILL.md +282 -0
- package/skills/tdd/SKILL.md +178 -0
- package/skills/testing-strategy/SKILL.md +260 -0
- package/starter-pack/solutions/starter-api-error-responses.md +37 -0
- package/starter-pack/solutions/starter-async-patterns.md +40 -0
- package/starter-pack/solutions/starter-caching-strategy.md +40 -0
- package/starter-pack/solutions/starter-code-review-checklist.md +39 -0
- package/starter-pack/solutions/starter-debugging-systematic.md +40 -0
- package/starter-pack/solutions/starter-dependency-injection.md +40 -0
- package/starter-pack/solutions/starter-error-handling-patterns.md +38 -0
- package/starter-pack/solutions/starter-git-atomic-commits.md +36 -0
- package/starter-pack/solutions/starter-input-validation.md +40 -0
- package/starter-pack/solutions/starter-n-plus-one-queries.md +37 -0
- package/starter-pack/solutions/starter-refactor-safely.md +38 -0
- package/starter-pack/solutions/starter-secret-management.md +37 -0
- package/starter-pack/solutions/starter-separation-of-concerns.md +36 -0
- package/starter-pack/solutions/starter-tdd-red-green-refactor.md +40 -0
- package/starter-pack/solutions/starter-typescript-strict-types.md +39 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: testing-strategy
|
|
3
|
+
description: This skill should be used when the user asks to "testing strategy,테스트 전략,test plan,테스트 계획,coverage plan". Test strategy design, coverage analysis, and quality planning
|
|
4
|
+
triggers:
|
|
5
|
+
- "testing strategy"
|
|
6
|
+
- "테스트 전략"
|
|
7
|
+
- "test plan"
|
|
8
|
+
- "테스트 계획"
|
|
9
|
+
- "coverage plan"
|
|
10
|
+
---
|
|
11
|
+
<!-- forgen-managed -->
|
|
12
|
+
|
|
13
|
+
<Purpose>
|
|
14
|
+
테스트 전략을 수립하고 커버리지 계획을 설계합니다.
|
|
15
|
+
TDD 스킬이 Red-Green-Refactor 워크플로우를 다루는 반면,
|
|
16
|
+
이 스킬은 "무엇을 어떻게 테스트할 것인가"의 전략적 관점을 다룹니다.
|
|
17
|
+
테스트 피라미드, 커버리지 갭 분석, 우선순위 결정을 수행합니다.
|
|
18
|
+
</Purpose>
|
|
19
|
+
|
|
20
|
+
<Steps>
|
|
21
|
+
1. **현재 커버리지 평가**: 기존 테스트 상태를 분석합니다
|
|
22
|
+
- 라인/브랜치/함수 커버리지 측정
|
|
23
|
+
- 테스트 피라미드 비율 분석 (unit : integration : e2e)
|
|
24
|
+
- 테스트 실행 시간 측정
|
|
25
|
+
- 불안정(flaky) 테스트 식별
|
|
26
|
+
- 테스트 없는 핵심 경로 식별
|
|
27
|
+
- 최근 버그의 테스트 커버리지 분석 (커버 되었는가?)
|
|
28
|
+
|
|
29
|
+
2. **갭 분석**: 테스트가 부족한 영역을 식별합니다
|
|
30
|
+
- 비즈니스 로직의 테스트 커버리지 확인
|
|
31
|
+
- 에러 핸들링 경로의 테스트 존재 여부
|
|
32
|
+
- 엣지 케이스 누락 식별
|
|
33
|
+
* 빈 입력, null/undefined
|
|
34
|
+
* 경계값 (최소/최대)
|
|
35
|
+
* 동시성/경쟁 조건
|
|
36
|
+
* 타임아웃/네트워크 에러
|
|
37
|
+
- 보안 관련 로직의 테스트 확인 (인증, 인가, 입력 검증)
|
|
38
|
+
- 통합 테스트 누락 (모듈 간 상호작용)
|
|
39
|
+
- 외부 의존성 모킹의 적절성
|
|
40
|
+
|
|
41
|
+
3. **우선순위 결정**: 테스트 작성 순서를 결정합니다
|
|
42
|
+
- 위험도 기반 우선순위 매트릭스:
|
|
43
|
+
* [P0] 결제, 인증, 데이터 무결성 -- 반드시 테스트
|
|
44
|
+
* [P1] 핵심 비즈니스 로직 -- 높은 우선순위
|
|
45
|
+
* [P2] 일반 CRUD 작업 -- 중간 우선순위
|
|
46
|
+
* [P3] 유틸리티, 헬퍼 -- 낮은 우선순위
|
|
47
|
+
- 변경 빈도가 높은 코드 우선
|
|
48
|
+
- 최근 버그가 발생한 모듈 우선
|
|
49
|
+
- 복잡도가 높은 함수 우선
|
|
50
|
+
|
|
51
|
+
4. **테스트 전략 수립**: 테스트 유형별 전략을 설계합니다
|
|
52
|
+
- Unit Test 전략:
|
|
53
|
+
* 순수 함수: 입력/출력 테스트
|
|
54
|
+
* 사이드 이펙트: 모킹/스터빙
|
|
55
|
+
* 커버리지 목표: 85%+ (브랜치 기준)
|
|
56
|
+
- Integration Test 전략:
|
|
57
|
+
* 모듈 간 상호작용 검증
|
|
58
|
+
* 외부 서비스 모킹 범위
|
|
59
|
+
* 데이터베이스 테스트 (인메모리 vs 컨테이너)
|
|
60
|
+
- E2E Test 전략:
|
|
61
|
+
* 핵심 사용자 시나리오 선정
|
|
62
|
+
* 테스트 환경 구성
|
|
63
|
+
* 데이터 시딩 전략
|
|
64
|
+
- 성능 Test 전략:
|
|
65
|
+
* 부하 테스트 시나리오
|
|
66
|
+
* 성능 기준값 설정
|
|
67
|
+
|
|
68
|
+
5. **구현 및 추적**: 테스트를 점진적으로 구현합니다
|
|
69
|
+
- 테스트 작성 태스크 목록 생성
|
|
70
|
+
- 커버리지 추적 대시보드 설정
|
|
71
|
+
- CI 파이프라인에 커버리지 게이트 추가
|
|
72
|
+
- 정기적인 커버리지 리뷰 일정 설정
|
|
73
|
+
- 뮤테이션 테스트 도입 계획 (핵심 비즈니스 로직)
|
|
74
|
+
</Steps>
|
|
75
|
+
|
|
76
|
+
## 에이전트 위임
|
|
77
|
+
|
|
78
|
+
`test-engineer` 에이전트(Opus 모델)에 위임하여 테스트 전략을 수립합니다:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
Agent(
|
|
82
|
+
subagent_type="test-engineer",
|
|
83
|
+
model="opus",
|
|
84
|
+
prompt="TESTING STRATEGY TASK
|
|
85
|
+
|
|
86
|
+
테스트 전략을 수립하고 커버리지 계획을 설계하세요.
|
|
87
|
+
|
|
88
|
+
Project: [프로젝트 설명]
|
|
89
|
+
Current Coverage: [현재 커버리지 또는 '미측정']
|
|
90
|
+
|
|
91
|
+
Strategy Checklist:
|
|
92
|
+
1. 현재 커버리지 측정 및 분석
|
|
93
|
+
2. 테스트 갭 식별 (에러 경로, 엣지 케이스)
|
|
94
|
+
3. 위험도 기반 우선순위 결정
|
|
95
|
+
4. 테스트 유형별 전략 (unit/integration/e2e)
|
|
96
|
+
5. 구현 로드맵 및 커버리지 목표
|
|
97
|
+
|
|
98
|
+
Output: 테스트 전략 문서:
|
|
99
|
+
- 현재 커버리지 분석
|
|
100
|
+
- 갭 분석 결과
|
|
101
|
+
- 우선순위 목록
|
|
102
|
+
- 유형별 전략
|
|
103
|
+
- 커버리지 목표 및 로드맵"
|
|
104
|
+
)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## External Consultation (Optional)
|
|
108
|
+
|
|
109
|
+
test-engineer 에이전트는 교차 검증을 위해 Claude Task 에이전트에 자문할 수 있습니다.
|
|
110
|
+
|
|
111
|
+
### Protocol
|
|
112
|
+
1. **자체 전략을 먼저 수립** -- 독립적으로 분석 수행
|
|
113
|
+
2. **검증을 위한 자문** -- Claude Task 에이전트를 통해 전략 교차 확인
|
|
114
|
+
3. **비판적 평가** -- 외부 제안을 맹목적으로 수용하지 않음
|
|
115
|
+
4. **우아한 폴백** -- 위임이 불가능할 경우 절대 차단하지 않음
|
|
116
|
+
|
|
117
|
+
### 자문이 필요한 경우
|
|
118
|
+
- 복잡한 도메인의 테스트 커버리지 전략
|
|
119
|
+
- 마이크로서비스 간 통합 테스트 설계
|
|
120
|
+
- 성능/부하 테스트 시나리오 설계
|
|
121
|
+
- 뮤테이션 테스트 도입 전략
|
|
122
|
+
|
|
123
|
+
### 자문을 생략하는 경우
|
|
124
|
+
- 단순 유닛 테스트 계획
|
|
125
|
+
- 명확한 CRUD 테스트
|
|
126
|
+
- 기존 패턴 반복
|
|
127
|
+
- 소규모 모듈의 테스트
|
|
128
|
+
|
|
129
|
+
## 테스트 피라미드 기준
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
/ E2E \ 5~10% (핵심 시나리오만)
|
|
133
|
+
/----------\
|
|
134
|
+
/ Integration \ 15~25% (모듈 간 상호작용)
|
|
135
|
+
/----------------\
|
|
136
|
+
/ Unit Tests \ 70~80% (모든 비즈니스 로직)
|
|
137
|
+
/--------------------\
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## 커버리지 목표 가이드
|
|
141
|
+
|
|
142
|
+
| 코드 유형 | 라인 | 브랜치 | 뮤테이션 |
|
|
143
|
+
|-----------|------|--------|----------|
|
|
144
|
+
| 결제/인증 | 95%+ | 90%+ | 80%+ |
|
|
145
|
+
| 비즈니스 로직 | 85%+ | 80%+ | 70%+ |
|
|
146
|
+
| 유틸리티 | 80%+ | 75%+ | -- |
|
|
147
|
+
| UI 컴포넌트 | 70%+ | 60%+ | -- |
|
|
148
|
+
| 설정/부트스트랩 | 60%+ | -- | -- |
|
|
149
|
+
|
|
150
|
+
## 테스트 품질 지표
|
|
151
|
+
|
|
152
|
+
| 지표 | 건강 | 주의 | 위험 |
|
|
153
|
+
|------|------|------|------|
|
|
154
|
+
| 브랜치 커버리지 | > 80% | 60~80% | < 60% |
|
|
155
|
+
| 테스트 실행 시간 | < 30s | 30s~2m | > 2m |
|
|
156
|
+
| Flaky 테스트 비율 | 0% | < 2% | > 2% |
|
|
157
|
+
| 뮤테이션 점수 | > 70% | 50~70% | < 50% |
|
|
158
|
+
|
|
159
|
+
<Output>
|
|
160
|
+
```
|
|
161
|
+
TESTING STRATEGY / 테스트 전략 문서
|
|
162
|
+
=====================================
|
|
163
|
+
|
|
164
|
+
Project: [프로젝트명]
|
|
165
|
+
Date: YYYY-MM-DD
|
|
166
|
+
Test Framework: [vitest / jest / playwright]
|
|
167
|
+
|
|
168
|
+
CURRENT STATE / 현재 상태
|
|
169
|
+
---------------------------
|
|
170
|
+
Line Coverage: 72% (target: 85%)
|
|
171
|
+
Branch Coverage: 58% (target: 80%)
|
|
172
|
+
Test Count: 142 (unit: 120, integration: 18, e2e: 4)
|
|
173
|
+
Execution Time: 24s
|
|
174
|
+
Flaky Tests: 2 (src/api/order.test.ts, src/utils/date.test.ts)
|
|
175
|
+
|
|
176
|
+
GAP ANALYSIS / 갭 분석
|
|
177
|
+
------------------------
|
|
178
|
+
[P0] CRITICAL GAPS (테스트 없는 핵심 경로):
|
|
179
|
+
- src/services/payment.ts -- 결제 처리 로직 (0% coverage)
|
|
180
|
+
- src/middleware/auth.ts -- 인증 미들웨어 에러 경로 (30% branch)
|
|
181
|
+
|
|
182
|
+
[P1] HIGH GAPS (불충분한 커버리지):
|
|
183
|
+
- src/services/order.ts -- 할인 계산 엣지 케이스 미테스트
|
|
184
|
+
- src/utils/validation.ts -- 경계값 테스트 부재
|
|
185
|
+
|
|
186
|
+
[P2] MEDIUM GAPS:
|
|
187
|
+
- src/api/users.ts -- 통합 테스트 부재
|
|
188
|
+
- src/components/Cart.tsx -- 상태 변경 테스트 부족
|
|
189
|
+
|
|
190
|
+
IMPLEMENTATION ROADMAP / 구현 로드맵
|
|
191
|
+
--------------------------------------
|
|
192
|
+
Week 1: P0 갭 해소 (결제, 인증)
|
|
193
|
+
- [ ] payment.ts 유닛 테스트 (15개)
|
|
194
|
+
- [ ] auth.ts 에러 경로 테스트 (8개)
|
|
195
|
+
|
|
196
|
+
Week 2: P1 갭 해소 (비즈니스 로직)
|
|
197
|
+
- [ ] order.ts 엣지 케이스 테스트 (10개)
|
|
198
|
+
- [ ] validation.ts 경계값 테스트 (12개)
|
|
199
|
+
|
|
200
|
+
Week 3: 통합 테스트 보강
|
|
201
|
+
- [ ] API 엔드포인트 통합 테스트 (20개)
|
|
202
|
+
- [ ] E2E 핵심 시나리오 (3개)
|
|
203
|
+
|
|
204
|
+
TARGET / 목표
|
|
205
|
+
--------------
|
|
206
|
+
Line Coverage: 72% → 85%
|
|
207
|
+
Branch Coverage: 58% → 80%
|
|
208
|
+
Test Count: 142 → 210
|
|
209
|
+
Flaky Tests: 2 → 0
|
|
210
|
+
```
|
|
211
|
+
</Output>
|
|
212
|
+
|
|
213
|
+
<Policy>
|
|
214
|
+
- 커버리지 숫자보다 테스트 품질을 우선합니다
|
|
215
|
+
- 브랜치 커버리지를 라인 커버리지보다 중요하게 봅니다
|
|
216
|
+
- 핵심 비즈니스 로직에 뮤테이션 테스트를 권장합니다
|
|
217
|
+
- Flaky 테스트는 즉시 수정하거나 격리합니다
|
|
218
|
+
- 테스트는 빠르게 실행되어야 합니다 (전체 30초 이내 목표)
|
|
219
|
+
- 테스트 작성 계획은 스프린트에 통합하여 추적합니다
|
|
220
|
+
</Policy>
|
|
221
|
+
|
|
222
|
+
## 다른 스킬과의 연동
|
|
223
|
+
|
|
224
|
+
**TDD 연동:**
|
|
225
|
+
```
|
|
226
|
+
/forgen:tdd 전략에서 식별된 갭 구현
|
|
227
|
+
```
|
|
228
|
+
전략에서 식별된 테스트 갭을 TDD로 구현
|
|
229
|
+
|
|
230
|
+
**코드 리뷰 연동:**
|
|
231
|
+
```
|
|
232
|
+
/forgen:code-review 테스트 코드 품질 확인
|
|
233
|
+
```
|
|
234
|
+
작성된 테스트 코드의 품질 검증
|
|
235
|
+
|
|
236
|
+
**CI/CD 연동:**
|
|
237
|
+
```
|
|
238
|
+
/forgen:ci-cd 커버리지 게이트 설정
|
|
239
|
+
```
|
|
240
|
+
CI에서 커버리지 임계값 강제
|
|
241
|
+
|
|
242
|
+
## Best Practices
|
|
243
|
+
|
|
244
|
+
- **전략 먼저** -- 무작정 테스트를 쓰기 전에 어디가 중요한지 파악
|
|
245
|
+
- **위험 기반** -- 모든 코드를 균일하게 테스트하지 않음
|
|
246
|
+
- **피라미드 유지** -- E2E에 의존하지 않고 유닛 테스트 기반 구축
|
|
247
|
+
- **Flaky 제로** -- 불안정한 테스트는 신뢰를 해침
|
|
248
|
+
- **지속 추적** -- 커버리지 트렌드를 모니터링
|
|
249
|
+
|
|
250
|
+
<Arguments>
|
|
251
|
+
## 사용법
|
|
252
|
+
`/forgen:testing-strategy {대상}`
|
|
253
|
+
|
|
254
|
+
### 예시
|
|
255
|
+
- `/forgen:testing-strategy 전체 프로젝트 커버리지 분석`
|
|
256
|
+
- `/forgen:testing-strategy 결제 모듈 테스트 계획`
|
|
257
|
+
- `/forgen:testing-strategy E2E 테스트 전략 수립`
|
|
258
|
+
- `/forgen:testing-strategy 현재 테스트의 갭 분석`
|
|
259
|
+
|
|
260
|
+
### 인자
|
|
261
|
+
- 분석 대상 모듈, 테스트 유형, 목표 등을 설명
|
|
262
|
+
- 인자 없으면 프로젝트 전체의 테스트 전략을 수립
|
|
263
|
+
</Arguments>
|
|
264
|
+
|
|
265
|
+
$ARGUMENTS
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const nodeVersion = parseInt(process.version.slice(1).split('.')[0], 10);
|
|
3
|
+
if (nodeVersion < 20) {
|
|
4
|
+
console.error(`[Forgen] Node.js 20 or higher is required. Current: ${process.version}`);
|
|
5
|
+
process.exit(1);
|
|
6
|
+
}
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { prepareHarness, isFirstRun } from './core/harness.js';
|
|
11
|
+
import { spawnClaude } from './core/spawn.js';
|
|
12
|
+
// global-config is used by harness internally
|
|
13
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const pkgJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'package.json'), 'utf-8'));
|
|
15
|
+
const PKG_VERSION = pkgJson.version ?? '0.0.0';
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
const commands = [
|
|
18
|
+
{
|
|
19
|
+
name: 'forge',
|
|
20
|
+
description: 'Personalization profile (--profile|--export|--reset)',
|
|
21
|
+
handler: async (args) => {
|
|
22
|
+
const { handleForge } = await import('./forge/cli.js');
|
|
23
|
+
await handleForge(args);
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'compound',
|
|
28
|
+
description: 'Preview/save compound insights and manage accumulated knowledge',
|
|
29
|
+
handler: async (args) => {
|
|
30
|
+
const { handleCompound } = await import('./engine/compound-loop.js');
|
|
31
|
+
await handleCompound(args);
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'skill',
|
|
36
|
+
description: 'Skill management (promote|list)',
|
|
37
|
+
handler: async (args) => {
|
|
38
|
+
const sub = args[0];
|
|
39
|
+
if (sub === 'promote' && args[1]) {
|
|
40
|
+
const { promoteSolution } = await import('./engine/skill-promoter.js');
|
|
41
|
+
const triggers = args.includes('--trigger')
|
|
42
|
+
? args.slice(args.indexOf('--trigger') + 1).filter(a => !a.startsWith('-'))
|
|
43
|
+
: undefined;
|
|
44
|
+
const result = promoteSolution(args[1], triggers);
|
|
45
|
+
if (result.success) {
|
|
46
|
+
console.log(`\n ✓ Promoted: ${args[1]} → ${result.skillPath}\n`);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
console.log(`\n ✗ ${result.reason}\n`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else if (sub === 'list') {
|
|
53
|
+
const { listSkills } = await import('./engine/skill-promoter.js');
|
|
54
|
+
const skills = listSkills();
|
|
55
|
+
if (skills.length === 0) {
|
|
56
|
+
console.log('\n No promoted skills yet. Use `forgen skill promote <solution-name>`\n');
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
console.log(`\n Promoted Skills (${skills.length}):\n`);
|
|
60
|
+
for (const s of skills) {
|
|
61
|
+
console.log(` ${s.name} [${s.status}] triggers: ${s.triggers.join(', ')}`);
|
|
62
|
+
}
|
|
63
|
+
console.log('');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
console.log(' Usage:\n forgen skill promote <solution-name> [--trigger "keyword"]\n forgen skill list');
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'me',
|
|
73
|
+
description: 'Personal dashboard (→ inspect profile)',
|
|
74
|
+
handler: async (_args) => {
|
|
75
|
+
const { handleInspect } = await import('./core/inspect-cli.js');
|
|
76
|
+
await handleInspect(['profile']);
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'config',
|
|
81
|
+
description: 'Configuration (hooks [--regenerate])',
|
|
82
|
+
handler: async (args) => {
|
|
83
|
+
const sub = args[0];
|
|
84
|
+
if (sub === 'hooks') {
|
|
85
|
+
if (args.includes('--regenerate')) {
|
|
86
|
+
const { writeHooksJson } = await import('./hooks/hooks-generator.js');
|
|
87
|
+
const hooksDir = path.join(process.cwd(), 'hooks');
|
|
88
|
+
const result = writeHooksJson(hooksDir, { cwd: process.cwd() });
|
|
89
|
+
console.log(`[forgen] hooks.json regenerated: ${result.active} active, ${result.disabled} disabled`);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
const { displayHookStatus } = await import('./core/config-hooks.js');
|
|
93
|
+
await displayHookStatus(process.cwd());
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
console.log('Usage: forgen config hooks [--regenerate]');
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'mcp',
|
|
103
|
+
description: 'MCP server management (list|templates|add|remove)',
|
|
104
|
+
handler: async (args) => {
|
|
105
|
+
const { handleMcp } = await import('./core/mcp-config.js');
|
|
106
|
+
await handleMcp(args);
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: 'init',
|
|
111
|
+
description: 'Initialize project',
|
|
112
|
+
handler: async (args) => {
|
|
113
|
+
const { handleInit } = await import('./core/init.js');
|
|
114
|
+
await handleInit(args);
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: 'notepad',
|
|
119
|
+
description: 'Notepad (show|add|clear)',
|
|
120
|
+
handler: async (args) => {
|
|
121
|
+
const { handleNotepad } = await import('./core/notepad.js');
|
|
122
|
+
await handleNotepad(args);
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'inspect',
|
|
127
|
+
description: 'v1 state inspector (profile|rules|evidence|session)',
|
|
128
|
+
handler: async (args) => {
|
|
129
|
+
const { handleInspect } = await import('./core/inspect-cli.js');
|
|
130
|
+
await handleInspect(args);
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'onboarding',
|
|
135
|
+
description: 'v1 2-question onboarding flow',
|
|
136
|
+
handler: async (_args) => {
|
|
137
|
+
const { runOnboarding } = await import('./forge/onboarding-cli.js');
|
|
138
|
+
await runOnboarding();
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'doctor',
|
|
143
|
+
description: 'Diagnostics',
|
|
144
|
+
handler: async (_args) => {
|
|
145
|
+
const { runDoctor } = await import('./core/doctor.js');
|
|
146
|
+
await runDoctor();
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
// install --plugin 제거됨 — postinstall이 유일한 설치 경로
|
|
150
|
+
// 수동 재설치: node scripts/postinstall.js
|
|
151
|
+
{
|
|
152
|
+
name: 'uninstall',
|
|
153
|
+
description: 'Remove forgen from settings [--force]',
|
|
154
|
+
handler: async (args) => {
|
|
155
|
+
const { handleUninstall } = await import('./core/uninstall.js');
|
|
156
|
+
await handleUninstall(process.cwd(), { force: args.includes('--force') });
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
];
|
|
160
|
+
/** 최소 편집 거리 (유사 명령 제안용) */
|
|
161
|
+
function levenshtein(a, b) {
|
|
162
|
+
const m = a.length, n = b.length;
|
|
163
|
+
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
|
|
164
|
+
for (let i = 0; i <= m; i++)
|
|
165
|
+
dp[i][0] = i;
|
|
166
|
+
for (let j = 0; j <= n; j++)
|
|
167
|
+
dp[0][j] = j;
|
|
168
|
+
for (let i = 1; i <= m; i++) {
|
|
169
|
+
for (let j = 1; j <= n; j++) {
|
|
170
|
+
dp[i][j] = a[i - 1] === b[j - 1]
|
|
171
|
+
? dp[i - 1][j - 1]
|
|
172
|
+
: 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return dp[m][n];
|
|
176
|
+
}
|
|
177
|
+
function findCommand(name) {
|
|
178
|
+
return commands.find((c) => c.name === name || (c.aliases?.includes(name)));
|
|
179
|
+
}
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// main
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
async function main() {
|
|
184
|
+
if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') {
|
|
185
|
+
printHelp();
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (args[0] === '--version' || args[0] === '-V') {
|
|
189
|
+
console.log(PKG_VERSION);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const cmd = findCommand(args[0]);
|
|
193
|
+
if (cmd) {
|
|
194
|
+
await cmd.handler(args.slice(1));
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
// 등록되지 않은 서브커맨드는 에러 처리
|
|
198
|
+
// 플래그(--resume 등), 따옴표 프롬프트, 인자 없는 실행은 하네스로 통과
|
|
199
|
+
if (args[0] && !args[0].startsWith('-') && !args[0].startsWith('"') && !args[0].startsWith("'")) {
|
|
200
|
+
const suggestion = commands
|
|
201
|
+
.map(c => ({ name: c.name, dist: levenshtein(args[0], c.name) }))
|
|
202
|
+
.filter(c => c.dist <= 3)
|
|
203
|
+
.sort((a, b) => a.dist - b.dist)[0];
|
|
204
|
+
const hint = suggestion ? `\n Did you mean: forgen ${suggestion.name}` : '';
|
|
205
|
+
console.error(`[forgen] Unknown command: ${args[0]}${hint}\n Run "forgen help" for available commands.`);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
// Default: run Claude Code with harness
|
|
209
|
+
try {
|
|
210
|
+
const firstRun = isFirstRun();
|
|
211
|
+
if (firstRun) {
|
|
212
|
+
console.log(`
|
|
213
|
+
╔══════════════════════════════════════════════╗
|
|
214
|
+
║ Welcome to Forgen ║
|
|
215
|
+
╚══════════════════════════════════════════════╝
|
|
216
|
+
|
|
217
|
+
The more you use Claude with Forgen,
|
|
218
|
+
the better Claude gets at helping YOU.
|
|
219
|
+
|
|
220
|
+
Setting up...`);
|
|
221
|
+
}
|
|
222
|
+
let context = await prepareHarness(process.cwd());
|
|
223
|
+
// 첫 실행 또는 프로필 없음 → 자동 온보딩 (interactive 환경)
|
|
224
|
+
if (context.v1.needsOnboarding && process.stdin.isTTY) {
|
|
225
|
+
console.log('\n 프로필이 없습니다. 온보딩을 시작합니다.\n');
|
|
226
|
+
const { runOnboarding } = await import('./forge/onboarding-cli.js');
|
|
227
|
+
await runOnboarding();
|
|
228
|
+
// 온보딩 후 harness 재실행 (프로필 반영)
|
|
229
|
+
context = await prepareHarness(process.cwd());
|
|
230
|
+
}
|
|
231
|
+
if (firstRun && !context.v1.needsOnboarding) {
|
|
232
|
+
console.log(`
|
|
233
|
+
Setup complete!
|
|
234
|
+
`);
|
|
235
|
+
}
|
|
236
|
+
const bold = '\x1b[1m';
|
|
237
|
+
const cyan = '\x1b[36m';
|
|
238
|
+
const dim = '\x1b[2m';
|
|
239
|
+
const reset = '\x1b[0m';
|
|
240
|
+
console.log(`
|
|
241
|
+
${bold}${cyan}█▀▀ █▀█ █▀█ █▀▀ █▀▀ █▄░█${reset}
|
|
242
|
+
${bold}${cyan}█▀▀ █▄█ █▀▄ █▄█ ██▄ █░▀█${reset} ${dim}v${PKG_VERSION}${reset}
|
|
243
|
+
|
|
244
|
+
${dim}Code, forged for you.${reset}
|
|
245
|
+
${dim}Scope: v1(${context.v1.session?.quality_pack ?? 'onboarding needed'})${reset}
|
|
246
|
+
`);
|
|
247
|
+
console.log('[forgen] Starting Claude Code...\n');
|
|
248
|
+
await spawnClaude(args, context);
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
252
|
+
if (msg.includes('ENOENT') && msg.includes('claude')) {
|
|
253
|
+
console.error('[forgen] Claude Code not found. Install: npm install -g @anthropic-ai/claude-code');
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
console.error('[forgen] Error:', msg);
|
|
257
|
+
}
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
// printHelp
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
function printHelp() {
|
|
265
|
+
console.log(`
|
|
266
|
+
Forgen v${PKG_VERSION}
|
|
267
|
+
The more you use Claude, the better it knows you.
|
|
268
|
+
|
|
269
|
+
Usage:
|
|
270
|
+
forgen Start Claude Code (harness mode)
|
|
271
|
+
forgen "prompt" Start with a prompt
|
|
272
|
+
forgen --resume Resume previous session
|
|
273
|
+
|
|
274
|
+
Commands:
|
|
275
|
+
forgen forge Personalize your coding profile
|
|
276
|
+
forgen onboarding Run 2-question onboarding
|
|
277
|
+
forgen inspect [profile|rules|evidence|session]
|
|
278
|
+
Inspect v1 state
|
|
279
|
+
forgen compound Manage accumulated knowledge
|
|
280
|
+
forgen me Personal dashboard
|
|
281
|
+
forgen init Initialize project
|
|
282
|
+
forgen config hooks Hook management
|
|
283
|
+
forgen mcp MCP server management
|
|
284
|
+
forgen skill promote|list Skill management
|
|
285
|
+
forgen notepad show|add|clear Session notepad
|
|
286
|
+
forgen doctor System diagnostics
|
|
287
|
+
forgen uninstall Remove forgen
|
|
288
|
+
|
|
289
|
+
Harness mode (default):
|
|
290
|
+
Wraps Claude Code with personalization, auto-compound, and safety hooks.
|
|
291
|
+
`);
|
|
292
|
+
}
|
|
293
|
+
main().catch(() => {
|
|
294
|
+
process.exit(1);
|
|
295
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Forgen — Auto Compound Runner
|
|
4
|
+
*
|
|
5
|
+
* Detached process로 실행. 이전 세션의 transcript를 분석하여:
|
|
6
|
+
* 1. 재사용 가능한 솔루션 추출 (compound --solution)
|
|
7
|
+
* 2. 사용자 패턴을 USER.md에 축적
|
|
8
|
+
*
|
|
9
|
+
* 호출: session-recovery hook 또는 spawn.ts에서 detached spawn
|
|
10
|
+
* 인자: [cwd] [transcriptPath] [sessionId]
|
|
11
|
+
*/
|
|
12
|
+
export {};
|