aiwf 0.3.14 → 0.3.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.ko.md +19 -10
  2. package/README.md +54 -9
  3. package/docs/AI-WORKFLOW.ko.md +1 -1
  4. package/docs/AI-WORKFLOW.md +1 -1
  5. package/docs/API_REFERENCE_FULL.ko.md +979 -0
  6. package/docs/API_REFERENCE_FULL.md +979 -0
  7. package/docs/ARCHITECTURE.ko.md +314 -0
  8. package/docs/ARCHITECTURE.md +314 -0
  9. package/docs/GETTING_STARTED.ko.md +219 -0
  10. package/docs/GETTING_STARTED.md +1 -2
  11. package/docs/PERFORMANCE_GUIDELINES.ko.md +388 -0
  12. package/docs/ROADMAP_v0.4.0.md +286 -0
  13. package/docs/TROUBLESHOOTING.ko.md +366 -0
  14. package/package.json +50 -2
  15. package/src/DEPENDENCY_MAP.md +90 -0
  16. package/src/cli/index.js +1 -1
  17. package/src/lib/installer.js +204 -15
  18. package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +0 -14
  19. package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +0 -11
  20. package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +2 -5
  21. package/src/lib/resources/utils/token-reporter.js +2 -3
  22. package/src/utils/checkpoint-manager.js +11 -1
  23. package/src/utils/engineering-guard.js +11 -1
  24. package/src/utils/messages.js +16 -0
  25. package/src/utils/paths.js +2 -2
  26. package/templates/api-server/template/src/controllers/aiwfController.ts +0 -14
  27. package/templates/api-server/template/src/routes/aiwf.ts +0 -11
  28. package/templates/web-app/template/src/pages/AiwfDashboard.tsx +2 -5
  29. package/docs/CLEANUP_RECOMMENDATIONS.md +0 -86
  30. package/docs/git-feature-integration-guide.md +0 -253
  31. package/docs/guides/feature-git-integration-guide-ko.md +0 -481
  32. package/docs/guides/feature-git-integration-guide.md +0 -481
  33. package/docs/lightweight-evaluation.md +0 -198
  34. package/docs/performance-benchmark-report.md +0 -159
  35. package/docs/reports/S02-sprint-report.md +0 -190
  36. package/docs/usability-test-report.md +0 -170
  37. package/src/commands/cache-templates.js +0 -209
  38. package/src/commands/create-offline.js +0 -86
  39. package/src/commands/state-refactored.js +0 -419
  40. package/src/lib/resources/commands/feature-ledger.js +0 -565
  41. package/src/lib/resources/commands/feature_commit_report.js +0 -370
  42. package/src/lib/resources/commands/scan_git_history.js +0 -234
  43. package/src/lib/resources/commands/sync_feature_commits.js +0 -154
  44. package/src/lib/resources/templates/web-app/template/src/components/aiwf/FeatureLedger.tsx +0 -93
  45. package/src/lib/resources/utils/feature-updater.js +0 -271
  46. package/src/utils/git-integration.js +0 -173
  47. package/templates/web-app/template/src/components/aiwf/FeatureLedger.tsx +0 -93
  48. /package/docs/{AI_PERSONA_SYSTEM_DESIGN.md → designs/AI_PERSONA_SYSTEM_DESIGN.md} +0 -0
  49. /package/docs/{API_DOCUMENTATION.md → designs/API_DOCUMENTATION.md} +0 -0
  50. /package/docs/{API_REFERENCE.md → designs/API_REFERENCE.md} +0 -0
  51. /package/docs/{Enhanced_Installation_Flow_Design.md → designs/Enhanced_Installation_Flow_Design.md} +0 -0
  52. /package/docs/{moonklabs-metadata-system-prd.md → designs/aiwf-metadata-system-prd.md} +0 -0
  53. /package/docs/{offline-template-cache.md → designs/offline-template-cache.md} +0 -0
  54. /package/docs/{persona-aware-compression.md → designs/persona-aware-compression.md} +0 -0
@@ -1,370 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const { Command } = require('commander');
4
- const chalk = require('chalk');
5
- const ora = require('ora');
6
- const fs = require('fs').promises;
7
- const path = require('path');
8
- const {
9
- getCommitsByFeatureId,
10
- isGitRepository,
11
- runGitCommand
12
- } = require('../utils/git-utils');
13
- const {
14
- findFeatureFile,
15
- readFeatureFile
16
- } = require('../utils/feature-updater');
17
-
18
- const program = new Command();
19
-
20
- /**
21
- * feature_commit_report 명령어
22
- * Feature와 Git 커밋의 매핑 리포트 생성
23
- */
24
-
25
- program
26
- .name('feature_commit_report')
27
- .description('Feature와 Git 커밋의 매핑 리포트를 생성합니다')
28
- .option('-f, --feature <id>', '특정 Feature ID의 리포트만 생성')
29
- .option('-s, --status <status>', 'Feature 상태별 필터링 (active|completed|archived)')
30
- .option('-o, --output <path>', '리포트 출력 경로', './feature_commit_report.md')
31
- .option('-F, --format <format>', '출력 형식 (markdown|json|html)', 'markdown')
32
- .option('-d, --detailed', '상세 커밋 정보 포함')
33
- .option('-t, --timeline', '타임라인 형식으로 표시')
34
- .option('-v, --verbose', '상세 로그 출력')
35
- .action(async (options) => {
36
- const spinner = ora();
37
-
38
- try {
39
- // Git 저장소 확인
40
- if (!await isGitRepository()) {
41
- console.error(chalk.red('❌ 현재 디렉토리는 Git 저장소가 아닙니다.'));
42
- process.exit(1);
43
- }
44
-
45
- spinner.start('Feature 정보 수집 중...');
46
-
47
- // Feature 파일 수집
48
- const basePath = path.join(process.cwd(), '.aiwf', '06_FEATURE_LEDGERS');
49
- const statuses = options.status ? [options.status] : ['active', 'completed', 'archived'];
50
- const features = [];
51
-
52
- for (const status of statuses) {
53
- const dirPath = path.join(basePath, status);
54
- try {
55
- const files = await fs.readdir(dirPath);
56
- const featureFiles = files.filter(f => f.endsWith('.md'));
57
-
58
- for (const file of featureFiles) {
59
- const featureId = file.split('_')[0];
60
-
61
- // 특정 Feature ID 필터링
62
- if (options.feature && featureId !== options.feature) {
63
- continue;
64
- }
65
-
66
- const filePath = path.join(dirPath, file);
67
- const { frontmatter } = await readFeatureFile(filePath);
68
-
69
- features.push({
70
- id: featureId,
71
- status: status,
72
- title: frontmatter.title || 'Untitled',
73
- milestone: frontmatter.milestone || 'N/A',
74
- assignee: frontmatter.assignee || 'Unassigned',
75
- created: frontmatter.created_date || 'Unknown',
76
- updated: frontmatter.last_updated || 'Unknown',
77
- gitCommits: frontmatter.git_commits || [],
78
- progress: frontmatter.progress_percentage || 0
79
- });
80
- }
81
- } catch (error) {
82
- // 디렉토리가 없을 수 있음
83
- continue;
84
- }
85
- }
86
-
87
- spinner.succeed(chalk.green(`${features.length}개 Feature 정보 수집 완료`));
88
-
89
- if (features.length === 0) {
90
- console.log(chalk.yellow('리포트를 생성할 Feature가 없습니다.'));
91
- return;
92
- }
93
-
94
- // 각 Feature의 커밋 정보 수집
95
- spinner.start('Git 커밋 정보 수집 중...');
96
- const reportData = [];
97
-
98
- for (const feature of features) {
99
- const commits = await getCommitsByFeatureId(feature.id);
100
-
101
- reportData.push({
102
- ...feature,
103
- commits: commits,
104
- commitCount: commits.length,
105
- firstCommit: commits.length > 0 ? commits[commits.length - 1] : null,
106
- lastCommit: commits.length > 0 ? commits[0] : null
107
- });
108
- }
109
-
110
- spinner.succeed(chalk.green('커밋 정보 수집 완료'));
111
-
112
- // 리포트 생성
113
- spinner.start('리포트 생성 중...');
114
-
115
- let reportContent;
116
- switch (options.format) {
117
- case 'json':
118
- reportContent = generateJsonReport(reportData, options);
119
- break;
120
- case 'html':
121
- reportContent = generateHtmlReport(reportData, options);
122
- break;
123
- case 'markdown':
124
- default:
125
- reportContent = generateMarkdownReport(reportData, options);
126
- }
127
-
128
- // 리포트 저장
129
- await fs.writeFile(options.output, reportContent, 'utf8');
130
- spinner.succeed(chalk.green(`리포트 생성 완료: ${options.output}`));
131
-
132
- // 요약 정보 출력
133
- console.log(chalk.cyan('\n📊 리포트 요약:'));
134
- console.log(chalk.white(` 총 Feature: ${reportData.length}개`));
135
- console.log(chalk.white(` 총 커밋: ${reportData.reduce((sum, f) => sum + f.commitCount, 0)}개`));
136
-
137
- const byStatus = {};
138
- reportData.forEach(f => {
139
- byStatus[f.status] = (byStatus[f.status] || 0) + 1;
140
- });
141
-
142
- console.log(chalk.cyan('\n 상태별 분포:'));
143
- Object.entries(byStatus).forEach(([status, count]) => {
144
- const icon = status === 'active' ? '🟢' : status === 'completed' ? '✅' : '📦';
145
- console.log(chalk.white(` ${icon} ${status}: ${count}개`));
146
- });
147
-
148
- } catch (error) {
149
- spinner.fail(chalk.red('리포트 생성 중 오류 발생'));
150
- console.error(chalk.red(error.message));
151
- if (options.verbose) {
152
- console.error(error.stack);
153
- }
154
- process.exit(1);
155
- }
156
- });
157
-
158
- /**
159
- * Markdown 형식 리포트 생성
160
- */
161
- function generateMarkdownReport(data, options) {
162
- let report = '# Feature-Git Commit Mapping Report\n\n';
163
- report += `Generated: ${new Date().toISOString()}\n\n`;
164
-
165
- // 요약 섹션
166
- report += '## Summary\n\n';
167
- report += `- Total Features: ${data.length}\n`;
168
- report += `- Total Commits: ${data.reduce((sum, f) => sum + f.commitCount, 0)}\n`;
169
- report += '\n';
170
-
171
- // 상태별 통계
172
- const byStatus = {};
173
- data.forEach(f => {
174
- byStatus[f.status] = (byStatus[f.status] || []);
175
- byStatus[f.status].push(f);
176
- });
177
-
178
- report += '## Features by Status\n\n';
179
-
180
- Object.entries(byStatus).forEach(([status, features]) => {
181
- report += `### ${status.charAt(0).toUpperCase() + status.slice(1)} (${features.length})\n\n`;
182
-
183
- features.forEach(feature => {
184
- report += `#### ${feature.id}: ${feature.title}\n\n`;
185
- report += `- **Milestone**: ${feature.milestone}\n`;
186
- report += `- **Assignee**: ${feature.assignee}\n`;
187
- report += `- **Progress**: ${feature.progress}%\n`;
188
- report += `- **Commits**: ${feature.commitCount}\n`;
189
-
190
- if (feature.firstCommit) {
191
- report += `- **First Commit**: ${feature.firstCommit.date.substring(0, 10)}\n`;
192
- report += `- **Last Commit**: ${feature.lastCommit.date.substring(0, 10)}\n`;
193
- }
194
-
195
- if (options.detailed && feature.commits.length > 0) {
196
- report += '\n**Commit History**:\n\n';
197
- feature.commits.forEach(commit => {
198
- const shortHash = commit.hash.substring(0, 7);
199
- const date = commit.date.substring(0, 10);
200
- const message = commit.message.split('\n')[0];
201
- report += `- \`${shortHash}\` (${date}): ${message}\n`;
202
- });
203
- }
204
-
205
- report += '\n';
206
- });
207
- });
208
-
209
- // 타임라인 뷰 (옵션)
210
- if (options.timeline) {
211
- report += '## Timeline View\n\n';
212
-
213
- // 모든 커밋을 날짜순으로 정렬
214
- const allCommits = [];
215
- data.forEach(feature => {
216
- feature.commits.forEach(commit => {
217
- allCommits.push({
218
- ...commit,
219
- featureId: feature.id,
220
- featureTitle: feature.title
221
- });
222
- });
223
- });
224
-
225
- allCommits.sort((a, b) => new Date(b.date) - new Date(a.date));
226
-
227
- let currentMonth = '';
228
- allCommits.forEach(commit => {
229
- const month = commit.date.substring(0, 7);
230
- if (month !== currentMonth) {
231
- currentMonth = month;
232
- report += `\n### ${month}\n\n`;
233
- }
234
-
235
- const shortHash = commit.hash.substring(0, 7);
236
- const date = commit.date.substring(0, 10);
237
- const message = commit.message.split('\n')[0];
238
- report += `- **${date}** - ${commit.featureId}: ${message} (\`${shortHash}\`)\n`;
239
- });
240
- }
241
-
242
- return report;
243
- }
244
-
245
- /**
246
- * JSON 형식 리포트 생성
247
- */
248
- function generateJsonReport(data, options) {
249
- const report = {
250
- generated: new Date().toISOString(),
251
- summary: {
252
- totalFeatures: data.length,
253
- totalCommits: data.reduce((sum, f) => sum + f.commitCount, 0),
254
- byStatus: {}
255
- },
256
- features: []
257
- };
258
-
259
- // 상태별 집계
260
- data.forEach(f => {
261
- report.summary.byStatus[f.status] = (report.summary.byStatus[f.status] || 0) + 1;
262
- });
263
-
264
- // Feature 데이터
265
- report.features = data.map(feature => {
266
- const featureData = {
267
- id: feature.id,
268
- title: feature.title,
269
- status: feature.status,
270
- milestone: feature.milestone,
271
- assignee: feature.assignee,
272
- progress: feature.progress,
273
- commitCount: feature.commitCount,
274
- created: feature.created,
275
- updated: feature.updated
276
- };
277
-
278
- if (options.detailed) {
279
- featureData.commits = feature.commits.map(commit => ({
280
- hash: commit.hash,
281
- author: commit.author,
282
- date: commit.date,
283
- message: commit.message
284
- }));
285
- }
286
-
287
- return featureData;
288
- });
289
-
290
- return JSON.stringify(report, null, 2);
291
- }
292
-
293
- /**
294
- * HTML 형식 리포트 생성
295
- */
296
- function generateHtmlReport(data, options) {
297
- let html = `<!DOCTYPE html>
298
- <html>
299
- <head>
300
- <meta charset="UTF-8">
301
- <title>Feature-Git Commit Mapping Report</title>
302
- <style>
303
- body { font-family: Arial, sans-serif; margin: 20px; }
304
- h1, h2, h3 { color: #333; }
305
- .summary { background: #f5f5f5; padding: 15px; border-radius: 5px; }
306
- .feature { border: 1px solid #ddd; margin: 10px 0; padding: 15px; border-radius: 5px; }
307
- .active { border-left: 4px solid #28a745; }
308
- .completed { border-left: 4px solid #007bff; }
309
- .archived { border-left: 4px solid #6c757d; }
310
- .progress { background: #e9ecef; height: 20px; border-radius: 3px; }
311
- .progress-bar { background: #007bff; height: 100%; border-radius: 3px; }
312
- .commit { font-family: monospace; font-size: 0.9em; margin: 5px 0; }
313
- code { background: #f5f5f5; padding: 2px 4px; border-radius: 3px; }
314
- </style>
315
- </head>
316
- <body>
317
- <h1>Feature-Git Commit Mapping Report</h1>
318
- <p>Generated: ${new Date().toISOString()}</p>
319
-
320
- <div class="summary">
321
- <h2>Summary</h2>
322
- <ul>
323
- <li>Total Features: ${data.length}</li>
324
- <li>Total Commits: ${data.reduce((sum, f) => sum + f.commitCount, 0)}</li>
325
- </ul>
326
- </div>
327
- `;
328
-
329
- // Features by status
330
- const byStatus = {};
331
- data.forEach(f => {
332
- byStatus[f.status] = (byStatus[f.status] || []);
333
- byStatus[f.status].push(f);
334
- });
335
-
336
- Object.entries(byStatus).forEach(([status, features]) => {
337
- html += `<h2>${status.charAt(0).toUpperCase() + status.slice(1)} Features (${features.length})</h2>`;
338
-
339
- features.forEach(feature => {
340
- html += `<div class="feature ${status}">`;
341
- html += `<h3>${feature.id}: ${feature.title}</h3>`;
342
- html += `<p><strong>Milestone:</strong> ${feature.milestone} | `;
343
- html += `<strong>Assignee:</strong> ${feature.assignee} | `;
344
- html += `<strong>Commits:</strong> ${feature.commitCount}</p>`;
345
-
346
- html += `<div class="progress">`;
347
- html += `<div class="progress-bar" style="width: ${feature.progress}%"></div>`;
348
- html += `</div>`;
349
- html += `<p>Progress: ${feature.progress}%</p>`;
350
-
351
- if (options.detailed && feature.commits.length > 0) {
352
- html += '<h4>Commit History:</h4>';
353
- feature.commits.forEach(commit => {
354
- const shortHash = commit.hash.substring(0, 7);
355
- const date = commit.date.substring(0, 10);
356
- const message = commit.message.split('\n')[0];
357
- html += `<div class="commit"><code>${shortHash}</code> (${date}): ${message}</div>`;
358
- });
359
- }
360
-
361
- html += '</div>';
362
- });
363
- });
364
-
365
- html += '</body></html>';
366
- return html;
367
- }
368
-
369
- // 명령어 실행
370
- program.parse(process.argv);
@@ -1,234 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const { Command } = require('commander');
4
- const chalk = require('chalk');
5
- const ora = require('ora');
6
- const fs = require('fs').promises;
7
- const path = require('path');
8
- const {
9
- getFeatureRelatedCommits,
10
- isGitRepository,
11
- runGitCommand
12
- } = require('../utils/git-utils');
13
- const {
14
- syncFeatureWithGit,
15
- findFeatureFile
16
- } = require('../utils/feature-updater');
17
-
18
- const program = new Command();
19
-
20
- /**
21
- * scan_git_history 명령어
22
- * 기존 Git 히스토리를 스캔하여 Feature와 연결
23
- */
24
-
25
- program
26
- .name('scan_git_history')
27
- .description('기존 Git 히스토리를 스캔하여 Feature Ledger와 연결합니다')
28
- .option('-s, --since <date>', '스캔 시작 날짜 (YYYY-MM-DD)', '2025-01-01')
29
- .option('-u, --until <date>', '스캔 종료 날짜 (YYYY-MM-DD)', 'HEAD')
30
- .option('-l, --limit <number>', '스캔할 최대 커밋 수', '1000')
31
- .option('-d, --dry-run', '실제 업데이트 없이 결과만 표시')
32
- .option('-f, --feature <id>', '특정 Feature ID만 스캔')
33
- .option('-c, --create-missing', '누락된 Feature 파일 자동 생성')
34
- .option('-v, --verbose', '상세 로그 출력')
35
- .action(async (options) => {
36
- const spinner = ora();
37
-
38
- try {
39
- // Git 저장소 확인
40
- if (!await isGitRepository()) {
41
- console.error(chalk.red('❌ 현재 디렉토리는 Git 저장소가 아닙니다.'));
42
- process.exit(1);
43
- }
44
-
45
- console.log(chalk.cyan('🔍 Git 히스토리 스캔 시작'));
46
- console.log(chalk.gray(` 기간: ${options.since} ~ ${options.until}`));
47
- console.log(chalk.gray(` 최대 커밋 수: ${options.limit}`));
48
- if (options.dryRun) {
49
- console.log(chalk.yellow(' 🏃 Dry-run 모드 (실제 업데이트 없음)'));
50
- }
51
-
52
- spinner.start('Git 히스토리 분석 중...');
53
-
54
- // Git log 명령어로 커밋 목록 가져오기
55
- let gitCommand = `log --since="${options.since}"`;
56
- if (options.until !== 'HEAD') {
57
- gitCommand += ` --until="${options.until}"`;
58
- }
59
- gitCommand += ` --max-count=${options.limit} --pretty=format:"%H"`;
60
-
61
- if (options.feature) {
62
- gitCommand += ` --grep="${options.feature}"`;
63
- } else {
64
- gitCommand += ` --grep="FL[0-9]\\{3\\}"`;
65
- }
66
-
67
- const commitHashes = await runGitCommand(gitCommand);
68
-
69
- if (!commitHashes) {
70
- spinner.warn(chalk.yellow('Feature 관련 커밋을 찾을 수 없습니다.'));
71
- return;
72
- }
73
-
74
- const hashes = commitHashes.split('\n').filter(Boolean);
75
- spinner.succeed(chalk.green(`${hashes.length}개의 관련 커밋 발견`));
76
-
77
- // 커밋 분석 및 Feature별 그룹화
78
- spinner.start('커밋 분석 중...');
79
- const { getCommitInfo } = require('../utils/git-utils');
80
- const featureCommitMap = {};
81
- const missingFeatures = new Set();
82
-
83
- for (const hash of hashes) {
84
- const commitInfo = await getCommitInfo(hash);
85
-
86
- for (const featureId of commitInfo.featureIds) {
87
- if (options.feature && featureId !== options.feature) {
88
- continue;
89
- }
90
-
91
- if (!featureCommitMap[featureId]) {
92
- featureCommitMap[featureId] = [];
93
- }
94
- featureCommitMap[featureId].push(commitInfo);
95
-
96
- // Feature 파일 존재 확인
97
- const featureFile = await findFeatureFile(featureId);
98
- if (!featureFile) {
99
- missingFeatures.add(featureId);
100
- }
101
- }
102
- }
103
-
104
- spinner.succeed(chalk.green('커밋 분석 완료'));
105
-
106
- // 분석 결과 출력
107
- console.log(chalk.cyan('\n📊 스캔 결과:'));
108
- console.log(chalk.white(` 발견된 Feature: ${Object.keys(featureCommitMap).length}개`));
109
- console.log(chalk.white(` 총 커밋 수: ${hashes.length}개`));
110
-
111
- if (missingFeatures.size > 0) {
112
- console.log(chalk.yellow(` 누락된 Feature: ${missingFeatures.size}개`));
113
- if (options.verbose) {
114
- console.log(chalk.gray(` ${[...missingFeatures].join(', ')}`));
115
- }
116
- }
117
-
118
- // Feature별 상세 정보
119
- console.log(chalk.cyan('\n📋 Feature별 커밋 수:'));
120
- for (const [featureId, commits] of Object.entries(featureCommitMap)) {
121
- const isMissing = missingFeatures.has(featureId);
122
- const statusIcon = isMissing ? '❌' : '✅';
123
- const statusText = isMissing ? ' (파일 없음)' : '';
124
- console.log(chalk.white(` ${statusIcon} ${featureId}: ${commits.length}개 커밋${statusText}`));
125
- }
126
-
127
- // 누락된 Feature 파일 생성 (옵션)
128
- if (options.createMissing && missingFeatures.size > 0 && !options.dryRun) {
129
- console.log(chalk.cyan('\n📝 누락된 Feature 파일 생성 중...'));
130
-
131
- for (const featureId of missingFeatures) {
132
- spinner.start(`${featureId} 파일 생성 중...`);
133
-
134
- try {
135
- // Feature 파일 생성
136
- const featureDir = path.join(process.cwd(), '.aiwf', '06_FEATURE_LEDGERS', 'active');
137
- await fs.mkdir(featureDir, { recursive: true });
138
-
139
- const fileName = `${featureId}_Auto_Generated.md`;
140
- const filePath = path.join(featureDir, fileName);
141
-
142
- const content = `---
143
- feature_id: ${featureId}
144
- title: Auto Generated Feature
145
- status: active
146
- created_date: ${new Date().toISOString().substring(0, 10)}
147
- last_updated: ${new Date().toISOString().replace('T', ' ').substring(0, 16)}
148
- milestone: TBD
149
- sprint_ids: []
150
- tasks: []
151
- assignee: TBD
152
- tags: [auto-generated]
153
- ---
154
-
155
- # Feature: Auto Generated Feature
156
-
157
- ## Overview
158
- 이 Feature는 Git 히스토리 스캔 중 자동으로 생성되었습니다.
159
- 커밋 메시지에서 ${featureId} 참조가 발견되었지만 해당 Feature 파일이 없어 생성되었습니다.
160
-
161
- ## Requirements
162
- - [ ] 실제 요구사항으로 업데이트 필요
163
-
164
- ## Git History
165
- ### Commits
166
- `;
167
-
168
- await fs.writeFile(filePath, content, 'utf8');
169
- spinner.succeed(chalk.green(`✅ ${featureId} 파일 생성 완료`));
170
-
171
- // missingFeatures에서 제거
172
- missingFeatures.delete(featureId);
173
- } catch (error) {
174
- spinner.fail(chalk.red(`❌ ${featureId} 파일 생성 실패: ${error.message}`));
175
- }
176
- }
177
- }
178
-
179
- // Feature 업데이트
180
- if (!options.dryRun) {
181
- console.log(chalk.cyan('\n🔄 Feature 파일 업데이트 중...'));
182
-
183
- let successCount = 0;
184
- let failCount = 0;
185
-
186
- for (const [featureId, commits] of Object.entries(featureCommitMap)) {
187
- if (missingFeatures.has(featureId)) {
188
- continue;
189
- }
190
-
191
- spinner.start(`${featureId} 업데이트 중 (${commits.length}개 커밋)...`);
192
-
193
- try {
194
- const { updateFeatureWithCommits } = require('../utils/feature-updater');
195
- const success = await updateFeatureWithCommits(featureId, commits);
196
-
197
- if (success) {
198
- spinner.succeed(chalk.green(`✅ ${featureId} 업데이트 완료`));
199
- successCount++;
200
- } else {
201
- spinner.fail(chalk.red(`❌ ${featureId} 업데이트 실패`));
202
- failCount++;
203
- }
204
- } catch (error) {
205
- spinner.fail(chalk.red(`❌ ${featureId} 업데이트 중 오류: ${error.message}`));
206
- failCount++;
207
- }
208
- }
209
-
210
- console.log(chalk.cyan('\n✨ 업데이트 완료!'));
211
- console.log(chalk.green(` 성공: ${successCount}개`));
212
- if (failCount > 0) {
213
- console.log(chalk.red(` 실패: ${failCount}개`));
214
- }
215
- }
216
-
217
- // 추가 권장사항
218
- if (missingFeatures.size > 0 && !options.createMissing) {
219
- console.log(chalk.yellow('\n💡 팁:'));
220
- console.log(chalk.gray(' --create-missing 옵션을 사용하면 누락된 Feature 파일을 자동으로 생성할 수 있습니다.'));
221
- }
222
-
223
- } catch (error) {
224
- spinner.fail(chalk.red('스캔 중 오류 발생'));
225
- console.error(chalk.red(error.message));
226
- if (options.verbose) {
227
- console.error(error.stack);
228
- }
229
- process.exit(1);
230
- }
231
- });
232
-
233
- // 명령어 실행
234
- program.parse(process.argv);