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,180 +0,0 @@
1
- /**
2
- * 페르소나 컨텍스트 규칙 파서
3
- * AI 페르소나별 컨텍스트 규칙을 파싱하고 처리하는 모듈
4
- */
5
-
6
- const fs = require('fs').promises;
7
- const path = require('path');
8
- const yaml = require('js-yaml');
9
-
10
- class ContextRuleParser {
11
- constructor() {
12
- this.cache = new Map();
13
- this.lastModifiedTimes = new Map();
14
- }
15
-
16
- /**
17
- * 페르소나 컨텍스트 규칙 파일 파싱
18
- * @param {string} personaName - 페르소나 이름
19
- * @returns {Object} 파싱된 컨텍스트 규칙
20
- */
21
- async parseContextRules(personaName) {
22
- const contextPath = path.join(
23
- process.cwd(),
24
- '.aiwf',
25
- '07_AI_PERSONAS',
26
- personaName,
27
- 'context_rules.md'
28
- );
29
-
30
- try {
31
- // 캐시 확인
32
- const cached = await this.getCachedRules(contextPath);
33
- if (cached) return cached;
34
-
35
- // 파일 읽기
36
- const content = await fs.readFile(contextPath, 'utf-8');
37
-
38
- // YAML frontmatter와 마크다운 본문 분리
39
- const { frontmatter, body } = this.separateFrontmatter(content);
40
-
41
- // 컨텍스트 규칙 구조화
42
- const rules = {
43
- ...frontmatter,
44
- content: body,
45
- parsedAt: new Date().toISOString()
46
- };
47
-
48
- // 캐시 저장
49
- this.cache.set(contextPath, rules);
50
-
51
- return rules;
52
- } catch (error) {
53
- console.error(`컨텍스트 규칙 파싱 오류 (${personaName}):`, error.message);
54
- return this.getDefaultContext(personaName);
55
- }
56
- }
57
-
58
- /**
59
- * YAML frontmatter와 마크다운 본문 분리
60
- * @param {string} content - 전체 파일 내용
61
- * @returns {Object} frontmatter와 body
62
- */
63
- separateFrontmatter(content) {
64
- const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
65
- const match = content.match(frontmatterRegex);
66
-
67
- if (match) {
68
- try {
69
- const frontmatter = yaml.load(match[1]);
70
- const body = match[2].trim();
71
- return { frontmatter, body };
72
- } catch (error) {
73
- console.error('YAML 파싱 오류:', error);
74
- return { frontmatter: {}, body: content };
75
- }
76
- }
77
-
78
- return { frontmatter: {}, body: content };
79
- }
80
-
81
- /**
82
- * 캐시된 규칙 확인 및 반환
83
- * @param {string} filePath - 파일 경로
84
- * @returns {Object|null} 캐시된 규칙 또는 null
85
- */
86
- async getCachedRules(filePath) {
87
- try {
88
- const stats = await fs.stat(filePath);
89
- const lastModified = stats.mtimeMs;
90
-
91
- const cachedTime = this.lastModifiedTimes.get(filePath);
92
- if (cachedTime && cachedTime === lastModified) {
93
- return this.cache.get(filePath);
94
- }
95
-
96
- this.lastModifiedTimes.set(filePath, lastModified);
97
- return null;
98
- } catch {
99
- return null;
100
- }
101
- }
102
-
103
- /**
104
- * 기본 컨텍스트 규칙 반환
105
- * @param {string} personaName - 페르소나 이름
106
- * @returns {Object} 기본 컨텍스트 규칙
107
- */
108
- getDefaultContext(personaName) {
109
- const defaults = {
110
- architect: {
111
- persona_name: 'architect',
112
- analysis_approach: '시스템 설계 중심',
113
- design_principles: ['확장성', '유지보수성', '성능'],
114
- communication_style: '구조적이고 논리적',
115
- content: '아키텍트 페르소나는 시스템 전체 구조를 고려하여 설계합니다.'
116
- },
117
- frontend: {
118
- persona_name: 'frontend',
119
- analysis_approach: 'UI/UX 중심',
120
- design_principles: ['사용성', '접근성', '반응성'],
121
- communication_style: '시각적이고 직관적',
122
- content: '프론트엔드 페르소나는 사용자 경험을 최우선으로 고려합니다.'
123
- },
124
- backend: {
125
- persona_name: 'backend',
126
- analysis_approach: 'API 및 데이터 처리 중심',
127
- design_principles: ['보안', '효율성', '확장성'],
128
- communication_style: '기술적이고 정확함',
129
- content: '백엔드 페르소나는 서버 사이드 로직과 데이터 처리에 집중합니다.'
130
- },
131
- data_analyst: {
132
- persona_name: 'data_analyst',
133
- analysis_approach: '데이터 기반 의사결정',
134
- design_principles: ['정확성', '통찰력', '시각화'],
135
- communication_style: '분석적이고 통계적',
136
- content: '데이터 분석가 페르소나는 데이터를 통한 인사이트 도출에 집중합니다.'
137
- },
138
- security: {
139
- persona_name: 'security',
140
- analysis_approach: '보안 취약점 중심',
141
- design_principles: ['보안성', '무결성', '기밀성'],
142
- communication_style: '신중하고 방어적',
143
- content: '보안 페르소나는 시스템의 보안 측면을 최우선으로 고려합니다.'
144
- }
145
- };
146
-
147
- return defaults[personaName] || {
148
- persona_name: personaName,
149
- analysis_approach: '일반적 접근',
150
- design_principles: ['품질', '효율성'],
151
- communication_style: '표준적',
152
- content: '기본 페르소나 컨텍스트입니다.'
153
- };
154
- }
155
-
156
- /**
157
- * 모든 페르소나의 컨텍스트 규칙 로드
158
- * @returns {Object} 모든 페르소나의 컨텍스트 규칙
159
- */
160
- async loadAllPersonaContexts() {
161
- const personas = ['architect', 'frontend', 'backend', 'data_analyst', 'security'];
162
- const contexts = {};
163
-
164
- for (const persona of personas) {
165
- contexts[persona] = await this.parseContextRules(persona);
166
- }
167
-
168
- return contexts;
169
- }
170
-
171
- /**
172
- * 캐시 초기화
173
- */
174
- clearCache() {
175
- this.cache.clear();
176
- this.lastModifiedTimes.clear();
177
- }
178
- }
179
-
180
- module.exports = ContextRuleParser;
@@ -1,463 +0,0 @@
1
- /**
2
- * 컨텍스트 토큰 모니터
3
- * 페르소나 컨텍스트 주입으로 인한 토큰 사용량을 모니터링하는 모듈
4
- */
5
-
6
- const TokenCounter = require('./token-counter');
7
- const fs = require('fs').promises;
8
- const path = require('path');
9
-
10
- class ContextTokenMonitor {
11
- constructor() {
12
- this.tokenCounter = new TokenCounter();
13
- this.usageHistory = [];
14
- this.personaStats = new Map();
15
- this.thresholds = {
16
- warning: 2000, // 경고 임계값 (토큰)
17
- critical: 3000 // 위험 임계값 (토큰)
18
- };
19
- }
20
-
21
- /**
22
- * 컨텍스트 주입 토큰 사용량 추적
23
- * @param {Object} injectionResult - 프롬프트 주입 결과
24
- * @returns {Object} 토큰 사용량 분석
25
- */
26
- async trackContextUsage(injectionResult) {
27
- const { tokenMetrics, personaName, context } = injectionResult;
28
-
29
- const usage = {
30
- personaName,
31
- timestamp: new Date().toISOString(),
32
- originalTokens: tokenMetrics.originalTokens,
33
- contextTokens: tokenMetrics.contextTokens,
34
- totalTokens: tokenMetrics.injectedTokens,
35
- percentageIncrease: tokenMetrics.percentageIncrease,
36
- contextElements: await this.analyzeContextElements(context),
37
- alert: this.checkThresholds(tokenMetrics.contextTokens)
38
- };
39
-
40
- // 히스토리에 추가
41
- this.usageHistory.push(usage);
42
-
43
- // 페르소나별 통계 업데이트
44
- this.updatePersonaStats(personaName, usage);
45
-
46
- return usage;
47
- }
48
-
49
- /**
50
- * 컨텍스트 요소별 토큰 사용량 분석
51
- * @param {Object} context - 페르소나 컨텍스트
52
- * @returns {Object} 요소별 토큰 사용량
53
- */
54
- async analyzeContextElements(context) {
55
- const elements = {};
56
-
57
- // 각 컨텍스트 요소의 토큰 수 계산
58
- if (context.persona_name) {
59
- elements.persona_name = await this.tokenCounter.estimateTokens(context.persona_name);
60
- }
61
-
62
- if (context.analysis_approach) {
63
- elements.analysis_approach = await this.tokenCounter.estimateTokens(context.analysis_approach);
64
- }
65
-
66
- if (context.design_principles) {
67
- const principlesText = context.design_principles.join(', ');
68
- elements.design_principles = await this.tokenCounter.estimateTokens(principlesText);
69
- }
70
-
71
- if (context.communication_style) {
72
- elements.communication_style = await this.tokenCounter.estimateTokens(context.communication_style);
73
- }
74
-
75
- if (context.content) {
76
- elements.content = await this.tokenCounter.estimateTokens(context.content);
77
- }
78
-
79
- // 총 토큰 수
80
- elements.total = Object.values(elements).reduce((sum, tokens) => sum + tokens, 0);
81
-
82
- return elements;
83
- }
84
-
85
- /**
86
- * 임계값 확인
87
- * @param {number} contextTokens - 컨텍스트 토큰 수
88
- * @returns {Object} 알림 정보
89
- */
90
- checkThresholds(contextTokens) {
91
- if (contextTokens >= this.thresholds.critical) {
92
- return {
93
- level: 'critical',
94
- message: `컨텍스트 토큰이 위험 수준(${this.thresholds.critical})을 초과했습니다: ${contextTokens}`
95
- };
96
- } else if (contextTokens >= this.thresholds.warning) {
97
- return {
98
- level: 'warning',
99
- message: `컨텍스트 토큰이 경고 수준(${this.thresholds.warning})에 도달했습니다: ${contextTokens}`
100
- };
101
- }
102
- return null;
103
- }
104
-
105
- /**
106
- * 페르소나별 통계 업데이트
107
- * @param {string} personaName - 페르소나 이름
108
- * @param {Object} usage - 사용량 정보
109
- */
110
- updatePersonaStats(personaName, usage) {
111
- if (!this.personaStats.has(personaName)) {
112
- this.personaStats.set(personaName, {
113
- totalUsages: 0,
114
- totalContextTokens: 0,
115
- averageContextTokens: 0,
116
- maxContextTokens: 0,
117
- minContextTokens: Infinity,
118
- warningCount: 0,
119
- criticalCount: 0
120
- });
121
- }
122
-
123
- const stats = this.personaStats.get(personaName);
124
-
125
- // 통계 업데이트
126
- stats.totalUsages++;
127
- stats.totalContextTokens += usage.contextTokens;
128
- stats.averageContextTokens = stats.totalContextTokens / stats.totalUsages;
129
- stats.maxContextTokens = Math.max(stats.maxContextTokens, usage.contextTokens);
130
- stats.minContextTokens = Math.min(stats.minContextTokens, usage.contextTokens);
131
-
132
- // 알림 카운트
133
- if (usage.alert) {
134
- if (usage.alert.level === 'warning') stats.warningCount++;
135
- if (usage.alert.level === 'critical') stats.criticalCount++;
136
- }
137
-
138
- this.personaStats.set(personaName, stats);
139
- }
140
-
141
- /**
142
- * 토큰 사용량 리포트 생성
143
- * @param {Object} options - 리포트 옵션
144
- * @returns {Object} 토큰 사용량 리포트
145
- */
146
- generateReport(options = {}) {
147
- const {
148
- startDate = null,
149
- endDate = null,
150
- personaName = null,
151
- format = 'summary'
152
- } = options;
153
-
154
- // 필터링된 데이터 가져오기
155
- let filteredHistory = this.filterHistory(startDate, endDate, personaName);
156
-
157
- if (format === 'detailed') {
158
- return this.generateDetailedReport(filteredHistory);
159
- } else {
160
- return this.generateSummaryReport(filteredHistory);
161
- }
162
- }
163
-
164
- /**
165
- * 히스토리 필터링
166
- * @param {Date} startDate - 시작 날짜
167
- * @param {Date} endDate - 종료 날짜
168
- * @param {string} personaName - 페르소나 이름
169
- * @returns {Array} 필터링된 히스토리
170
- */
171
- filterHistory(startDate, endDate, personaName) {
172
- let filtered = this.usageHistory;
173
-
174
- if (startDate) {
175
- filtered = filtered.filter(u => new Date(u.timestamp) >= startDate);
176
- }
177
-
178
- if (endDate) {
179
- filtered = filtered.filter(u => new Date(u.timestamp) <= endDate);
180
- }
181
-
182
- if (personaName) {
183
- filtered = filtered.filter(u => u.personaName === personaName);
184
- }
185
-
186
- return filtered;
187
- }
188
-
189
- /**
190
- * 요약 리포트 생성
191
- * @param {Array} history - 필터링된 히스토리
192
- * @returns {Object} 요약 리포트
193
- */
194
- generateSummaryReport(history) {
195
- if (history.length === 0) {
196
- return { message: '해당 기간의 데이터가 없습니다.' };
197
- }
198
-
199
- const totalContextTokens = history.reduce((sum, u) => sum + u.contextTokens, 0);
200
- const averageContextTokens = totalContextTokens / history.length;
201
- const maxUsage = history.reduce((max, u) => u.contextTokens > max.contextTokens ? u : max);
202
- const minUsage = history.reduce((min, u) => u.contextTokens < min.contextTokens ? u : min);
203
-
204
- // 페르소나별 집계
205
- const personaSummary = {};
206
- history.forEach(usage => {
207
- if (!personaSummary[usage.personaName]) {
208
- personaSummary[usage.personaName] = {
209
- count: 0,
210
- totalTokens: 0,
211
- alerts: { warning: 0, critical: 0 }
212
- };
213
- }
214
-
215
- personaSummary[usage.personaName].count++;
216
- personaSummary[usage.personaName].totalTokens += usage.contextTokens;
217
-
218
- if (usage.alert) {
219
- personaSummary[usage.personaName].alerts[usage.alert.level]++;
220
- }
221
- });
222
-
223
- return {
224
- period: {
225
- start: history[0].timestamp,
226
- end: history[history.length - 1].timestamp
227
- },
228
- totalUsages: history.length,
229
- tokenStats: {
230
- total: totalContextTokens,
231
- average: Math.round(averageContextTokens),
232
- max: {
233
- tokens: maxUsage.contextTokens,
234
- persona: maxUsage.personaName,
235
- timestamp: maxUsage.timestamp
236
- },
237
- min: {
238
- tokens: minUsage.contextTokens,
239
- persona: minUsage.personaName,
240
- timestamp: minUsage.timestamp
241
- }
242
- },
243
- personaSummary,
244
- alerts: {
245
- total: history.filter(u => u.alert).length,
246
- warning: history.filter(u => u.alert?.level === 'warning').length,
247
- critical: history.filter(u => u.alert?.level === 'critical').length
248
- }
249
- };
250
- }
251
-
252
- /**
253
- * 상세 리포트 생성
254
- * @param {Array} history - 필터링된 히스토리
255
- * @returns {Object} 상세 리포트
256
- */
257
- generateDetailedReport(history) {
258
- const summary = this.generateSummaryReport(history);
259
-
260
- // 시간대별 분석
261
- const hourlyAnalysis = this.analyzeHourlyPattern(history);
262
-
263
- // 컨텍스트 요소별 분석
264
- const elementAnalysis = this.analyzeContextElements(history);
265
-
266
- // 트렌드 분석
267
- const trendAnalysis = this.analyzeTrends(history);
268
-
269
- return {
270
- summary,
271
- hourlyAnalysis,
272
- elementAnalysis,
273
- trendAnalysis,
274
- rawData: history
275
- };
276
- }
277
-
278
- /**
279
- * 시간대별 패턴 분석
280
- * @param {Array} history - 필터링된 히스토리
281
- * @returns {Object} 시간대별 분석
282
- */
283
- analyzeHourlyPattern(history) {
284
- const hourlyData = {};
285
-
286
- history.forEach(usage => {
287
- const hour = new Date(usage.timestamp).getHours();
288
- if (!hourlyData[hour]) {
289
- hourlyData[hour] = {
290
- count: 0,
291
- totalTokens: 0
292
- };
293
- }
294
- hourlyData[hour].count++;
295
- hourlyData[hour].totalTokens += usage.contextTokens;
296
- });
297
-
298
- // 평균 계산
299
- Object.keys(hourlyData).forEach(hour => {
300
- hourlyData[hour].average = hourlyData[hour].totalTokens / hourlyData[hour].count;
301
- });
302
-
303
- return hourlyData;
304
- }
305
-
306
- /**
307
- * 트렌드 분석
308
- * @param {Array} history - 필터링된 히스토리
309
- * @returns {Object} 트렌드 정보
310
- */
311
- analyzeTrends(history) {
312
- if (history.length < 2) {
313
- return { message: '트렌드 분석을 위한 데이터가 부족합니다.' };
314
- }
315
-
316
- // 시간 순 정렬
317
- const sorted = [...history].sort((a, b) =>
318
- new Date(a.timestamp) - new Date(b.timestamp)
319
- );
320
-
321
- // 이동 평균 계산
322
- const windowSize = Math.min(5, Math.floor(sorted.length / 3));
323
- const movingAverages = [];
324
-
325
- for (let i = windowSize - 1; i < sorted.length; i++) {
326
- const window = sorted.slice(i - windowSize + 1, i + 1);
327
- const average = window.reduce((sum, u) => sum + u.contextTokens, 0) / windowSize;
328
- movingAverages.push({
329
- timestamp: sorted[i].timestamp,
330
- average: Math.round(average)
331
- });
332
- }
333
-
334
- // 증가/감소 트렌드 판단
335
- const firstAvg = movingAverages[0].average;
336
- const lastAvg = movingAverages[movingAverages.length - 1].average;
337
- const trendDirection = lastAvg > firstAvg ? 'increasing' :
338
- lastAvg < firstAvg ? 'decreasing' : 'stable';
339
- const trendPercentage = ((lastAvg - firstAvg) / firstAvg * 100).toFixed(2);
340
-
341
- return {
342
- direction: trendDirection,
343
- changePercentage: trendPercentage + '%',
344
- movingAverages,
345
- prediction: this.predictNextUsage(movingAverages)
346
- };
347
- }
348
-
349
- /**
350
- * 다음 사용량 예측
351
- * @param {Array} movingAverages - 이동 평균 데이터
352
- * @returns {number} 예측 토큰 수
353
- */
354
- predictNextUsage(movingAverages) {
355
- if (movingAverages.length < 3) {
356
- return null;
357
- }
358
-
359
- // 간단한 선형 예측
360
- const recent = movingAverages.slice(-3);
361
- const trend = (recent[2].average - recent[0].average) / 2;
362
- const prediction = recent[2].average + trend;
363
-
364
- return Math.round(Math.max(0, prediction));
365
- }
366
-
367
- /**
368
- * 토큰 사용량 최적화 제안
369
- * @param {string} personaName - 페르소나 이름
370
- * @returns {Object} 최적화 제안
371
- */
372
- getOptimizationSuggestions(personaName) {
373
- const stats = this.personaStats.get(personaName);
374
- if (!stats) {
375
- return { message: '해당 페르소나의 사용 기록이 없습니다.' };
376
- }
377
-
378
- const suggestions = [];
379
-
380
- // 평균 토큰 사용량이 높은 경우
381
- if (stats.averageContextTokens > this.thresholds.warning * 0.8) {
382
- suggestions.push({
383
- type: 'high_average',
384
- message: '평균 컨텍스트 토큰 사용량이 높습니다. 컨텍스트 내용을 간소화하는 것을 고려하세요.',
385
- currentAverage: Math.round(stats.averageContextTokens),
386
- recommendedMax: Math.round(this.thresholds.warning * 0.8)
387
- });
388
- }
389
-
390
- // 경고/위험 발생률이 높은 경우
391
- const alertRate = (stats.warningCount + stats.criticalCount) / stats.totalUsages;
392
- if (alertRate > 0.2) {
393
- suggestions.push({
394
- type: 'high_alert_rate',
395
- message: '토큰 한계 초과가 자주 발생합니다. 컨텍스트 압축을 활성화하세요.',
396
- alertRate: (alertRate * 100).toFixed(2) + '%',
397
- warningCount: stats.warningCount,
398
- criticalCount: stats.criticalCount
399
- });
400
- }
401
-
402
- // 최대 사용량이 위험 수준에 근접한 경우
403
- if (stats.maxContextTokens > this.thresholds.critical * 0.9) {
404
- suggestions.push({
405
- type: 'near_critical',
406
- message: '최대 토큰 사용량이 위험 수준에 근접합니다. 긴 컨텍스트를 분할하거나 요약하세요.',
407
- maxTokens: stats.maxContextTokens,
408
- criticalThreshold: this.thresholds.critical
409
- });
410
- }
411
-
412
- return {
413
- personaName,
414
- suggestions,
415
- currentStats: stats
416
- };
417
- }
418
-
419
- /**
420
- * 리포트 파일로 저장
421
- * @param {Object} report - 리포트 데이터
422
- * @param {string} filename - 파일명
423
- */
424
- async saveReport(report, filename) {
425
- const reportsDir = path.join(process.cwd(), '.aiwf', 'reports', 'token-usage');
426
-
427
- // 디렉토리 생성
428
- await fs.mkdir(reportsDir, { recursive: true });
429
-
430
- // 파일 경로
431
- const filePath = path.join(reportsDir, filename);
432
-
433
- // JSON 형식으로 저장
434
- await fs.writeFile(filePath, JSON.stringify(report, null, 2), 'utf-8');
435
-
436
- console.log(`토큰 사용량 리포트 저장: ${filePath}`);
437
- return filePath;
438
- }
439
-
440
- /**
441
- * 임계값 설정
442
- * @param {Object} thresholds - 새로운 임계값
443
- */
444
- setThresholds(thresholds) {
445
- if (thresholds.warning) {
446
- this.thresholds.warning = thresholds.warning;
447
- }
448
- if (thresholds.critical) {
449
- this.thresholds.critical = thresholds.critical;
450
- }
451
- }
452
-
453
- /**
454
- * 모니터링 데이터 초기화
455
- */
456
- reset() {
457
- this.usageHistory = [];
458
- this.personaStats.clear();
459
- console.log('토큰 모니터링 데이터가 초기화되었습니다.');
460
- }
461
- }
462
-
463
- module.exports = ContextTokenMonitor;