aiwf 0.3.21 → 0.3.23
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 +3 -2
- package/src/cli/index.js +39 -336
- package/src/lib/installer.js +310 -90
- package/src/utils/messages.js +24 -0
- package/src/DEPENDENCY_MAP.md +0 -90
- package/src/cli/cache-cli.js +0 -459
- package/src/cli/checkpoint-cli.js +0 -417
- package/src/cli/language-cli.js +0 -286
- package/src/cli/sprint-cli.js +0 -287
- package/src/commands/ai-tool.js +0 -383
- package/src/commands/compress.js +0 -60
- package/src/commands/create-project.js +0 -160
- package/src/commands/evaluate.js +0 -459
- package/src/commands/persona.js +0 -309
- package/src/commands/sprint-task.js +0 -262
- package/src/commands/token.js +0 -312
- package/src/lib/ai-persona-manager.js +0 -711
- package/src/lib/cache-system.js +0 -332
- package/src/lib/context-engine.js +0 -568
- package/src/lib/github-integration.js +0 -402
- package/src/lib/memory-profiler.js +0 -471
- package/src/lib/metrics-collector.js +0 -885
- package/src/lib/offline-detector.js +0 -386
- package/src/lib/resource-loader-enhanced.js +0 -399
- package/src/lib/resource-loader.js +0 -250
- package/src/lib/resources/commands/ai-persona.js +0 -602
- package/src/lib/resources/commands/compress-context.js +0 -389
- package/src/lib/resources/commands/evaluate.js +0 -246
- package/src/lib/resources/commands/persona-context-apply.js +0 -562
- package/src/lib/resources/commands/token-tracking.js +0 -443
- package/src/lib/resources/config/commit-patterns.js +0 -103
- package/src/lib/resources/config/language.json +0 -6
- package/src/lib/resources/personas/PERSONA_INDEX.md +0 -57
- package/src/lib/resources/personas/analyst.json +0 -44
- package/src/lib/resources/personas/architect/best_practices.md +0 -136
- package/src/lib/resources/personas/architect/knowledge_base.md +0 -377
- package/src/lib/resources/personas/architect.json +0 -44
- package/src/lib/resources/personas/backend/best_practices.md +0 -766
- package/src/lib/resources/personas/backend/knowledge_base.md +0 -1070
- package/src/lib/resources/personas/data_analyst/best_practices.md +0 -594
- package/src/lib/resources/personas/data_analyst/knowledge_base.md +0 -1057
- package/src/lib/resources/personas/developer.json +0 -44
- package/src/lib/resources/personas/developer.md +0 -37
- package/src/lib/resources/personas/evaluation_criteria.json +0 -137
- package/src/lib/resources/personas/frontend/best_practices.md +0 -445
- package/src/lib/resources/personas/frontend/knowledge_base.md +0 -729
- package/src/lib/resources/personas/persona-index.json +0 -31
- package/src/lib/resources/personas/reviewer.json +0 -44
- package/src/lib/resources/personas/security/best_practices.md +0 -307
- package/src/lib/resources/personas/security/knowledge_base.md +0 -498
- package/src/lib/resources/personas/tester.json +0 -44
- package/src/lib/resources/templates/README.md +0 -78
- package/src/lib/resources/templates/api-server/config.json +0 -64
- package/src/lib/resources/templates/api-server/template/.aiwf/config.json +0 -56
- package/src/lib/resources/templates/api-server/template/.aiwf/feature-ledger.json +0 -42
- package/src/lib/resources/templates/api-server/template/.aiwf/personas/backend-engineer.json +0 -40
- package/src/lib/resources/templates/api-server/template/.aiwf/scripts/cli.js +0 -111
- package/src/lib/resources/templates/api-server/template/.env.example +0 -29
- package/src/lib/resources/templates/api-server/template/.eslintrc.json +0 -23
- package/src/lib/resources/templates/api-server/template/README.md +0 -171
- package/src/lib/resources/templates/api-server/template/jest.config.js +0 -26
- package/src/lib/resources/templates/api-server/template/nodemon.json +0 -9
- package/src/lib/resources/templates/api-server/template/package.json +0 -61
- package/src/lib/resources/templates/api-server/template/src/app.ts +0 -60
- package/src/lib/resources/templates/api-server/template/src/config/swagger.ts +0 -38
- package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +0 -54
- package/src/lib/resources/templates/api-server/template/src/controllers/authController.ts +0 -128
- package/src/lib/resources/templates/api-server/template/src/controllers/statusController.ts +0 -28
- package/src/lib/resources/templates/api-server/template/src/index.ts +0 -28
- package/src/lib/resources/templates/api-server/template/src/middleware/aiwfMiddleware.ts +0 -50
- package/src/lib/resources/templates/api-server/template/src/middleware/authMiddleware.ts +0 -41
- package/src/lib/resources/templates/api-server/template/src/middleware/errorHandler.ts +0 -42
- package/src/lib/resources/templates/api-server/template/src/middleware/notFoundHandler.ts +0 -14
- package/src/lib/resources/templates/api-server/template/src/middleware/requestLogger.ts +0 -20
- package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +0 -31
- package/src/lib/resources/templates/api-server/template/src/routes/index.ts +0 -13
- package/src/lib/resources/templates/api-server/template/src/routes/v1/index.ts +0 -67
- package/src/lib/resources/templates/api-server/template/src/utils/logger.ts +0 -37
- package/src/lib/resources/templates/api-server/template/tests/app.test.ts +0 -40
- package/src/lib/resources/templates/api-server/template/tsconfig.json +0 -43
- package/src/lib/resources/templates/npm-library/config.json +0 -61
- package/src/lib/resources/templates/npm-library/template/LICENSE +0 -21
- package/src/lib/resources/templates/npm-library/template/README.md +0 -201
- package/src/lib/resources/templates/npm-library/template/package.json +0 -78
- package/src/lib/resources/templates/web-app/config.json +0 -53
- package/src/lib/resources/templates/web-app/template/.aiwf/config.json +0 -54
- package/src/lib/resources/templates/web-app/template/.aiwf/feature-ledger.json +0 -37
- package/src/lib/resources/templates/web-app/template/.aiwf/personas/fullstack-developer.json +0 -40
- package/src/lib/resources/templates/web-app/template/.aiwf/scripts/cli.js +0 -110
- package/src/lib/resources/templates/web-app/template/.eslintrc.cjs +0 -20
- package/src/lib/resources/templates/web-app/template/README.md +0 -151
- package/src/lib/resources/templates/web-app/template/index.html +0 -14
- package/src/lib/resources/templates/web-app/template/package.json +0 -44
- package/src/lib/resources/templates/web-app/template/postcss.config.js +0 -6
- package/src/lib/resources/templates/web-app/template/public/vite.svg +0 -1
- package/src/lib/resources/templates/web-app/template/src/App.tsx +0 -21
- package/src/lib/resources/templates/web-app/template/src/components/Layout.tsx +0 -57
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +0 -107
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +0 -71
- package/src/lib/resources/templates/web-app/template/src/index.css +0 -60
- package/src/lib/resources/templates/web-app/template/src/main.tsx +0 -10
- package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +0 -57
- package/src/lib/resources/templates/web-app/template/src/pages/HomePage.tsx +0 -89
- package/src/lib/resources/templates/web-app/template/src/pages/NotFound.tsx +0 -25
- package/src/lib/resources/templates/web-app/template/src/stores/aiwfStore.ts +0 -126
- package/src/lib/resources/templates/web-app/template/src/types/global.d.ts +0 -9
- package/src/lib/resources/templates/web-app/template/src/vite-env.d.ts +0 -1
- package/src/lib/resources/templates/web-app/template/tailwind.config.js +0 -30
- package/src/lib/resources/templates/web-app/template/tsconfig.json +0 -37
- package/src/lib/resources/templates/web-app/template/tsconfig.node.json +0 -10
- package/src/lib/resources/templates/web-app/template/vite.config.ts +0 -23
- package/src/lib/resources/utils/background-monitor.js +0 -223
- package/src/lib/resources/utils/compression-metrics.js +0 -1031
- package/src/lib/resources/utils/compression-strategies.js +0 -557
- package/src/lib/resources/utils/content-normalizer.js +0 -455
- package/src/lib/resources/utils/context-compressor.js +0 -583
- package/src/lib/resources/utils/context-rule-parser.js +0 -180
- package/src/lib/resources/utils/context-token-monitor.js +0 -463
- package/src/lib/resources/utils/context-update-manager.js +0 -502
- package/src/lib/resources/utils/git-utils.js +0 -220
- package/src/lib/resources/utils/importance-classifier.js +0 -645
- package/src/lib/resources/utils/information-filter.js +0 -710
- package/src/lib/resources/utils/persona-aware-compressor.js +0 -598
- package/src/lib/resources/utils/prompt-injector.js +0 -242
- package/src/lib/resources/utils/simplified-evaluator.js +0 -119
- package/src/lib/resources/utils/text-summarizer.js +0 -492
- package/src/lib/resources/utils/token-counter.js +0 -177
- package/src/lib/resources/utils/token-monitor.js +0 -484
- package/src/lib/resources/utils/token-reporter.js +0 -452
- package/src/lib/resources/utils/token-storage.js +0 -433
- package/src/lib/resources/utils/token-tracker.js +0 -283
- package/src/lib/state/priority-calculator.js +0 -217
- package/src/lib/state/state-index.js +0 -155
- package/src/lib/state/task-scanner.js +0 -328
- package/src/lib/task-analyzer.js +0 -599
- package/src/lib/template-cache-system.js +0 -559
- package/src/lib/template-downloader.js +0 -467
- package/src/lib/template-version-manager.js +0 -491
- package/src/lib/token-optimizer.js +0 -517
- package/src/utils/engineering-guard.js +0 -399
|
@@ -1,502 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 컨텍스트 실시간 업데이트 관리자
|
|
3
|
-
* 페르소나 컨텍스트 파일의 변경을 감지하고 실시간으로 반영하는 모듈
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const EventEmitter = require('events');
|
|
9
|
-
const chokidar = require('chokidar');
|
|
10
|
-
const ContextRuleParser = require('./context-rule-parser');
|
|
11
|
-
|
|
12
|
-
class ContextUpdateManager extends EventEmitter {
|
|
13
|
-
constructor() {
|
|
14
|
-
super();
|
|
15
|
-
this.contextParser = new ContextRuleParser();
|
|
16
|
-
this.watchers = new Map();
|
|
17
|
-
this.updateQueue = [];
|
|
18
|
-
this.isProcessing = false;
|
|
19
|
-
this.pollingInterval = null;
|
|
20
|
-
this.lastUpdateTimes = new Map();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* 컨텍스트 파일 감시 시작
|
|
25
|
-
* @param {Array} personas - 감시할 페르소나 목록
|
|
26
|
-
* @param {Object} options - 감시 옵션
|
|
27
|
-
*/
|
|
28
|
-
startWatching(personas = ['architect', 'frontend', 'backend', 'data_analyst', 'security'], options = {}) {
|
|
29
|
-
const watchOptions = {
|
|
30
|
-
persistent: true,
|
|
31
|
-
ignoreInitial: true,
|
|
32
|
-
awaitWriteFinish: {
|
|
33
|
-
stabilityThreshold: 1000,
|
|
34
|
-
pollInterval: 100
|
|
35
|
-
},
|
|
36
|
-
...options
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
personas.forEach(persona => {
|
|
40
|
-
const contextPath = path.join(
|
|
41
|
-
process.cwd(),
|
|
42
|
-
'.aiwf',
|
|
43
|
-
'07_AI_PERSONAS',
|
|
44
|
-
persona,
|
|
45
|
-
'context_rules.md'
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
// 파일이 존재하는지 확인
|
|
50
|
-
if (!fs.existsSync(contextPath)) {
|
|
51
|
-
this.createDefaultContextFile(persona, contextPath);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Chokidar 감시자 생성
|
|
55
|
-
const watcher = chokidar.watch(contextPath, watchOptions);
|
|
56
|
-
|
|
57
|
-
watcher
|
|
58
|
-
.on('change', () => this.handleFileChange(persona, contextPath))
|
|
59
|
-
.on('add', () => this.handleFileAdd(persona, contextPath))
|
|
60
|
-
.on('unlink', () => this.handleFileRemove(persona, contextPath))
|
|
61
|
-
.on('error', error => this.handleWatchError(persona, error));
|
|
62
|
-
|
|
63
|
-
this.watchers.set(persona, watcher);
|
|
64
|
-
console.log(`컨텍스트 감시 시작: ${persona}`);
|
|
65
|
-
} catch (error) {
|
|
66
|
-
console.error(`감시 설정 실패 (${persona}):`, error);
|
|
67
|
-
// 폴백: 폴링 방식으로 전환
|
|
68
|
-
this.startPolling(persona, contextPath);
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// 이벤트 리스너 설정
|
|
73
|
-
this.setupEventHandlers();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* 기본 컨텍스트 파일 생성
|
|
78
|
-
* @param {string} persona - 페르소나 이름
|
|
79
|
-
* @param {string} filePath - 파일 경로
|
|
80
|
-
*/
|
|
81
|
-
createDefaultContextFile(persona, filePath) {
|
|
82
|
-
const defaultContexts = {
|
|
83
|
-
architect: `---
|
|
84
|
-
persona_name: architect
|
|
85
|
-
analysis_approach: 시스템 설계 중심
|
|
86
|
-
design_principles:
|
|
87
|
-
- 확장성
|
|
88
|
-
- 유지보수성
|
|
89
|
-
- 성능
|
|
90
|
-
communication_style: 구조적이고 논리적
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
# 아키텍트 페르소나 컨텍스트
|
|
94
|
-
|
|
95
|
-
## 핵심 역할
|
|
96
|
-
시스템 전체 구조를 설계하고 기술적 결정을 내리는 역할을 수행합니다.
|
|
97
|
-
|
|
98
|
-
## 분석 접근법
|
|
99
|
-
- 전체 시스템 관점에서 문제 분석
|
|
100
|
-
- 컴포넌트 간 상호작용 고려
|
|
101
|
-
- 장기적 확장성과 유지보수성 중시
|
|
102
|
-
|
|
103
|
-
## 소통 스타일
|
|
104
|
-
- 다이어그램과 구조도를 활용한 설명
|
|
105
|
-
- 기술적 트레이드오프 명확히 제시
|
|
106
|
-
- 단계별 구현 계획 제공`,
|
|
107
|
-
|
|
108
|
-
frontend: `---
|
|
109
|
-
persona_name: frontend
|
|
110
|
-
analysis_approach: UI/UX 중심
|
|
111
|
-
design_principles:
|
|
112
|
-
- 사용성
|
|
113
|
-
- 접근성
|
|
114
|
-
- 반응성
|
|
115
|
-
communication_style: 시각적이고 직관적
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
# 프론트엔드 페르소나 컨텍스트
|
|
119
|
-
|
|
120
|
-
## 핵심 역할
|
|
121
|
-
사용자 인터페이스와 경험을 설계하고 구현하는 역할을 수행합니다.
|
|
122
|
-
|
|
123
|
-
## 분석 접근법
|
|
124
|
-
- 사용자 관점에서 문제 접근
|
|
125
|
-
- 시각적 요소와 인터랙션 중시
|
|
126
|
-
- 다양한 디바이스와 브라우저 고려
|
|
127
|
-
|
|
128
|
-
## 소통 스타일
|
|
129
|
-
- 목업과 프로토타입 활용
|
|
130
|
-
- 사용자 시나리오 기반 설명
|
|
131
|
-
- 직관적이고 이해하기 쉬운 표현`,
|
|
132
|
-
|
|
133
|
-
backend: `---
|
|
134
|
-
persona_name: backend
|
|
135
|
-
analysis_approach: API 및 데이터 처리 중심
|
|
136
|
-
design_principles:
|
|
137
|
-
- 보안
|
|
138
|
-
- 효율성
|
|
139
|
-
- 확장성
|
|
140
|
-
communication_style: 기술적이고 정확함
|
|
141
|
-
---
|
|
142
|
-
|
|
143
|
-
# 백엔드 페르소나 컨텍스트
|
|
144
|
-
|
|
145
|
-
## 핵심 역할
|
|
146
|
-
서버 사이드 로직과 데이터 처리를 담당하는 역할을 수행합니다.
|
|
147
|
-
|
|
148
|
-
## 분석 접근법
|
|
149
|
-
- 데이터 흐름과 처리 로직 중심
|
|
150
|
-
- 성능과 보안 최적화 고려
|
|
151
|
-
- API 설계와 데이터베이스 구조 중시
|
|
152
|
-
|
|
153
|
-
## 소통 스타일
|
|
154
|
-
- API 문서와 데이터 스키마 제공
|
|
155
|
-
- 기술적 세부사항 명확히 전달
|
|
156
|
-
- 성능 지표와 보안 고려사항 강조`,
|
|
157
|
-
|
|
158
|
-
data_analyst: `---
|
|
159
|
-
persona_name: data_analyst
|
|
160
|
-
analysis_approach: 데이터 기반 의사결정
|
|
161
|
-
design_principles:
|
|
162
|
-
- 정확성
|
|
163
|
-
- 통찰력
|
|
164
|
-
- 시각화
|
|
165
|
-
communication_style: 분석적이고 통계적
|
|
166
|
-
---
|
|
167
|
-
|
|
168
|
-
# 데이터 분석가 페르소나 컨텍스트
|
|
169
|
-
|
|
170
|
-
## 핵심 역할
|
|
171
|
-
데이터를 분석하여 인사이트를 도출하고 의사결정을 지원하는 역할을 수행합니다.
|
|
172
|
-
|
|
173
|
-
## 분석 접근법
|
|
174
|
-
- 정량적 데이터 분석 우선
|
|
175
|
-
- 통계적 방법론 적용
|
|
176
|
-
- 시각화를 통한 인사이트 전달
|
|
177
|
-
|
|
178
|
-
## 소통 스타일
|
|
179
|
-
- 차트와 그래프 활용
|
|
180
|
-
- 데이터 기반 근거 제시
|
|
181
|
-
- 객관적이고 중립적인 관점`,
|
|
182
|
-
|
|
183
|
-
security: `---
|
|
184
|
-
persona_name: security
|
|
185
|
-
analysis_approach: 보안 취약점 중심
|
|
186
|
-
design_principles:
|
|
187
|
-
- 보안성
|
|
188
|
-
- 무결성
|
|
189
|
-
- 기밀성
|
|
190
|
-
communication_style: 신중하고 방어적
|
|
191
|
-
---
|
|
192
|
-
|
|
193
|
-
# 보안 페르소나 컨텍스트
|
|
194
|
-
|
|
195
|
-
## 핵심 역할
|
|
196
|
-
시스템의 보안 취약점을 식별하고 보호 방안을 제시하는 역할을 수행합니다.
|
|
197
|
-
|
|
198
|
-
## 분석 접근법
|
|
199
|
-
- 위협 모델링과 취약점 분석
|
|
200
|
-
- 보안 모범 사례 적용
|
|
201
|
-
- 컴플라이언스 요구사항 고려
|
|
202
|
-
|
|
203
|
-
## 소통 스타일
|
|
204
|
-
- 위험도 평가와 우선순위 제시
|
|
205
|
-
- 구체적인 보안 대책 제안
|
|
206
|
-
- 예방적 접근과 지속적 모니터링 강조`
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
const content = defaultContexts[persona] || `---
|
|
210
|
-
persona_name: ${persona}
|
|
211
|
-
analysis_approach: 일반적 접근
|
|
212
|
-
design_principles:
|
|
213
|
-
- 품질
|
|
214
|
-
- 효율성
|
|
215
|
-
communication_style: 표준적
|
|
216
|
-
---
|
|
217
|
-
|
|
218
|
-
# ${persona} 페르소나 컨텍스트
|
|
219
|
-
|
|
220
|
-
기본 페르소나 컨텍스트입니다.`;
|
|
221
|
-
|
|
222
|
-
// 디렉토리 생성
|
|
223
|
-
const dir = path.dirname(filePath);
|
|
224
|
-
if (!fs.existsSync(dir)) {
|
|
225
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// 파일 생성
|
|
229
|
-
fs.writeFileSync(filePath, content, 'utf-8');
|
|
230
|
-
console.log(`기본 컨텍스트 파일 생성: ${persona}`);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* 파일 변경 처리
|
|
235
|
-
* @param {string} persona - 페르소나 이름
|
|
236
|
-
* @param {string} filePath - 파일 경로
|
|
237
|
-
*/
|
|
238
|
-
async handleFileChange(persona, filePath) {
|
|
239
|
-
console.log(`컨텍스트 변경 감지: ${persona}`);
|
|
240
|
-
|
|
241
|
-
// 업데이트 큐에 추가
|
|
242
|
-
this.updateQueue.push({
|
|
243
|
-
type: 'change',
|
|
244
|
-
persona,
|
|
245
|
-
filePath,
|
|
246
|
-
timestamp: new Date().toISOString()
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
// 큐 처리
|
|
250
|
-
this.processUpdateQueue();
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* 파일 추가 처리
|
|
255
|
-
* @param {string} persona - 페르소나 이름
|
|
256
|
-
* @param {string} filePath - 파일 경로
|
|
257
|
-
*/
|
|
258
|
-
handleFileAdd(persona, filePath) {
|
|
259
|
-
console.log(`컨텍스트 파일 추가: ${persona}`);
|
|
260
|
-
|
|
261
|
-
this.updateQueue.push({
|
|
262
|
-
type: 'add',
|
|
263
|
-
persona,
|
|
264
|
-
filePath,
|
|
265
|
-
timestamp: new Date().toISOString()
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
this.processUpdateQueue();
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* 파일 제거 처리
|
|
273
|
-
* @param {string} persona - 페르소나 이름
|
|
274
|
-
* @param {string} filePath - 파일 경로
|
|
275
|
-
*/
|
|
276
|
-
handleFileRemove(persona, filePath) {
|
|
277
|
-
console.log(`컨텍스트 파일 제거: ${persona}`);
|
|
278
|
-
|
|
279
|
-
this.updateQueue.push({
|
|
280
|
-
type: 'remove',
|
|
281
|
-
persona,
|
|
282
|
-
filePath,
|
|
283
|
-
timestamp: new Date().toISOString()
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
this.processUpdateQueue();
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* 감시 오류 처리
|
|
291
|
-
* @param {string} persona - 페르소나 이름
|
|
292
|
-
* @param {Error} error - 오류 객체
|
|
293
|
-
*/
|
|
294
|
-
handleWatchError(persona, error) {
|
|
295
|
-
console.error(`감시 오류 (${persona}):`, error);
|
|
296
|
-
|
|
297
|
-
// 폴링으로 전환
|
|
298
|
-
const contextPath = path.join(
|
|
299
|
-
process.cwd(),
|
|
300
|
-
'.aiwf',
|
|
301
|
-
'07_AI_PERSONAS',
|
|
302
|
-
persona,
|
|
303
|
-
'context_rules.md'
|
|
304
|
-
);
|
|
305
|
-
this.startPolling(persona, contextPath);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* 폴링 방식으로 파일 감시
|
|
310
|
-
* @param {string} persona - 페르소나 이름
|
|
311
|
-
* @param {string} filePath - 파일 경로
|
|
312
|
-
*/
|
|
313
|
-
startPolling(persona, filePath) {
|
|
314
|
-
const pollInterval = 5000; // 5초마다 확인
|
|
315
|
-
|
|
316
|
-
const checkFile = async () => {
|
|
317
|
-
try {
|
|
318
|
-
const stats = await fs.promises.stat(filePath);
|
|
319
|
-
const lastModified = stats.mtimeMs;
|
|
320
|
-
const previousModified = this.lastUpdateTimes.get(persona);
|
|
321
|
-
|
|
322
|
-
if (previousModified && lastModified !== previousModified) {
|
|
323
|
-
this.handleFileChange(persona, filePath);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
this.lastUpdateTimes.set(persona, lastModified);
|
|
327
|
-
} catch (error) {
|
|
328
|
-
// 파일이 없으면 생성
|
|
329
|
-
if (error.code === 'ENOENT') {
|
|
330
|
-
this.createDefaultContextFile(persona, filePath);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
};
|
|
334
|
-
|
|
335
|
-
// 기존 인터벌 정리
|
|
336
|
-
if (this.pollingInterval) {
|
|
337
|
-
clearInterval(this.pollingInterval);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// 새 인터벌 설정
|
|
341
|
-
this.pollingInterval = setInterval(checkFile, pollInterval);
|
|
342
|
-
console.log(`폴링 모드로 전환: ${persona}`);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* 업데이트 큐 처리
|
|
347
|
-
*/
|
|
348
|
-
async processUpdateQueue() {
|
|
349
|
-
if (this.isProcessing || this.updateQueue.length === 0) {
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
this.isProcessing = true;
|
|
354
|
-
|
|
355
|
-
while (this.updateQueue.length > 0) {
|
|
356
|
-
const update = this.updateQueue.shift();
|
|
357
|
-
|
|
358
|
-
try {
|
|
359
|
-
await this.processUpdate(update);
|
|
360
|
-
} catch (error) {
|
|
361
|
-
console.error('업데이트 처리 오류:', error);
|
|
362
|
-
this.emit('error', { update, error });
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
this.isProcessing = false;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* 개별 업데이트 처리
|
|
371
|
-
* @param {Object} update - 업데이트 정보
|
|
372
|
-
*/
|
|
373
|
-
async processUpdate(update) {
|
|
374
|
-
const { type, persona, filePath, timestamp } = update;
|
|
375
|
-
|
|
376
|
-
switch (type) {
|
|
377
|
-
case 'change':
|
|
378
|
-
case 'add':
|
|
379
|
-
// 컨텍스트 다시 파싱
|
|
380
|
-
const newContext = await this.contextParser.parseContextRules(persona);
|
|
381
|
-
|
|
382
|
-
// 캐시 무효화
|
|
383
|
-
this.contextParser.clearCache();
|
|
384
|
-
|
|
385
|
-
// 이벤트 발생
|
|
386
|
-
this.emit('contextUpdated', {
|
|
387
|
-
persona,
|
|
388
|
-
context: newContext,
|
|
389
|
-
updateType: type,
|
|
390
|
-
timestamp
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
console.log(`컨텍스트 업데이트 완료: ${persona}`);
|
|
394
|
-
break;
|
|
395
|
-
|
|
396
|
-
case 'remove':
|
|
397
|
-
// 기본 컨텍스트로 복원
|
|
398
|
-
this.createDefaultContextFile(persona, filePath);
|
|
399
|
-
|
|
400
|
-
this.emit('contextRemoved', {
|
|
401
|
-
persona,
|
|
402
|
-
timestamp
|
|
403
|
-
});
|
|
404
|
-
break;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* 이벤트 핸들러 설정
|
|
410
|
-
*/
|
|
411
|
-
setupEventHandlers() {
|
|
412
|
-
// 컨텍스트 업데이트 시 로깅
|
|
413
|
-
this.on('contextUpdated', (data) => {
|
|
414
|
-
console.log(`[이벤트] 컨텍스트 업데이트: ${data.persona} (${data.updateType})`);
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
// 오류 발생 시 로깅
|
|
418
|
-
this.on('error', (data) => {
|
|
419
|
-
console.error(`[이벤트] 오류 발생:`, data.error);
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* 모든 감시 중지
|
|
425
|
-
*/
|
|
426
|
-
stopWatching() {
|
|
427
|
-
// Chokidar 감시자 정리
|
|
428
|
-
this.watchers.forEach((watcher, persona) => {
|
|
429
|
-
watcher.close();
|
|
430
|
-
console.log(`감시 중지: ${persona}`);
|
|
431
|
-
});
|
|
432
|
-
this.watchers.clear();
|
|
433
|
-
|
|
434
|
-
// 폴링 인터벌 정리
|
|
435
|
-
if (this.pollingInterval) {
|
|
436
|
-
clearInterval(this.pollingInterval);
|
|
437
|
-
this.pollingInterval = null;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// 큐 초기화
|
|
441
|
-
this.updateQueue = [];
|
|
442
|
-
this.isProcessing = false;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
* 특정 페르소나 감시 중지
|
|
447
|
-
* @param {string} persona - 페르소나 이름
|
|
448
|
-
*/
|
|
449
|
-
stopWatchingPersona(persona) {
|
|
450
|
-
const watcher = this.watchers.get(persona);
|
|
451
|
-
if (watcher) {
|
|
452
|
-
watcher.close();
|
|
453
|
-
this.watchers.delete(persona);
|
|
454
|
-
console.log(`감시 중지: ${persona}`);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* 감시 상태 확인
|
|
460
|
-
* @returns {Object} 감시 상태 정보
|
|
461
|
-
*/
|
|
462
|
-
getWatchStatus() {
|
|
463
|
-
const activeWatchers = Array.from(this.watchers.keys());
|
|
464
|
-
const queueLength = this.updateQueue.length;
|
|
465
|
-
const isPolling = this.pollingInterval !== null;
|
|
466
|
-
|
|
467
|
-
return {
|
|
468
|
-
activeWatchers,
|
|
469
|
-
watcherCount: activeWatchers.length,
|
|
470
|
-
queueLength,
|
|
471
|
-
isProcessing: this.isProcessing,
|
|
472
|
-
isPolling,
|
|
473
|
-
lastUpdateTimes: Object.fromEntries(this.lastUpdateTimes)
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
/**
|
|
478
|
-
* 강제 업데이트 트리거
|
|
479
|
-
* @param {string} persona - 페르소나 이름
|
|
480
|
-
*/
|
|
481
|
-
forceUpdate(persona) {
|
|
482
|
-
const contextPath = path.join(
|
|
483
|
-
process.cwd(),
|
|
484
|
-
'.aiwf',
|
|
485
|
-
'07_AI_PERSONAS',
|
|
486
|
-
persona,
|
|
487
|
-
'context_rules.md'
|
|
488
|
-
);
|
|
489
|
-
|
|
490
|
-
this.handleFileChange(persona, contextPath);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* 모든 페르소나 강제 업데이트
|
|
495
|
-
*/
|
|
496
|
-
forceUpdateAll() {
|
|
497
|
-
const personas = Array.from(this.watchers.keys());
|
|
498
|
-
personas.forEach(persona => this.forceUpdate(persona));
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
module.exports = ContextUpdateManager;
|
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
const { exec } = require('child_process');
|
|
2
|
-
const { promisify } = require('util');
|
|
3
|
-
const execAsync = promisify(exec);
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { extractFeatureIds, extractFeatureIdFromBranch } = require('../config/commit-patterns');
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Git 커밋 정보를 추출하는 유틸리티 모듈
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Git 명령어 실행
|
|
13
|
-
* @param {string} command - 실행할 git 명령어
|
|
14
|
-
* @param {string} cwd - 작업 디렉토리 (선택사항)
|
|
15
|
-
* @returns {Promise<string>} - 명령어 출력
|
|
16
|
-
*/
|
|
17
|
-
async function runGitCommand(command, cwd = process.cwd()) {
|
|
18
|
-
try {
|
|
19
|
-
const { stdout, stderr } = await execAsync(`git ${command}`, { cwd });
|
|
20
|
-
if (stderr && !stderr.includes('warning:')) {
|
|
21
|
-
throw new Error(`Git command failed: ${stderr}`);
|
|
22
|
-
}
|
|
23
|
-
return stdout.trim();
|
|
24
|
-
} catch (error) {
|
|
25
|
-
throw new Error(`Git command execution failed: ${error.message}`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* 현재 브랜치 이름 가져오기
|
|
31
|
-
* @returns {Promise<string>} - 브랜치 이름
|
|
32
|
-
*/
|
|
33
|
-
async function getCurrentBranch() {
|
|
34
|
-
return await runGitCommand('rev-parse --abbrev-ref HEAD');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* 커밋 정보 객체 구조
|
|
39
|
-
* @typedef {Object} CommitInfo
|
|
40
|
-
* @property {string} hash - 커밋 해시
|
|
41
|
-
* @property {string} author - 작성자
|
|
42
|
-
* @property {string} date - 커밋 날짜
|
|
43
|
-
* @property {string} message - 커밋 메시지
|
|
44
|
-
* @property {Array<string>} featureIds - 관련 Feature ID 목록
|
|
45
|
-
*/
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* 특정 커밋의 상세 정보 가져오기
|
|
49
|
-
* @param {string} commitHash - 커밋 해시
|
|
50
|
-
* @returns {Promise<CommitInfo>} - 커밋 정보
|
|
51
|
-
*/
|
|
52
|
-
async function getCommitInfo(commitHash) {
|
|
53
|
-
const format = '%H%n%an%n%aI%n%s%n%b';
|
|
54
|
-
const output = await runGitCommand(`show -s --format="${format}" ${commitHash}`);
|
|
55
|
-
const lines = output.split('\n');
|
|
56
|
-
|
|
57
|
-
const message = lines.slice(3).join('\n').trim();
|
|
58
|
-
const featureIds = extractFeatureIds(message);
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
hash: lines[0],
|
|
62
|
-
author: lines[1],
|
|
63
|
-
date: lines[2],
|
|
64
|
-
message: message,
|
|
65
|
-
featureIds: featureIds
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* 커밋 범위에서 Feature 관련 커밋 찾기
|
|
71
|
-
* @param {string} since - 시작 날짜 (예: '2025-01-01')
|
|
72
|
-
* @param {string} until - 종료 날짜 (선택사항)
|
|
73
|
-
* @returns {Promise<Array<CommitInfo>>} - Feature 관련 커밋 목록
|
|
74
|
-
*/
|
|
75
|
-
async function getFeatureRelatedCommits(since, until = 'HEAD') {
|
|
76
|
-
try {
|
|
77
|
-
// git log 명령어 구성
|
|
78
|
-
let command = `log --since="${since}"`;
|
|
79
|
-
if (until !== 'HEAD') {
|
|
80
|
-
command += ` --until="${until}"`;
|
|
81
|
-
}
|
|
82
|
-
command += ' --pretty=format:"%H" --grep="FL[0-9]\\{3\\}"';
|
|
83
|
-
|
|
84
|
-
const output = await runGitCommand(command);
|
|
85
|
-
if (!output) {
|
|
86
|
-
return [];
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const commitHashes = output.split('\n').filter(Boolean);
|
|
90
|
-
const commits = [];
|
|
91
|
-
|
|
92
|
-
for (const hash of commitHashes) {
|
|
93
|
-
const commitInfo = await getCommitInfo(hash);
|
|
94
|
-
if (commitInfo.featureIds.length > 0) {
|
|
95
|
-
commits.push(commitInfo);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return commits;
|
|
100
|
-
} catch (error) {
|
|
101
|
-
console.error('Error getting feature related commits:', error);
|
|
102
|
-
return [];
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* 특정 Feature ID와 관련된 모든 커밋 찾기
|
|
108
|
-
* @param {string} featureId - Feature ID (예: 'FL001')
|
|
109
|
-
* @returns {Promise<Array<CommitInfo>>} - 해당 Feature의 커밋 목록
|
|
110
|
-
*/
|
|
111
|
-
async function getCommitsByFeatureId(featureId) {
|
|
112
|
-
try {
|
|
113
|
-
const command = `log --pretty=format:"%H" --grep="${featureId}"`;
|
|
114
|
-
const output = await runGitCommand(command);
|
|
115
|
-
|
|
116
|
-
if (!output) {
|
|
117
|
-
return [];
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const commitHashes = output.split('\n').filter(Boolean);
|
|
121
|
-
const commits = [];
|
|
122
|
-
|
|
123
|
-
for (const hash of commitHashes) {
|
|
124
|
-
const commitInfo = await getCommitInfo(hash);
|
|
125
|
-
if (commitInfo.featureIds.includes(featureId)) {
|
|
126
|
-
commits.push(commitInfo);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return commits;
|
|
131
|
-
} catch (error) {
|
|
132
|
-
console.error(`Error getting commits for feature ${featureId}:`, error);
|
|
133
|
-
return [];
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* 현재 브랜치의 Feature ID 추출
|
|
139
|
-
* @returns {Promise<string|null>} - Feature ID 또는 null
|
|
140
|
-
*/
|
|
141
|
-
async function getCurrentFeatureId() {
|
|
142
|
-
try {
|
|
143
|
-
const branch = await getCurrentBranch();
|
|
144
|
-
return extractFeatureIdFromBranch(branch);
|
|
145
|
-
} catch (error) {
|
|
146
|
-
console.error('Error getting current feature ID:', error);
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* 최근 N개의 커밋에서 Feature 관련 커밋 찾기
|
|
153
|
-
* @param {number} count - 확인할 커밋 수
|
|
154
|
-
* @returns {Promise<Array<CommitInfo>>} - Feature 관련 커밋 목록
|
|
155
|
-
*/
|
|
156
|
-
async function getRecentFeatureCommits(count = 10) {
|
|
157
|
-
try {
|
|
158
|
-
const command = `log -${count} --pretty=format:"%H"`;
|
|
159
|
-
const output = await runGitCommand(command);
|
|
160
|
-
|
|
161
|
-
if (!output) {
|
|
162
|
-
return [];
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const commitHashes = output.split('\n').filter(Boolean);
|
|
166
|
-
const featureCommits = [];
|
|
167
|
-
|
|
168
|
-
for (const hash of commitHashes) {
|
|
169
|
-
const commitInfo = await getCommitInfo(hash);
|
|
170
|
-
if (commitInfo.featureIds.length > 0) {
|
|
171
|
-
featureCommits.push(commitInfo);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return featureCommits;
|
|
176
|
-
} catch (error) {
|
|
177
|
-
console.error('Error getting recent feature commits:', error);
|
|
178
|
-
return [];
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Git 저장소인지 확인
|
|
184
|
-
* @param {string} dir - 확인할 디렉토리
|
|
185
|
-
* @returns {Promise<boolean>}
|
|
186
|
-
*/
|
|
187
|
-
async function isGitRepository(dir = process.cwd()) {
|
|
188
|
-
try {
|
|
189
|
-
await runGitCommand('rev-parse --git-dir', dir);
|
|
190
|
-
return true;
|
|
191
|
-
} catch (error) {
|
|
192
|
-
return false;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* 변경된 파일 목록 가져오기
|
|
198
|
-
* @returns {Promise<Array<string>>} - 변경된 파일 경로 목록
|
|
199
|
-
*/
|
|
200
|
-
async function getChangedFiles() {
|
|
201
|
-
try {
|
|
202
|
-
const output = await runGitCommand('diff --name-only HEAD');
|
|
203
|
-
return output ? output.split('\n').filter(Boolean) : [];
|
|
204
|
-
} catch (error) {
|
|
205
|
-
console.error('Error getting changed files:', error);
|
|
206
|
-
return [];
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
module.exports = {
|
|
211
|
-
runGitCommand,
|
|
212
|
-
getCurrentBranch,
|
|
213
|
-
getCommitInfo,
|
|
214
|
-
getFeatureRelatedCommits,
|
|
215
|
-
getCommitsByFeatureId,
|
|
216
|
-
getCurrentFeatureId,
|
|
217
|
-
getRecentFeatureCommits,
|
|
218
|
-
isGitRepository,
|
|
219
|
-
getChangedFiles
|
|
220
|
-
};
|