ccjk 2.4.3 → 2.5.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 (53) hide show
  1. package/dist/chunks/api-providers.mjs +73 -1
  2. package/dist/chunks/ccjk-config.mjs +13 -77
  3. package/dist/chunks/ccr.mjs +9 -4
  4. package/dist/chunks/check-updates.mjs +4 -2
  5. package/dist/chunks/claude-code-config-manager.mjs +9 -15
  6. package/dist/chunks/claude-code-incremental-manager.mjs +5 -8
  7. package/dist/chunks/codex.mjs +10 -569
  8. package/dist/chunks/config-switch.mjs +7 -5
  9. package/dist/chunks/config.mjs +573 -0
  10. package/dist/chunks/config2.mjs +451 -0
  11. package/dist/chunks/doctor.mjs +89 -1
  12. package/dist/chunks/features.mjs +13 -10
  13. package/dist/chunks/index.mjs +10 -1164
  14. package/dist/chunks/index2.mjs +8 -2
  15. package/dist/chunks/init.mjs +14 -11
  16. package/dist/chunks/json-config.mjs +59 -0
  17. package/dist/chunks/mcp-server.mjs +776 -0
  18. package/dist/chunks/mcp.mjs +10 -8
  19. package/dist/chunks/menu.mjs +5 -5
  20. package/dist/chunks/package.mjs +1 -1
  21. package/dist/chunks/permissions.mjs +420 -0
  22. package/dist/chunks/prompts.mjs +2 -1
  23. package/dist/chunks/providers.mjs +261 -0
  24. package/dist/chunks/session.mjs +484 -41
  25. package/dist/chunks/skills.mjs +553 -0
  26. package/dist/chunks/stats.mjs +411 -0
  27. package/dist/chunks/uninstall.mjs +4 -3
  28. package/dist/chunks/update.mjs +6 -3
  29. package/dist/chunks/workflows2.mjs +140 -0
  30. package/dist/cli.mjs +316 -10
  31. package/dist/i18n/locales/en/hooks.json +47 -0
  32. package/dist/i18n/locales/en/mcp.json +55 -0
  33. package/dist/i18n/locales/en/permissions.json +43 -0
  34. package/dist/i18n/locales/en/sandbox.json +44 -0
  35. package/dist/i18n/locales/en/skills.json +89 -129
  36. package/dist/i18n/locales/en/stats.json +20 -0
  37. package/dist/i18n/locales/zh-CN/hooks.json +47 -0
  38. package/dist/i18n/locales/zh-CN/mcp.json +55 -0
  39. package/dist/i18n/locales/zh-CN/permissions.json +43 -0
  40. package/dist/i18n/locales/zh-CN/sandbox.json +44 -0
  41. package/dist/i18n/locales/zh-CN/skills.json +88 -128
  42. package/dist/i18n/locales/zh-CN/stats.json +20 -0
  43. package/dist/index.mjs +12 -8
  44. package/dist/shared/ccjk.B-lZxV2u.mjs +1162 -0
  45. package/dist/shared/{ccjk.CURU8gbR.mjs → ccjk.CUdzQluX.mjs} +1 -1
  46. package/dist/shared/{ccjk.ByTIGCUC.mjs → ccjk.Dut3wyoP.mjs} +1 -1
  47. package/dist/shared/ccjk.J8YiPsOw.mjs +259 -0
  48. package/dist/shared/{ccjk.CGTmRqsu.mjs → ccjk.rLRHmcqD.mjs} +5 -134
  49. package/dist/shared/{ccjk.QbS8EAOd.mjs → ccjk.uVUeWAt8.mjs} +2 -1
  50. package/package.json +1 -1
  51. package/templates/common/skills/code-review.md +343 -0
  52. package/templates/common/skills/summarize.md +312 -0
  53. package/templates/common/skills/translate.md +202 -0
@@ -1,14 +1,304 @@
1
1
  import { existsSync } from 'node:fs';
2
- import { mkdir, writeFile, readdir, readFile, rm, stat } from 'node:fs/promises';
2
+ import { mkdir, writeFile, readFile, readdir, rm, stat } from 'node:fs/promises';
3
3
  import { homedir } from 'node:os';
4
- import { join } from 'node:path';
4
+ import { join as join$1 } from 'node:path';
5
5
  import process__default from 'node:process';
6
6
  import ansis from 'ansis';
7
7
  import inquirer from 'inquirer';
8
+ import { join } from 'pathe';
8
9
 
9
- const SESSIONS_DIR = join(homedir(), ".ccjk", "sessions");
10
- const CCJK_DIR = join(homedir(), ".ccjk");
11
- const CLAUDE_DIR = join(homedir(), ".claude");
10
+ class SessionManager {
11
+ sessionsDir;
12
+ autoCleanupDays;
13
+ constructor(options = {}) {
14
+ this.sessionsDir = options.sessionsDir || join(homedir(), ".ccjk", "sessions");
15
+ this.autoCleanupDays = options.autoCleanupDays || 30;
16
+ }
17
+ /**
18
+ * Initialize sessions directory
19
+ */
20
+ async ensureSessionsDir() {
21
+ if (!existsSync(this.sessionsDir)) {
22
+ await mkdir(this.sessionsDir, { recursive: true });
23
+ }
24
+ }
25
+ /**
26
+ * Generate unique session ID
27
+ */
28
+ generateSessionId() {
29
+ return `session-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
30
+ }
31
+ /**
32
+ * Get session file path
33
+ */
34
+ getSessionPath(sessionId) {
35
+ return join(this.sessionsDir, `${sessionId}.json`);
36
+ }
37
+ /**
38
+ * Create a new session
39
+ */
40
+ async createSession(name, provider, apiKey, options) {
41
+ await this.ensureSessionsDir();
42
+ const session = {
43
+ id: this.generateSessionId(),
44
+ name,
45
+ provider,
46
+ apiKey,
47
+ apiUrl: options?.apiUrl,
48
+ model: options?.model,
49
+ codeType: options?.codeType,
50
+ createdAt: /* @__PURE__ */ new Date(),
51
+ lastUsedAt: /* @__PURE__ */ new Date(),
52
+ history: [],
53
+ metadata: options?.metadata
54
+ };
55
+ await this.saveSession(session);
56
+ return session;
57
+ }
58
+ /**
59
+ * Save session to storage
60
+ */
61
+ async saveSession(session) {
62
+ await this.ensureSessionsDir();
63
+ const sessionPath = this.getSessionPath(session.id);
64
+ const sessionData = {
65
+ ...session,
66
+ createdAt: session.createdAt.toISOString(),
67
+ lastUsedAt: session.lastUsedAt.toISOString(),
68
+ history: session.history.map((entry) => ({
69
+ ...entry,
70
+ timestamp: entry.timestamp.toISOString()
71
+ }))
72
+ };
73
+ await writeFile(sessionPath, JSON.stringify(sessionData, null, 2), "utf-8");
74
+ }
75
+ /**
76
+ * Load session by ID or name
77
+ */
78
+ async loadSession(nameOrId) {
79
+ await this.ensureSessionsDir();
80
+ let sessionPath = this.getSessionPath(nameOrId);
81
+ if (!existsSync(sessionPath)) {
82
+ const sessions = await this.listSessions();
83
+ const found = sessions.find((s) => s.name === nameOrId);
84
+ if (!found) {
85
+ return null;
86
+ }
87
+ sessionPath = this.getSessionPath(found.id);
88
+ }
89
+ try {
90
+ const content = await readFile(sessionPath, "utf-8");
91
+ const data = JSON.parse(content);
92
+ return {
93
+ ...data,
94
+ createdAt: new Date(data.createdAt),
95
+ lastUsedAt: new Date(data.lastUsedAt),
96
+ history: data.history.map((entry) => ({
97
+ ...entry,
98
+ timestamp: new Date(entry.timestamp)
99
+ }))
100
+ };
101
+ } catch (error) {
102
+ console.error(`Failed to load session ${nameOrId}:`, error);
103
+ return null;
104
+ }
105
+ }
106
+ /**
107
+ * List all sessions
108
+ */
109
+ async listSessions(options = {}) {
110
+ await this.ensureSessionsDir();
111
+ try {
112
+ const files = await readdir(this.sessionsDir);
113
+ const sessionFiles = files.filter((f) => f.endsWith(".json"));
114
+ const sessions = [];
115
+ for (const file of sessionFiles) {
116
+ const sessionPath = join(this.sessionsDir, file);
117
+ try {
118
+ const content = await readFile(sessionPath, "utf-8");
119
+ const data = JSON.parse(content);
120
+ sessions.push({
121
+ ...data,
122
+ createdAt: new Date(data.createdAt),
123
+ lastUsedAt: new Date(data.lastUsedAt),
124
+ history: data.history.map((entry) => ({
125
+ ...entry,
126
+ timestamp: new Date(entry.timestamp)
127
+ }))
128
+ });
129
+ } catch (error) {
130
+ console.error(`Failed to load session file ${file}:`, error);
131
+ }
132
+ }
133
+ const sortBy = options.sortBy || "lastUsedAt";
134
+ const order = options.order || "desc";
135
+ sessions.sort((a, b) => {
136
+ let comparison = 0;
137
+ if (sortBy === "name") {
138
+ comparison = (a.name || a.id).localeCompare(b.name || b.id);
139
+ } else if (sortBy === "createdAt") {
140
+ comparison = a.createdAt.getTime() - b.createdAt.getTime();
141
+ } else if (sortBy === "lastUsedAt") {
142
+ comparison = a.lastUsedAt.getTime() - b.lastUsedAt.getTime();
143
+ }
144
+ return order === "asc" ? comparison : -comparison;
145
+ });
146
+ if (options.limit && options.limit > 0) {
147
+ return sessions.slice(0, options.limit);
148
+ }
149
+ return sessions;
150
+ } catch (error) {
151
+ console.error("Failed to list sessions:", error);
152
+ return [];
153
+ }
154
+ }
155
+ /**
156
+ * Delete session by ID or name
157
+ */
158
+ async deleteSession(nameOrId) {
159
+ const session = await this.loadSession(nameOrId);
160
+ if (!session) {
161
+ return false;
162
+ }
163
+ const sessionPath = this.getSessionPath(session.id);
164
+ try {
165
+ const fs = await import('node:fs/promises');
166
+ await fs.unlink(sessionPath);
167
+ return true;
168
+ } catch (error) {
169
+ console.error(`Failed to delete session ${nameOrId}:`, error);
170
+ return false;
171
+ }
172
+ }
173
+ /**
174
+ * Rename session
175
+ */
176
+ async renameSession(oldName, newName) {
177
+ const session = await this.loadSession(oldName);
178
+ if (!session) {
179
+ return false;
180
+ }
181
+ session.name = newName;
182
+ session.lastUsedAt = /* @__PURE__ */ new Date();
183
+ await this.saveSession(session);
184
+ return true;
185
+ }
186
+ /**
187
+ * Add history entry to session
188
+ */
189
+ async addHistoryEntry(sessionId, role, content, tokens) {
190
+ const session = await this.loadSession(sessionId);
191
+ if (!session) {
192
+ return false;
193
+ }
194
+ session.history.push({
195
+ timestamp: /* @__PURE__ */ new Date(),
196
+ role,
197
+ content,
198
+ tokens
199
+ });
200
+ session.lastUsedAt = /* @__PURE__ */ new Date();
201
+ await this.saveSession(session);
202
+ return true;
203
+ }
204
+ /**
205
+ * Update session metadata
206
+ */
207
+ async updateSession(sessionId, updates) {
208
+ const session = await this.loadSession(sessionId);
209
+ if (!session) {
210
+ return false;
211
+ }
212
+ Object.assign(session, updates);
213
+ session.lastUsedAt = /* @__PURE__ */ new Date();
214
+ await this.saveSession(session);
215
+ return true;
216
+ }
217
+ /**
218
+ * Clean up old sessions (older than autoCleanupDays)
219
+ */
220
+ async cleanupOldSessions() {
221
+ const sessions = await this.listSessions();
222
+ const cutoffDate = /* @__PURE__ */ new Date();
223
+ cutoffDate.setDate(cutoffDate.getDate() - this.autoCleanupDays);
224
+ let deletedCount = 0;
225
+ for (const session of sessions) {
226
+ if (session.lastUsedAt < cutoffDate) {
227
+ const deleted = await this.deleteSession(session.id);
228
+ if (deleted) {
229
+ deletedCount++;
230
+ }
231
+ }
232
+ }
233
+ return deletedCount;
234
+ }
235
+ /**
236
+ * Get session statistics
237
+ */
238
+ async getStatistics() {
239
+ const sessions = await this.listSessions();
240
+ if (sessions.length === 0) {
241
+ return {
242
+ totalSessions: 0,
243
+ totalHistoryEntries: 0
244
+ };
245
+ }
246
+ const totalHistoryEntries = sessions.reduce((sum, s) => sum + s.history.length, 0);
247
+ const sortedByCreated = [...sessions].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
248
+ const sortedByUsed = [...sessions].sort((a, b) => b.lastUsedAt.getTime() - a.lastUsedAt.getTime());
249
+ return {
250
+ totalSessions: sessions.length,
251
+ totalHistoryEntries,
252
+ oldestSession: sortedByCreated[0]?.createdAt,
253
+ newestSession: sortedByCreated[sortedByCreated.length - 1]?.createdAt,
254
+ mostRecentlyUsed: sortedByUsed[0]?.lastUsedAt
255
+ };
256
+ }
257
+ /**
258
+ * Export session to JSON
259
+ */
260
+ async exportSession(sessionId) {
261
+ const session = await this.loadSession(sessionId);
262
+ if (!session) {
263
+ return null;
264
+ }
265
+ return JSON.stringify(session, null, 2);
266
+ }
267
+ /**
268
+ * Import session from JSON
269
+ */
270
+ async importSession(jsonData) {
271
+ try {
272
+ const data = JSON.parse(jsonData);
273
+ const session = {
274
+ ...data,
275
+ id: this.generateSessionId(),
276
+ createdAt: new Date(data.createdAt),
277
+ lastUsedAt: /* @__PURE__ */ new Date(),
278
+ history: data.history.map((entry) => ({
279
+ ...entry,
280
+ timestamp: new Date(entry.timestamp)
281
+ }))
282
+ };
283
+ await this.saveSession(session);
284
+ return session;
285
+ } catch (error) {
286
+ console.error("Failed to import session:", error);
287
+ return null;
288
+ }
289
+ }
290
+ }
291
+ let sessionManagerInstance = null;
292
+ function getSessionManager(options) {
293
+ if (!sessionManagerInstance) {
294
+ sessionManagerInstance = new SessionManager(options);
295
+ }
296
+ return sessionManagerInstance;
297
+ }
298
+
299
+ const SESSIONS_DIR = join$1(homedir(), ".ccjk", "sessions");
300
+ const CCJK_DIR = join$1(homedir(), ".ccjk");
301
+ const CLAUDE_DIR = join$1(homedir(), ".claude");
12
302
  async function saveSession() {
13
303
  try {
14
304
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
@@ -26,7 +316,7 @@ async function saveSession() {
26
316
  if (!existsSync(SESSIONS_DIR)) {
27
317
  await mkdir(SESSIONS_DIR, { recursive: true });
28
318
  }
29
- const sessionFile = join(SESSIONS_DIR, `${sessionId}.json`);
319
+ const sessionFile = join$1(SESSIONS_DIR, `${sessionId}.json`);
30
320
  await writeFile(sessionFile, JSON.stringify(metadata, null, 2));
31
321
  console.log(ansis.green(`\u2714 Session saved: ${sessionId}`));
32
322
  if (description) {
@@ -38,28 +328,41 @@ async function saveSession() {
38
328
  }
39
329
  async function listSessions() {
40
330
  try {
41
- if (!existsSync(SESSIONS_DIR)) {
42
- console.log(ansis.yellow("No sessions found"));
43
- return;
44
- }
45
- const files = await readdir(SESSIONS_DIR);
46
- const sessions = files.filter((f) => f.endsWith(".json"));
331
+ const sessionManager = getSessionManager();
332
+ const sessions = await sessionManager.listSessions();
47
333
  if (sessions.length === 0) {
48
334
  console.log(ansis.yellow("No sessions found"));
335
+ console.log(ansis.gray(`
336
+ Create a new session with: ${ansis.cyan("ccjk session create")}`));
49
337
  return;
50
338
  }
51
339
  console.log(ansis.cyan.bold("\n\u{1F4CB} Saved Sessions:\n"));
52
- for (const file of sessions) {
53
- const content = await readFile(join(SESSIONS_DIR, file), "utf-8");
54
- const metadata = JSON.parse(content);
55
- const date = new Date(metadata.timestamp).toLocaleString();
56
- console.log(ansis.white(` ${metadata.id}`));
57
- console.log(ansis.gray(` ${date}`));
58
- if (metadata.description) {
59
- console.log(ansis.gray(` ${metadata.description}`));
340
+ const sortedSessions = [...sessions].sort((a, b) => {
341
+ const aTime = a.lastAccessedAt?.getTime() || a.createdAt.getTime();
342
+ const bTime = b.lastAccessedAt?.getTime() || b.createdAt.getTime();
343
+ return bTime - aTime;
344
+ });
345
+ for (const session of sortedSessions) {
346
+ const nameDisplay = session.name ? ansis.cyan(session.name) : ansis.gray("(unnamed)");
347
+ const idDisplay = ansis.gray(`[${session.id.substring(0, 8)}]`);
348
+ console.log(` ${nameDisplay} ${idDisplay}`);
349
+ if (session.provider) {
350
+ console.log(ansis.gray(` Provider: ${session.provider}`));
351
+ }
352
+ const createdDate = session.createdAt.toLocaleString();
353
+ console.log(ansis.gray(` Created: ${createdDate}`));
354
+ if (session.lastAccessedAt) {
355
+ const accessedDate = session.lastAccessedAt.toLocaleString();
356
+ console.log(ansis.gray(` Last accessed: ${accessedDate}`));
357
+ }
358
+ if (session.history.length > 0) {
359
+ console.log(ansis.gray(` History: ${session.history.length} entries`));
60
360
  }
61
361
  console.log("");
62
362
  }
363
+ console.log(ansis.gray(`Total: ${sessions.length} session(s)`));
364
+ console.log(ansis.gray(`
365
+ Use ${ansis.cyan("ccjk --resume <name|id>")} to resume a session`));
63
366
  } catch (error) {
64
367
  console.error(ansis.red("Failed to list sessions:"), error);
65
368
  }
@@ -79,7 +382,7 @@ async function restoreSession(sessionId) {
79
382
  if (!sessionId) {
80
383
  const choices = await Promise.all(
81
384
  sessions.map(async (file) => {
82
- const content2 = await readFile(join(SESSIONS_DIR, file), "utf-8");
385
+ const content2 = await readFile(join$1(SESSIONS_DIR, file), "utf-8");
83
386
  const metadata2 = JSON.parse(content2);
84
387
  const date = new Date(metadata2.timestamp).toLocaleString();
85
388
  return {
@@ -96,7 +399,7 @@ async function restoreSession(sessionId) {
96
399
  });
97
400
  sessionId = selected;
98
401
  }
99
- const sessionFile = join(SESSIONS_DIR, `${sessionId}.json`);
402
+ const sessionFile = join$1(SESSIONS_DIR, `${sessionId}.json`);
100
403
  if (!existsSync(sessionFile)) {
101
404
  console.log(ansis.red(`Session not found: ${sessionId}`));
102
405
  return;
@@ -126,7 +429,7 @@ async function exportSession(sessionId) {
126
429
  if (!sessionId) {
127
430
  const choices = await Promise.all(
128
431
  sessions.map(async (file) => {
129
- const content2 = await readFile(join(SESSIONS_DIR, file), "utf-8");
432
+ const content2 = await readFile(join$1(SESSIONS_DIR, file), "utf-8");
130
433
  const metadata2 = JSON.parse(content2);
131
434
  const date = new Date(metadata2.timestamp).toLocaleString();
132
435
  return {
@@ -143,7 +446,7 @@ async function exportSession(sessionId) {
143
446
  });
144
447
  sessionId = selected;
145
448
  }
146
- const sessionFile = join(SESSIONS_DIR, `${sessionId}.json`);
449
+ const sessionFile = join$1(SESSIONS_DIR, `${sessionId}.json`);
147
450
  if (!existsSync(sessionFile)) {
148
451
  console.log(ansis.red(`Session not found: ${sessionId}`));
149
452
  return;
@@ -159,7 +462,7 @@ ${metadata.description ? `**Description:** ${metadata.description}
159
462
 
160
463
  Session data would be exported here.
161
464
  `;
162
- const outputFile = join(process__default.cwd(), `${sessionId}.md`);
465
+ const outputFile = join$1(process__default.cwd(), `${sessionId}.md`);
163
466
  await writeFile(outputFile, markdown);
164
467
  console.log(ansis.green(`\u2714 Session exported: ${outputFile}`));
165
468
  } catch (error) {
@@ -181,7 +484,7 @@ async function getDirSize(dirPath) {
181
484
  try {
182
485
  const entries = await readdir(dirPath, { withFileTypes: true });
183
486
  for (const entry of entries) {
184
- const fullPath = join(dirPath, entry.name);
487
+ const fullPath = join$1(dirPath, entry.name);
185
488
  if (entry.isDirectory()) {
186
489
  totalSize += await getDirSize(fullPath);
187
490
  } else {
@@ -200,7 +503,7 @@ async function countFiles(dirPath) {
200
503
  try {
201
504
  const entries = await readdir(dirPath, { withFileTypes: true });
202
505
  for (const entry of entries) {
203
- const fullPath = join(dirPath, entry.name);
506
+ const fullPath = join$1(dirPath, entry.name);
204
507
  if (entry.isDirectory()) {
205
508
  count += await countFiles(fullPath);
206
509
  } else {
@@ -214,21 +517,21 @@ async function countFiles(dirPath) {
214
517
  async function analyzeCleanupTargets() {
215
518
  const targets = [];
216
519
  const ccjkCacheDirs = [
217
- { name: "ccjk/cache", path: join(CCJK_DIR, "cache"), desc: "General cache files", safe: true },
218
- { name: "ccjk/sessions", path: join(CCJK_DIR, "sessions"), desc: "Saved session data", safe: true },
219
- { name: "ccjk/logs", path: join(CCJK_DIR, "logs"), desc: "Log files", safe: true },
220
- { name: "ccjk/temp", path: join(CCJK_DIR, "temp"), desc: "Temporary files", safe: true }
520
+ { name: "ccjk/cache", path: join$1(CCJK_DIR, "cache"), desc: "General cache files", safe: true },
521
+ { name: "ccjk/sessions", path: join$1(CCJK_DIR, "sessions"), desc: "Saved session data", safe: true },
522
+ { name: "ccjk/logs", path: join$1(CCJK_DIR, "logs"), desc: "Log files", safe: true },
523
+ { name: "ccjk/temp", path: join$1(CCJK_DIR, "temp"), desc: "Temporary files", safe: true }
221
524
  ];
222
525
  const claudeDirs = [
223
- { name: "claude/projects", path: join(CLAUDE_DIR, "projects"), desc: "Claude project caches", safe: true },
224
- { name: "claude/debug", path: join(CLAUDE_DIR, "debug"), desc: "Debug logs and traces", safe: true },
225
- { name: "claude/file-history", path: join(CLAUDE_DIR, "file-history"), desc: "File edit history", safe: true },
226
- { name: "claude/shell-snapshots", path: join(CLAUDE_DIR, "shell-snapshots"), desc: "Shell state snapshots", safe: true },
227
- { name: "claude/paste-cache", path: join(CLAUDE_DIR, "paste-cache"), desc: "Clipboard paste cache", safe: true },
228
- { name: "claude/todos", path: join(CLAUDE_DIR, "todos"), desc: "Todo list files", safe: false },
229
- { name: "claude/plans", path: join(CLAUDE_DIR, "plans"), desc: "Plan files", safe: true },
230
- { name: "claude/session-env", path: join(CLAUDE_DIR, "session-env"), desc: "Session environment data", safe: true },
231
- { name: "claude/ide", path: join(CLAUDE_DIR, "ide"), desc: "IDE integration cache", safe: true }
526
+ { name: "claude/projects", path: join$1(CLAUDE_DIR, "projects"), desc: "Claude project caches", safe: true },
527
+ { name: "claude/debug", path: join$1(CLAUDE_DIR, "debug"), desc: "Debug logs and traces", safe: true },
528
+ { name: "claude/file-history", path: join$1(CLAUDE_DIR, "file-history"), desc: "File edit history", safe: true },
529
+ { name: "claude/shell-snapshots", path: join$1(CLAUDE_DIR, "shell-snapshots"), desc: "Shell state snapshots", safe: true },
530
+ { name: "claude/paste-cache", path: join$1(CLAUDE_DIR, "paste-cache"), desc: "Clipboard paste cache", safe: true },
531
+ { name: "claude/todos", path: join$1(CLAUDE_DIR, "todos"), desc: "Todo list files", safe: false },
532
+ { name: "claude/plans", path: join$1(CLAUDE_DIR, "plans"), desc: "Plan files", safe: true },
533
+ { name: "claude/session-env", path: join$1(CLAUDE_DIR, "session-env"), desc: "Session environment data", safe: true },
534
+ { name: "claude/ide", path: join$1(CLAUDE_DIR, "ide"), desc: "IDE integration cache", safe: true }
232
535
  ];
233
536
  for (const dir of [...ccjkCacheDirs, ...claudeDirs]) {
234
537
  if (existsSync(dir.path)) {
@@ -347,9 +650,149 @@ async function sessionStatus() {
347
650
  console.log(`${ansis.white.bold("Total".padEnd(24))} ${ansis.cyan.bold(formatBytes(totalSize).padStart(10))} ${ansis.gray(String(totalFiles).padStart(8))}`);
348
651
  console.log("");
349
652
  console.log(ansis.gray(`Run ${ansis.cyan("ccjk session cleanup")} to free up space`));
653
+ const sessionManager = getSessionManager();
654
+ const stats = await sessionManager.getStatistics();
655
+ if (stats.totalSessions > 0) {
656
+ console.log(ansis.cyan.bold("\n\u{1F4DD} Session Statistics\n"));
657
+ console.log(ansis.white(`Total Sessions: ${ansis.yellow(String(stats.totalSessions))}`));
658
+ console.log(ansis.white(`Total History Entries: ${ansis.yellow(String(stats.totalHistoryEntries))}`));
659
+ if (stats.oldestSession) {
660
+ console.log(ansis.white(`Oldest Session: ${ansis.gray(stats.oldestSession.toLocaleString())}`));
661
+ }
662
+ if (stats.newestSession) {
663
+ console.log(ansis.white(`Newest Session: ${ansis.gray(stats.newestSession.toLocaleString())}`));
664
+ }
665
+ if (stats.mostRecentlyUsed) {
666
+ console.log(ansis.white(`Most Recently Used: ${ansis.gray(stats.mostRecentlyUsed.toLocaleString())}`));
667
+ }
668
+ }
350
669
  } catch (error) {
351
670
  console.error(ansis.red("Failed to get status:"), error);
352
671
  }
353
672
  }
673
+ async function createSessionCommand(options) {
674
+ try {
675
+ const sessionManager = getSessionManager();
676
+ let name = options.name;
677
+ let provider = options.provider;
678
+ let apiKey = options.apiKey;
679
+ if (!name) {
680
+ const { sessionName } = await inquirer.prompt({
681
+ type: "input",
682
+ name: "sessionName",
683
+ message: "Session name (optional):"
684
+ });
685
+ name = sessionName || void 0;
686
+ }
687
+ if (!provider) {
688
+ const { providerChoice } = await inquirer.prompt({
689
+ type: "list",
690
+ name: "providerChoice",
691
+ message: "Select API provider:",
692
+ choices: [
693
+ { name: "Anthropic (Claude)", value: "anthropic" },
694
+ { name: "OpenAI", value: "openai" },
695
+ { name: "Azure OpenAI", value: "azure" },
696
+ { name: "Custom", value: "custom" },
697
+ { name: "Skip (use default)", value: "" }
698
+ ]
699
+ });
700
+ provider = providerChoice || void 0;
701
+ }
702
+ if (provider && !apiKey) {
703
+ const { key } = await inquirer.prompt({
704
+ type: "password",
705
+ name: "key",
706
+ message: `Enter API key for ${provider} (optional):`,
707
+ mask: "*"
708
+ });
709
+ apiKey = key || void 0;
710
+ }
711
+ const session = await sessionManager.createSession(name, provider, apiKey, {
712
+ codeType: options.codeType
713
+ });
714
+ console.log(ansis.green(`
715
+ \u2714 Session created successfully!`));
716
+ console.log(ansis.white(` ID: ${ansis.cyan(session.id)}`));
717
+ if (session.name) {
718
+ console.log(ansis.white(` Name: ${ansis.cyan(session.name)}`));
719
+ }
720
+ if (session.provider) {
721
+ console.log(ansis.white(` Provider: ${ansis.cyan(session.provider)}`));
722
+ }
723
+ console.log(ansis.gray(`
724
+ Use ${ansis.cyan(`ccjk --resume ${session.name || session.id}`)} to resume this session`));
725
+ } catch (error) {
726
+ console.error(ansis.red("Failed to create session:"), error);
727
+ }
728
+ }
729
+ async function renameSessionCommand(sessionId, options) {
730
+ try {
731
+ const sessionManager = getSessionManager();
732
+ if (!sessionId) {
733
+ console.log(ansis.red("Please provide a session ID or name"));
734
+ return;
735
+ }
736
+ let newName = options.name;
737
+ if (!newName) {
738
+ const { name } = await inquirer.prompt({
739
+ type: "input",
740
+ name: "name",
741
+ message: "Enter new session name:",
742
+ validate: (input) => input.trim().length > 0 || "Name cannot be empty"
743
+ });
744
+ newName = name;
745
+ }
746
+ const success = await sessionManager.renameSession(sessionId, newName);
747
+ if (success) {
748
+ console.log(ansis.green(`\u2714 Session renamed to: ${ansis.cyan(newName)}`));
749
+ } else {
750
+ console.log(ansis.red(`Session not found: ${sessionId}`));
751
+ }
752
+ } catch (error) {
753
+ console.error(ansis.red("Failed to rename session:"), error);
754
+ }
755
+ }
756
+ async function deleteSessionCommand(sessionId, options) {
757
+ try {
758
+ const sessionManager = getSessionManager();
759
+ if (!sessionId) {
760
+ console.log(ansis.red("Please provide a session ID or name"));
761
+ return;
762
+ }
763
+ const session = await sessionManager.loadSession(sessionId);
764
+ if (!session) {
765
+ console.log(ansis.red(`Session not found: ${sessionId}`));
766
+ return;
767
+ }
768
+ if (!options.force) {
769
+ console.log(ansis.yellow("\n\u26A0\uFE0F You are about to delete:"));
770
+ console.log(ansis.white(` ID: ${ansis.cyan(session.id)}`));
771
+ if (session.name) {
772
+ console.log(ansis.white(` Name: ${ansis.cyan(session.name)}`));
773
+ }
774
+ console.log(ansis.white(` Created: ${ansis.gray(session.createdAt.toLocaleString())}`));
775
+ console.log(ansis.white(` History entries: ${ansis.gray(String(session.history.length))}`));
776
+ const { confirm } = await inquirer.prompt({
777
+ type: "confirm",
778
+ name: "confirm",
779
+ message: "Are you sure you want to delete this session?",
780
+ default: false
781
+ });
782
+ if (!confirm) {
783
+ console.log(ansis.yellow("Deletion cancelled"));
784
+ return;
785
+ }
786
+ }
787
+ const success = await sessionManager.deleteSession(sessionId);
788
+ if (success) {
789
+ console.log(ansis.green(`\u2714 Session deleted: ${sessionId}`));
790
+ } else {
791
+ console.log(ansis.red(`Failed to delete session: ${sessionId}`));
792
+ }
793
+ } catch (error) {
794
+ console.error(ansis.red("Failed to delete session:"), error);
795
+ }
796
+ }
354
797
 
355
- export { cleanupSession, exportSession, listSessions, restoreSession, saveSession, sessionStatus };
798
+ export { cleanupSession, createSessionCommand, deleteSessionCommand, exportSession, listSessions, renameSessionCommand, restoreSession, saveSession, sessionStatus };