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,455 +0,0 @@
1
- import { TokenCounter } from './token-counter.js';
2
-
3
- /**
4
- * 콘텐츠 중복 제거 및 정규화 유틸리티
5
- */
6
- export class ContentNormalizer {
7
- constructor() {
8
- this.tokenCounter = new TokenCounter();
9
- }
10
-
11
- /**
12
- * 콘텐츠를 정규화합니다
13
- * @param {string} content - 정규화할 콘텐츠
14
- * @param {Object} options - 정규화 옵션
15
- * @returns {Object} 정규화 결과
16
- */
17
- normalize(content, options = {}) {
18
- const {
19
- removeDuplicates = true,
20
- normalizeWhitespace = true,
21
- normalizeMarkdown = true,
22
- normalizeLinks = true,
23
- normalizeHeaders = true,
24
- preserveStructure = true
25
- } = options;
26
-
27
- let normalized = content;
28
- const metadata = {
29
- operations: [],
30
- originalTokens: this.tokenCounter.countTokens(content),
31
- removedDuplicates: 0,
32
- normalizedElements: 0
33
- };
34
-
35
- // 1. 중복 제거
36
- if (removeDuplicates) {
37
- const duplicateResult = this.removeDuplicates(normalized);
38
- normalized = duplicateResult.content;
39
- metadata.removedDuplicates = duplicateResult.removedCount;
40
- metadata.operations.push('duplicate_removal');
41
- }
42
-
43
- // 2. 공백 정규화
44
- if (normalizeWhitespace) {
45
- normalized = this.normalizeWhitespace(normalized);
46
- metadata.operations.push('whitespace_normalization');
47
- }
48
-
49
- // 3. 마크다운 정규화
50
- if (normalizeMarkdown) {
51
- normalized = this.normalizeMarkdown(normalized);
52
- metadata.operations.push('markdown_normalization');
53
- }
54
-
55
- // 4. 링크 정규화
56
- if (normalizeLinks) {
57
- normalized = this.normalizeLinks(normalized);
58
- metadata.operations.push('link_normalization');
59
- }
60
-
61
- // 5. 헤더 정규화
62
- if (normalizeHeaders) {
63
- normalized = this.normalizeHeaders(normalized);
64
- metadata.operations.push('header_normalization');
65
- }
66
-
67
- // 6. 구조 보존
68
- if (preserveStructure) {
69
- normalized = this.preserveStructure(normalized);
70
- metadata.operations.push('structure_preservation');
71
- }
72
-
73
- metadata.normalizedTokens = this.tokenCounter.countTokens(normalized);
74
- metadata.compressionRatio = this.calculateCompressionRatio(content, normalized);
75
-
76
- return {
77
- original: content,
78
- normalized,
79
- metadata
80
- };
81
- }
82
-
83
- /**
84
- * 중복 콘텐츠를 제거합니다
85
- * @param {string} content - 콘텐츠
86
- * @returns {Object} 중복 제거 결과
87
- */
88
- removeDuplicates(content) {
89
- const lines = content.split('\n');
90
- const uniqueLines = [];
91
- const seenLines = new Set();
92
- let removedCount = 0;
93
-
94
- // 1. 완전히 동일한 라인 제거
95
- for (const line of lines) {
96
- const normalizedLine = this.normalizeLineForComparison(line);
97
-
98
- if (normalizedLine.trim() === '') {
99
- uniqueLines.push(line); // 빈 라인은 유지
100
- } else if (!seenLines.has(normalizedLine)) {
101
- seenLines.add(normalizedLine);
102
- uniqueLines.push(line);
103
- } else {
104
- removedCount++;
105
- }
106
- }
107
-
108
- // 2. 비슷한 헤더 제거
109
- const dedupedHeaders = this.removeDuplicateHeaders(uniqueLines.join('\n'));
110
- const headerRemovedCount = uniqueLines.length - dedupedHeaders.split('\n').length;
111
- removedCount += headerRemovedCount;
112
-
113
- // 3. 중복 목록 항목 제거
114
- const dedupedLists = this.removeDuplicateListItems(dedupedHeaders);
115
- const listRemovedCount = dedupedHeaders.split('\n').length - dedupedLists.split('\n').length;
116
- removedCount += listRemovedCount;
117
-
118
- // 4. 반복되는 패턴 제거
119
- const dedupedPatterns = this.removeRepeatingPatterns(dedupedLists);
120
- const patternRemovedCount = dedupedLists.split('\n').length - dedupedPatterns.split('\n').length;
121
- removedCount += patternRemovedCount;
122
-
123
- return {
124
- content: dedupedPatterns,
125
- removedCount
126
- };
127
- }
128
-
129
- /**
130
- * 비교를 위해 라인을 정규화합니다
131
- * @param {string} line - 라인
132
- * @returns {string} 정규화된 라인
133
- */
134
- normalizeLineForComparison(line) {
135
- return line
136
- .trim()
137
- .toLowerCase()
138
- .replace(/\s+/g, ' ')
139
- .replace(/[^\w\s가-힣]/g, '');
140
- }
141
-
142
- /**
143
- * 중복 헤더를 제거합니다
144
- * @param {string} content - 콘텐츠
145
- * @returns {string} 중복 헤더가 제거된 콘텐츠
146
- */
147
- removeDuplicateHeaders(content) {
148
- const lines = content.split('\n');
149
- const filteredLines = [];
150
- const seenHeaders = new Set();
151
-
152
- for (const line of lines) {
153
- const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
154
-
155
- if (headerMatch) {
156
- const headerLevel = headerMatch[1].length;
157
- const headerText = headerMatch[2].trim().toLowerCase();
158
- const headerKey = `${headerLevel}:${headerText}`;
159
-
160
- if (!seenHeaders.has(headerKey)) {
161
- seenHeaders.add(headerKey);
162
- filteredLines.push(line);
163
- }
164
- } else {
165
- filteredLines.push(line);
166
- }
167
- }
168
-
169
- return filteredLines.join('\n');
170
- }
171
-
172
- /**
173
- * 중복 목록 항목을 제거합니다
174
- * @param {string} content - 콘텐츠
175
- * @returns {string} 중복 목록 항목이 제거된 콘텐츠
176
- */
177
- removeDuplicateListItems(content) {
178
- const lines = content.split('\n');
179
- const filteredLines = [];
180
- const seenListItems = new Set();
181
-
182
- for (const line of lines) {
183
- const listMatch = line.match(/^\s*([-*+]|\d+\.)\s+(.+)$/);
184
-
185
- if (listMatch) {
186
- const itemText = listMatch[2].trim().toLowerCase();
187
-
188
- if (!seenListItems.has(itemText)) {
189
- seenListItems.add(itemText);
190
- filteredLines.push(line);
191
- }
192
- } else {
193
- filteredLines.push(line);
194
- }
195
- }
196
-
197
- return filteredLines.join('\n');
198
- }
199
-
200
- /**
201
- * 반복되는 패턴을 제거합니다
202
- * @param {string} content - 콘텐츠
203
- * @returns {string} 반복 패턴이 제거된 콘텐츠
204
- */
205
- removeRepeatingPatterns(content) {
206
- let processed = content;
207
-
208
- // 1. 연속된 동일한 문자열 제거
209
- processed = processed.replace(/(.{10,})\1{2,}/g, '$1');
210
-
211
- // 2. 반복되는 구분선 제거
212
- processed = processed.replace(/(-{3,})\n\1(\n\1)*/g, '$1');
213
-
214
- // 3. 반복되는 빈 줄 제거
215
- processed = processed.replace(/\n{4,}/g, '\n\n\n');
216
-
217
- // 4. 반복되는 마크다운 태그 제거
218
- processed = processed.replace(/(\*{1,3})\s*\1+/g, '$1');
219
-
220
- return processed;
221
- }
222
-
223
- /**
224
- * 공백을 정규화합니다
225
- * @param {string} content - 콘텐츠
226
- * @returns {string} 공백이 정규화된 콘텐츠
227
- */
228
- normalizeWhitespace(content) {
229
- return content
230
- // 탭을 스페이스로 변환
231
- .replace(/\t/g, ' ')
232
- // 줄 끝 공백 제거
233
- .replace(/[ \t]+$/gm, '')
234
- // 연속된 스페이스를 하나로 변환 (마크다운 구조 유지)
235
- .replace(/([^\n]) {3,}/g, '$1 ')
236
- // 과도한 빈 줄 제거
237
- .replace(/\n{4,}/g, '\n\n\n')
238
- // 시작과 끝의 불필요한 공백 제거
239
- .replace(/^\s+|\s+$/g, '');
240
- }
241
-
242
- /**
243
- * 마크다운을 정규화합니다
244
- * @param {string} content - 콘텐츠
245
- * @returns {string} 마크다운이 정규화된 콘텐츠
246
- */
247
- normalizeMarkdown(content) {
248
- return content
249
- // 헤더 스타일 통일
250
- .replace(/^(#{1,6})\s*(.+)\s*$/gm, '$1 $2')
251
- // 목록 스타일 통일
252
- .replace(/^\s*[*+]\s+/gm, '- ')
253
- // 강조 스타일 통일
254
- .replace(/\*\*([^*]+)\*\*/g, '**$1**')
255
- .replace(/\*([^*]+)\*/g, '*$1*')
256
- // 코드 블록 스타일 통일
257
- .replace(/```([^`]*?)```/gs, '```$1```')
258
- // 인라인 코드 스타일 통일
259
- .replace(/`([^`]+)`/g, '`$1`')
260
- // 인용구 스타일 통일
261
- .replace(/^\s*>\s*/gm, '> ')
262
- // 수평선 스타일 통일
263
- .replace(/^\s*[-*_]{3,}\s*$/gm, '---');
264
- }
265
-
266
- /**
267
- * 링크를 정규화합니다
268
- * @param {string} content - 콘텐츠
269
- * @returns {string} 링크가 정규화된 콘텐츠
270
- */
271
- normalizeLinks(content) {
272
- // 1. 중복 링크 제거
273
- const linkMap = new Map();
274
- let linkCounter = 0;
275
-
276
- return content
277
- // 마크다운 링크 정규화
278
- .replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, text, url) => {
279
- const normalizedUrl = url.trim();
280
-
281
- if (linkMap.has(normalizedUrl)) {
282
- return linkMap.get(normalizedUrl);
283
- } else {
284
- linkCounter++;
285
- const linkRef = `[${text}][${linkCounter}]`;
286
- linkMap.set(normalizedUrl, linkRef);
287
- return linkRef;
288
- }
289
- })
290
- // 링크 참조 섹션 추가
291
- + (linkMap.size > 0 ? '\n\n' + Array.from(linkMap.entries())
292
- .map(([url], index) => `[${index + 1}]: ${url}`)
293
- .join('\n') : '');
294
- }
295
-
296
- /**
297
- * 헤더를 정규화합니다
298
- * @param {string} content - 콘텐츠
299
- * @returns {string} 헤더가 정규화된 콘텐츠
300
- */
301
- normalizeHeaders(content) {
302
- const lines = content.split('\n');
303
- const normalizedLines = [];
304
- const headerHierarchy = new Map();
305
-
306
- for (const line of lines) {
307
- const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
308
-
309
- if (headerMatch) {
310
- const level = headerMatch[1].length;
311
- const text = headerMatch[2].trim();
312
-
313
- // 헤더 계층 구조 정규화
314
- if (level > 1) {
315
- let parentLevel = level - 1;
316
- while (parentLevel > 0 && !headerHierarchy.has(parentLevel)) {
317
- parentLevel--;
318
- }
319
-
320
- if (parentLevel === 0 && level > 2) {
321
- // 부모 헤더가 없는 경우 레벨 조정
322
- const adjustedLevel = Math.max(1, level - 1);
323
- normalizedLines.push(`${'#'.repeat(adjustedLevel)} ${text}`);
324
- headerHierarchy.set(adjustedLevel, text);
325
- } else {
326
- normalizedLines.push(line);
327
- headerHierarchy.set(level, text);
328
- }
329
- } else {
330
- normalizedLines.push(line);
331
- headerHierarchy.set(level, text);
332
- }
333
- } else {
334
- normalizedLines.push(line);
335
- }
336
- }
337
-
338
- return normalizedLines.join('\n');
339
- }
340
-
341
- /**
342
- * 구조를 보존합니다
343
- * @param {string} content - 콘텐츠
344
- * @returns {string} 구조가 보존된 콘텐츠
345
- */
346
- preserveStructure(content) {
347
- const lines = content.split('\n');
348
- const preservedLines = [];
349
- let inCodeBlock = false;
350
- let codeBlockType = '';
351
-
352
- for (const line of lines) {
353
- // 코드 블록 감지
354
- if (line.startsWith('```')) {
355
- if (!inCodeBlock) {
356
- inCodeBlock = true;
357
- codeBlockType = line.substring(3);
358
- } else if (inCodeBlock && line === '```') {
359
- inCodeBlock = false;
360
- codeBlockType = '';
361
- }
362
- }
363
-
364
- // 코드 블록 내부는 그대로 유지
365
- if (inCodeBlock) {
366
- preservedLines.push(line);
367
- } else {
368
- // 일반 텍스트 처리
369
- if (line.trim() === '') {
370
- // 빈 줄은 컨텍스트에 따라 처리
371
- const lastLine = preservedLines[preservedLines.length - 1];
372
- const nextLineIndex = lines.indexOf(line) + 1;
373
- const nextLine = nextLineIndex < lines.length ? lines[nextLineIndex] : '';
374
-
375
- // 헤더 전후, 목록 전후에는 빈 줄 유지
376
- if (lastLine && (lastLine.match(/^#{1,6}\s+/) || lastLine.match(/^\s*[-*+]\s+/)) ||
377
- nextLine.match(/^#{1,6}\s+/) || nextLine.match(/^\s*[-*+]\s+/)) {
378
- preservedLines.push(line);
379
- } else if (preservedLines.length > 0 && preservedLines[preservedLines.length - 1] !== '') {
380
- preservedLines.push(line);
381
- }
382
- } else {
383
- preservedLines.push(line);
384
- }
385
- }
386
- }
387
-
388
- return preservedLines.join('\n');
389
- }
390
-
391
- /**
392
- * 텍스트 유사성을 계산합니다
393
- * @param {string} text1 - 첫 번째 텍스트
394
- * @param {string} text2 - 두 번째 텍스트
395
- * @returns {number} 유사성 점수 (0-1)
396
- */
397
- calculateTextSimilarity(text1, text2) {
398
- const words1 = new Set(text1.toLowerCase().split(/\s+/));
399
- const words2 = new Set(text2.toLowerCase().split(/\s+/));
400
-
401
- const intersection = new Set([...words1].filter(word => words2.has(word)));
402
- const union = new Set([...words1, ...words2]);
403
-
404
- return intersection.size / union.size;
405
- }
406
-
407
- /**
408
- * 압축률을 계산합니다
409
- * @param {string} original - 원본 콘텐츠
410
- * @param {string} normalized - 정규화된 콘텐츠
411
- * @returns {number} 압축률 (백분율)
412
- */
413
- calculateCompressionRatio(original, normalized) {
414
- const originalTokens = this.tokenCounter.countTokens(original);
415
- const normalizedTokens = this.tokenCounter.countTokens(normalized);
416
-
417
- if (originalTokens === 0) return 0;
418
- return ((originalTokens - normalizedTokens) / originalTokens) * 100;
419
- }
420
-
421
- /**
422
- * 정규화 통계를 생성합니다
423
- * @param {string} original - 원본 콘텐츠
424
- * @param {string} normalized - 정규화된 콘텐츠
425
- * @returns {Object} 통계 정보
426
- */
427
- generateStats(original, normalized) {
428
- const originalLines = original.split('\n');
429
- const normalizedLines = normalized.split('\n');
430
-
431
- return {
432
- originalLines: originalLines.length,
433
- normalizedLines: normalizedLines.length,
434
- linesRemoved: originalLines.length - normalizedLines.length,
435
- originalTokens: this.tokenCounter.countTokens(original),
436
- normalizedTokens: this.tokenCounter.countTokens(normalized),
437
- tokensReduced: this.tokenCounter.countTokens(original) - this.tokenCounter.countTokens(normalized),
438
- compressionRatio: this.calculateCompressionRatio(original, normalized),
439
- originalSize: Buffer.byteLength(original, 'utf8'),
440
- normalizedSize: Buffer.byteLength(normalized, 'utf8'),
441
- sizeReduction: Buffer.byteLength(original, 'utf8') - Buffer.byteLength(normalized, 'utf8')
442
- };
443
- }
444
-
445
- /**
446
- * 리소스를 정리합니다
447
- */
448
- cleanup() {
449
- if (this.tokenCounter) {
450
- this.tokenCounter.cleanup();
451
- }
452
- }
453
- }
454
-
455
- export default ContentNormalizer;