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.
Files changed (140) hide show
  1. package/package.json +3 -2
  2. package/src/cli/index.js +39 -336
  3. package/src/lib/installer.js +310 -90
  4. package/src/utils/messages.js +24 -0
  5. package/src/DEPENDENCY_MAP.md +0 -90
  6. package/src/cli/cache-cli.js +0 -459
  7. package/src/cli/checkpoint-cli.js +0 -417
  8. package/src/cli/language-cli.js +0 -286
  9. package/src/cli/sprint-cli.js +0 -287
  10. package/src/commands/ai-tool.js +0 -383
  11. package/src/commands/compress.js +0 -60
  12. package/src/commands/create-project.js +0 -160
  13. package/src/commands/evaluate.js +0 -459
  14. package/src/commands/persona.js +0 -309
  15. package/src/commands/sprint-task.js +0 -262
  16. package/src/commands/token.js +0 -312
  17. package/src/lib/ai-persona-manager.js +0 -711
  18. package/src/lib/cache-system.js +0 -332
  19. package/src/lib/context-engine.js +0 -568
  20. package/src/lib/github-integration.js +0 -402
  21. package/src/lib/memory-profiler.js +0 -471
  22. package/src/lib/metrics-collector.js +0 -885
  23. package/src/lib/offline-detector.js +0 -386
  24. package/src/lib/resource-loader-enhanced.js +0 -399
  25. package/src/lib/resource-loader.js +0 -250
  26. package/src/lib/resources/commands/ai-persona.js +0 -602
  27. package/src/lib/resources/commands/compress-context.js +0 -389
  28. package/src/lib/resources/commands/evaluate.js +0 -246
  29. package/src/lib/resources/commands/persona-context-apply.js +0 -562
  30. package/src/lib/resources/commands/token-tracking.js +0 -443
  31. package/src/lib/resources/config/commit-patterns.js +0 -103
  32. package/src/lib/resources/config/language.json +0 -6
  33. package/src/lib/resources/personas/PERSONA_INDEX.md +0 -57
  34. package/src/lib/resources/personas/analyst.json +0 -44
  35. package/src/lib/resources/personas/architect/best_practices.md +0 -136
  36. package/src/lib/resources/personas/architect/knowledge_base.md +0 -377
  37. package/src/lib/resources/personas/architect.json +0 -44
  38. package/src/lib/resources/personas/backend/best_practices.md +0 -766
  39. package/src/lib/resources/personas/backend/knowledge_base.md +0 -1070
  40. package/src/lib/resources/personas/data_analyst/best_practices.md +0 -594
  41. package/src/lib/resources/personas/data_analyst/knowledge_base.md +0 -1057
  42. package/src/lib/resources/personas/developer.json +0 -44
  43. package/src/lib/resources/personas/developer.md +0 -37
  44. package/src/lib/resources/personas/evaluation_criteria.json +0 -137
  45. package/src/lib/resources/personas/frontend/best_practices.md +0 -445
  46. package/src/lib/resources/personas/frontend/knowledge_base.md +0 -729
  47. package/src/lib/resources/personas/persona-index.json +0 -31
  48. package/src/lib/resources/personas/reviewer.json +0 -44
  49. package/src/lib/resources/personas/security/best_practices.md +0 -307
  50. package/src/lib/resources/personas/security/knowledge_base.md +0 -498
  51. package/src/lib/resources/personas/tester.json +0 -44
  52. package/src/lib/resources/templates/README.md +0 -78
  53. package/src/lib/resources/templates/api-server/config.json +0 -64
  54. package/src/lib/resources/templates/api-server/template/.aiwf/config.json +0 -56
  55. package/src/lib/resources/templates/api-server/template/.aiwf/feature-ledger.json +0 -42
  56. package/src/lib/resources/templates/api-server/template/.aiwf/personas/backend-engineer.json +0 -40
  57. package/src/lib/resources/templates/api-server/template/.aiwf/scripts/cli.js +0 -111
  58. package/src/lib/resources/templates/api-server/template/.env.example +0 -29
  59. package/src/lib/resources/templates/api-server/template/.eslintrc.json +0 -23
  60. package/src/lib/resources/templates/api-server/template/README.md +0 -171
  61. package/src/lib/resources/templates/api-server/template/jest.config.js +0 -26
  62. package/src/lib/resources/templates/api-server/template/nodemon.json +0 -9
  63. package/src/lib/resources/templates/api-server/template/package.json +0 -61
  64. package/src/lib/resources/templates/api-server/template/src/app.ts +0 -60
  65. package/src/lib/resources/templates/api-server/template/src/config/swagger.ts +0 -38
  66. package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +0 -54
  67. package/src/lib/resources/templates/api-server/template/src/controllers/authController.ts +0 -128
  68. package/src/lib/resources/templates/api-server/template/src/controllers/statusController.ts +0 -28
  69. package/src/lib/resources/templates/api-server/template/src/index.ts +0 -28
  70. package/src/lib/resources/templates/api-server/template/src/middleware/aiwfMiddleware.ts +0 -50
  71. package/src/lib/resources/templates/api-server/template/src/middleware/authMiddleware.ts +0 -41
  72. package/src/lib/resources/templates/api-server/template/src/middleware/errorHandler.ts +0 -42
  73. package/src/lib/resources/templates/api-server/template/src/middleware/notFoundHandler.ts +0 -14
  74. package/src/lib/resources/templates/api-server/template/src/middleware/requestLogger.ts +0 -20
  75. package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +0 -31
  76. package/src/lib/resources/templates/api-server/template/src/routes/index.ts +0 -13
  77. package/src/lib/resources/templates/api-server/template/src/routes/v1/index.ts +0 -67
  78. package/src/lib/resources/templates/api-server/template/src/utils/logger.ts +0 -37
  79. package/src/lib/resources/templates/api-server/template/tests/app.test.ts +0 -40
  80. package/src/lib/resources/templates/api-server/template/tsconfig.json +0 -43
  81. package/src/lib/resources/templates/npm-library/config.json +0 -61
  82. package/src/lib/resources/templates/npm-library/template/LICENSE +0 -21
  83. package/src/lib/resources/templates/npm-library/template/README.md +0 -201
  84. package/src/lib/resources/templates/npm-library/template/package.json +0 -78
  85. package/src/lib/resources/templates/web-app/config.json +0 -53
  86. package/src/lib/resources/templates/web-app/template/.aiwf/config.json +0 -54
  87. package/src/lib/resources/templates/web-app/template/.aiwf/feature-ledger.json +0 -37
  88. package/src/lib/resources/templates/web-app/template/.aiwf/personas/fullstack-developer.json +0 -40
  89. package/src/lib/resources/templates/web-app/template/.aiwf/scripts/cli.js +0 -110
  90. package/src/lib/resources/templates/web-app/template/.eslintrc.cjs +0 -20
  91. package/src/lib/resources/templates/web-app/template/README.md +0 -151
  92. package/src/lib/resources/templates/web-app/template/index.html +0 -14
  93. package/src/lib/resources/templates/web-app/template/package.json +0 -44
  94. package/src/lib/resources/templates/web-app/template/postcss.config.js +0 -6
  95. package/src/lib/resources/templates/web-app/template/public/vite.svg +0 -1
  96. package/src/lib/resources/templates/web-app/template/src/App.tsx +0 -21
  97. package/src/lib/resources/templates/web-app/template/src/components/Layout.tsx +0 -57
  98. package/src/lib/resources/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +0 -107
  99. package/src/lib/resources/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +0 -71
  100. package/src/lib/resources/templates/web-app/template/src/index.css +0 -60
  101. package/src/lib/resources/templates/web-app/template/src/main.tsx +0 -10
  102. package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +0 -57
  103. package/src/lib/resources/templates/web-app/template/src/pages/HomePage.tsx +0 -89
  104. package/src/lib/resources/templates/web-app/template/src/pages/NotFound.tsx +0 -25
  105. package/src/lib/resources/templates/web-app/template/src/stores/aiwfStore.ts +0 -126
  106. package/src/lib/resources/templates/web-app/template/src/types/global.d.ts +0 -9
  107. package/src/lib/resources/templates/web-app/template/src/vite-env.d.ts +0 -1
  108. package/src/lib/resources/templates/web-app/template/tailwind.config.js +0 -30
  109. package/src/lib/resources/templates/web-app/template/tsconfig.json +0 -37
  110. package/src/lib/resources/templates/web-app/template/tsconfig.node.json +0 -10
  111. package/src/lib/resources/templates/web-app/template/vite.config.ts +0 -23
  112. package/src/lib/resources/utils/background-monitor.js +0 -223
  113. package/src/lib/resources/utils/compression-metrics.js +0 -1031
  114. package/src/lib/resources/utils/compression-strategies.js +0 -557
  115. package/src/lib/resources/utils/content-normalizer.js +0 -455
  116. package/src/lib/resources/utils/context-compressor.js +0 -583
  117. package/src/lib/resources/utils/context-rule-parser.js +0 -180
  118. package/src/lib/resources/utils/context-token-monitor.js +0 -463
  119. package/src/lib/resources/utils/context-update-manager.js +0 -502
  120. package/src/lib/resources/utils/git-utils.js +0 -220
  121. package/src/lib/resources/utils/importance-classifier.js +0 -645
  122. package/src/lib/resources/utils/information-filter.js +0 -710
  123. package/src/lib/resources/utils/persona-aware-compressor.js +0 -598
  124. package/src/lib/resources/utils/prompt-injector.js +0 -242
  125. package/src/lib/resources/utils/simplified-evaluator.js +0 -119
  126. package/src/lib/resources/utils/text-summarizer.js +0 -492
  127. package/src/lib/resources/utils/token-counter.js +0 -177
  128. package/src/lib/resources/utils/token-monitor.js +0 -484
  129. package/src/lib/resources/utils/token-reporter.js +0 -452
  130. package/src/lib/resources/utils/token-storage.js +0 -433
  131. package/src/lib/resources/utils/token-tracker.js +0 -283
  132. package/src/lib/state/priority-calculator.js +0 -217
  133. package/src/lib/state/state-index.js +0 -155
  134. package/src/lib/state/task-scanner.js +0 -328
  135. package/src/lib/task-analyzer.js +0 -599
  136. package/src/lib/template-cache-system.js +0 -559
  137. package/src/lib/template-downloader.js +0 -467
  138. package/src/lib/template-version-manager.js +0 -491
  139. package/src/lib/token-optimizer.js +0 -517
  140. package/src/utils/engineering-guard.js +0 -399
@@ -1,452 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { TokenStorage } from './token-storage.js';
4
- import { TokenCounter } from './token-counter.js';
5
-
6
- /**
7
- * 토큰 사용량 보고서를 생성하고 포맷팅하는 클래스
8
- */
9
- export class TokenReporter {
10
- constructor() {
11
- this.storage = new TokenStorage();
12
- this.counter = new TokenCounter();
13
- }
14
-
15
- /**
16
- * 프로젝트 전체 토큰 분석 보고서를 생성합니다
17
- * @param {string} projectPath - 프로젝트 경로
18
- * @returns {Object} 프로젝트 토큰 분석 보고서
19
- */
20
- generateProjectAnalysisReport(projectPath = '.') {
21
- try {
22
- const report = {
23
- generatedAt: new Date().toISOString(),
24
- projectPath,
25
- analysis: {
26
- totalTokens: 0,
27
- fileBreakdown: {},
28
- directoryBreakdown: {},
29
- compressionOpportunities: [],
30
- recommendations: []
31
- }
32
- };
33
-
34
- // 주요 AIWF 디렉토리 분석
35
- const directories = [
36
- '.aiwf/01_PROJECT_DOCS',
37
- '.aiwf/02_REQUIREMENTS',
38
- '.aiwf/03_SPRINTS',
39
- '.aiwf/04_GENERAL_TASKS',
40
- '.aiwf/05_ARCHITECTURAL_DECISIONS',
41
- '.aiwf/10_STATE_OF_PROJECT'
42
- ];
43
-
44
- // 각 디렉토리별 토큰 수 분석
45
- for (const dir of directories) {
46
- const dirPath = path.join(projectPath, dir);
47
- if (fs.existsSync(dirPath)) {
48
- const tokens = this.counter.countDirectoryTokens(dirPath);
49
- report.analysis.directoryBreakdown[dir] = {
50
- tokens,
51
- percentage: 0, // 나중에 계산
52
- files: this.getFileAnalysis(dirPath)
53
- };
54
- report.analysis.totalTokens += tokens;
55
- }
56
- }
57
-
58
- // 퍼센티지 계산
59
- Object.keys(report.analysis.directoryBreakdown).forEach(dir => {
60
- const breakdown = report.analysis.directoryBreakdown[dir];
61
- breakdown.percentage = report.analysis.totalTokens > 0
62
- ? ((breakdown.tokens / report.analysis.totalTokens) * 100).toFixed(1)
63
- : 0;
64
- });
65
-
66
- // 압축 기회 식별
67
- report.analysis.compressionOpportunities = this.identifyCompressionOpportunities(
68
- report.analysis.directoryBreakdown
69
- );
70
-
71
- // 권장사항 생성
72
- report.analysis.recommendations = this.generateRecommendations(
73
- report.analysis.totalTokens,
74
- report.analysis.compressionOpportunities
75
- );
76
-
77
- return report;
78
- } catch (error) {
79
- console.error('프로젝트 분석 보고서 생성 중 오류 발생:', error);
80
- return null;
81
- }
82
- }
83
-
84
- /**
85
- * 디렉토리 내 파일별 토큰 분석을 수행합니다
86
- * @param {string} dirPath - 디렉토리 경로
87
- * @returns {Array<Object>} 파일별 분석 결과
88
- */
89
- getFileAnalysis(dirPath) {
90
- try {
91
- const files = this.counter.getMarkdownFiles(dirPath);
92
- const analysis = [];
93
-
94
- for (const file of files) {
95
- const tokens = this.counter.countFileTokens(file);
96
- const relativePath = path.relative(process.cwd(), file);
97
-
98
- analysis.push({
99
- path: relativePath,
100
- tokens,
101
- size: fs.statSync(file).size,
102
- tokensPerByte: tokens > 0 ? (tokens / fs.statSync(file).size).toFixed(3) : 0
103
- });
104
- }
105
-
106
- return analysis.sort((a, b) => b.tokens - a.tokens);
107
- } catch (error) {
108
- console.error('파일 분석 중 오류 발생:', error);
109
- return [];
110
- }
111
- }
112
-
113
- /**
114
- * 압축 기회를 식별합니다
115
- * @param {Object} directoryBreakdown - 디렉토리별 분석 결과
116
- * @returns {Array<Object>} 압축 기회 목록
117
- */
118
- identifyCompressionOpportunities(directoryBreakdown) {
119
- const opportunities = [];
120
-
121
- try {
122
- // 대용량 디렉토리 식별
123
- const largeDirectories = Object.entries(directoryBreakdown)
124
- .filter(([_, data]) => data.tokens > 5000)
125
- .sort((a, b) => b[1].tokens - a[1].tokens);
126
-
127
- for (const [dir, data] of largeDirectories) {
128
- opportunities.push({
129
- type: 'large_directory',
130
- target: dir,
131
- currentTokens: data.tokens,
132
- description: `대용량 디렉토리 (${data.tokens} 토큰)`,
133
- potentialSavings: Math.round(data.tokens * 0.3),
134
- priority: data.tokens > 10000 ? 'high' : 'medium'
135
- });
136
- }
137
-
138
- // 반복 콘텐츠 식별
139
- opportunities.push({
140
- type: 'repetitive_content',
141
- target: 'all',
142
- description: '반복되는 템플릿 및 보일러플레이트 콘텐츠',
143
- potentialSavings: Math.round(Object.values(directoryBreakdown).reduce((sum, data) => sum + data.tokens, 0) * 0.15),
144
- priority: 'medium'
145
- });
146
-
147
- // 완료된 태스크 압축
148
- if (directoryBreakdown['.aiwf/05_COMPLETED_TASKS']) {
149
- opportunities.push({
150
- type: 'completed_tasks',
151
- target: '.aiwf/05_COMPLETED_TASKS',
152
- currentTokens: directoryBreakdown['.aiwf/05_COMPLETED_TASKS'].tokens,
153
- description: '완료된 태스크 아카이브',
154
- potentialSavings: Math.round(directoryBreakdown['.aiwf/05_COMPLETED_TASKS'].tokens * 0.6),
155
- priority: 'low'
156
- });
157
- }
158
- } catch (error) {
159
- console.error('압축 기회 식별 중 오류 발생:', error);
160
- }
161
-
162
- return opportunities.sort((a, b) => {
163
- const priorityOrder = { high: 3, medium: 2, low: 1 };
164
- return priorityOrder[b.priority] - priorityOrder[a.priority];
165
- });
166
- }
167
-
168
- /**
169
- * 권장사항을 생성합니다
170
- * @param {number} totalTokens - 총 토큰 수
171
- * @param {Array<Object>} opportunities - 압축 기회 목록
172
- * @returns {Array<Object>} 권장사항 목록
173
- */
174
- generateRecommendations(totalTokens, opportunities) {
175
- const recommendations = [];
176
-
177
- try {
178
- // 총 토큰 수에 따른 권장사항
179
- if (totalTokens > 50000) {
180
- recommendations.push({
181
- type: 'aggressive_compression',
182
- priority: 'high',
183
- title: '적극적 압축 모드 권장',
184
- description: `프로젝트 크기가 ${totalTokens.toLocaleString()} 토큰으로 매우 큽니다. 적극적 압축 모드를 사용하여 50-70% 압축을 권장합니다.`,
185
- action: 'aiwf compress --mode aggressive --level 4'
186
- });
187
- } else if (totalTokens > 20000) {
188
- recommendations.push({
189
- type: 'balanced_compression',
190
- priority: 'medium',
191
- title: '균형 압축 모드 권장',
192
- description: `프로젝트 크기가 ${totalTokens.toLocaleString()} 토큰입니다. 균형 압축 모드를 사용하여 30-50% 압축을 권장합니다.`,
193
- action: 'aiwf compress --mode balanced --level 3'
194
- });
195
- }
196
-
197
- // 압축 기회 기반 권장사항
198
- const highPriorityOpportunities = opportunities.filter(op => op.priority === 'high');
199
- if (highPriorityOpportunities.length > 0) {
200
- recommendations.push({
201
- type: 'targeted_compression',
202
- priority: 'high',
203
- title: '특정 디렉토리 압축 권장',
204
- description: `${highPriorityOpportunities.length}개의 대용량 디렉토리가 식별되었습니다.`,
205
- action: 'aiwf compress --target-directories'
206
- });
207
- }
208
-
209
- // 정기적 정리 권장사항
210
- recommendations.push({
211
- type: 'regular_cleanup',
212
- priority: 'low',
213
- title: '정기적 프로젝트 정리',
214
- description: '오래된 로그, 완료된 태스크 등을 정기적으로 정리하여 토큰 사용량을 최적화하세요.',
215
- action: 'aiwf cleanup --older-than 30d'
216
- });
217
-
218
- // 토큰 모니터링 권장사항
219
- recommendations.push({
220
- type: 'monitoring',
221
- priority: 'medium',
222
- title: '토큰 사용량 모니터링 설정',
223
- description: '토큰 사용량을 실시간으로 모니터링하고 임계값을 설정하세요.',
224
- action: 'aiwf token-monitor --enable --threshold 10000'
225
- });
226
- } catch (error) {
227
- console.error('권장사항 생성 중 오류 발생:', error);
228
- }
229
-
230
- return recommendations.sort((a, b) => {
231
- const priorityOrder = { high: 3, medium: 2, low: 1 };
232
- return priorityOrder[b.priority] - priorityOrder[a.priority];
233
- });
234
- }
235
-
236
- /**
237
- * 사용량 보고서를 생성합니다
238
- * @param {string} type - 보고서 타입
239
- * @returns {Object} 사용량 보고서
240
- */
241
- generateUsageReport(type = 'daily') {
242
- try {
243
- const report = this.storage.generateUsageReport(type);
244
-
245
- if (!report) {
246
- return null;
247
- }
248
-
249
- // 보고서 포맷팅
250
- const formattedReport = {
251
- ...report,
252
- formatted: {
253
- summary: this.formatSummary(report.summary),
254
- dailyUsage: this.formatDailyUsage(report.dailyUsage),
255
- topCommands: this.formatTopCommands(report.topCommands),
256
- compressionStats: this.formatCompressionStats(report.compressionStats),
257
- trends: this.formatTrends(report.trends)
258
- }
259
- };
260
-
261
- return formattedReport;
262
- } catch (error) {
263
- console.error('사용량 보고서 생성 중 오류 발생:', error);
264
- return null;
265
- }
266
- }
267
-
268
- /**
269
- * 요약 정보를 포맷팅합니다
270
- * @param {Object} summary - 요약 정보
271
- * @returns {Object} 포맷팅된 요약 정보
272
- */
273
- formatSummary(summary) {
274
- return {
275
- totalTokens: summary.totalTokens.toLocaleString(),
276
- totalSessions: summary.totalSessions.toLocaleString(),
277
- avgTokensPerSession: summary.avgTokensPerSession.toLocaleString(),
278
- efficiency: summary.avgTokensPerSession > 1000 ? 'High' :
279
- summary.avgTokensPerSession > 500 ? 'Medium' : 'Low'
280
- };
281
- }
282
-
283
- /**
284
- * 일일 사용량을 포맷팅합니다
285
- * @param {Object} dailyUsage - 일일 사용량
286
- * @returns {Array<Object>} 포맷팅된 일일 사용량
287
- */
288
- formatDailyUsage(dailyUsage) {
289
- return Object.entries(dailyUsage)
290
- .map(([date, usage]) => ({
291
- date,
292
- tokens: usage.tokens.toLocaleString(),
293
- sessions: usage.sessions,
294
- commands: usage.commands,
295
- avgTokensPerSession: usage.sessions > 0 ?
296
- Math.round(usage.tokens / usage.sessions).toLocaleString() : '0'
297
- }))
298
- .sort((a, b) => new Date(b.date) - new Date(a.date));
299
- }
300
-
301
- /**
302
- * 상위 명령어를 포맷팅합니다
303
- * @param {Array<Object>} topCommands - 상위 명령어 목록
304
- * @returns {Array<Object>} 포맷팅된 상위 명령어 목록
305
- */
306
- formatTopCommands(topCommands) {
307
- return topCommands.map(cmd => ({
308
- command: cmd.command,
309
- count: cmd.count.toLocaleString(),
310
- totalTokens: cmd.totalTokens.toLocaleString(),
311
- avgTokens: cmd.avgTokens.toLocaleString(),
312
- efficiency: cmd.avgTokens > 1000 ? 'High' :
313
- cmd.avgTokens > 500 ? 'Medium' : 'Low'
314
- }));
315
- }
316
-
317
- /**
318
- * 압축 통계를 포맷팅합니다
319
- * @param {Object} compressionStats - 압축 통계
320
- * @returns {Object} 포맷팅된 압축 통계
321
- */
322
- formatCompressionStats(compressionStats) {
323
- return {
324
- totalOriginal: compressionStats.totalOriginal.toLocaleString(),
325
- totalCompressed: compressionStats.totalCompressed.toLocaleString(),
326
- totalSaved: compressionStats.totalSaved.toLocaleString(),
327
- avgRatio: compressionStats.avgRatio.toFixed(1) + '%',
328
- efficiency: compressionStats.avgRatio > 50 ? 'High' :
329
- compressionStats.avgRatio > 25 ? 'Medium' : 'Low'
330
- };
331
- }
332
-
333
- /**
334
- * 트렌드를 포맷팅합니다
335
- * @param {Object} trends - 트렌드 정보
336
- * @returns {Object} 포맷팅된 트렌드 정보
337
- */
338
- formatTrends(trends) {
339
- const trendEmoji = {
340
- increasing: '📈',
341
- decreasing: '📉',
342
- stable: '➡️',
343
- insufficient_data: '❓',
344
- error: '❌'
345
- };
346
-
347
- return {
348
- trend: trends.trend,
349
- emoji: trendEmoji[trends.trend] || '❓',
350
- change: trends.change > 0 ? `+${trends.change}%` : `${trends.change}%`,
351
- recentAvg: trends.recentAvg?.toLocaleString() || '0',
352
- olderAvg: trends.olderAvg?.toLocaleString() || '0',
353
- description: this.getTrendDescription(trends.trend, trends.change)
354
- };
355
- }
356
-
357
- /**
358
- * 트렌드 설명을 생성합니다
359
- * @param {string} trend - 트렌드
360
- * @param {number} change - 변화율
361
- * @returns {string} 트렌드 설명
362
- */
363
- getTrendDescription(trend, change) {
364
- switch (trend) {
365
- case 'increasing':
366
- return `토큰 사용량이 ${Math.abs(change)}% 증가하고 있습니다`;
367
- case 'decreasing':
368
- return `토큰 사용량이 ${Math.abs(change)}% 감소하고 있습니다`;
369
- case 'stable':
370
- return '토큰 사용량이 안정적입니다';
371
- case 'insufficient_data':
372
- return '분석할 데이터가 부족합니다';
373
- default:
374
- return '트렌드 분석에 오류가 발생했습니다';
375
- }
376
- }
377
-
378
- /**
379
- * 보고서를 콘솔에 출력합니다
380
- * @param {Object} report - 보고서 데이터
381
- */
382
- printReport(report) {
383
- try {
384
- console.log('\n=== 토큰 사용량 보고서 ===');
385
- console.log(`생성일시: ${new Date(report.generatedAt).toLocaleString()}`);
386
- console.log(`보고서 타입: ${report.type}`);
387
- console.log(`기간: ${report.period.start} ~ ${report.period.end}`);
388
-
389
- if (report.formatted) {
390
- console.log('\n--- 요약 ---');
391
- console.log(`총 토큰: ${report.formatted.summary.totalTokens}`);
392
- console.log(`총 세션: ${report.formatted.summary.totalSessions}`);
393
- console.log(`세션당 평균 토큰: ${report.formatted.summary.avgTokensPerSession}`);
394
- console.log(`효율성: ${report.formatted.summary.efficiency}`);
395
-
396
- console.log('\n--- 트렌드 ---');
397
- console.log(`${report.formatted.trends.emoji} ${report.formatted.trends.description}`);
398
- console.log(`변화율: ${report.formatted.trends.change}`);
399
-
400
- if (report.formatted.topCommands.length > 0) {
401
- console.log('\n--- 상위 명령어 ---');
402
- report.formatted.topCommands.slice(0, 5).forEach((cmd, idx) => {
403
- console.log(`${idx + 1}. ${cmd.command}: ${cmd.totalTokens} 토큰 (${cmd.count}회)`);
404
- });
405
- }
406
-
407
- if (report.formatted.compressionStats.totalOriginal > 0) {
408
- console.log('\n--- 압축 통계 ---');
409
- console.log(`원본: ${report.formatted.compressionStats.totalOriginal} 토큰`);
410
- console.log(`압축: ${report.formatted.compressionStats.totalCompressed} 토큰`);
411
- console.log(`절약: ${report.formatted.compressionStats.totalSaved} 토큰`);
412
- console.log(`압축률: ${report.formatted.compressionStats.avgRatio}`);
413
- }
414
- }
415
-
416
- console.log('\n========================\n');
417
- } catch (error) {
418
- console.error('보고서 출력 중 오류 발생:', error);
419
- }
420
- }
421
-
422
- /**
423
- * 보고서를 파일로 저장합니다
424
- * @param {Object} report - 보고서 데이터
425
- * @param {string} filename - 파일명
426
- */
427
- saveReportToFile(report, filename) {
428
- try {
429
- const reportDir = '.aiwf/token-data/reports';
430
- if (!fs.existsSync(reportDir)) {
431
- fs.mkdirSync(reportDir, { recursive: true });
432
- }
433
-
434
- const filepath = path.join(reportDir, filename);
435
- fs.writeFileSync(filepath, JSON.stringify(report, null, 2));
436
- console.log(`보고서가 ${filepath}에 저장되었습니다.`);
437
- } catch (error) {
438
- console.error('보고서 저장 중 오류 발생:', error);
439
- }
440
- }
441
-
442
- /**
443
- * 리소스를 정리합니다
444
- */
445
- cleanup() {
446
- if (this.counter) {
447
- this.counter.cleanup();
448
- }
449
- }
450
- }
451
-
452
- export default TokenReporter;