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.
- package/package.json +3 -2
- package/src/cli/index.js +39 -336
- package/src/lib/installer.js +310 -90
- package/src/utils/messages.js +24 -0
- package/src/DEPENDENCY_MAP.md +0 -90
- package/src/cli/cache-cli.js +0 -459
- package/src/cli/checkpoint-cli.js +0 -417
- package/src/cli/language-cli.js +0 -286
- package/src/cli/sprint-cli.js +0 -287
- package/src/commands/ai-tool.js +0 -383
- package/src/commands/compress.js +0 -60
- package/src/commands/create-project.js +0 -160
- package/src/commands/evaluate.js +0 -459
- package/src/commands/persona.js +0 -309
- package/src/commands/sprint-task.js +0 -262
- package/src/commands/token.js +0 -312
- package/src/lib/ai-persona-manager.js +0 -711
- package/src/lib/cache-system.js +0 -332
- package/src/lib/context-engine.js +0 -568
- package/src/lib/github-integration.js +0 -402
- package/src/lib/memory-profiler.js +0 -471
- package/src/lib/metrics-collector.js +0 -885
- package/src/lib/offline-detector.js +0 -386
- package/src/lib/resource-loader-enhanced.js +0 -399
- package/src/lib/resource-loader.js +0 -250
- package/src/lib/resources/commands/ai-persona.js +0 -602
- package/src/lib/resources/commands/compress-context.js +0 -389
- package/src/lib/resources/commands/evaluate.js +0 -246
- package/src/lib/resources/commands/persona-context-apply.js +0 -562
- package/src/lib/resources/commands/token-tracking.js +0 -443
- package/src/lib/resources/config/commit-patterns.js +0 -103
- package/src/lib/resources/config/language.json +0 -6
- package/src/lib/resources/personas/PERSONA_INDEX.md +0 -57
- package/src/lib/resources/personas/analyst.json +0 -44
- package/src/lib/resources/personas/architect/best_practices.md +0 -136
- package/src/lib/resources/personas/architect/knowledge_base.md +0 -377
- package/src/lib/resources/personas/architect.json +0 -44
- package/src/lib/resources/personas/backend/best_practices.md +0 -766
- package/src/lib/resources/personas/backend/knowledge_base.md +0 -1070
- package/src/lib/resources/personas/data_analyst/best_practices.md +0 -594
- package/src/lib/resources/personas/data_analyst/knowledge_base.md +0 -1057
- package/src/lib/resources/personas/developer.json +0 -44
- package/src/lib/resources/personas/developer.md +0 -37
- package/src/lib/resources/personas/evaluation_criteria.json +0 -137
- package/src/lib/resources/personas/frontend/best_practices.md +0 -445
- package/src/lib/resources/personas/frontend/knowledge_base.md +0 -729
- package/src/lib/resources/personas/persona-index.json +0 -31
- package/src/lib/resources/personas/reviewer.json +0 -44
- package/src/lib/resources/personas/security/best_practices.md +0 -307
- package/src/lib/resources/personas/security/knowledge_base.md +0 -498
- package/src/lib/resources/personas/tester.json +0 -44
- package/src/lib/resources/templates/README.md +0 -78
- package/src/lib/resources/templates/api-server/config.json +0 -64
- package/src/lib/resources/templates/api-server/template/.aiwf/config.json +0 -56
- package/src/lib/resources/templates/api-server/template/.aiwf/feature-ledger.json +0 -42
- package/src/lib/resources/templates/api-server/template/.aiwf/personas/backend-engineer.json +0 -40
- package/src/lib/resources/templates/api-server/template/.aiwf/scripts/cli.js +0 -111
- package/src/lib/resources/templates/api-server/template/.env.example +0 -29
- package/src/lib/resources/templates/api-server/template/.eslintrc.json +0 -23
- package/src/lib/resources/templates/api-server/template/README.md +0 -171
- package/src/lib/resources/templates/api-server/template/jest.config.js +0 -26
- package/src/lib/resources/templates/api-server/template/nodemon.json +0 -9
- package/src/lib/resources/templates/api-server/template/package.json +0 -61
- package/src/lib/resources/templates/api-server/template/src/app.ts +0 -60
- package/src/lib/resources/templates/api-server/template/src/config/swagger.ts +0 -38
- package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +0 -54
- package/src/lib/resources/templates/api-server/template/src/controllers/authController.ts +0 -128
- package/src/lib/resources/templates/api-server/template/src/controllers/statusController.ts +0 -28
- package/src/lib/resources/templates/api-server/template/src/index.ts +0 -28
- package/src/lib/resources/templates/api-server/template/src/middleware/aiwfMiddleware.ts +0 -50
- package/src/lib/resources/templates/api-server/template/src/middleware/authMiddleware.ts +0 -41
- package/src/lib/resources/templates/api-server/template/src/middleware/errorHandler.ts +0 -42
- package/src/lib/resources/templates/api-server/template/src/middleware/notFoundHandler.ts +0 -14
- package/src/lib/resources/templates/api-server/template/src/middleware/requestLogger.ts +0 -20
- package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +0 -31
- package/src/lib/resources/templates/api-server/template/src/routes/index.ts +0 -13
- package/src/lib/resources/templates/api-server/template/src/routes/v1/index.ts +0 -67
- package/src/lib/resources/templates/api-server/template/src/utils/logger.ts +0 -37
- package/src/lib/resources/templates/api-server/template/tests/app.test.ts +0 -40
- package/src/lib/resources/templates/api-server/template/tsconfig.json +0 -43
- package/src/lib/resources/templates/npm-library/config.json +0 -61
- package/src/lib/resources/templates/npm-library/template/LICENSE +0 -21
- package/src/lib/resources/templates/npm-library/template/README.md +0 -201
- package/src/lib/resources/templates/npm-library/template/package.json +0 -78
- package/src/lib/resources/templates/web-app/config.json +0 -53
- package/src/lib/resources/templates/web-app/template/.aiwf/config.json +0 -54
- package/src/lib/resources/templates/web-app/template/.aiwf/feature-ledger.json +0 -37
- package/src/lib/resources/templates/web-app/template/.aiwf/personas/fullstack-developer.json +0 -40
- package/src/lib/resources/templates/web-app/template/.aiwf/scripts/cli.js +0 -110
- package/src/lib/resources/templates/web-app/template/.eslintrc.cjs +0 -20
- package/src/lib/resources/templates/web-app/template/README.md +0 -151
- package/src/lib/resources/templates/web-app/template/index.html +0 -14
- package/src/lib/resources/templates/web-app/template/package.json +0 -44
- package/src/lib/resources/templates/web-app/template/postcss.config.js +0 -6
- package/src/lib/resources/templates/web-app/template/public/vite.svg +0 -1
- package/src/lib/resources/templates/web-app/template/src/App.tsx +0 -21
- package/src/lib/resources/templates/web-app/template/src/components/Layout.tsx +0 -57
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +0 -107
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +0 -71
- package/src/lib/resources/templates/web-app/template/src/index.css +0 -60
- package/src/lib/resources/templates/web-app/template/src/main.tsx +0 -10
- package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +0 -57
- package/src/lib/resources/templates/web-app/template/src/pages/HomePage.tsx +0 -89
- package/src/lib/resources/templates/web-app/template/src/pages/NotFound.tsx +0 -25
- package/src/lib/resources/templates/web-app/template/src/stores/aiwfStore.ts +0 -126
- package/src/lib/resources/templates/web-app/template/src/types/global.d.ts +0 -9
- package/src/lib/resources/templates/web-app/template/src/vite-env.d.ts +0 -1
- package/src/lib/resources/templates/web-app/template/tailwind.config.js +0 -30
- package/src/lib/resources/templates/web-app/template/tsconfig.json +0 -37
- package/src/lib/resources/templates/web-app/template/tsconfig.node.json +0 -10
- package/src/lib/resources/templates/web-app/template/vite.config.ts +0 -23
- package/src/lib/resources/utils/background-monitor.js +0 -223
- package/src/lib/resources/utils/compression-metrics.js +0 -1031
- package/src/lib/resources/utils/compression-strategies.js +0 -557
- package/src/lib/resources/utils/content-normalizer.js +0 -455
- package/src/lib/resources/utils/context-compressor.js +0 -583
- package/src/lib/resources/utils/context-rule-parser.js +0 -180
- package/src/lib/resources/utils/context-token-monitor.js +0 -463
- package/src/lib/resources/utils/context-update-manager.js +0 -502
- package/src/lib/resources/utils/git-utils.js +0 -220
- package/src/lib/resources/utils/importance-classifier.js +0 -645
- package/src/lib/resources/utils/information-filter.js +0 -710
- package/src/lib/resources/utils/persona-aware-compressor.js +0 -598
- package/src/lib/resources/utils/prompt-injector.js +0 -242
- package/src/lib/resources/utils/simplified-evaluator.js +0 -119
- package/src/lib/resources/utils/text-summarizer.js +0 -492
- package/src/lib/resources/utils/token-counter.js +0 -177
- package/src/lib/resources/utils/token-monitor.js +0 -484
- package/src/lib/resources/utils/token-reporter.js +0 -452
- package/src/lib/resources/utils/token-storage.js +0 -433
- package/src/lib/resources/utils/token-tracker.js +0 -283
- package/src/lib/state/priority-calculator.js +0 -217
- package/src/lib/state/state-index.js +0 -155
- package/src/lib/state/task-scanner.js +0 -328
- package/src/lib/task-analyzer.js +0 -599
- package/src/lib/template-cache-system.js +0 -559
- package/src/lib/template-downloader.js +0 -467
- package/src/lib/template-version-manager.js +0 -491
- package/src/lib/token-optimizer.js +0 -517
- package/src/utils/engineering-guard.js +0 -399
|
@@ -1,557 +0,0 @@
|
|
|
1
|
-
import { TokenCounter } from './token-counter.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* 압축 전략의 기본 인터페이스
|
|
5
|
-
*/
|
|
6
|
-
export class CompressionStrategy {
|
|
7
|
-
constructor(name) {
|
|
8
|
-
this.name = name;
|
|
9
|
-
this.tokenCounter = new TokenCounter();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* 콘텐츠를 압축합니다
|
|
14
|
-
* @param {string} content - 압축할 콘텐츠
|
|
15
|
-
* @param {Object} options - 압축 옵션
|
|
16
|
-
* @returns {Object} 압축 결과
|
|
17
|
-
*/
|
|
18
|
-
compress(content, options = {}) {
|
|
19
|
-
throw new Error('compress method must be implemented');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 압축된 콘텐츠를 복원합니다
|
|
24
|
-
* @param {string} compressed - 압축된 콘텐츠
|
|
25
|
-
* @param {Object} metadata - 복원 메타데이터
|
|
26
|
-
* @returns {string} 복원된 콘텐츠
|
|
27
|
-
*/
|
|
28
|
-
decompress(compressed, metadata) {
|
|
29
|
-
throw new Error('decompress method must be implemented');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* 압축률을 계산합니다
|
|
34
|
-
* @param {string} original - 원본 콘텐츠
|
|
35
|
-
* @param {string} compressed - 압축된 콘텐츠
|
|
36
|
-
* @returns {number} 압축률 (백분율)
|
|
37
|
-
*/
|
|
38
|
-
calculateCompressionRatio(original, compressed) {
|
|
39
|
-
const originalTokens = this.tokenCounter.countTokens(original);
|
|
40
|
-
const compressedTokens = this.tokenCounter.countTokens(compressed);
|
|
41
|
-
|
|
42
|
-
if (originalTokens === 0) return 0;
|
|
43
|
-
return ((originalTokens - compressedTokens) / originalTokens) * 100;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* 마크다운 콘텐츠를 섹션으로 파싱합니다
|
|
48
|
-
* @param {string} content - 마크다운 콘텐츠
|
|
49
|
-
* @returns {Array<Object>} 섹션 배열
|
|
50
|
-
*/
|
|
51
|
-
parseMarkdownSections(content) {
|
|
52
|
-
const sections = [];
|
|
53
|
-
const lines = content.split('\n');
|
|
54
|
-
let currentSection = null;
|
|
55
|
-
let currentContent = [];
|
|
56
|
-
let currentLevel = 0;
|
|
57
|
-
|
|
58
|
-
for (const line of lines) {
|
|
59
|
-
const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
60
|
-
|
|
61
|
-
if (headerMatch) {
|
|
62
|
-
// 이전 섹션 저장
|
|
63
|
-
if (currentSection) {
|
|
64
|
-
sections.push({
|
|
65
|
-
name: currentSection,
|
|
66
|
-
level: currentLevel,
|
|
67
|
-
content: currentContent.join('\n').trim(),
|
|
68
|
-
tokens: this.tokenCounter.countTokens(currentContent.join('\n').trim())
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// 새 섹션 시작
|
|
73
|
-
currentLevel = headerMatch[1].length;
|
|
74
|
-
currentSection = headerMatch[2];
|
|
75
|
-
currentContent = [line];
|
|
76
|
-
} else {
|
|
77
|
-
if (currentSection) {
|
|
78
|
-
currentContent.push(line);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// 마지막 섹션 저장
|
|
84
|
-
if (currentSection) {
|
|
85
|
-
sections.push({
|
|
86
|
-
name: currentSection,
|
|
87
|
-
level: currentLevel,
|
|
88
|
-
content: currentContent.join('\n').trim(),
|
|
89
|
-
tokens: this.tokenCounter.countTokens(currentContent.join('\n').trim())
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return sections;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* 중요도를 기반으로 섹션을 분류합니다
|
|
98
|
-
* @param {Array<Object>} sections - 섹션 배열
|
|
99
|
-
* @returns {Array<Object>} 중요도가 추가된 섹션 배열
|
|
100
|
-
*/
|
|
101
|
-
classifyImportance(sections) {
|
|
102
|
-
const importanceKeywords = {
|
|
103
|
-
critical: ['urgent', 'critical', 'blocking', 'error', 'failed', 'in_progress', 'active'],
|
|
104
|
-
high: ['important', 'priority', 'deadline', 'milestone', 'requirements', 'goal'],
|
|
105
|
-
medium: ['enhancement', 'improvement', 'optimize', 'planned', 'task'],
|
|
106
|
-
low: ['note', 'example', 'history', 'log', 'template', 'completed']
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const importanceSections = {
|
|
110
|
-
critical: ['subtasks', 'acceptance criteria', 'goal', 'objectives', 'current sprint'],
|
|
111
|
-
high: ['description', 'requirements', 'architecture', 'specifications'],
|
|
112
|
-
medium: ['implementation', 'testing', 'documentation', 'notes'],
|
|
113
|
-
low: ['output log', 'history', 'examples', 'templates']
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
return sections.map(section => {
|
|
117
|
-
let importance = 'low';
|
|
118
|
-
let score = 0;
|
|
119
|
-
|
|
120
|
-
// 키워드 기반 점수 계산
|
|
121
|
-
for (const [level, keywords] of Object.entries(importanceKeywords)) {
|
|
122
|
-
const matches = keywords.filter(keyword =>
|
|
123
|
-
section.content.toLowerCase().includes(keyword.toLowerCase())
|
|
124
|
-
).length;
|
|
125
|
-
|
|
126
|
-
if (matches > 0) {
|
|
127
|
-
switch (level) {
|
|
128
|
-
case 'critical': score += matches * 4; break;
|
|
129
|
-
case 'high': score += matches * 3; break;
|
|
130
|
-
case 'medium': score += matches * 2; break;
|
|
131
|
-
case 'low': score += matches * 1; break;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// 섹션 타입 기반 점수 계산
|
|
137
|
-
for (const [level, sectionTypes] of Object.entries(importanceSections)) {
|
|
138
|
-
const isImportantSection = sectionTypes.some(type =>
|
|
139
|
-
section.name.toLowerCase().includes(type.toLowerCase())
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
if (isImportantSection) {
|
|
143
|
-
switch (level) {
|
|
144
|
-
case 'critical': score += 5; break;
|
|
145
|
-
case 'high': score += 4; break;
|
|
146
|
-
case 'medium': score += 3; break;
|
|
147
|
-
case 'low': score += 1; break;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// 점수를 카테고리로 변환
|
|
153
|
-
if (score >= 8) importance = 'critical';
|
|
154
|
-
else if (score >= 5) importance = 'high';
|
|
155
|
-
else if (score >= 3) importance = 'medium';
|
|
156
|
-
else importance = 'low';
|
|
157
|
-
|
|
158
|
-
return {
|
|
159
|
-
...section,
|
|
160
|
-
importance,
|
|
161
|
-
score
|
|
162
|
-
};
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* 반복적인 콘텐츠를 식별합니다
|
|
168
|
-
* @param {string} content - 콘텐츠
|
|
169
|
-
* @returns {Array<Object>} 반복 패턴 배열
|
|
170
|
-
*/
|
|
171
|
-
identifyRepetitiveContent(content) {
|
|
172
|
-
const repetitivePatterns = [
|
|
173
|
-
/---\n[\s\S]*?\n---/g, // Front matter
|
|
174
|
-
/\*\*.*?\*\*:/g, // Bold labels
|
|
175
|
-
/\[.*?\]\(.*?\)/g, // Markdown links
|
|
176
|
-
/```[\s\S]*?```/g, // Code blocks
|
|
177
|
-
/^\s*[-*+]\s+/gm, // List items
|
|
178
|
-
/^\s*\d+\.\s+/gm, // Numbered lists
|
|
179
|
-
/#{1,6}\s+/g, // Headers
|
|
180
|
-
];
|
|
181
|
-
|
|
182
|
-
const patterns = [];
|
|
183
|
-
|
|
184
|
-
for (const pattern of repetitivePatterns) {
|
|
185
|
-
const matches = content.match(pattern);
|
|
186
|
-
if (matches && matches.length > 3) {
|
|
187
|
-
patterns.push({
|
|
188
|
-
pattern: pattern.source,
|
|
189
|
-
occurrences: matches.length,
|
|
190
|
-
examples: matches.slice(0, 3)
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return patterns;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* 오래된 로그를 식별합니다
|
|
200
|
-
* @param {string} content - 콘텐츠
|
|
201
|
-
* @returns {Array<string>} 오래된 로그 라인
|
|
202
|
-
*/
|
|
203
|
-
identifyOldLogs(content) {
|
|
204
|
-
const lines = content.split('\n');
|
|
205
|
-
const oldLogs = [];
|
|
206
|
-
const cutoffDate = new Date();
|
|
207
|
-
cutoffDate.setDate(cutoffDate.getDate() - 7); // 7일 전
|
|
208
|
-
|
|
209
|
-
for (const line of lines) {
|
|
210
|
-
// 날짜 패턴 매칭
|
|
211
|
-
const dateMatch = line.match(/\[(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})\]/);
|
|
212
|
-
if (dateMatch) {
|
|
213
|
-
const logDate = new Date(dateMatch[1]);
|
|
214
|
-
if (logDate < cutoffDate) {
|
|
215
|
-
oldLogs.push(line);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return oldLogs;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* 리소스를 정리합니다
|
|
225
|
-
*/
|
|
226
|
-
cleanup() {
|
|
227
|
-
if (this.tokenCounter) {
|
|
228
|
-
this.tokenCounter.cleanup();
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* 적극적 압축 전략 (50-70% 압축)
|
|
235
|
-
*/
|
|
236
|
-
export class AggressiveCompressionStrategy extends CompressionStrategy {
|
|
237
|
-
constructor() {
|
|
238
|
-
super('aggressive');
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
compress(content, options = {}) {
|
|
242
|
-
let compressed = content;
|
|
243
|
-
const metadata = {
|
|
244
|
-
strategy: 'aggressive',
|
|
245
|
-
removedSections: [],
|
|
246
|
-
compressionLevel: 'high',
|
|
247
|
-
preservedSections: []
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
// 1. 섹션 분석
|
|
251
|
-
const sections = this.parseMarkdownSections(content);
|
|
252
|
-
const classifiedSections = this.classifyImportance(sections);
|
|
253
|
-
|
|
254
|
-
// 2. 중요도 기반 필터링 (high, critical만 유지)
|
|
255
|
-
const filteredSections = classifiedSections.filter(section => {
|
|
256
|
-
if (section.importance === 'critical' || section.importance === 'high') {
|
|
257
|
-
metadata.preservedSections.push(section.name);
|
|
258
|
-
return true;
|
|
259
|
-
} else {
|
|
260
|
-
metadata.removedSections.push(section.name);
|
|
261
|
-
return false;
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
// 3. 반복 콘텐츠 제거
|
|
266
|
-
compressed = this.removeRepetitiveContent(compressed);
|
|
267
|
-
|
|
268
|
-
// 4. 오래된 로그 제거
|
|
269
|
-
const oldLogs = this.identifyOldLogs(compressed);
|
|
270
|
-
for (const log of oldLogs) {
|
|
271
|
-
compressed = compressed.replace(log + '\n', '');
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// 5. 상세 설명 요약
|
|
275
|
-
compressed = this.summarizeDescriptions(compressed);
|
|
276
|
-
|
|
277
|
-
// 6. 필터링된 섹션으로 재구성
|
|
278
|
-
compressed = filteredSections.map(section => section.content).join('\n\n');
|
|
279
|
-
|
|
280
|
-
return {
|
|
281
|
-
content: compressed,
|
|
282
|
-
metadata
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* 반복 콘텐츠를 제거합니다
|
|
288
|
-
*/
|
|
289
|
-
removeRepetitiveContent(content) {
|
|
290
|
-
// 중복된 헤더 제거
|
|
291
|
-
const lines = content.split('\n');
|
|
292
|
-
const uniqueLines = [];
|
|
293
|
-
const seenHeaders = new Set();
|
|
294
|
-
|
|
295
|
-
for (const line of lines) {
|
|
296
|
-
const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
297
|
-
if (headerMatch) {
|
|
298
|
-
const headerText = headerMatch[2];
|
|
299
|
-
if (!seenHeaders.has(headerText)) {
|
|
300
|
-
seenHeaders.add(headerText);
|
|
301
|
-
uniqueLines.push(line);
|
|
302
|
-
}
|
|
303
|
-
} else {
|
|
304
|
-
uniqueLines.push(line);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
return uniqueLines.join('\n');
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* 오래된 로그를 제거합니다
|
|
313
|
-
*/
|
|
314
|
-
removeOldLogs(content) {
|
|
315
|
-
const oldLogs = this.identifyOldLogs(content);
|
|
316
|
-
let filtered = content;
|
|
317
|
-
|
|
318
|
-
for (const log of oldLogs) {
|
|
319
|
-
filtered = filtered.replace(log + '\n', '');
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
return filtered;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* 상세 설명을 요약합니다
|
|
327
|
-
*/
|
|
328
|
-
summarizeDescriptions(content) {
|
|
329
|
-
// 긴 단락을 요약
|
|
330
|
-
const paragraphs = content.split('\n\n');
|
|
331
|
-
const summarized = paragraphs.map(paragraph => {
|
|
332
|
-
if (paragraph.length > 500) {
|
|
333
|
-
// 첫 번째와 마지막 문장만 유지
|
|
334
|
-
const sentences = paragraph.split('. ');
|
|
335
|
-
if (sentences.length > 2) {
|
|
336
|
-
return sentences[0] + '. ... ' + sentences[sentences.length - 1];
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
return paragraph;
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
return summarized.join('\n\n');
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* 필터링된 섹션으로 콘텐츠를 재구성합니다
|
|
347
|
-
*/
|
|
348
|
-
reconstructContent(sections) {
|
|
349
|
-
return sections.map(section => section.content).join('\n\n');
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* 균형 압축 전략 (30-50% 압축)
|
|
355
|
-
*/
|
|
356
|
-
export class BalancedCompressionStrategy extends CompressionStrategy {
|
|
357
|
-
constructor() {
|
|
358
|
-
super('balanced');
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
compress(content, options = {}) {
|
|
362
|
-
let compressed = content;
|
|
363
|
-
const metadata = {
|
|
364
|
-
strategy: 'balanced',
|
|
365
|
-
removedSections: [],
|
|
366
|
-
compressionLevel: 'medium',
|
|
367
|
-
preservedSections: []
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
// 1. 섹션 분석
|
|
371
|
-
const sections = this.parseMarkdownSections(content);
|
|
372
|
-
const classifiedSections = this.classifyImportance(sections);
|
|
373
|
-
|
|
374
|
-
// 2. 중요도 기반 필터링 (medium 이상 유지)
|
|
375
|
-
const filteredSections = classifiedSections.filter(section => {
|
|
376
|
-
if (section.importance !== 'low') {
|
|
377
|
-
metadata.preservedSections.push(section.name);
|
|
378
|
-
return true;
|
|
379
|
-
} else {
|
|
380
|
-
metadata.removedSections.push(section.name);
|
|
381
|
-
return false;
|
|
382
|
-
}
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
// 3. 반복 콘텐츠 축약
|
|
386
|
-
compressed = this.condenseRepetitiveContent(compressed);
|
|
387
|
-
|
|
388
|
-
// 4. 오래된 로그 제거
|
|
389
|
-
const oldLogs = this.identifyOldLogs(compressed);
|
|
390
|
-
for (const log of oldLogs) {
|
|
391
|
-
compressed = compressed.replace(log + '\n', '');
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// 5. 장문 설명 압축
|
|
395
|
-
compressed = this.compressLongDescriptions(compressed);
|
|
396
|
-
|
|
397
|
-
// 6. 코드 블록 축소
|
|
398
|
-
compressed = this.compressCodeBlocks(compressed);
|
|
399
|
-
|
|
400
|
-
// 7. 필터링된 섹션으로 재구성
|
|
401
|
-
compressed = filteredSections.map(section => {
|
|
402
|
-
// 각 섹션 내용도 압축
|
|
403
|
-
let sectionContent = section.content;
|
|
404
|
-
sectionContent = this.condenseRepetitiveContent(sectionContent);
|
|
405
|
-
sectionContent = this.compressLongDescriptions(sectionContent);
|
|
406
|
-
return sectionContent;
|
|
407
|
-
}).join('\n\n');
|
|
408
|
-
|
|
409
|
-
return {
|
|
410
|
-
content: compressed,
|
|
411
|
-
metadata
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* 반복 콘텐츠를 축약합니다
|
|
417
|
-
*/
|
|
418
|
-
condenseRepetitiveContent(content) {
|
|
419
|
-
// 연속된 빈 줄 제거
|
|
420
|
-
content = content.replace(/\n{3,}/g, '\n\n');
|
|
421
|
-
|
|
422
|
-
// 중복된 목록 항목 제거
|
|
423
|
-
const lines = content.split('\n');
|
|
424
|
-
const condensed = [];
|
|
425
|
-
const seenListItems = new Set();
|
|
426
|
-
|
|
427
|
-
for (const line of lines) {
|
|
428
|
-
const listMatch = line.match(/^\s*[-*+]\s+(.+)$/);
|
|
429
|
-
if (listMatch) {
|
|
430
|
-
const listText = listMatch[1];
|
|
431
|
-
if (!seenListItems.has(listText)) {
|
|
432
|
-
seenListItems.add(listText);
|
|
433
|
-
condensed.push(line);
|
|
434
|
-
}
|
|
435
|
-
} else {
|
|
436
|
-
condensed.push(line);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
return condensed.join('\n');
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* 장문 설명을 압축합니다
|
|
445
|
-
*/
|
|
446
|
-
compressLongDescriptions(content) {
|
|
447
|
-
const paragraphs = content.split('\n\n');
|
|
448
|
-
const compressed = paragraphs.map(paragraph => {
|
|
449
|
-
if (paragraph.length > 300) {
|
|
450
|
-
// 핵심 문장만 유지
|
|
451
|
-
const sentences = paragraph.split('. ');
|
|
452
|
-
if (sentences.length > 3) {
|
|
453
|
-
return sentences.slice(0, 2).join('. ') + '. [압축됨]';
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
return paragraph;
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
return compressed.join('\n\n');
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* 코드 블록을 압축합니다
|
|
464
|
-
*/
|
|
465
|
-
compressCodeBlocks(content) {
|
|
466
|
-
// 긴 코드 블록을 축소
|
|
467
|
-
const codeBlockPattern = /```[\s\S]*?```/g;
|
|
468
|
-
return content.replace(codeBlockPattern, (match) => {
|
|
469
|
-
const lines = match.split('\n');
|
|
470
|
-
if (lines.length > 20) {
|
|
471
|
-
// 처음 5줄과 마지막 3줄만 유지
|
|
472
|
-
const language = lines[0];
|
|
473
|
-
const firstLines = lines.slice(1, 6).join('\n');
|
|
474
|
-
const lastLines = lines.slice(-4, -1).join('\n');
|
|
475
|
-
return `${language}\n${firstLines}\n... [${lines.length - 10}줄 생략] ...\n${lastLines}\n\`\`\``;
|
|
476
|
-
}
|
|
477
|
-
return match;
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* 필터링된 섹션으로 콘텐츠를 재구성합니다
|
|
483
|
-
*/
|
|
484
|
-
reconstructContent(sections) {
|
|
485
|
-
return sections.map(section => section.content).join('\n\n');
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* 최소 압축 전략 (10-30% 압축)
|
|
491
|
-
*/
|
|
492
|
-
export class MinimalCompressionStrategy extends CompressionStrategy {
|
|
493
|
-
constructor() {
|
|
494
|
-
super('minimal');
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
compress(content, options = {}) {
|
|
498
|
-
let compressed = content;
|
|
499
|
-
const metadata = {
|
|
500
|
-
strategy: 'minimal',
|
|
501
|
-
removedSections: [],
|
|
502
|
-
compressionLevel: 'low',
|
|
503
|
-
preservedSections: []
|
|
504
|
-
};
|
|
505
|
-
|
|
506
|
-
// 1. 포맷팅 최적화
|
|
507
|
-
compressed = this.optimizeFormatting(compressed);
|
|
508
|
-
|
|
509
|
-
// 2. 오래된 로그만 제거
|
|
510
|
-
const oldLogs = this.identifyOldLogs(compressed);
|
|
511
|
-
for (const log of oldLogs) {
|
|
512
|
-
compressed = compressed.replace(log + '\n', '');
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
// 3. 기본 정리
|
|
516
|
-
compressed = this.basicCleanup(compressed);
|
|
517
|
-
|
|
518
|
-
return {
|
|
519
|
-
content: compressed,
|
|
520
|
-
metadata
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* 포맷팅을 최적화합니다
|
|
526
|
-
*/
|
|
527
|
-
optimizeFormatting(content) {
|
|
528
|
-
// 불필요한 공백 제거
|
|
529
|
-
content = content.replace(/[ \t]+$/gm, ''); // 줄 끝 공백
|
|
530
|
-
content = content.replace(/\n{4,}/g, '\n\n\n'); // 과도한 빈 줄
|
|
531
|
-
content = content.replace(/^\n+/, ''); // 시작 빈 줄
|
|
532
|
-
content = content.replace(/\n+$/, '\n'); // 끝 빈 줄
|
|
533
|
-
|
|
534
|
-
return content;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* 기본 정리를 수행합니다
|
|
539
|
-
*/
|
|
540
|
-
basicCleanup(content) {
|
|
541
|
-
// 중복된 구분선 제거
|
|
542
|
-
content = content.replace(/---+/g, '---');
|
|
543
|
-
|
|
544
|
-
// 불필요한 마크다운 태그 정리
|
|
545
|
-
content = content.replace(/\*\*\s*\*\*/g, ''); // 빈 bold 태그
|
|
546
|
-
content = content.replace(/\*\s*\*/g, ''); // 빈 italic 태그
|
|
547
|
-
|
|
548
|
-
return content;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
export default {
|
|
553
|
-
CompressionStrategy,
|
|
554
|
-
AggressiveCompressionStrategy,
|
|
555
|
-
BalancedCompressionStrategy,
|
|
556
|
-
MinimalCompressionStrategy
|
|
557
|
-
};
|