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.
- package/dist/chunks/agent-teams.mjs +1 -1
- package/dist/chunks/agent.mjs +1 -1
- package/dist/chunks/api-providers.mjs +1 -1
- package/dist/chunks/api.mjs +3 -3
- package/dist/chunks/auto-bootstrap.mjs +1 -1
- package/dist/chunks/auto-updater.mjs +1 -1
- package/dist/{shared/ccjk.Br91zBIG.mjs → chunks/banner.mjs} +52 -3
- package/dist/chunks/boost.mjs +2 -2
- package/dist/chunks/ccjk-agents.mjs +1 -1
- package/dist/chunks/ccjk-all.mjs +1 -1
- package/dist/chunks/ccjk-config.mjs +1 -1
- package/dist/chunks/ccjk-hooks.mjs +1 -1
- package/dist/chunks/ccjk-mcp.mjs +2 -2
- package/dist/chunks/ccjk-setup.mjs +1 -1
- package/dist/chunks/ccjk-skills.mjs +1 -1
- package/dist/chunks/ccr.mjs +10 -10
- package/dist/chunks/ccu.mjs +1 -1
- package/dist/chunks/check-updates.mjs +3 -3
- package/dist/chunks/claude-code-config-manager.mjs +7 -7
- package/dist/chunks/claude-code-incremental-manager.mjs +2 -2
- package/dist/chunks/claude-config.mjs +3 -3
- package/dist/chunks/claude-wrapper.mjs +2 -2
- package/dist/chunks/codex-config-switch.mjs +2 -2
- package/dist/chunks/codex-provider-manager.mjs +2 -2
- package/dist/chunks/codex-uninstaller.mjs +2 -2
- package/dist/chunks/codex.mjs +5 -5
- package/dist/chunks/commands.mjs +88 -391
- package/dist/chunks/commands2.mjs +391 -88
- package/dist/chunks/completion.mjs +1 -1
- package/dist/chunks/config-consolidator.mjs +2 -2
- package/dist/chunks/config-switch.mjs +3 -3
- package/dist/chunks/config.mjs +5 -5
- package/dist/chunks/config2.mjs +410 -400
- package/dist/chunks/config3.mjs +400 -410
- package/dist/chunks/constants.mjs +1 -1
- package/dist/chunks/context.mjs +283 -1
- package/dist/chunks/dashboard.mjs +365 -0
- package/dist/chunks/doctor.mjs +4 -4
- package/dist/chunks/features.mjs +11 -11
- package/dist/chunks/fs-operations.mjs +1 -1
- package/dist/chunks/health-alerts.mjs +304 -0
- package/dist/chunks/health-check.mjs +532 -0
- package/dist/chunks/index.mjs +10 -177
- package/dist/chunks/index2.mjs +168 -1162
- package/dist/chunks/index3.mjs +1076 -910
- package/dist/chunks/index4.mjs +947 -137
- package/dist/chunks/index5.mjs +167 -635
- package/dist/chunks/index6.mjs +663 -0
- package/dist/chunks/init.mjs +19 -19
- package/dist/chunks/installer.mjs +649 -147
- package/dist/chunks/installer2.mjs +147 -649
- package/dist/chunks/interview.mjs +2 -2
- package/dist/chunks/marketplace.mjs +1 -1
- package/dist/chunks/mcp.mjs +4 -4
- package/dist/chunks/menu.mjs +16 -9
- package/dist/chunks/metrics-display.mjs +152 -0
- package/dist/chunks/migrator.mjs +1 -1
- package/dist/chunks/monitor.mjs +2 -2
- package/dist/chunks/notification.mjs +1 -1
- package/dist/chunks/onboarding.mjs +2 -2
- package/dist/chunks/package.mjs +1 -1
- package/dist/chunks/permission-manager.mjs +2 -2
- package/dist/chunks/permissions.mjs +1 -1
- package/dist/chunks/persistence-manager.mjs +781 -0
- package/dist/chunks/persistence.mjs +667 -0
- package/dist/chunks/platform.mjs +1 -1
- package/dist/chunks/plugin.mjs +1 -1
- package/dist/chunks/prompts.mjs +1 -1
- package/dist/chunks/providers.mjs +1 -1
- package/dist/chunks/quick-actions.mjs +321 -0
- package/dist/chunks/quick-setup.mjs +8 -8
- package/dist/chunks/silent-updater.mjs +1 -1
- package/dist/chunks/simple-config.mjs +2 -2
- package/dist/chunks/skill.mjs +1 -1
- package/dist/chunks/skills-sync.mjs +1 -1
- package/dist/chunks/skills.mjs +1 -1
- package/dist/chunks/slash-commands.mjs +208 -0
- package/dist/chunks/smart-defaults.mjs +1 -1
- package/dist/chunks/startup.mjs +1 -1
- package/dist/chunks/stats.mjs +1 -1
- package/dist/chunks/status.mjs +31 -2
- package/dist/chunks/team.mjs +1 -1
- package/dist/chunks/thinking.mjs +2 -2
- package/dist/chunks/uninstall.mjs +5 -5
- package/dist/chunks/update.mjs +7 -7
- package/dist/chunks/upgrade-manager.mjs +2 -2
- package/dist/chunks/version-checker.mjs +3 -3
- package/dist/chunks/vim.mjs +1 -1
- package/dist/cli.mjs +191 -21
- package/dist/i18n/locales/en/cli.json +14 -1
- package/dist/i18n/locales/en/common.json +27 -0
- package/dist/i18n/locales/en/context.json +54 -1
- package/dist/i18n/locales/en/dashboard.json +78 -0
- package/dist/i18n/locales/en/persistence.json +127 -0
- package/dist/i18n/locales/en/quick-actions.json +78 -0
- package/dist/i18n/locales/zh-CN/cli.json +14 -1
- package/dist/i18n/locales/zh-CN/common.json +27 -0
- package/dist/i18n/locales/zh-CN/context.json +54 -1
- package/dist/i18n/locales/zh-CN/dashboard.json +78 -0
- package/dist/i18n/locales/zh-CN/persistence.json +127 -0
- package/dist/i18n/locales/zh-CN/quick-actions.json +78 -0
- package/dist/index.mjs +2 -2
- package/dist/shared/{ccjk.DE91nClQ.mjs → ccjk.BKoi8-Hy.mjs} +1 -1
- package/dist/shared/{ccjk.Dpw86UX0.mjs → ccjk.CxtuJxaS.mjs} +1 -1
- package/dist/shared/{ccjk.ClzTOz9n.mjs → ccjk.DB2UYcq0.mjs} +5 -5
- package/dist/shared/{ccjk.Bndhan7G.mjs → ccjk.DfwJOEok.mjs} +1 -1
- package/dist/shared/{ccjk.DvIrK0wz.mjs → ccjk.DrMygfCF.mjs} +1 -1
- package/dist/shared/{ccjk.CmsW23FN.mjs → ccjk.IbImMVWM.mjs} +3 -3
- 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 };
|