ccjk 10.1.0 → 10.2.0

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 (109) hide show
  1. package/dist/chunks/agent-teams.mjs +1 -1
  2. package/dist/chunks/agent.mjs +1 -1
  3. package/dist/chunks/api-providers.mjs +1 -1
  4. package/dist/chunks/api.mjs +3 -3
  5. package/dist/chunks/auto-bootstrap.mjs +1 -1
  6. package/dist/chunks/auto-updater.mjs +1 -1
  7. package/dist/{shared/ccjk.Br91zBIG.mjs → chunks/banner.mjs} +52 -3
  8. package/dist/chunks/boost.mjs +2 -2
  9. package/dist/chunks/ccjk-agents.mjs +1 -1
  10. package/dist/chunks/ccjk-all.mjs +1 -1
  11. package/dist/chunks/ccjk-config.mjs +1 -1
  12. package/dist/chunks/ccjk-hooks.mjs +1 -1
  13. package/dist/chunks/ccjk-mcp.mjs +2 -2
  14. package/dist/chunks/ccjk-setup.mjs +1 -1
  15. package/dist/chunks/ccjk-skills.mjs +1 -1
  16. package/dist/chunks/ccr.mjs +10 -10
  17. package/dist/chunks/ccu.mjs +1 -1
  18. package/dist/chunks/check-updates.mjs +3 -3
  19. package/dist/chunks/claude-code-config-manager.mjs +7 -7
  20. package/dist/chunks/claude-code-incremental-manager.mjs +2 -2
  21. package/dist/chunks/claude-config.mjs +3 -3
  22. package/dist/chunks/claude-wrapper.mjs +2 -2
  23. package/dist/chunks/codex-config-switch.mjs +2 -2
  24. package/dist/chunks/codex-provider-manager.mjs +2 -2
  25. package/dist/chunks/codex-uninstaller.mjs +2 -2
  26. package/dist/chunks/codex.mjs +5 -5
  27. package/dist/chunks/commands.mjs +88 -391
  28. package/dist/chunks/commands2.mjs +391 -88
  29. package/dist/chunks/completion.mjs +1 -1
  30. package/dist/chunks/config-consolidator.mjs +2 -2
  31. package/dist/chunks/config-switch.mjs +3 -3
  32. package/dist/chunks/config.mjs +5 -5
  33. package/dist/chunks/config2.mjs +410 -400
  34. package/dist/chunks/config3.mjs +400 -410
  35. package/dist/chunks/constants.mjs +1 -1
  36. package/dist/chunks/context.mjs +283 -1
  37. package/dist/chunks/dashboard.mjs +365 -0
  38. package/dist/chunks/doctor.mjs +4 -4
  39. package/dist/chunks/features.mjs +11 -11
  40. package/dist/chunks/fs-operations.mjs +1 -1
  41. package/dist/chunks/health-alerts.mjs +304 -0
  42. package/dist/chunks/health-check.mjs +532 -0
  43. package/dist/chunks/index.mjs +10 -177
  44. package/dist/chunks/index2.mjs +168 -1162
  45. package/dist/chunks/index3.mjs +1076 -910
  46. package/dist/chunks/index4.mjs +947 -137
  47. package/dist/chunks/index5.mjs +167 -635
  48. package/dist/chunks/index6.mjs +663 -0
  49. package/dist/chunks/init.mjs +19 -19
  50. package/dist/chunks/installer.mjs +649 -147
  51. package/dist/chunks/installer2.mjs +147 -649
  52. package/dist/chunks/interview.mjs +2 -2
  53. package/dist/chunks/marketplace.mjs +1 -1
  54. package/dist/chunks/mcp.mjs +4 -4
  55. package/dist/chunks/menu.mjs +16 -9
  56. package/dist/chunks/metrics-display.mjs +152 -0
  57. package/dist/chunks/migrator.mjs +1 -1
  58. package/dist/chunks/monitor.mjs +2 -2
  59. package/dist/chunks/notification.mjs +1 -1
  60. package/dist/chunks/onboarding.mjs +2 -2
  61. package/dist/chunks/package.mjs +1 -1
  62. package/dist/chunks/permission-manager.mjs +2 -2
  63. package/dist/chunks/permissions.mjs +1 -1
  64. package/dist/chunks/persistence-manager.mjs +781 -0
  65. package/dist/chunks/persistence.mjs +667 -0
  66. package/dist/chunks/platform.mjs +1 -1
  67. package/dist/chunks/plugin.mjs +1 -1
  68. package/dist/chunks/prompts.mjs +1 -1
  69. package/dist/chunks/providers.mjs +1 -1
  70. package/dist/chunks/quick-actions.mjs +321 -0
  71. package/dist/chunks/quick-setup.mjs +8 -8
  72. package/dist/chunks/silent-updater.mjs +1 -1
  73. package/dist/chunks/simple-config.mjs +2 -2
  74. package/dist/chunks/skill.mjs +1 -1
  75. package/dist/chunks/skills-sync.mjs +1 -1
  76. package/dist/chunks/skills.mjs +1 -1
  77. package/dist/chunks/slash-commands.mjs +208 -0
  78. package/dist/chunks/smart-defaults.mjs +1 -1
  79. package/dist/chunks/startup.mjs +1 -1
  80. package/dist/chunks/stats.mjs +1 -1
  81. package/dist/chunks/status.mjs +31 -2
  82. package/dist/chunks/team.mjs +1 -1
  83. package/dist/chunks/thinking.mjs +2 -2
  84. package/dist/chunks/uninstall.mjs +5 -5
  85. package/dist/chunks/update.mjs +7 -7
  86. package/dist/chunks/upgrade-manager.mjs +2 -2
  87. package/dist/chunks/version-checker.mjs +3 -3
  88. package/dist/chunks/vim.mjs +1 -1
  89. package/dist/cli.mjs +191 -21
  90. package/dist/i18n/locales/en/cli.json +14 -1
  91. package/dist/i18n/locales/en/common.json +27 -0
  92. package/dist/i18n/locales/en/context.json +54 -1
  93. package/dist/i18n/locales/en/dashboard.json +78 -0
  94. package/dist/i18n/locales/en/persistence.json +127 -0
  95. package/dist/i18n/locales/en/quick-actions.json +78 -0
  96. package/dist/i18n/locales/zh-CN/cli.json +14 -1
  97. package/dist/i18n/locales/zh-CN/common.json +27 -0
  98. package/dist/i18n/locales/zh-CN/context.json +54 -1
  99. package/dist/i18n/locales/zh-CN/dashboard.json +78 -0
  100. package/dist/i18n/locales/zh-CN/persistence.json +127 -0
  101. package/dist/i18n/locales/zh-CN/quick-actions.json +78 -0
  102. package/dist/index.mjs +2 -2
  103. package/dist/shared/{ccjk.DE91nClQ.mjs → ccjk.BKoi8-Hy.mjs} +1 -1
  104. package/dist/shared/{ccjk.Dpw86UX0.mjs → ccjk.CxtuJxaS.mjs} +1 -1
  105. package/dist/shared/{ccjk.ClzTOz9n.mjs → ccjk.DB2UYcq0.mjs} +5 -5
  106. package/dist/shared/{ccjk.Bndhan7G.mjs → ccjk.DfwJOEok.mjs} +1 -1
  107. package/dist/shared/{ccjk.DvIrK0wz.mjs → ccjk.DrMygfCF.mjs} +1 -1
  108. package/dist/shared/{ccjk.CmsW23FN.mjs → ccjk.IbImMVWM.mjs} +3 -3
  109. package/package.json +19 -19
@@ -0,0 +1,781 @@
1
+ import ansis from 'ansis';
2
+ import inquirer from 'inquirer';
3
+ import { writeFileSync } from 'node:fs';
4
+ import { join } from 'pathe';
5
+ import { i18n } from './index2.mjs';
6
+ import { getContextPersistence } from './persistence.mjs';
7
+ import 'node:process';
8
+ import 'node:url';
9
+ import 'i18next';
10
+ import 'i18next-fs-backend';
11
+ import 'better-sqlite3';
12
+
13
+ class L0Cache {
14
+ cache;
15
+ maxEntries;
16
+ maxSize;
17
+ currentSize;
18
+ hits;
19
+ misses;
20
+ constructor(maxEntries, maxSize) {
21
+ this.cache = /* @__PURE__ */ new Map();
22
+ this.maxEntries = maxEntries;
23
+ this.maxSize = maxSize;
24
+ this.currentSize = 0;
25
+ this.hits = 0;
26
+ this.misses = 0;
27
+ }
28
+ get(id) {
29
+ const entry = this.cache.get(id);
30
+ if (!entry) {
31
+ this.misses++;
32
+ return null;
33
+ }
34
+ entry.lastAccessed = Date.now();
35
+ entry.accessCount++;
36
+ this.cache.delete(id);
37
+ this.cache.set(id, entry);
38
+ this.hits++;
39
+ return entry;
40
+ }
41
+ set(id, context) {
42
+ const size = this.estimateSize(context.context);
43
+ while ((this.currentSize + size > this.maxSize || this.cache.size >= this.maxEntries) && this.cache.size > 0) {
44
+ this.evictLRU();
45
+ }
46
+ const existing = this.cache.get(id);
47
+ if (existing) {
48
+ this.currentSize -= this.estimateSize(existing.context);
49
+ this.cache.delete(id);
50
+ }
51
+ this.cache.set(id, context);
52
+ this.currentSize += size;
53
+ }
54
+ has(id) {
55
+ return this.cache.has(id);
56
+ }
57
+ delete(id) {
58
+ const entry = this.cache.get(id);
59
+ if (!entry) return false;
60
+ this.currentSize -= this.estimateSize(entry.context);
61
+ return this.cache.delete(id);
62
+ }
63
+ clear() {
64
+ this.cache.clear();
65
+ this.currentSize = 0;
66
+ this.hits = 0;
67
+ this.misses = 0;
68
+ }
69
+ getAll() {
70
+ return Array.from(this.cache.values());
71
+ }
72
+ getStats() {
73
+ const total = this.hits + this.misses;
74
+ return {
75
+ count: this.cache.size,
76
+ size: this.currentSize,
77
+ hitRate: total > 0 ? this.hits / total : 0
78
+ };
79
+ }
80
+ evictLRU() {
81
+ const firstKey = this.cache.keys().next().value;
82
+ if (firstKey) {
83
+ this.delete(firstKey);
84
+ }
85
+ }
86
+ estimateSize(context) {
87
+ let size = context.compressed.length * 2;
88
+ if (context.metadata) {
89
+ size += JSON.stringify(context.metadata).length * 2;
90
+ }
91
+ size += 200;
92
+ return size;
93
+ }
94
+ }
95
+ class HierarchicalContextLoader {
96
+ persistence;
97
+ projectHash;
98
+ config;
99
+ l0Cache;
100
+ tierMigrations;
101
+ constructor(persistence, projectHash, config) {
102
+ this.persistence = persistence;
103
+ this.projectHash = projectHash;
104
+ this.config = {
105
+ hotThreshold: 24 * 60 * 60 * 1e3,
106
+ // 1 day
107
+ warmThreshold: 7 * 24 * 60 * 60 * 1e3,
108
+ // 7 days
109
+ l0MaxEntries: 100,
110
+ l0MaxSize: 5 * 1024 * 1024,
111
+ // 5MB
112
+ ...config
113
+ };
114
+ this.l0Cache = new L0Cache(this.config.l0MaxEntries, this.config.l0MaxSize);
115
+ this.tierMigrations = {
116
+ hotToWarm: 0,
117
+ warmToCold: 0,
118
+ coldToWarm: 0,
119
+ warmToHot: 0
120
+ };
121
+ this.initializeL0Cache();
122
+ }
123
+ /**
124
+ * Get context by ID (checks all tiers)
125
+ */
126
+ async getContext(contextId) {
127
+ const l0Entry = this.l0Cache.get(contextId);
128
+ if (l0Entry) {
129
+ return l0Entry.context;
130
+ }
131
+ const persisted = this.persistence.getContext(contextId);
132
+ if (!persisted) return null;
133
+ const context = this.persistedToCompressed(persisted);
134
+ const tier = this.determineTier(persisted.lastAccessed);
135
+ if (tier === "L0" /* HOT */ || persisted.accessCount > 5) {
136
+ this.promoteToL0(contextId, context, persisted.lastAccessed, persisted.accessCount);
137
+ }
138
+ return context;
139
+ }
140
+ /**
141
+ * Get hot contexts (L0 tier, <1 day)
142
+ */
143
+ getHotContexts() {
144
+ return this.l0Cache.getAll();
145
+ }
146
+ /**
147
+ * Get warm contexts (L1 tier, 1-7 days)
148
+ */
149
+ getWarmContexts(limit) {
150
+ const now = Date.now();
151
+ const warmStart = now - this.config.warmThreshold;
152
+ const warmEnd = now - this.config.hotThreshold;
153
+ const contexts = this.persistence.getProjectContexts(this.projectHash, {
154
+ startTime: warmStart,
155
+ endTime: warmEnd,
156
+ sortBy: "lastAccessed",
157
+ sortOrder: "desc",
158
+ limit
159
+ });
160
+ return contexts.map((p) => this.persistedToTiered(p, "L1" /* WARM */));
161
+ }
162
+ /**
163
+ * Get cold contexts (L2 tier, >7 days)
164
+ */
165
+ getColdContexts(limit) {
166
+ const cutoff = Date.now() - this.config.warmThreshold;
167
+ const contexts = this.persistence.getProjectContexts(this.projectHash, {
168
+ endTime: cutoff,
169
+ sortBy: "lastAccessed",
170
+ sortOrder: "desc",
171
+ limit
172
+ });
173
+ return contexts.map((p) => this.persistedToTiered(p, "L2" /* COLD */));
174
+ }
175
+ /**
176
+ * Get contexts by tier
177
+ */
178
+ getContextsByTier(tier, limit) {
179
+ switch (tier) {
180
+ case "L0" /* HOT */:
181
+ return this.getHotContexts();
182
+ case "L1" /* WARM */:
183
+ return this.getWarmContexts(limit);
184
+ case "L2" /* COLD */:
185
+ return this.getColdContexts(limit);
186
+ }
187
+ }
188
+ /**
189
+ * Lazy load cold contexts (batch loading)
190
+ */
191
+ async lazyColdContexts(offset = 0, limit = 50) {
192
+ const cutoff = Date.now() - this.config.warmThreshold;
193
+ const contexts = this.persistence.queryContexts({
194
+ projectHash: this.projectHash,
195
+ endTime: cutoff,
196
+ sortBy: "lastAccessed",
197
+ sortOrder: "desc",
198
+ limit
199
+ });
200
+ const sliced = contexts.slice(offset, offset + limit);
201
+ return sliced.map((p) => this.persistedToTiered(p, "L2" /* COLD */));
202
+ }
203
+ /**
204
+ * Migrate contexts between tiers based on access patterns
205
+ */
206
+ migrateContexts() {
207
+ const now = Date.now();
208
+ let promoted = 0;
209
+ let demoted = 0;
210
+ const l0Contexts = this.l0Cache.getAll();
211
+ for (const entry of l0Contexts) {
212
+ const age = now - entry.lastAccessed;
213
+ if (age > this.config.hotThreshold) {
214
+ this.l0Cache.delete(entry.id);
215
+ this.tierMigrations.hotToWarm++;
216
+ demoted++;
217
+ }
218
+ }
219
+ const warmContexts = this.getWarmContexts(50);
220
+ for (const entry of warmContexts) {
221
+ if (entry.accessCount > 10) {
222
+ this.promoteToL0(entry.id, entry.context, entry.lastAccessed, entry.accessCount);
223
+ this.tierMigrations.warmToHot++;
224
+ promoted++;
225
+ }
226
+ }
227
+ return { promoted, demoted };
228
+ }
229
+ /**
230
+ * Get tier statistics
231
+ */
232
+ getStats() {
233
+ const l0Stats = this.l0Cache.getStats();
234
+ const now = Date.now();
235
+ const warmStart = now - this.config.warmThreshold;
236
+ const warmEnd = now - this.config.hotThreshold;
237
+ const l1Contexts = this.persistence.queryContexts({
238
+ projectHash: this.projectHash,
239
+ startTime: warmStart,
240
+ endTime: warmEnd
241
+ });
242
+ const l2Contexts = this.persistence.queryContexts({
243
+ projectHash: this.projectHash,
244
+ endTime: warmStart
245
+ });
246
+ return {
247
+ l0: l0Stats,
248
+ l1: {
249
+ count: l1Contexts.length,
250
+ avgAccessTime: this.calculateAvgAccessTime(l1Contexts)
251
+ },
252
+ l2: {
253
+ count: l2Contexts.length,
254
+ avgAccessTime: this.calculateAvgAccessTime(l2Contexts)
255
+ },
256
+ migrations: { ...this.tierMigrations }
257
+ };
258
+ }
259
+ /**
260
+ * Clear L0 cache
261
+ */
262
+ clearL0Cache() {
263
+ this.l0Cache.clear();
264
+ }
265
+ /**
266
+ * Refresh L0 cache with current hot contexts
267
+ */
268
+ refreshL0Cache() {
269
+ this.l0Cache.clear();
270
+ this.initializeL0Cache();
271
+ }
272
+ /**
273
+ * Initialize L0 cache with hot contexts from persistence
274
+ */
275
+ initializeL0Cache() {
276
+ const cutoff = Date.now() - this.config.hotThreshold;
277
+ const hotContexts = this.persistence.getProjectContexts(this.projectHash, {
278
+ startTime: cutoff,
279
+ sortBy: "lastAccessed",
280
+ sortOrder: "desc",
281
+ limit: this.config.l0MaxEntries
282
+ });
283
+ for (const persisted of hotContexts) {
284
+ const context = this.persistedToCompressed(persisted);
285
+ const tiered = {
286
+ id: persisted.id,
287
+ tier: "L0" /* HOT */,
288
+ lastAccessed: persisted.lastAccessed,
289
+ accessCount: persisted.accessCount,
290
+ context
291
+ };
292
+ this.l0Cache.set(persisted.id, tiered);
293
+ }
294
+ }
295
+ /**
296
+ * Promote context to L0 cache
297
+ */
298
+ promoteToL0(id, context, lastAccessed, accessCount) {
299
+ const tiered = {
300
+ id,
301
+ tier: "L0" /* HOT */,
302
+ lastAccessed,
303
+ accessCount,
304
+ context
305
+ };
306
+ this.l0Cache.set(id, tiered);
307
+ }
308
+ /**
309
+ * Determine tier based on last accessed time
310
+ */
311
+ determineTier(lastAccessed) {
312
+ const age = Date.now() - lastAccessed;
313
+ if (age < this.config.hotThreshold) {
314
+ return "L0" /* HOT */;
315
+ } else if (age < this.config.warmThreshold) {
316
+ return "L1" /* WARM */;
317
+ } else {
318
+ return "L2" /* COLD */;
319
+ }
320
+ }
321
+ /**
322
+ * Convert persisted context to compressed context
323
+ */
324
+ persistedToCompressed(persisted) {
325
+ return {
326
+ id: persisted.id,
327
+ compressed: persisted.compressed,
328
+ algorithm: persisted.algorithm,
329
+ strategy: persisted.strategy,
330
+ originalTokens: persisted.originalTokens,
331
+ compressedTokens: persisted.compressedTokens,
332
+ compressionRatio: persisted.compressionRatio,
333
+ metadata: JSON.parse(persisted.metadata),
334
+ compressedAt: persisted.timestamp
335
+ };
336
+ }
337
+ /**
338
+ * Convert persisted context to tiered context
339
+ */
340
+ persistedToTiered(persisted, tier) {
341
+ return {
342
+ id: persisted.id,
343
+ tier,
344
+ lastAccessed: persisted.lastAccessed,
345
+ accessCount: persisted.accessCount,
346
+ context: this.persistedToCompressed(persisted)
347
+ };
348
+ }
349
+ /**
350
+ * Calculate average access time for contexts
351
+ */
352
+ calculateAvgAccessTime(contexts) {
353
+ if (contexts.length === 0) return 0;
354
+ const now = Date.now();
355
+ const totalAge = contexts.reduce((sum, c) => sum + (now - c.lastAccessed), 0);
356
+ return totalAge / contexts.length;
357
+ }
358
+ }
359
+ function createHierarchicalLoader(persistence, projectHash, config) {
360
+ return new HierarchicalContextLoader(persistence, projectHash, config);
361
+ }
362
+
363
+ function formatBytes(bytes) {
364
+ if (bytes === 0) return "0 B";
365
+ const k = 1024;
366
+ const sizes = ["B", "KB", "MB", "GB"];
367
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
368
+ return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
369
+ }
370
+ function formatDate(timestamp) {
371
+ const date = new Date(timestamp);
372
+ return date.toLocaleString();
373
+ }
374
+ function formatDuration(ms) {
375
+ const seconds = Math.floor(ms / 1e3);
376
+ const minutes = Math.floor(seconds / 60);
377
+ const hours = Math.floor(minutes / 60);
378
+ const days = Math.floor(hours / 24);
379
+ if (days > 0) return `${days}d ${hours % 24}h`;
380
+ if (hours > 0) return `${hours}h ${minutes % 60}m`;
381
+ if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
382
+ return `${seconds}s`;
383
+ }
384
+ async function listContexts(projectHash) {
385
+ const isZh = i18n.language === "zh-CN";
386
+ const persistence = getContextPersistence();
387
+ console.log("");
388
+ console.log(ansis.bold.cyan(isZh ? "\u{1F4CB} \u5B58\u50A8\u7684\u4E0A\u4E0B\u6587\u5217\u8868" : "\u{1F4CB} Stored Contexts"));
389
+ console.log(ansis.dim("\u2500".repeat(80)));
390
+ const PAGE_SIZE = 20;
391
+ let offset = 0;
392
+ let showMore = true;
393
+ while (showMore) {
394
+ const contexts = persistence.queryContexts({
395
+ projectHash,
396
+ sortBy: "lastAccessed",
397
+ sortOrder: "desc",
398
+ limit: PAGE_SIZE
399
+ });
400
+ if (contexts.length === 0) {
401
+ console.log(ansis.yellow(isZh ? " \u6CA1\u6709\u627E\u5230\u4E0A\u4E0B\u6587" : " No contexts found"));
402
+ break;
403
+ }
404
+ const displayContexts = contexts.slice(offset, offset + PAGE_SIZE);
405
+ for (const ctx of displayContexts) {
406
+ const age = Date.now() - ctx.lastAccessed;
407
+ const ratio = (ctx.compressionRatio * 100).toFixed(1);
408
+ console.log(` ${ansis.green(ctx.id.substring(0, 8))}... ${ansis.dim("|")} ${ansis.cyan(ctx.algorithm)} ${ansis.dim("|")} ${ratio}% ${ansis.dim("|")} ${ansis.yellow(ctx.accessCount)} ${isZh ? "\u6B21\u8BBF\u95EE" : "accesses"} ${ansis.dim("|")} ${formatDuration(age)} ${isZh ? "\u524D" : "ago"}`);
409
+ }
410
+ if (offset + PAGE_SIZE < contexts.length) {
411
+ const { action } = await inquirer.prompt({
412
+ type: "list",
413
+ name: "action",
414
+ message: isZh ? "\u663E\u793A\u66F4\u591A?" : "Show more?",
415
+ choices: [
416
+ { name: isZh ? "\u4E0B\u4E00\u9875" : "Next page", value: "next" },
417
+ { name: isZh ? "\u8FD4\u56DE" : "Back", value: "back" }
418
+ ]
419
+ });
420
+ if (action === "next") {
421
+ offset += PAGE_SIZE;
422
+ } else {
423
+ showMore = false;
424
+ }
425
+ } else {
426
+ showMore = false;
427
+ }
428
+ }
429
+ console.log("");
430
+ }
431
+ async function searchContexts() {
432
+ const isZh = i18n.language === "zh-CN";
433
+ const persistence = getContextPersistence();
434
+ const { query } = await inquirer.prompt({
435
+ type: "input",
436
+ name: "query",
437
+ message: isZh ? '\u8F93\u5165\u641C\u7D22\u5173\u952E\u8BCD (\u652F\u6301 AND, OR, NOT, "\u77ED\u8BED"):' : 'Enter search query (supports AND, OR, NOT, "phrases"):',
438
+ validate: (value) => {
439
+ if (!value || value.trim().length === 0) {
440
+ return isZh ? "\u8BF7\u8F93\u5165\u641C\u7D22\u5173\u952E\u8BCD" : "Please enter a search query";
441
+ }
442
+ return true;
443
+ }
444
+ });
445
+ console.log("");
446
+ console.log(ansis.bold.cyan(isZh ? "\u{1F50D} \u641C\u7D22\u7ED3\u679C" : "\u{1F50D} Search Results"));
447
+ console.log(ansis.dim("\u2500".repeat(80)));
448
+ const results = persistence.searchContexts(query, {
449
+ sortBy: "relevance",
450
+ sortOrder: "desc",
451
+ limit: 50
452
+ });
453
+ if (results.length === 0) {
454
+ console.log(ansis.yellow(isZh ? " \u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u4E0A\u4E0B\u6587" : " No matching contexts found"));
455
+ } else {
456
+ for (const result of results) {
457
+ const ratio = (result.compressionRatio * 100).toFixed(1);
458
+ console.log(` ${ansis.green(result.id.substring(0, 8))}... ${ansis.dim("|")} ${ansis.cyan(result.algorithm)} ${ansis.dim("|")} ${ratio}% ${ansis.dim("|")} ${ansis.magenta(`rank: ${result.rank.toFixed(2)}`)}`);
459
+ if (result.snippet) {
460
+ console.log(` ${ansis.dim(result.snippet.replace(/<mark>/g, ansis.yellow.open).replace(/<\/mark>/g, ansis.yellow.close))}`);
461
+ }
462
+ }
463
+ }
464
+ console.log("");
465
+ }
466
+ async function viewContextDetails() {
467
+ const isZh = i18n.language === "zh-CN";
468
+ const persistence = getContextPersistence();
469
+ const { contextId } = await inquirer.prompt({
470
+ type: "input",
471
+ name: "contextId",
472
+ message: isZh ? "\u8F93\u5165\u4E0A\u4E0B\u6587 ID (\u5B8C\u6574\u6216\u524D\u7F00):" : "Enter context ID (full or prefix):",
473
+ validate: (value) => {
474
+ if (!value || value.trim().length === 0) {
475
+ return isZh ? "\u8BF7\u8F93\u5165\u4E0A\u4E0B\u6587 ID" : "Please enter a context ID";
476
+ }
477
+ return true;
478
+ }
479
+ });
480
+ const allContexts = persistence.queryContexts({ limit: 1e4 });
481
+ const context = allContexts.find((c) => c.id.startsWith(contextId.trim()));
482
+ if (!context) {
483
+ console.log(ansis.red(isZh ? " \u672A\u627E\u5230\u4E0A\u4E0B\u6587" : " Context not found"));
484
+ return;
485
+ }
486
+ console.log("");
487
+ console.log(ansis.bold.cyan(isZh ? "\u{1F4C4} \u4E0A\u4E0B\u6587\u8BE6\u60C5" : "\u{1F4C4} Context Details"));
488
+ console.log(ansis.dim("\u2500".repeat(80)));
489
+ console.log(` ${ansis.bold("ID:")} ${context.id}`);
490
+ console.log(` ${ansis.bold(isZh ? "\u9879\u76EE:" : "Project:")} ${context.projectHash}`);
491
+ console.log(` ${ansis.bold(isZh ? "\u7B97\u6CD5:" : "Algorithm:")} ${context.algorithm}`);
492
+ console.log(` ${ansis.bold(isZh ? "\u7B56\u7565:" : "Strategy:")} ${context.strategy}`);
493
+ console.log(` ${ansis.bold(isZh ? "\u539F\u59CB Tokens:" : "Original Tokens:")} ${context.originalTokens.toLocaleString()}`);
494
+ console.log(` ${ansis.bold(isZh ? "\u538B\u7F29\u540E Tokens:" : "Compressed Tokens:")} ${context.compressedTokens.toLocaleString()}`);
495
+ console.log(` ${ansis.bold(isZh ? "\u538B\u7F29\u7387:" : "Compression Ratio:")} ${(context.compressionRatio * 100).toFixed(1)}%`);
496
+ console.log(` ${ansis.bold(isZh ? "\u8BBF\u95EE\u6B21\u6570:" : "Access Count:")} ${context.accessCount}`);
497
+ console.log(` ${ansis.bold(isZh ? "\u521B\u5EFA\u65F6\u95F4:" : "Created:")} ${formatDate(context.timestamp)}`);
498
+ console.log(` ${ansis.bold(isZh ? "\u6700\u540E\u8BBF\u95EE:" : "Last Accessed:")} ${formatDate(context.lastAccessed)}`);
499
+ try {
500
+ const metadata = JSON.parse(context.metadata);
501
+ if (Object.keys(metadata).length > 0) {
502
+ console.log(` ${ansis.bold(isZh ? "\u5143\u6570\u636E:" : "Metadata:")}`);
503
+ for (const [key, value] of Object.entries(metadata)) {
504
+ console.log(` ${ansis.cyan(key)}: ${JSON.stringify(value)}`);
505
+ }
506
+ }
507
+ } catch {
508
+ }
509
+ console.log("");
510
+ }
511
+ async function exportContexts() {
512
+ const isZh = i18n.language === "zh-CN";
513
+ const persistence = getContextPersistence();
514
+ const { projectHash } = await inquirer.prompt({
515
+ type: "input",
516
+ name: "projectHash",
517
+ message: isZh ? "\u8F93\u5165\u9879\u76EE\u54C8\u5E0C (\u7559\u7A7A\u5BFC\u51FA\u5168\u90E8):" : "Enter project hash (leave empty for all):"
518
+ });
519
+ const { outputPath } = await inquirer.prompt({
520
+ type: "input",
521
+ name: "outputPath",
522
+ message: isZh ? "\u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84:" : "Output file path:",
523
+ default: join(process.cwd(), "contexts-export.json")
524
+ });
525
+ const contexts = persistence.exportContexts(projectHash || void 0);
526
+ writeFileSync(outputPath, JSON.stringify(contexts, null, 2), "utf-8");
527
+ console.log("");
528
+ console.log(ansis.green(`\u2714 ${isZh ? "\u5DF2\u5BFC\u51FA" : "Exported"} ${contexts.length} ${isZh ? "\u4E2A\u4E0A\u4E0B\u6587\u5230" : "contexts to"} ${outputPath}`));
529
+ console.log("");
530
+ }
531
+ async function importContexts() {
532
+ const isZh = i18n.language === "zh-CN";
533
+ const persistence = getContextPersistence();
534
+ const { inputPath } = await inquirer.prompt({
535
+ type: "input",
536
+ name: "inputPath",
537
+ message: isZh ? "\u8F93\u5165\u6587\u4EF6\u8DEF\u5F84:" : "Input file path:",
538
+ validate: (value) => {
539
+ if (!value || value.trim().length === 0) {
540
+ return isZh ? "\u8BF7\u8F93\u5165\u6587\u4EF6\u8DEF\u5F84" : "Please enter a file path";
541
+ }
542
+ return true;
543
+ }
544
+ });
545
+ try {
546
+ const fs = require("node:fs");
547
+ const content = fs.readFileSync(inputPath, "utf-8");
548
+ const contexts = JSON.parse(content);
549
+ const imported = persistence.importContexts(contexts);
550
+ console.log("");
551
+ console.log(ansis.green(`\u2714 ${isZh ? "\u5DF2\u5BFC\u5165" : "Imported"} ${imported} ${isZh ? "\u4E2A\u4E0A\u4E0B\u6587" : "contexts"}`));
552
+ console.log("");
553
+ } catch (error) {
554
+ console.log("");
555
+ console.log(ansis.red(`\u2716 ${isZh ? "\u5BFC\u5165\u5931\u8D25:" : "Import failed:"} ${error instanceof Error ? error.message : String(error)}`));
556
+ console.log("");
557
+ }
558
+ }
559
+ async function clearOldContexts() {
560
+ const isZh = i18n.language === "zh-CN";
561
+ const persistence = getContextPersistence();
562
+ const { ageChoice } = await inquirer.prompt({
563
+ type: "list",
564
+ name: "ageChoice",
565
+ message: isZh ? "\u6E05\u7406\u591A\u4E45\u4E4B\u524D\u7684\u4E0A\u4E0B\u6587?" : "Clear contexts older than:",
566
+ choices: [
567
+ { name: isZh ? "7 \u5929" : "7 days", value: "7" },
568
+ { name: isZh ? "30 \u5929" : "30 days", value: "30" },
569
+ { name: isZh ? "90 \u5929" : "90 days", value: "90" },
570
+ { name: isZh ? "180 \u5929" : "180 days", value: "180" },
571
+ { name: isZh ? "\u81EA\u5B9A\u4E49" : "Custom", value: "custom" }
572
+ ]
573
+ });
574
+ let days;
575
+ if (ageChoice === "custom") {
576
+ const { customDays } = await inquirer.prompt({
577
+ type: "input",
578
+ name: "customDays",
579
+ message: isZh ? "\u8F93\u5165\u5929\u6570:" : "Enter days:",
580
+ validate: (value) => {
581
+ const num = parseInt(value, 10);
582
+ if (isNaN(num) || num <= 0) {
583
+ return isZh ? "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u5929\u6570" : "Please enter a valid number of days";
584
+ }
585
+ return true;
586
+ }
587
+ });
588
+ days = parseInt(customDays, 10);
589
+ } else {
590
+ days = parseInt(ageChoice, 10);
591
+ }
592
+ const maxAge = days * 24 * 60 * 60 * 1e3;
593
+ const { confirm } = await inquirer.prompt({
594
+ type: "confirm",
595
+ name: "confirm",
596
+ message: isZh ? `\u786E\u8BA4\u6E05\u7406 ${days} \u5929\u524D\u7684\u4E0A\u4E0B\u6587?` : `Confirm clearing contexts older than ${days} days?`,
597
+ default: false
598
+ });
599
+ if (!confirm) {
600
+ console.log(ansis.yellow(isZh ? " \u5DF2\u53D6\u6D88" : " Cancelled"));
601
+ return;
602
+ }
603
+ const deleted = persistence.cleanup(maxAge);
604
+ console.log("");
605
+ console.log(ansis.green(`\u2714 ${isZh ? "\u5DF2\u6E05\u7406" : "Cleared"} ${deleted} ${isZh ? "\u4E2A\u4E0A\u4E0B\u6587" : "contexts"}`));
606
+ console.log("");
607
+ }
608
+ async function viewTierDistribution() {
609
+ const isZh = i18n.language === "zh-CN";
610
+ const persistence = getContextPersistence();
611
+ const { projectHash } = await inquirer.prompt({
612
+ type: "input",
613
+ name: "projectHash",
614
+ message: isZh ? "\u8F93\u5165\u9879\u76EE\u54C8\u5E0C (\u7559\u7A7A\u67E5\u770B\u5168\u90E8):" : "Enter project hash (leave empty for all):"
615
+ });
616
+ const loader = createHierarchicalLoader(
617
+ persistence,
618
+ projectHash || "global"
619
+ );
620
+ const stats = loader.getStats();
621
+ console.log("");
622
+ console.log(ansis.bold.cyan(isZh ? "\u{1F4CA} \u5C42\u7EA7\u5206\u5E03\u7EDF\u8BA1" : "\u{1F4CA} Tier Distribution"));
623
+ console.log(ansis.dim("\u2500".repeat(80)));
624
+ console.log(` ${ansis.bold.red("L0 (Hot)")} ${ansis.dim("- <1 day, in-memory cache")}`);
625
+ console.log(` ${ansis.cyan(isZh ? "\u6570\u91CF:" : "Count:")} ${stats.l0.count}`);
626
+ console.log(` ${ansis.cyan(isZh ? "\u5927\u5C0F:" : "Size:")} ${formatBytes(stats.l0.size)}`);
627
+ console.log(` ${ansis.cyan(isZh ? "\u547D\u4E2D\u7387:" : "Hit Rate:")} ${(stats.l0.hitRate * 100).toFixed(1)}%`);
628
+ console.log(` ${ansis.bold.yellow("L1 (Warm)")} ${ansis.dim("- 1-7 days, indexed in DB")}`);
629
+ console.log(` ${ansis.cyan(isZh ? "\u6570\u91CF:" : "Count:")} ${stats.l1.count}`);
630
+ console.log(` ${ansis.cyan(isZh ? "\u5E73\u5747\u8BBF\u95EE\u65F6\u95F4:" : "Avg Access Time:")} ${formatDuration(stats.l1.avgAccessTime)}`);
631
+ console.log(` ${ansis.bold.blue("L2 (Cold)")} ${ansis.dim("- >7 days, lazy-loaded")}`);
632
+ console.log(` ${ansis.cyan(isZh ? "\u6570\u91CF:" : "Count:")} ${stats.l2.count}`);
633
+ console.log(` ${ansis.cyan(isZh ? "\u5E73\u5747\u8BBF\u95EE\u65F6\u95F4:" : "Avg Access Time:")} ${formatDuration(stats.l2.avgAccessTime)}`);
634
+ console.log(` ${ansis.bold.magenta(isZh ? "\u5C42\u7EA7\u8FC1\u79FB:" : "Tier Migrations:")}`);
635
+ console.log(` ${ansis.dim("Hot \u2192 Warm:")} ${stats.migrations.hotToWarm}`);
636
+ console.log(` ${ansis.dim("Warm \u2192 Cold:")} ${stats.migrations.warmToCold}`);
637
+ console.log(` ${ansis.dim("Cold \u2192 Warm:")} ${stats.migrations.coldToWarm}`);
638
+ console.log(` ${ansis.dim("Warm \u2192 Hot:")} ${stats.migrations.warmToHot}`);
639
+ console.log("");
640
+ }
641
+ async function migrateTiers() {
642
+ const isZh = i18n.language === "zh-CN";
643
+ const persistence = getContextPersistence();
644
+ const { projectHash } = await inquirer.prompt({
645
+ type: "input",
646
+ name: "projectHash",
647
+ message: isZh ? "\u8F93\u5165\u9879\u76EE\u54C8\u5E0C (\u7559\u7A7A\u8FC1\u79FB\u5168\u90E8):" : "Enter project hash (leave empty for all):"
648
+ });
649
+ const loader = createHierarchicalLoader(
650
+ persistence,
651
+ projectHash || "global"
652
+ );
653
+ console.log("");
654
+ console.log(ansis.cyan(isZh ? "\u6B63\u5728\u8FC1\u79FB\u5C42\u7EA7..." : "Migrating tiers..."));
655
+ const result = loader.migrateContexts();
656
+ console.log("");
657
+ console.log(ansis.green(`\u2714 ${isZh ? "\u8FC1\u79FB\u5B8C\u6210" : "Migration complete"}`));
658
+ console.log(` ${ansis.cyan(isZh ? "\u63D0\u5347:" : "Promoted:")} ${result.promoted}`);
659
+ console.log(` ${ansis.cyan(isZh ? "\u964D\u7EA7:" : "Demoted:")} ${result.demoted}`);
660
+ console.log("");
661
+ }
662
+ async function viewDatabaseStats() {
663
+ const isZh = i18n.language === "zh-CN";
664
+ const persistence = getContextPersistence();
665
+ const stats = persistence.getStats();
666
+ console.log("");
667
+ console.log(ansis.bold.cyan(isZh ? "\u{1F4C8} \u6570\u636E\u5E93\u7EDF\u8BA1" : "\u{1F4C8} Database Statistics"));
668
+ console.log(ansis.dim("\u2500".repeat(80)));
669
+ console.log(` ${ansis.bold(isZh ? "\u603B\u4E0A\u4E0B\u6587\u6570:" : "Total Contexts:")} ${stats.totalContexts.toLocaleString()}`);
670
+ console.log(` ${ansis.bold(isZh ? "\u603B\u9879\u76EE\u6570:" : "Total Projects:")} ${stats.totalProjects.toLocaleString()}`);
671
+ console.log(` ${ansis.bold(isZh ? "\u539F\u59CB Tokens:" : "Original Tokens:")} ${stats.totalOriginalTokens.toLocaleString()}`);
672
+ console.log(` ${ansis.bold(isZh ? "\u538B\u7F29\u540E Tokens:" : "Compressed Tokens:")} ${stats.totalCompressedTokens.toLocaleString()}`);
673
+ console.log(` ${ansis.bold(isZh ? "\u5E73\u5747\u538B\u7F29\u7387:" : "Avg Compression:")} ${(stats.averageCompressionRatio * 100).toFixed(1)}%`);
674
+ console.log(` ${ansis.bold(isZh ? "\u6570\u636E\u5E93\u5927\u5C0F:" : "Database Size:")} ${formatBytes(stats.totalSize)}`);
675
+ if (stats.oldestContext) {
676
+ console.log(` ${ansis.bold(isZh ? "\u6700\u65E9\u4E0A\u4E0B\u6587:" : "Oldest Context:")} ${formatDate(stats.oldestContext)}`);
677
+ }
678
+ if (stats.newestContext) {
679
+ console.log(` ${ansis.bold(isZh ? "\u6700\u65B0\u4E0A\u4E0B\u6587:" : "Newest Context:")} ${formatDate(stats.newestContext)}`);
680
+ }
681
+ const saved = stats.totalOriginalTokens - stats.totalCompressedTokens;
682
+ console.log(` ${ansis.bold.green(isZh ? "\u8282\u7701 Tokens:" : "Tokens Saved:")} ${saved.toLocaleString()} (${(stats.averageCompressionRatio * 100).toFixed(1)}%)`);
683
+ console.log("");
684
+ }
685
+ async function vacuumDatabase() {
686
+ const isZh = i18n.language === "zh-CN";
687
+ const persistence = getContextPersistence();
688
+ const { confirm } = await inquirer.prompt({
689
+ type: "confirm",
690
+ name: "confirm",
691
+ message: isZh ? "\u786E\u8BA4\u538B\u7F29\u6570\u636E\u5E93? (\u8FD9\u53EF\u80FD\u9700\u8981\u4E00\u4E9B\u65F6\u95F4)" : "Confirm vacuum database? (This may take some time)",
692
+ default: false
693
+ });
694
+ if (!confirm) {
695
+ console.log(ansis.yellow(isZh ? " \u5DF2\u53D6\u6D88" : " Cancelled"));
696
+ return;
697
+ }
698
+ console.log("");
699
+ console.log(ansis.cyan(isZh ? "\u6B63\u5728\u538B\u7F29\u6570\u636E\u5E93..." : "Vacuuming database..."));
700
+ persistence.vacuum();
701
+ console.log("");
702
+ console.log(ansis.green(`\u2714 ${isZh ? "\u6570\u636E\u5E93\u538B\u7F29\u5B8C\u6210" : "Database vacuum complete"}`));
703
+ console.log("");
704
+ }
705
+ async function showPersistenceMenu() {
706
+ const isZh = i18n.language === "zh-CN";
707
+ console.log("");
708
+ console.log(ansis.bold.yellow(isZh ? "\u{1F4BE} \u6301\u4E45\u5316\u7BA1\u7406\u5668" : "\u{1F4BE} Persistence Manager"));
709
+ console.log(ansis.dim("\u2500".repeat(80)));
710
+ console.log(` ${ansis.green("1.")} ${isZh ? "\u5217\u51FA\u5B58\u50A8\u7684\u4E0A\u4E0B\u6587" : "List stored contexts"} ${ansis.dim(isZh ? "- \u5206\u9875\u663E\u793A\u6240\u6709\u4E0A\u4E0B\u6587" : "- Paginated view of all contexts")}`);
711
+ console.log(` ${ansis.green("2.")} ${isZh ? "\u641C\u7D22\u4E0A\u4E0B\u6587" : "Search contexts"} ${ansis.dim(isZh ? "- \u5168\u6587\u641C\u7D22 (FTS5)" : "- Full-text search (FTS5)")}`);
712
+ console.log(` ${ansis.green("3.")} ${isZh ? "\u67E5\u770B\u4E0A\u4E0B\u6587\u8BE6\u60C5" : "View context details"} ${ansis.dim(isZh ? "- \u663E\u793A\u5143\u6570\u636E\u548C\u7EDF\u8BA1" : "- Show metadata and stats")}`);
713
+ console.log(` ${ansis.green("4.")} ${isZh ? "\u5BFC\u51FA\u4E0A\u4E0B\u6587" : "Export contexts"} ${ansis.dim(isZh ? "- \u5BFC\u51FA\u4E3A JSON \u683C\u5F0F" : "- Export to JSON format")}`);
714
+ console.log(` ${ansis.green("5.")} ${isZh ? "\u5BFC\u5165\u4E0A\u4E0B\u6587" : "Import contexts"} ${ansis.dim(isZh ? "- \u4ECE JSON \u5BFC\u5165" : "- Import from JSON")}`);
715
+ console.log(` ${ansis.green("6.")} ${isZh ? "\u6E05\u7406\u65E7\u4E0A\u4E0B\u6587" : "Clear old contexts"} ${ansis.dim(isZh ? "- \u6309\u65F6\u95F4\u6E05\u7406" : "- Clear by age")}`);
716
+ console.log(` ${ansis.green("7.")} ${isZh ? "\u67E5\u770B\u5C42\u7EA7\u5206\u5E03" : "View tier distribution"} ${ansis.dim(isZh ? "- L0/L1/L2 \u7EDF\u8BA1" : "- L0/L1/L2 stats")}`);
717
+ console.log(` ${ansis.green("8.")} ${isZh ? "\u624B\u52A8\u8FC1\u79FB\u5C42\u7EA7" : "Migrate tiers manually"} ${ansis.dim(isZh ? "- \u4F18\u5316\u5C42\u7EA7\u5206\u5E03" : "- Optimize tier distribution")}`);
718
+ console.log(` ${ansis.green("9.")} ${isZh ? "\u6570\u636E\u5E93\u7EDF\u8BA1" : "Database statistics"} ${ansis.dim(isZh ? "- \u67E5\u770B\u5168\u5C40\u7EDF\u8BA1" : "- View global stats")}`);
719
+ console.log(` ${ansis.green("V.")} ${isZh ? "\u538B\u7F29\u6570\u636E\u5E93" : "Vacuum database"} ${ansis.dim(isZh ? "- \u56DE\u6536\u7A7A\u95F4" : "- Reclaim space")}`);
720
+ console.log(` ${ansis.green("Q.")} ${isZh ? "\u8FD4\u56DE" : "Back"}`);
721
+ console.log("");
722
+ const { choice } = await inquirer.prompt({
723
+ type: "input",
724
+ name: "choice",
725
+ message: isZh ? "\u8BF7\u9009\u62E9\u64CD\u4F5C:" : "Select operation:",
726
+ validate: (value) => {
727
+ const normalized2 = value.trim().toLowerCase();
728
+ const valid = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "v", "q"];
729
+ return valid.includes(normalized2) || (isZh ? "\u8BF7\u8F93\u5165\u6709\u6548\u9009\u9879" : "Please enter a valid option");
730
+ }
731
+ });
732
+ const normalized = choice.trim().toLowerCase();
733
+ switch (normalized) {
734
+ case "1":
735
+ await listContexts();
736
+ break;
737
+ case "2":
738
+ await searchContexts();
739
+ break;
740
+ case "3":
741
+ await viewContextDetails();
742
+ break;
743
+ case "4":
744
+ await exportContexts();
745
+ break;
746
+ case "5":
747
+ await importContexts();
748
+ break;
749
+ case "6":
750
+ await clearOldContexts();
751
+ break;
752
+ case "7":
753
+ await viewTierDistribution();
754
+ break;
755
+ case "8":
756
+ await migrateTiers();
757
+ break;
758
+ case "9":
759
+ await viewDatabaseStats();
760
+ break;
761
+ case "v":
762
+ await vacuumDatabase();
763
+ break;
764
+ case "q":
765
+ return false;
766
+ }
767
+ return true;
768
+ }
769
+ async function persistenceManager() {
770
+ try {
771
+ let continueMenu = true;
772
+ while (continueMenu) {
773
+ continueMenu = await showPersistenceMenu();
774
+ }
775
+ } catch (error) {
776
+ const isZh = i18n.language === "zh-CN";
777
+ console.error(ansis.red(`${isZh ? "\u9519\u8BEF:" : "Error:"} ${error instanceof Error ? error.message : String(error)}`));
778
+ }
779
+ }
780
+
781
+ export { persistenceManager };