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,559 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from 'fs/promises';
4
- import path from 'path';
5
- import { createHash } from 'crypto';
6
- import { createReadStream, createWriteStream } from 'fs';
7
- import { pipeline } from 'stream/promises';
8
- import { createGzip, createGunzip } from 'zlib';
9
- import { promisify } from 'util';
10
-
11
- /**
12
- * 오프라인 템플릿 캐시 시스템
13
- * - 모든 AI 도구 템플릿과 프로젝트 템플릿 캐시 관리
14
- * - 압축 저장으로 디스크 사용량 최적화
15
- * - 버전 관리 및 자동 업데이트
16
- * - 네트워크 상태 감지 및 오프라인 폴백
17
- */
18
- class TemplateCache {
19
- constructor(options = {}) {
20
- this.cacheDir = options.cacheDir || path.join(process.env.HOME || process.env.USERPROFILE, '.aiwf', 'cache');
21
- this.templatesDir = path.join(this.cacheDir, 'templates');
22
- this.aiToolsDir = path.join(this.templatesDir, 'ai-tools');
23
- this.projectsDir = path.join(this.templatesDir, 'projects');
24
- this.manifestPath = path.join(this.cacheDir, 'manifest.json');
25
- this.lockPath = path.join(this.cacheDir, 'lock.json');
26
-
27
- // 기본 설정
28
- this.maxCacheSize = options.maxCacheSize || 100 * 1024 * 1024; // 100MB
29
- this.compressionLevel = options.compressionLevel || 6;
30
- this.updateCheckInterval = options.updateCheckInterval || 24 * 60 * 60 * 1000; // 24시간
31
-
32
- // 내부 상태
33
- this.manifest = null;
34
- this.lock = null;
35
- this.stats = {
36
- totalSize: 0,
37
- templatesCount: 0,
38
- lastUpdate: null,
39
- hitCount: 0,
40
- missCount: 0
41
- };
42
- }
43
-
44
- /**
45
- * 캐시 시스템 초기화
46
- */
47
- async init() {
48
- try {
49
- // 캐시 디렉토리 생성
50
- await fs.mkdir(this.cacheDir, { recursive: true });
51
- await fs.mkdir(this.templatesDir, { recursive: true });
52
- await fs.mkdir(this.aiToolsDir, { recursive: true });
53
- await fs.mkdir(this.projectsDir, { recursive: true });
54
-
55
- // 매니페스트 파일 로드 또는 생성
56
- await this.loadManifest();
57
- await this.loadLock();
58
-
59
- // 캐시 상태 계산
60
- await this.calculateStats();
61
-
62
- console.log('Template cache system initialized');
63
- } catch (error) {
64
- console.error('Failed to initialize template cache:', error.message);
65
- throw error;
66
- }
67
- }
68
-
69
- /**
70
- * 매니페스트 파일 로드
71
- */
72
- async loadManifest() {
73
- try {
74
- const manifestData = await fs.readFile(this.manifestPath, 'utf8');
75
- this.manifest = JSON.parse(manifestData);
76
- } catch (error) {
77
- // 매니페스트 파일이 없으면 새로 생성
78
- this.manifest = {
79
- version: '1.0.0',
80
- templates: {
81
- 'ai-tools': {},
82
- 'projects': {}
83
- },
84
- last_update_check: null,
85
- created_at: new Date().toISOString()
86
- };
87
- await this.saveManifest();
88
- }
89
- }
90
-
91
- /**
92
- * 락 파일 로드
93
- */
94
- async loadLock() {
95
- try {
96
- const lockData = await fs.readFile(this.lockPath, 'utf8');
97
- this.lock = JSON.parse(lockData);
98
- } catch (error) {
99
- this.lock = {
100
- locked: false,
101
- process_id: null,
102
- operation: null,
103
- timestamp: null
104
- };
105
- await this.saveLock();
106
- }
107
- }
108
-
109
- /**
110
- * 매니페스트 저장
111
- */
112
- async saveManifest() {
113
- await fs.writeFile(this.manifestPath, JSON.stringify(this.manifest, null, 2));
114
- }
115
-
116
- /**
117
- * 락 파일 저장
118
- */
119
- async saveLock() {
120
- await fs.writeFile(this.lockPath, JSON.stringify(this.lock, null, 2));
121
- }
122
-
123
- /**
124
- * 캐시 통계 계산
125
- */
126
- async calculateStats() {
127
- let totalSize = 0;
128
- let templatesCount = 0;
129
-
130
- const calculateDirectorySize = async (dir) => {
131
- try {
132
- const files = await fs.readdir(dir);
133
- let dirSize = 0;
134
- for (const file of files) {
135
- const filePath = path.join(dir, file);
136
- const stat = await fs.stat(filePath);
137
- if (stat.isDirectory()) {
138
- dirSize += await calculateDirectorySize(filePath);
139
- } else {
140
- dirSize += stat.size;
141
- if (file.endsWith('.tar.gz')) {
142
- templatesCount++;
143
- }
144
- }
145
- }
146
- return dirSize;
147
- } catch (error) {
148
- return 0;
149
- }
150
- };
151
-
152
- totalSize = await calculateDirectorySize(this.templatesDir);
153
-
154
- this.stats = {
155
- ...this.stats,
156
- totalSize,
157
- templatesCount,
158
- lastUpdate: this.manifest.last_update_check
159
- };
160
- }
161
-
162
- /**
163
- * 템플릿 캐시 키 생성
164
- */
165
- generateCacheKey(type, name, version = '1.0.0') {
166
- return `${type}/${name}@${version}`;
167
- }
168
-
169
- /**
170
- * 템플릿 압축 및 캐시 저장
171
- */
172
- async cacheTemplate(type, name, sourcePath, version = '1.0.0') {
173
- const cacheKey = this.generateCacheKey(type, name, version);
174
- const targetDir = type === 'ai-tools' ? this.aiToolsDir : this.projectsDir;
175
- const cacheFile = path.join(targetDir, `${name}@${version}.tar.gz`);
176
-
177
- try {
178
- // 디렉토리 압축
179
- await this.compressDirectory(sourcePath, cacheFile);
180
-
181
- // 체크섬 계산
182
- const checksum = await this.calculateChecksum(cacheFile);
183
- const stat = await fs.stat(cacheFile);
184
-
185
- // 매니페스트 업데이트
186
- this.manifest.templates[type][name] = {
187
- version,
188
- cached_at: new Date().toISOString(),
189
- size: stat.size,
190
- checksum,
191
- cache_file: cacheFile
192
- };
193
-
194
- await this.saveManifest();
195
- console.log(`Template cached: ${cacheKey}`);
196
-
197
- return {
198
- success: true,
199
- cacheKey,
200
- size: stat.size,
201
- checksum
202
- };
203
- } catch (error) {
204
- console.error(`Failed to cache template ${cacheKey}:`, error.message);
205
- throw error;
206
- }
207
- }
208
-
209
- /**
210
- * 템플릿 압축 (간단한 방식)
211
- */
212
- async compressDirectory(sourcePath, targetFile) {
213
- // Node.js 내장 zlib와 fs를 사용한 간단한 압축
214
- const { createGzip } = await import('zlib');
215
- const { createReadStream, createWriteStream } = await import('fs');
216
-
217
- // 디렉토리 내용을 JSON으로 직렬화
218
- const dirContents = await this.readDirectoryRecursive(sourcePath);
219
- const jsonData = JSON.stringify(dirContents, null, 2);
220
-
221
- // 압축하여 저장
222
- const gzip = createGzip();
223
- const input = Buffer.from(jsonData);
224
- const output = createWriteStream(targetFile);
225
-
226
- return new Promise((resolve, reject) => {
227
- gzip.pipe(output);
228
- gzip.on('error', reject);
229
- output.on('error', reject);
230
- output.on('finish', resolve);
231
- gzip.end(input);
232
- });
233
- }
234
-
235
- /**
236
- * 디렉토리 내용을 재귀적으로 읽기
237
- */
238
- async readDirectoryRecursive(dirPath) {
239
- const result = {};
240
- const items = await fs.readdir(dirPath);
241
-
242
- for (const item of items) {
243
- const itemPath = path.join(dirPath, item);
244
- const stat = await fs.stat(itemPath);
245
-
246
- if (stat.isDirectory()) {
247
- // node_modules와 .git 제외
248
- if (!item.includes('node_modules') && !item.includes('.git')) {
249
- result[item] = await this.readDirectoryRecursive(itemPath);
250
- }
251
- } else {
252
- // 파일 내용 읽기
253
- try {
254
- const content = await fs.readFile(itemPath, 'utf8');
255
- result[item] = {
256
- type: 'file',
257
- content: content,
258
- size: stat.size
259
- };
260
- } catch (error) {
261
- // 바이너리 파일은 base64로 저장
262
- const content = await fs.readFile(itemPath);
263
- result[item] = {
264
- type: 'binary',
265
- content: content.toString('base64'),
266
- size: stat.size
267
- };
268
- }
269
- }
270
- }
271
-
272
- return result;
273
- }
274
-
275
- /**
276
- * 템플릿 압축 해제
277
- */
278
- async extractTemplate(cacheFile, targetPath) {
279
- const { createGunzip } = await import('zlib');
280
- const { createReadStream } = await import('fs');
281
-
282
- // 압축 해제
283
- const gunzip = createGunzip();
284
- const input = createReadStream(cacheFile);
285
-
286
- return new Promise((resolve, reject) => {
287
- let data = '';
288
-
289
- input.pipe(gunzip);
290
- gunzip.on('data', chunk => data += chunk);
291
- gunzip.on('end', async () => {
292
- try {
293
- const dirContents = JSON.parse(data);
294
- await this.writeDirectoryRecursive(targetPath, dirContents);
295
- resolve();
296
- } catch (error) {
297
- reject(error);
298
- }
299
- });
300
- gunzip.on('error', reject);
301
- input.on('error', reject);
302
- });
303
- }
304
-
305
- /**
306
- * 디렉토리 내용을 재귀적으로 쓰기
307
- */
308
- async writeDirectoryRecursive(dirPath, contents) {
309
- await fs.mkdir(dirPath, { recursive: true });
310
-
311
- for (const [name, item] of Object.entries(contents)) {
312
- const itemPath = path.join(dirPath, name);
313
-
314
- if (item.type === 'file') {
315
- await fs.writeFile(itemPath, item.content, 'utf8');
316
- } else if (item.type === 'binary') {
317
- const buffer = Buffer.from(item.content, 'base64');
318
- await fs.writeFile(itemPath, buffer);
319
- } else {
320
- // 디렉토리
321
- await this.writeDirectoryRecursive(itemPath, item);
322
- }
323
- }
324
- }
325
-
326
- /**
327
- * 파일 체크섬 계산
328
- */
329
- async calculateChecksum(filePath) {
330
- return new Promise((resolve, reject) => {
331
- const hash = createHash('sha256');
332
- const stream = createReadStream(filePath);
333
-
334
- stream.on('error', reject);
335
- stream.on('data', chunk => hash.update(chunk));
336
- stream.on('end', () => resolve(hash.digest('hex')));
337
- });
338
- }
339
-
340
- /**
341
- * 캐시된 템플릿 가져오기
342
- */
343
- async getTemplate(type, name, version = '1.0.0') {
344
- const cacheKey = this.generateCacheKey(type, name, version);
345
-
346
- try {
347
- const templateInfo = this.manifest.templates[type][name];
348
- if (!templateInfo) {
349
- this.stats.missCount++;
350
- return null;
351
- }
352
-
353
- const cacheFile = templateInfo.cache_file;
354
- if (!await this.fileExists(cacheFile)) {
355
- this.stats.missCount++;
356
- return null;
357
- }
358
-
359
- // 체크섬 검증
360
- const currentChecksum = await this.calculateChecksum(cacheFile);
361
- if (currentChecksum !== templateInfo.checksum) {
362
- console.warn(`Checksum mismatch for ${cacheKey}, cache may be corrupted`);
363
- this.stats.missCount++;
364
- return null;
365
- }
366
-
367
- this.stats.hitCount++;
368
- return {
369
- cacheKey,
370
- cacheFile,
371
- templateInfo
372
- };
373
- } catch (error) {
374
- console.error(`Failed to get template ${cacheKey}:`, error.message);
375
- this.stats.missCount++;
376
- return null;
377
- }
378
- }
379
-
380
- /**
381
- * 템플릿을 대상 디렉토리로 추출
382
- */
383
- async extractToDirectory(type, name, targetPath, version = '1.0.0') {
384
- const template = await this.getTemplate(type, name, version);
385
- if (!template) {
386
- throw new Error(`Template ${type}/${name}@${version} not found in cache`);
387
- }
388
-
389
- await fs.mkdir(targetPath, { recursive: true });
390
- await this.extractTemplate(template.cacheFile, targetPath);
391
-
392
- return {
393
- success: true,
394
- extracted: true,
395
- targetPath,
396
- templateInfo: template.templateInfo
397
- };
398
- }
399
-
400
- /**
401
- * 파일 존재 확인
402
- */
403
- async fileExists(filePath) {
404
- try {
405
- await fs.access(filePath);
406
- return true;
407
- } catch {
408
- return false;
409
- }
410
- }
411
-
412
- /**
413
- * 사용 가능한 템플릿 목록 조회
414
- */
415
- async listTemplates(type = null) {
416
- const result = {};
417
-
418
- if (type) {
419
- result[type] = this.manifest.templates[type] || {};
420
- } else {
421
- result['ai-tools'] = this.manifest.templates['ai-tools'] || {};
422
- result['projects'] = this.manifest.templates['projects'] || {};
423
- }
424
-
425
- return result;
426
- }
427
-
428
- /**
429
- * 캐시 정리
430
- */
431
- async cleanup(options = {}) {
432
- const maxAge = options.maxAge || 7 * 24 * 60 * 60 * 1000; // 7일
433
- const now = Date.now();
434
- let removedCount = 0;
435
- let freedSpace = 0;
436
-
437
- for (const type of ['ai-tools', 'projects']) {
438
- const templates = this.manifest.templates[type];
439
-
440
- for (const [name, templateInfo] of Object.entries(templates)) {
441
- const cachedAt = new Date(templateInfo.cached_at).getTime();
442
-
443
- if (now - cachedAt > maxAge) {
444
- try {
445
- await fs.unlink(templateInfo.cache_file);
446
- freedSpace += templateInfo.size;
447
- removedCount++;
448
- delete templates[name];
449
- } catch (error) {
450
- console.warn(`Failed to remove expired cache: ${name}`, error.message);
451
- }
452
- }
453
- }
454
- }
455
-
456
- await this.saveManifest();
457
- await this.calculateStats();
458
-
459
- return {
460
- removedCount,
461
- freedSpace,
462
- totalSize: this.stats.totalSize
463
- };
464
- }
465
-
466
- /**
467
- * 전체 캐시 초기화
468
- */
469
- async clear() {
470
- try {
471
- await fs.rm(this.templatesDir, { recursive: true, force: true });
472
- await fs.mkdir(this.templatesDir, { recursive: true });
473
- await fs.mkdir(this.aiToolsDir, { recursive: true });
474
- await fs.mkdir(this.projectsDir, { recursive: true });
475
-
476
- this.manifest.templates = {
477
- 'ai-tools': {},
478
- 'projects': {}
479
- };
480
-
481
- await this.saveManifest();
482
- await this.calculateStats();
483
-
484
- return {
485
- success: true,
486
- message: 'Cache cleared successfully'
487
- };
488
- } catch (error) {
489
- console.error('Failed to clear cache:', error.message);
490
- throw error;
491
- }
492
- }
493
-
494
- /**
495
- * 캐시 통계 정보
496
- */
497
- getStats() {
498
- return {
499
- ...this.stats,
500
- maxCacheSize: this.maxCacheSize,
501
- usagePercentage: ((this.stats.totalSize / this.maxCacheSize) * 100).toFixed(2) + '%',
502
- hitRate: this.stats.hitCount + this.stats.missCount > 0
503
- ? ((this.stats.hitCount / (this.stats.hitCount + this.stats.missCount)) * 100).toFixed(2) + '%'
504
- : '0%'
505
- };
506
- }
507
-
508
- /**
509
- * 캐시 크기 제한 확인
510
- */
511
- async checkSizeLimit() {
512
- if (this.stats.totalSize > this.maxCacheSize) {
513
- // 가장 오래된 캐시부터 정리
514
- const templates = [];
515
-
516
- for (const type of ['ai-tools', 'projects']) {
517
- for (const [name, templateInfo] of Object.entries(this.manifest.templates[type])) {
518
- templates.push({
519
- type,
520
- name,
521
- ...templateInfo,
522
- cachedAt: new Date(templateInfo.cached_at).getTime()
523
- });
524
- }
525
- }
526
-
527
- templates.sort((a, b) => a.cachedAt - b.cachedAt);
528
-
529
- let currentSize = this.stats.totalSize;
530
- let removedCount = 0;
531
-
532
- for (const template of templates) {
533
- if (currentSize <= this.maxCacheSize * 0.8) break;
534
-
535
- try {
536
- await fs.unlink(template.cache_file);
537
- currentSize -= template.size;
538
- removedCount++;
539
- delete this.manifest.templates[template.type][template.name];
540
- } catch (error) {
541
- console.warn(`Failed to remove cache: ${template.name}`, error.message);
542
- }
543
- }
544
-
545
- await this.saveManifest();
546
- await this.calculateStats();
547
-
548
- return {
549
- cleanedUp: true,
550
- removedCount,
551
- currentSize: this.stats.totalSize
552
- };
553
- }
554
-
555
- return { cleanedUp: false };
556
- }
557
- }
558
-
559
- export { TemplateCache };