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,492 +0,0 @@
1
- import { TokenCounter } from './token-counter.js';
2
-
3
- /**
4
- * 텍스트 요약 알고리즘 프로토타입
5
- */
6
- export class TextSummarizer {
7
- constructor() {
8
- this.tokenCounter = new TokenCounter();
9
- }
10
-
11
- /**
12
- * 텍스트를 요약합니다
13
- * @param {string} text - 요약할 텍스트
14
- * @param {Object} options - 요약 옵션
15
- * @returns {Object} 요약 결과
16
- */
17
- summarize(text, options = {}) {
18
- const {
19
- targetRatio = 0.3, // 목표 압축률 (30%)
20
- strategy = 'extractive', // 'extractive' 또는 'abstractive'
21
- preserveStructure = true, // 구조 유지 여부
22
- minSentenceLength = 20, // 최소 문장 길이
23
- maxSentenceLength = 200 // 최대 문장 길이
24
- } = options;
25
-
26
- const summary = strategy === 'extractive'
27
- ? this.extractiveSummarization(text, targetRatio, options)
28
- : this.abstractiveSummarization(text, targetRatio, options);
29
-
30
- return {
31
- original: text,
32
- summary: summary.content,
33
- originalTokens: this.tokenCounter.countTokens(text),
34
- summaryTokens: this.tokenCounter.countTokens(summary.content),
35
- compressionRatio: summary.compressionRatio,
36
- keyPoints: summary.keyPoints,
37
- metadata: summary.metadata
38
- };
39
- }
40
-
41
- /**
42
- * 추출적 요약 (중요한 문장들을 선택)
43
- * @param {string} text - 요약할 텍스트
44
- * @param {number} targetRatio - 목표 압축률
45
- * @param {Object} options - 옵션
46
- * @returns {Object} 요약 결과
47
- */
48
- extractiveSummarization(text, targetRatio, options) {
49
- // 1. 텍스트를 문장으로 분할
50
- const sentences = this.splitIntoSentences(text);
51
-
52
- // 2. 각 문장의 중요도 계산
53
- const scoredSentences = this.scoreSentences(sentences);
54
-
55
- // 3. 중요도에 따라 정렬
56
- const sortedSentences = scoredSentences.sort((a, b) => b.score - a.score);
57
-
58
- // 4. 목표 비율에 맞춰 상위 문장 선택
59
- const targetSentenceCount = Math.ceil(sentences.length * targetRatio);
60
- const selectedSentences = sortedSentences.slice(0, targetSentenceCount);
61
-
62
- // 5. 원본 순서대로 재배열
63
- const finalSentences = selectedSentences.sort((a, b) => a.index - b.index);
64
-
65
- // 6. 요약 텍스트 생성
66
- const summaryText = finalSentences.map(s => s.text).join(' ');
67
-
68
- return {
69
- content: summaryText,
70
- compressionRatio: this.calculateCompressionRatio(text, summaryText),
71
- keyPoints: this.extractKeyPoints(finalSentences),
72
- metadata: {
73
- strategy: 'extractive',
74
- originalSentences: sentences.length,
75
- selectedSentences: finalSentences.length,
76
- avgSentenceScore: finalSentences.reduce((sum, s) => sum + s.score, 0) / finalSentences.length
77
- }
78
- };
79
- }
80
-
81
- /**
82
- * 추상적 요약 (내용을 재구성)
83
- * @param {string} text - 요약할 텍스트
84
- * @param {number} targetRatio - 목표 압축률
85
- * @param {Object} options - 옵션
86
- * @returns {Object} 요약 결과
87
- */
88
- abstractiveSummarization(text, targetRatio, options) {
89
- // 1. 주요 개념 추출
90
- const concepts = this.extractConcepts(text);
91
-
92
- // 2. 핵심 정보 식별
93
- const keyInfo = this.identifyKeyInformation(text);
94
-
95
- // 3. 구조 분석
96
- const structure = this.analyzeStructure(text);
97
-
98
- // 4. 요약 생성
99
- const summaryText = this.generateAbstractSummary(concepts, keyInfo, structure, targetRatio);
100
-
101
- return {
102
- content: summaryText,
103
- compressionRatio: this.calculateCompressionRatio(text, summaryText),
104
- keyPoints: concepts.slice(0, 5), // 상위 5개 개념
105
- metadata: {
106
- strategy: 'abstractive',
107
- conceptCount: concepts.length,
108
- keyInfoCount: keyInfo.length,
109
- structureElements: structure.length
110
- }
111
- };
112
- }
113
-
114
- /**
115
- * 텍스트를 문장으로 분할합니다
116
- * @param {string} text - 분할할 텍스트
117
- * @returns {Array<string>} 문장 배열
118
- */
119
- splitIntoSentences(text) {
120
- // 마크다운 구조 유지하면서 문장 분할
121
- const sentences = [];
122
- const lines = text.split('\n');
123
-
124
- for (const line of lines) {
125
- if (line.trim() === '') continue;
126
-
127
- // 헤더, 목록 항목은 그대로 유지
128
- if (line.match(/^#{1,6}\s+/) || line.match(/^\s*[-*+]\s+/) || line.match(/^\s*\d+\.\s+/)) {
129
- sentences.push(line.trim());
130
- } else {
131
- // 일반 텍스트는 문장으로 분할
132
- const lineSentences = line.split(/[.!?]+/).filter(s => s.trim().length > 0);
133
- sentences.push(...lineSentences.map(s => s.trim() + '.'));
134
- }
135
- }
136
-
137
- return sentences.filter(s => s.length > 10); // 너무 짧은 문장 제거
138
- }
139
-
140
- /**
141
- * 문장들의 중요도를 계산합니다
142
- * @param {Array<string>} sentences - 문장 배열
143
- * @returns {Array<Object>} 점수가 매겨진 문장 배열
144
- */
145
- scoreSentences(sentences) {
146
- const scoredSentences = [];
147
-
148
- // 키워드 빈도 계산
149
- const wordFreq = this.calculateWordFrequency(sentences.join(' '));
150
-
151
- // 중요 키워드 정의
152
- const importantKeywords = [
153
- 'goal', 'objective', 'requirement', 'critical', 'important', 'key', 'main',
154
- 'primary', 'essential', 'must', 'should', 'need', 'implement', 'develop',
155
- 'create', 'build', 'design', 'task', 'subtask', 'acceptance', 'criteria',
156
- '목표', '목적', '요구사항', '중요', '핵심', '주요', '필수', '구현', '개발',
157
- '생성', '구축', '설계', '태스크', '하위', '승인', '기준'
158
- ];
159
-
160
- sentences.forEach((sentence, index) => {
161
- let score = 0;
162
-
163
- // 1. 문장 길이 점수 (너무 짧거나 긴 문장 페널티)
164
- const length = sentence.length;
165
- if (length >= 30 && length <= 150) {
166
- score += 1;
167
- } else if (length < 30) {
168
- score -= 0.5;
169
- }
170
-
171
- // 2. 중요 키워드 점수
172
- const lowerSentence = sentence.toLowerCase();
173
- importantKeywords.forEach(keyword => {
174
- if (lowerSentence.includes(keyword.toLowerCase())) {
175
- score += 2;
176
- }
177
- });
178
-
179
- // 3. 단어 빈도 점수
180
- const words = sentence.toLowerCase().split(/\s+/);
181
- words.forEach(word => {
182
- if (wordFreq[word] && wordFreq[word] > 1) {
183
- score += Math.log(wordFreq[word]);
184
- }
185
- });
186
-
187
- // 4. 위치 점수 (시작과 끝 부분 문장에 높은 점수)
188
- if (index < sentences.length * 0.3) {
189
- score += 1; // 시작 부분
190
- }
191
- if (index > sentences.length * 0.7) {
192
- score += 0.5; // 끝 부분
193
- }
194
-
195
- // 5. 구조적 중요도 (헤더, 목록 항목)
196
- if (sentence.match(/^#{1,6}\s+/) || sentence.match(/^\s*[-*+]\s+/)) {
197
- score += 3;
198
- }
199
-
200
- // 6. 숫자 포함 점수 (통계, 메트릭 등)
201
- if (sentence.match(/\d+/)) {
202
- score += 0.5;
203
- }
204
-
205
- scoredSentences.push({
206
- text: sentence,
207
- score,
208
- index,
209
- length
210
- });
211
- });
212
-
213
- return scoredSentences;
214
- }
215
-
216
- /**
217
- * 단어 빈도를 계산합니다
218
- * @param {string} text - 분석할 텍스트
219
- * @returns {Object} 단어 빈도 객체
220
- */
221
- calculateWordFrequency(text) {
222
- const words = text.toLowerCase().split(/\s+/);
223
- const frequency = {};
224
-
225
- // 불용어 제거
226
- const stopWords = new Set([
227
- 'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of',
228
- 'with', 'by', 'from', 'up', 'about', 'into', 'through', 'during', 'before',
229
- 'after', 'above', 'below', 'between', 'among', 'is', 'are', 'was', 'were',
230
- 'be', 'been', 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will',
231
- 'would', 'should', 'could', 'can', 'may', 'might', 'must', 'this', 'that',
232
- 'these', 'those', 'i', 'you', 'he', 'she', 'it', 'we', 'they', 'me', 'him',
233
- 'her', 'us', 'them', 'my', 'your', 'his', 'its', 'our', 'their',
234
- '이', '그', '저', '이것', '그것', '저것', '이거', '그거', '저거', '여기',
235
- '거기', '저기', '이곳', '그곳', '저곳', '는', '은', '을', '를', '이',
236
- '가', '와', '과', '에', '에서', '으로', '로', '의', '도', '만', '부터',
237
- '까지', '처럼', '같이', '보다', '한테', '에게', '께', '한', '하나',
238
- '두', '세', '네', '다섯', '여섯', '일곱', '여덟', '아홉', '열'
239
- ]);
240
-
241
- words.forEach(word => {
242
- const cleanWord = word.replace(/[^\w가-힣]/g, '');
243
- if (cleanWord.length > 2 && !stopWords.has(cleanWord)) {
244
- frequency[cleanWord] = (frequency[cleanWord] || 0) + 1;
245
- }
246
- });
247
-
248
- return frequency;
249
- }
250
-
251
- /**
252
- * 핵심 포인트를 추출합니다
253
- * @param {Array<Object>} sentences - 선택된 문장들
254
- * @returns {Array<string>} 핵심 포인트 배열
255
- */
256
- extractKeyPoints(sentences) {
257
- const keyPoints = [];
258
-
259
- sentences.forEach(sentence => {
260
- const text = sentence.text;
261
-
262
- // 목표, 요구사항, 기준 등 핵심 정보 추출
263
- if (text.includes('goal') || text.includes('objective') || text.includes('목표')) {
264
- keyPoints.push(`목표: ${text.replace(/^[#\s*-]+/, '').trim()}`);
265
- } else if (text.includes('requirement') || text.includes('요구사항')) {
266
- keyPoints.push(`요구사항: ${text.replace(/^[#\s*-]+/, '').trim()}`);
267
- } else if (text.includes('criteria') || text.includes('기준')) {
268
- keyPoints.push(`기준: ${text.replace(/^[#\s*-]+/, '').trim()}`);
269
- } else if (text.includes('task') || text.includes('태스크')) {
270
- keyPoints.push(`태스크: ${text.replace(/^[#\s*-]+/, '').trim()}`);
271
- }
272
- });
273
-
274
- return keyPoints.slice(0, 5); // 상위 5개만 반환
275
- }
276
-
277
- /**
278
- * 주요 개념을 추출합니다
279
- * @param {string} text - 분석할 텍스트
280
- * @returns {Array<Object>} 개념 배열
281
- */
282
- extractConcepts(text) {
283
- const concepts = [];
284
- const lines = text.split('\n');
285
-
286
- for (const line of lines) {
287
- // 헤더에서 개념 추출
288
- const headerMatch = line.match(/^#{1,6}\s+(.+)$/);
289
- if (headerMatch) {
290
- concepts.push({
291
- text: headerMatch[1],
292
- type: 'header',
293
- importance: 6 - line.indexOf('#')
294
- });
295
- }
296
-
297
- // 목록 항목에서 개념 추출
298
- const listMatch = line.match(/^\s*[-*+]\s+(.+)$/);
299
- if (listMatch) {
300
- concepts.push({
301
- text: listMatch[1],
302
- type: 'list_item',
303
- importance: 2
304
- });
305
- }
306
- }
307
-
308
- return concepts.sort((a, b) => b.importance - a.importance);
309
- }
310
-
311
- /**
312
- * 핵심 정보를 식별합니다
313
- * @param {string} text - 분석할 텍스트
314
- * @returns {Array<Object>} 핵심 정보 배열
315
- */
316
- identifyKeyInformation(text) {
317
- const keyInfo = [];
318
- const sentences = this.splitIntoSentences(text);
319
-
320
- const keyPatterns = [
321
- /goal|objective|목표|목적/i,
322
- /requirement|요구사항|조건/i,
323
- /criteria|기준|조건/i,
324
- /task|태스크|작업/i,
325
- /deadline|마감일|기한/i,
326
- /important|중요|핵심/i,
327
- /critical|중대|치명적/i,
328
- /must|필수|반드시/i
329
- ];
330
-
331
- sentences.forEach(sentence => {
332
- keyPatterns.forEach(pattern => {
333
- if (pattern.test(sentence)) {
334
- keyInfo.push({
335
- text: sentence,
336
- pattern: pattern.source,
337
- importance: this.calculateImportanceScore(sentence)
338
- });
339
- }
340
- });
341
- });
342
-
343
- return keyInfo.sort((a, b) => b.importance - a.importance);
344
- }
345
-
346
- /**
347
- * 구조를 분석합니다
348
- * @param {string} text - 분석할 텍스트
349
- * @returns {Array<Object>} 구조 요소 배열
350
- */
351
- analyzeStructure(text) {
352
- const structure = [];
353
- const lines = text.split('\n');
354
-
355
- for (let i = 0; i < lines.length; i++) {
356
- const line = lines[i];
357
-
358
- // 헤더 구조
359
- const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
360
- if (headerMatch) {
361
- structure.push({
362
- type: 'header',
363
- level: headerMatch[1].length,
364
- text: headerMatch[2],
365
- line: i
366
- });
367
- }
368
-
369
- // 목록 구조
370
- const listMatch = line.match(/^\s*([-*+]|\d+\.)\s+(.+)$/);
371
- if (listMatch) {
372
- structure.push({
373
- type: 'list',
374
- marker: listMatch[1],
375
- text: listMatch[2],
376
- line: i
377
- });
378
- }
379
-
380
- // 코드 블록
381
- if (line.match(/^```/)) {
382
- structure.push({
383
- type: 'code_block',
384
- line: i
385
- });
386
- }
387
- }
388
-
389
- return structure;
390
- }
391
-
392
- /**
393
- * 추상적 요약을 생성합니다
394
- * @param {Array<Object>} concepts - 개념 배열
395
- * @param {Array<Object>} keyInfo - 핵심 정보 배열
396
- * @param {Array<Object>} structure - 구조 배열
397
- * @param {number} targetRatio - 목표 비율
398
- * @returns {string} 요약 텍스트
399
- */
400
- generateAbstractSummary(concepts, keyInfo, structure, targetRatio) {
401
- const summary = [];
402
-
403
- // 1. 주요 개념 요약
404
- const topConcepts = concepts.slice(0, Math.ceil(concepts.length * targetRatio));
405
- summary.push('## 주요 개념');
406
- topConcepts.forEach(concept => {
407
- summary.push(`- ${concept.text}`);
408
- });
409
-
410
- // 2. 핵심 정보 요약
411
- const topKeyInfo = keyInfo.slice(0, Math.ceil(keyInfo.length * targetRatio));
412
- if (topKeyInfo.length > 0) {
413
- summary.push('\n## 핵심 정보');
414
- topKeyInfo.forEach(info => {
415
- summary.push(`- ${info.text}`);
416
- });
417
- }
418
-
419
- // 3. 구조적 요약
420
- const headers = structure.filter(s => s.type === 'header' && s.level <= 3);
421
- if (headers.length > 0) {
422
- summary.push('\n## 구조');
423
- headers.forEach(header => {
424
- summary.push(`${'#'.repeat(header.level + 1)} ${header.text}`);
425
- });
426
- }
427
-
428
- return summary.join('\n');
429
- }
430
-
431
- /**
432
- * 중요도 점수를 계산합니다
433
- * @param {string} text - 분석할 텍스트
434
- * @returns {number} 중요도 점수
435
- */
436
- calculateImportanceScore(text) {
437
- let score = 0;
438
- const lowerText = text.toLowerCase();
439
-
440
- // 키워드 기반 점수
441
- const keywordWeights = {
442
- 'critical': 5, 'important': 4, 'key': 3, 'main': 3, 'primary': 3,
443
- 'essential': 4, 'must': 4, 'should': 3, 'need': 2, 'goal': 4,
444
- 'objective': 4, 'requirement': 4, 'criteria': 3, 'task': 2,
445
- '중요': 4, '핵심': 4, '주요': 3, '필수': 4, '목표': 4, '목적': 4,
446
- '요구사항': 4, '기준': 3, '태스크': 2, '반드시': 4
447
- };
448
-
449
- Object.entries(keywordWeights).forEach(([keyword, weight]) => {
450
- if (lowerText.includes(keyword)) {
451
- score += weight;
452
- }
453
- });
454
-
455
- // 길이 기반 점수
456
- if (text.length > 50 && text.length < 200) {
457
- score += 1;
458
- }
459
-
460
- // 구두점 기반 점수
461
- if (text.includes('!') || text.includes('?')) {
462
- score += 1;
463
- }
464
-
465
- return score;
466
- }
467
-
468
- /**
469
- * 압축률을 계산합니다
470
- * @param {string} original - 원본 텍스트
471
- * @param {string} compressed - 압축된 텍스트
472
- * @returns {number} 압축률 (백분율)
473
- */
474
- calculateCompressionRatio(original, compressed) {
475
- const originalTokens = this.tokenCounter.countTokens(original);
476
- const compressedTokens = this.tokenCounter.countTokens(compressed);
477
-
478
- if (originalTokens === 0) return 0;
479
- return ((originalTokens - compressedTokens) / originalTokens) * 100;
480
- }
481
-
482
- /**
483
- * 리소스를 정리합니다
484
- */
485
- cleanup() {
486
- if (this.tokenCounter) {
487
- this.tokenCounter.cleanup();
488
- }
489
- }
490
- }
491
-
492
- export default TextSummarizer;
@@ -1,177 +0,0 @@
1
- import tiktoken from 'tiktoken';
2
- import fs from 'fs';
3
- import path from 'path';
4
-
5
- /**
6
- * 토큰 카운팅 및 분석을 위한 유틸리티 클래스
7
- */
8
- export class TokenCounter {
9
- constructor(model = 'gpt-4') {
10
- this.model = model;
11
- this.encoder = tiktoken.get_encoding('cl100k_base'); // GPT-4 기본 인코딩
12
- }
13
-
14
- /**
15
- * 텍스트의 토큰 수를 계산합니다
16
- * @param {string} text - 분석할 텍스트
17
- * @returns {number} 토큰 수
18
- */
19
- countTokens(text) {
20
- if (!text || typeof text !== 'string') {
21
- return 0;
22
- }
23
-
24
- try {
25
- return this.encoder.encode(text).length;
26
- } catch (error) {
27
- console.error('토큰 카운팅 중 오류 발생:', error);
28
- return 0;
29
- }
30
- }
31
-
32
- /**
33
- * 파일의 토큰 수를 계산합니다
34
- * @param {string} filePath - 파일 경로
35
- * @returns {number} 토큰 수
36
- */
37
- countFileTokens(filePath) {
38
- try {
39
- if (!fs.existsSync(filePath)) {
40
- return 0;
41
- }
42
-
43
- const content = fs.readFileSync(filePath, 'utf8');
44
- return this.countTokens(content);
45
- } catch (error) {
46
- console.error(`파일 토큰 카운팅 중 오류 발생 (${filePath}):`, error);
47
- return 0;
48
- }
49
- }
50
-
51
- /**
52
- * 디렉토리의 모든 마크다운 파일 토큰 수를 계산합니다
53
- * @param {string} dirPath - 디렉토리 경로
54
- * @returns {number} 총 토큰 수
55
- */
56
- countDirectoryTokens(dirPath) {
57
- try {
58
- const files = this.getMarkdownFiles(dirPath);
59
- return files.reduce((total, file) => {
60
- return total + this.countFileTokens(file);
61
- }, 0);
62
- } catch (error) {
63
- console.error(`디렉토리 토큰 카운팅 중 오류 발생 (${dirPath}):`, error);
64
- return 0;
65
- }
66
- }
67
-
68
- /**
69
- * 디렉토리에서 마크다운 파일 목록을 가져옵니다
70
- * @param {string} dirPath - 디렉토리 경로
71
- * @returns {Array<string>} 마크다운 파일 경로 배열
72
- */
73
- getMarkdownFiles(dirPath) {
74
- const files = [];
75
-
76
- try {
77
- if (!fs.existsSync(dirPath)) {
78
- return files;
79
- }
80
-
81
- const entries = fs.readdirSync(dirPath, { withFileTypes: true });
82
-
83
- for (const entry of entries) {
84
- const fullPath = path.join(dirPath, entry.name);
85
-
86
- if (entry.isDirectory()) {
87
- files.push(...this.getMarkdownFiles(fullPath));
88
- } else if (entry.isFile() && entry.name.endsWith('.md')) {
89
- files.push(fullPath);
90
- }
91
- }
92
- } catch (error) {
93
- console.error(`마크다운 파일 탐색 중 오류 발생 (${dirPath}):`, error);
94
- }
95
-
96
- return files;
97
- }
98
-
99
- /**
100
- * 콘텐츠의 토큰 분포를 분석합니다
101
- * @param {string} content - 분석할 콘텐츠
102
- * @returns {Array<Object>} 섹션별 토큰 분포 정보
103
- */
104
- analyzeTokenDistribution(content) {
105
- try {
106
- const sections = this.parseSections(content);
107
- const totalTokens = this.countTokens(content);
108
-
109
- return sections.map(section => ({
110
- name: section.name,
111
- tokens: this.countTokens(section.content),
112
- percentage: totalTokens > 0 ? ((this.countTokens(section.content) / totalTokens) * 100).toFixed(1) : 0,
113
- content: section.content
114
- }));
115
- } catch (error) {
116
- console.error('토큰 분포 분석 중 오류 발생:', error);
117
- return [];
118
- }
119
- }
120
-
121
- /**
122
- * 마크다운 콘텐츠를 섹션별로 파싱합니다
123
- * @param {string} content - 마크다운 콘텐츠
124
- * @returns {Array<Object>} 섹션 정보 배열
125
- */
126
- parseSections(content) {
127
- const sections = [];
128
- const lines = content.split('\n');
129
- let currentSection = null;
130
- let currentContent = [];
131
-
132
- for (const line of lines) {
133
- // 헤더 라인 감지 (# ## ### 등)
134
- const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
135
-
136
- if (headerMatch) {
137
- // 이전 섹션 저장
138
- if (currentSection) {
139
- sections.push({
140
- name: currentSection,
141
- content: currentContent.join('\n').trim()
142
- });
143
- }
144
-
145
- // 새 섹션 시작
146
- currentSection = headerMatch[2];
147
- currentContent = [line];
148
- } else {
149
- // 현재 섹션에 라인 추가
150
- if (currentSection) {
151
- currentContent.push(line);
152
- }
153
- }
154
- }
155
-
156
- // 마지막 섹션 저장
157
- if (currentSection) {
158
- sections.push({
159
- name: currentSection,
160
- content: currentContent.join('\n').trim()
161
- });
162
- }
163
-
164
- return sections;
165
- }
166
-
167
- /**
168
- * 토큰 카운터 리소스를 정리합니다
169
- */
170
- cleanup() {
171
- if (this.encoder) {
172
- this.encoder.free();
173
- }
174
- }
175
- }
176
-
177
- export default TokenCounter;