ccjk 2.3.2 → 2.4.2

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 (37) hide show
  1. package/README.md +270 -444
  2. package/README.zh-CN.md +273 -447
  3. package/dist/chunks/api-providers.mjs +5 -35
  4. package/dist/chunks/auto-bootstrap.mjs +1 -1
  5. package/dist/chunks/ccr.mjs +5 -2
  6. package/dist/chunks/claude-wrapper.mjs +442 -0
  7. package/dist/chunks/cloud-sync.mjs +29 -0
  8. package/dist/chunks/constants.mjs +1 -1
  9. package/dist/chunks/context-manager.mjs +641 -0
  10. package/dist/chunks/context.mjs +248 -0
  11. package/dist/chunks/index2.mjs +2 -0
  12. package/dist/chunks/index3.mjs +19 -19
  13. package/dist/chunks/init.mjs +18 -8
  14. package/dist/chunks/marketplace.mjs +6 -2
  15. package/dist/chunks/mcp.mjs +1 -1
  16. package/dist/chunks/menu.mjs +3 -3
  17. package/dist/chunks/notification.mjs +27 -27
  18. package/dist/chunks/package.mjs +1 -1
  19. package/dist/chunks/platform.mjs +70 -21
  20. package/dist/chunks/skills-sync.mjs +1 -1
  21. package/dist/chunks/version-checker.mjs +31 -31
  22. package/dist/cli.mjs +55 -5
  23. package/dist/i18n/locales/en/context.json +32 -0
  24. package/dist/i18n/locales/en/marketplace.json +1 -0
  25. package/dist/i18n/locales/en/mcp.json +12 -1
  26. package/dist/i18n/locales/en/superpowers.json +46 -0
  27. package/dist/i18n/locales/zh-CN/context.json +32 -0
  28. package/dist/i18n/locales/zh-CN/marketplace.json +1 -0
  29. package/dist/i18n/locales/zh-CN/mcp.json +12 -1
  30. package/dist/i18n/locales/zh-CN/superpowers.json +46 -0
  31. package/dist/index.d.mts +2 -2
  32. package/dist/index.d.ts +2 -2
  33. package/dist/shared/ccjk.QbS8EAOd.mjs +1019 -0
  34. package/dist/shared/ccjk.RR9TS76h.mjs +698 -0
  35. package/package.json +4 -1
  36. package/dist/shared/ccjk.Bi-m3LKY.mjs +0 -357
  37. package/dist/shared/ccjk.D-RZS4E2.mjs +0 -416
@@ -0,0 +1,641 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ import * as crypto from 'node:crypto';
5
+ import process__default from 'node:process';
6
+
7
+ class ContextAnalyzer {
8
+ /**
9
+ * 分析消息重要性
10
+ */
11
+ static analyzeImportance(message) {
12
+ let score = 0;
13
+ const content = this.extractTextContent(message);
14
+ if (this.containsDecision(content))
15
+ score += 30;
16
+ if (this.containsCodeChange(message))
17
+ score += 25;
18
+ if (this.containsErrorResolution(content))
19
+ score += 20;
20
+ if (message.type === "user" && content.length > 50)
21
+ score += 15;
22
+ if (message.toolUseResult)
23
+ score += 10;
24
+ const age = Date.now() - new Date(message.timestamp).getTime();
25
+ const hourAge = age / (1e3 * 60 * 60);
26
+ score += Math.max(0, 20 - hourAge * 2);
27
+ return Math.min(100, score);
28
+ }
29
+ /**
30
+ * 提取文本内容
31
+ */
32
+ static extractTextContent(message) {
33
+ const content = message.message.content;
34
+ if (typeof content === "string")
35
+ return content;
36
+ return content.filter(
37
+ (block) => block.type === "text" && typeof block.text === "string"
38
+ ).map((block) => block.text).join("\n");
39
+ }
40
+ /**
41
+ * 检测是否包含决策
42
+ */
43
+ static containsDecision(content) {
44
+ const decisionPatterns = [
45
+ /决定|决策|选择|采用|使用|方案/,
46
+ /decide|decision|choose|adopt|use|approach/i,
47
+ /我们应该|建议|推荐/,
48
+ /should|recommend|suggest/i,
49
+ /✅|✔|确定|confirmed/i
50
+ ];
51
+ return decisionPatterns.some((p) => p.test(content));
52
+ }
53
+ /**
54
+ * 检测是否包含代码变更
55
+ */
56
+ static containsCodeChange(message) {
57
+ const content = message.message.content;
58
+ if (typeof content === "string")
59
+ return false;
60
+ return content.some(
61
+ (block) => block.type === "tool_use" && ["Write", "Edit", "Bash"].includes(block.name || "")
62
+ );
63
+ }
64
+ /**
65
+ * 检测是否包含错误解决
66
+ */
67
+ static containsErrorResolution(content) {
68
+ const patterns = [
69
+ /修复|解决|fix|resolve|solved/i,
70
+ /错误|error|bug|issue/i,
71
+ /成功|success|works|working/i
72
+ ];
73
+ return patterns.filter((p) => p.test(content)).length >= 2;
74
+ }
75
+ /**
76
+ * 提取关键决策
77
+ */
78
+ static extractDecisions(messages) {
79
+ const decisions = [];
80
+ for (const msg of messages) {
81
+ const content = this.extractTextContent(msg);
82
+ if (this.containsDecision(content)) {
83
+ const sentences = content.split(/[。.!!?\n]/).filter((s) => s.trim());
84
+ for (const sentence of sentences) {
85
+ if (this.containsDecision(sentence) && sentence.length < 200) {
86
+ decisions.push(sentence.trim());
87
+ }
88
+ }
89
+ }
90
+ }
91
+ return [...new Set(decisions)].slice(0, 10);
92
+ }
93
+ /**
94
+ * 提取代码变更
95
+ */
96
+ static extractCodeChanges(messages) {
97
+ const changes = [];
98
+ for (const msg of messages) {
99
+ const content = msg.message.content;
100
+ if (typeof content === "string")
101
+ continue;
102
+ for (const block of content) {
103
+ if (block.type === "tool_use") {
104
+ const input = block.input;
105
+ if (block.name === "Write" && input?.file_path) {
106
+ changes.push({
107
+ file: String(input.file_path),
108
+ action: "create",
109
+ description: `Created file`
110
+ });
111
+ } else if (block.name === "Edit" && input?.file_path) {
112
+ changes.push({
113
+ file: String(input.file_path),
114
+ action: "modify",
115
+ description: `Modified file`
116
+ });
117
+ }
118
+ }
119
+ }
120
+ }
121
+ const merged = /* @__PURE__ */ new Map();
122
+ for (const change of changes) {
123
+ const existing = merged.get(change.file);
124
+ if (existing) {
125
+ existing.action = "modify";
126
+ } else {
127
+ merged.set(change.file, change);
128
+ }
129
+ }
130
+ return Array.from(merged.values());
131
+ }
132
+ /**
133
+ * 提取主题
134
+ */
135
+ static extractTopics(messages) {
136
+ const topicKeywords = /* @__PURE__ */ new Map();
137
+ for (const msg of messages) {
138
+ const content = this.extractTextContent(msg).toLowerCase();
139
+ const techPatterns = [
140
+ /\b(mcp|api|cli|sdk|npm|git|docker)\b/gi,
141
+ /\b(typescript|javascript|python|rust|go)\b/gi,
142
+ /\b(react|vue|angular|node|express)\b/gi,
143
+ /\b(database|cache|redis|mongodb|postgres)\b/gi,
144
+ /\b(performance|optimization|refactor|debug)\b/gi
145
+ ];
146
+ for (const pattern of techPatterns) {
147
+ const matches = content.match(pattern) || [];
148
+ for (const match of matches) {
149
+ const key = match.toLowerCase();
150
+ topicKeywords.set(key, (topicKeywords.get(key) || 0) + 1);
151
+ }
152
+ }
153
+ }
154
+ return Array.from(topicKeywords.entries()).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([topic]) => topic);
155
+ }
156
+ /**
157
+ * 估算 token 数量
158
+ */
159
+ static estimateTokens(messages) {
160
+ let totalChars = 0;
161
+ for (const msg of messages) {
162
+ const content = this.extractTextContent(msg);
163
+ totalChars += content.length;
164
+ if (msg.toolUseResult?.stdout) {
165
+ totalChars += msg.toolUseResult.stdout.length;
166
+ }
167
+ }
168
+ return Math.ceil(totalChars / 4);
169
+ }
170
+ }
171
+ class ContextManager {
172
+ claudeDir;
173
+ projectsDir;
174
+ archiveDir;
175
+ summaryDir;
176
+ constructor() {
177
+ this.claudeDir = path.join(os.homedir(), ".claude");
178
+ this.projectsDir = path.join(this.claudeDir, "projects");
179
+ this.archiveDir = path.join(this.claudeDir, "archive");
180
+ this.summaryDir = path.join(this.claudeDir, "summaries");
181
+ this.ensureDirectories();
182
+ }
183
+ ensureDirectories() {
184
+ for (const dir of [this.archiveDir, this.summaryDir]) {
185
+ if (!fs.existsSync(dir)) {
186
+ fs.mkdirSync(dir, { recursive: true });
187
+ }
188
+ }
189
+ }
190
+ /**
191
+ * 获取当前项目的会话文件
192
+ */
193
+ getProjectSessions(projectPath) {
194
+ const projectKey = projectPath ? projectPath.replace(/\//g, "-").replace(/^-/, "") : this.detectCurrentProject();
195
+ const projectDir = path.join(this.projectsDir, projectKey);
196
+ if (!fs.existsSync(projectDir)) {
197
+ return [];
198
+ }
199
+ return fs.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).map((f) => path.join(projectDir, f));
200
+ }
201
+ /**
202
+ * 检测当前项目
203
+ */
204
+ detectCurrentProject() {
205
+ const cwd = process__default.cwd();
206
+ return cwd.replace(/\//g, "-").replace(/^-/, "");
207
+ }
208
+ /**
209
+ * 读取会话消息
210
+ */
211
+ readSessionMessages(sessionFile) {
212
+ if (!fs.existsSync(sessionFile)) {
213
+ return [];
214
+ }
215
+ const content = fs.readFileSync(sessionFile, "utf-8");
216
+ const lines = content.trim().split("\n").filter((l) => l.trim());
217
+ return lines.map((line) => {
218
+ try {
219
+ return JSON.parse(line);
220
+ } catch {
221
+ return null;
222
+ }
223
+ }).filter((msg) => msg !== null);
224
+ }
225
+ /**
226
+ * 压缩会话 - 核心功能
227
+ */
228
+ async compact(sessionFile, options = {}) {
229
+ const {
230
+ keepLastN = 20,
231
+ archiveThreshold = 200,
232
+ preserveDecisions = true,
233
+ preserveCodeChanges = true
234
+ } = options;
235
+ const messages = this.readSessionMessages(sessionFile);
236
+ const originalCount = messages.length;
237
+ const originalTokens = ContextAnalyzer.estimateTokens(messages);
238
+ if (messages.length <= keepLastN) {
239
+ return {
240
+ originalMessages: originalCount,
241
+ compactedMessages: originalCount,
242
+ summaryGenerated: false,
243
+ archived: false,
244
+ tokensSaved: 0
245
+ };
246
+ }
247
+ const scoredMessages = messages.map((msg) => ({
248
+ message: msg,
249
+ score: ContextAnalyzer.analyzeImportance(msg)
250
+ }));
251
+ const recentMessages = messages.slice(-keepLastN);
252
+ const olderMessages = messages.slice(0, -keepLastN);
253
+ const importantOlder = scoredMessages.slice(0, -keepLastN).filter((m) => m.score >= 50).map((m) => m.message);
254
+ const summary = this.generateSummary(olderMessages);
255
+ this.saveSummary(sessionFile, summary);
256
+ let archived = false;
257
+ if (originalCount >= archiveThreshold) {
258
+ this.archiveSession(sessionFile, olderMessages);
259
+ archived = true;
260
+ }
261
+ const compactedMessages = [];
262
+ compactedMessages.push(this.createSummaryMessage(summary));
263
+ if (preserveDecisions || preserveCodeChanges) {
264
+ compactedMessages.push(...importantOlder.slice(0, 10));
265
+ }
266
+ compactedMessages.push(...recentMessages);
267
+ this.writeSession(sessionFile, compactedMessages);
268
+ const compactedTokens = ContextAnalyzer.estimateTokens(compactedMessages);
269
+ return {
270
+ originalMessages: originalCount,
271
+ compactedMessages: compactedMessages.length,
272
+ summaryGenerated: true,
273
+ archived,
274
+ tokensSaved: originalTokens - compactedTokens,
275
+ summary
276
+ };
277
+ }
278
+ /**
279
+ * 生成会话摘要
280
+ */
281
+ generateSummary(messages) {
282
+ const id = crypto.randomUUID();
283
+ const now = (/* @__PURE__ */ new Date()).toISOString();
284
+ const keyDecisions = ContextAnalyzer.extractDecisions(messages);
285
+ const codeChanges = ContextAnalyzer.extractCodeChanges(messages);
286
+ const topics = ContextAnalyzer.extractTopics(messages);
287
+ const tokenEstimate = ContextAnalyzer.estimateTokens(messages);
288
+ const summaryParts = [];
289
+ if (topics.length > 0) {
290
+ summaryParts.push(`\u4E3B\u8981\u8BDD\u9898: ${topics.join(", ")}`);
291
+ }
292
+ if (keyDecisions.length > 0) {
293
+ summaryParts.push(`\u5173\u952E\u51B3\u7B56:
294
+ ${keyDecisions.map((d) => ` - ${d}`).join("\n")}`);
295
+ }
296
+ if (codeChanges.length > 0) {
297
+ summaryParts.push(`\u4EE3\u7801\u53D8\u66F4:
298
+ ${codeChanges.map((c) => ` - ${c.action}: ${c.file}`).join("\n")}`);
299
+ }
300
+ return {
301
+ id,
302
+ createdAt: now,
303
+ updatedAt: now,
304
+ messageCount: messages.length,
305
+ tokenEstimate,
306
+ keyDecisions,
307
+ codeChanges,
308
+ topics,
309
+ summary: summaryParts.join("\n\n")
310
+ };
311
+ }
312
+ /**
313
+ * 保存摘要
314
+ */
315
+ saveSummary(sessionFile, summary) {
316
+ const sessionId = path.basename(sessionFile, ".jsonl");
317
+ const summaryFile = path.join(this.summaryDir, `${sessionId}-${summary.id}.json`);
318
+ fs.writeFileSync(summaryFile, JSON.stringify(summary, null, 2));
319
+ return summaryFile;
320
+ }
321
+ /**
322
+ * 归档会话
323
+ */
324
+ archiveSession(sessionFile, messages) {
325
+ const sessionId = path.basename(sessionFile, ".jsonl");
326
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
327
+ const archiveFile = path.join(this.archiveDir, `${sessionId}-${timestamp}.jsonl`);
328
+ const content = messages.map((m) => JSON.stringify(m)).join("\n");
329
+ fs.writeFileSync(archiveFile, content);
330
+ return archiveFile;
331
+ }
332
+ /**
333
+ * 创建摘要消息
334
+ */
335
+ createSummaryMessage(summary) {
336
+ return {
337
+ uuid: crypto.randomUUID(),
338
+ type: "assistant",
339
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
340
+ message: {
341
+ role: "assistant",
342
+ content: `[\u4F1A\u8BDD\u6458\u8981 - ${summary.messageCount} \u6761\u6D88\u606F\u5DF2\u538B\u7F29]
343
+
344
+ ${summary.summary}`
345
+ },
346
+ metadata: {
347
+ isSummary: true,
348
+ summaryId: summary.id,
349
+ originalMessageCount: summary.messageCount,
350
+ originalTokenEstimate: summary.tokenEstimate
351
+ }
352
+ };
353
+ }
354
+ /**
355
+ * 写入会话
356
+ */
357
+ writeSession(sessionFile, messages) {
358
+ const content = messages.map((m) => JSON.stringify(m)).join("\n");
359
+ fs.writeFileSync(sessionFile, content);
360
+ }
361
+ /**
362
+ * 获取会话状态
363
+ */
364
+ getSessionStatus(sessionFile) {
365
+ const messages = this.readSessionMessages(sessionFile);
366
+ const tokenEstimate = ContextAnalyzer.estimateTokens(messages);
367
+ return {
368
+ messageCount: messages.length,
369
+ tokenEstimate,
370
+ oldestMessage: messages[0]?.timestamp || "N/A",
371
+ newestMessage: messages[messages.length - 1]?.timestamp || "N/A",
372
+ needsCompact: messages.length > 50 || tokenEstimate > 5e4
373
+ };
374
+ }
375
+ /**
376
+ * 列出所有摘要
377
+ */
378
+ listSummaries() {
379
+ if (!fs.existsSync(this.summaryDir)) {
380
+ return [];
381
+ }
382
+ return fs.readdirSync(this.summaryDir).filter((f) => f.endsWith(".json")).map((f) => {
383
+ try {
384
+ const content = fs.readFileSync(path.join(this.summaryDir, f), "utf-8");
385
+ return JSON.parse(content);
386
+ } catch {
387
+ return null;
388
+ }
389
+ }).filter((s) => s !== null);
390
+ }
391
+ /**
392
+ * 恢复归档的会话
393
+ */
394
+ restoreArchive(archiveFile, targetSession) {
395
+ if (!fs.existsSync(archiveFile)) {
396
+ return false;
397
+ }
398
+ const archiveContent = fs.readFileSync(archiveFile, "utf-8");
399
+ const currentContent = fs.existsSync(targetSession) ? fs.readFileSync(targetSession, "utf-8") : "";
400
+ const merged = `${archiveContent}
401
+ ${currentContent}`;
402
+ fs.writeFileSync(targetSession, merged.trim());
403
+ return true;
404
+ }
405
+ }
406
+ new ContextManager();
407
+
408
+ class ContextManagerAdapter {
409
+ baseManager;
410
+ configPath;
411
+ historyPath;
412
+ config;
413
+ constructor() {
414
+ this.baseManager = new ContextManager();
415
+ const ccjkDir = path.join(os.homedir(), ".ccjk");
416
+ const contextDir = path.join(ccjkDir, "context-compression");
417
+ if (!fs.existsSync(contextDir)) {
418
+ fs.mkdirSync(contextDir, { recursive: true });
419
+ }
420
+ this.configPath = path.join(contextDir, "config.json");
421
+ this.historyPath = path.join(contextDir, "history.json");
422
+ this.config = this.loadConfig();
423
+ }
424
+ /**
425
+ * 加载配置
426
+ */
427
+ loadConfig() {
428
+ const defaultConfig = {
429
+ autoCompress: true,
430
+ threshold: 5e4,
431
+ model: "haiku",
432
+ maxHistory: 100,
433
+ retentionDays: 30
434
+ };
435
+ if (fs.existsSync(this.configPath)) {
436
+ try {
437
+ const content = fs.readFileSync(this.configPath, "utf-8");
438
+ return { ...defaultConfig, ...JSON.parse(content) };
439
+ } catch {
440
+ return defaultConfig;
441
+ }
442
+ }
443
+ fs.writeFileSync(this.configPath, JSON.stringify(defaultConfig, null, 2));
444
+ return defaultConfig;
445
+ }
446
+ /**
447
+ * 获取当前状态
448
+ */
449
+ async getStatus() {
450
+ const sessions = this.baseManager.getProjectSessions();
451
+ const currentSession = sessions[sessions.length - 1];
452
+ let totalTokens = 0;
453
+ const compressedTokens = 0;
454
+ let sessionId = null;
455
+ let startTime = null;
456
+ if (currentSession) {
457
+ sessionId = path.basename(currentSession, ".jsonl");
458
+ const status = this.baseManager.getSessionStatus(currentSession);
459
+ totalTokens = status.tokenEstimate;
460
+ try {
461
+ const stats = fs.statSync(currentSession);
462
+ startTime = stats.birthtimeMs;
463
+ } catch {
464
+ }
465
+ }
466
+ const history = await this.getHistory();
467
+ const todayStart = /* @__PURE__ */ new Date();
468
+ todayStart.setHours(0, 0, 0, 0);
469
+ const compressionsToday = history.filter((h) => h.timestamp >= todayStart.getTime()).length;
470
+ const lastEntry = history[history.length - 1];
471
+ const lastCompressed = lastEntry?.timestamp || null;
472
+ const totalSaved = history.reduce((sum, h) => sum + (h.originalTokens - h.compressedTokens), 0);
473
+ return {
474
+ sessionId,
475
+ startTime,
476
+ duration: startTime ? this.formatDuration(Date.now() - startTime) : null,
477
+ totalTokens,
478
+ compressedTokens,
479
+ compressionRatio: totalTokens > 0 ? compressedTokens / totalTokens : null,
480
+ tokensSaved: totalSaved,
481
+ autoCompress: this.config.autoCompress,
482
+ threshold: this.config.threshold,
483
+ lastCompressed,
484
+ compressionsToday,
485
+ storagePath: path.dirname(this.configPath),
486
+ cacheSize: this.getCacheSize(),
487
+ model: this.config.model
488
+ };
489
+ }
490
+ /**
491
+ * 压缩上下文
492
+ */
493
+ async compress(sessionId) {
494
+ const startTime = Date.now();
495
+ const sessions = this.baseManager.getProjectSessions();
496
+ let targetSession;
497
+ if (sessionId) {
498
+ targetSession = sessions.find((s) => path.basename(s, ".jsonl").includes(sessionId));
499
+ } else {
500
+ targetSession = sessions[sessions.length - 1];
501
+ }
502
+ if (!targetSession) {
503
+ throw new Error("No session found to compress");
504
+ }
505
+ const beforeStatus = this.baseManager.getSessionStatus(targetSession);
506
+ await this.baseManager.compact(targetSession, {
507
+ keepLastN: 20,
508
+ preserveDecisions: true,
509
+ preserveCodeChanges: true
510
+ });
511
+ const afterStatus = this.baseManager.getSessionStatus(targetSession);
512
+ const duration = Date.now() - startTime;
513
+ await this.addHistoryEntry({
514
+ id: `compress-${Date.now()}`,
515
+ timestamp: Date.now(),
516
+ originalTokens: beforeStatus.tokenEstimate,
517
+ compressedTokens: afterStatus.tokenEstimate,
518
+ sessionId: path.basename(targetSession, ".jsonl")
519
+ });
520
+ return {
521
+ originalTokens: beforeStatus.tokenEstimate,
522
+ compressedTokens: afterStatus.tokenEstimate,
523
+ duration
524
+ };
525
+ }
526
+ /**
527
+ * 获取压缩历史
528
+ */
529
+ async getHistory() {
530
+ if (!fs.existsSync(this.historyPath)) {
531
+ return [];
532
+ }
533
+ try {
534
+ const content = fs.readFileSync(this.historyPath, "utf-8");
535
+ return JSON.parse(content);
536
+ } catch {
537
+ return [];
538
+ }
539
+ }
540
+ /**
541
+ * 添加历史记录
542
+ */
543
+ async addHistoryEntry(entry) {
544
+ const history = await this.getHistory();
545
+ history.push(entry);
546
+ const trimmed = history.slice(-this.config.maxHistory);
547
+ fs.writeFileSync(this.historyPath, JSON.stringify(trimmed, null, 2));
548
+ }
549
+ /**
550
+ * 恢复上下文
551
+ */
552
+ async restore(id) {
553
+ const archiveDir = path.join(os.homedir(), ".claude", "archive");
554
+ if (!fs.existsSync(archiveDir)) {
555
+ throw new Error("No archives found");
556
+ }
557
+ const archives = fs.readdirSync(archiveDir).filter((f) => f.includes(id));
558
+ if (archives.length === 0) {
559
+ throw new Error(`Archive not found: ${id}`);
560
+ }
561
+ const archiveFile = path.join(archiveDir, archives[0]);
562
+ const sessions = this.baseManager.getProjectSessions();
563
+ const currentSession = sessions[sessions.length - 1];
564
+ if (!currentSession) {
565
+ throw new Error("No current session to restore to");
566
+ }
567
+ const success = this.baseManager.restoreArchive(archiveFile, currentSession);
568
+ if (!success) {
569
+ throw new Error("Failed to restore archive");
570
+ }
571
+ }
572
+ /**
573
+ * 获取配置
574
+ */
575
+ async getConfig() {
576
+ return this.config;
577
+ }
578
+ /**
579
+ * 清除缓存
580
+ */
581
+ async clear() {
582
+ const contextDir = path.dirname(this.configPath);
583
+ const cacheFiles = ["history.json"];
584
+ for (const file of cacheFiles) {
585
+ const filePath = path.join(contextDir, file);
586
+ if (fs.existsSync(filePath)) {
587
+ fs.unlinkSync(filePath);
588
+ }
589
+ }
590
+ }
591
+ /**
592
+ * 格式化持续时间
593
+ */
594
+ formatDuration(ms) {
595
+ const seconds = Math.floor(ms / 1e3);
596
+ const minutes = Math.floor(seconds / 60);
597
+ const hours = Math.floor(minutes / 60);
598
+ if (hours > 0) {
599
+ return `${hours}h ${minutes % 60}m`;
600
+ }
601
+ if (minutes > 0) {
602
+ return `${minutes}m ${seconds % 60}s`;
603
+ }
604
+ return `${seconds}s`;
605
+ }
606
+ /**
607
+ * 获取缓存大小
608
+ */
609
+ getCacheSize() {
610
+ const contextDir = path.dirname(this.configPath);
611
+ let totalSize = 0;
612
+ try {
613
+ const files = fs.readdirSync(contextDir);
614
+ for (const file of files) {
615
+ const filePath = path.join(contextDir, file);
616
+ const stats = fs.statSync(filePath);
617
+ if (stats.isFile()) {
618
+ totalSize += stats.size;
619
+ }
620
+ }
621
+ } catch {
622
+ return "N/A";
623
+ }
624
+ if (totalSize < 1024) {
625
+ return `${totalSize} B`;
626
+ }
627
+ if (totalSize < 1024 * 1024) {
628
+ return `${(totalSize / 1024).toFixed(1)} KB`;
629
+ }
630
+ return `${(totalSize / (1024 * 1024)).toFixed(1)} MB`;
631
+ }
632
+ }
633
+ let instance = null;
634
+ function getContextManager() {
635
+ if (!instance) {
636
+ instance = new ContextManagerAdapter();
637
+ }
638
+ return instance;
639
+ }
640
+
641
+ export { ContextAnalyzer, getContextManager };