stigmergy 1.2.13 → 1.3.1

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 (88) hide show
  1. package/README.md +39 -3
  2. package/STIGMERGY.md +3 -0
  3. package/config/builtin-skills.json +43 -0
  4. package/config/enhanced-cli-config.json +438 -0
  5. package/docs/CLI_TOOLS_AGENT_SKILL_ANALYSIS.md +463 -0
  6. package/docs/DESIGN_CLI_HELP_ANALYZER_REFACTOR.md +726 -0
  7. package/docs/ENHANCED_CLI_AGENT_SKILL_CONFIG.md +285 -0
  8. package/docs/IMPLEMENTATION_CHECKLIST_CLI_HELP_ANALYZER_REFACTOR.md +1268 -0
  9. package/docs/INSTALLER_ARCHITECTURE.md +257 -0
  10. package/docs/LESSONS_LEARNED.md +252 -0
  11. package/docs/SPECS_CLI_HELP_ANALYZER_REFACTOR.md +287 -0
  12. package/docs/SUDO_PROBLEM_AND_SOLUTION.md +529 -0
  13. package/docs/correct-skillsio-implementation.md +368 -0
  14. package/docs/development_guidelines.md +276 -0
  15. package/docs/independent-resume-implementation.md +198 -0
  16. package/docs/resumesession-final-implementation.md +195 -0
  17. package/docs/resumesession-usage.md +87 -0
  18. package/package.json +146 -136
  19. package/scripts/analyze-router.js +168 -0
  20. package/scripts/run-comprehensive-tests.js +230 -0
  21. package/scripts/run-quick-tests.js +90 -0
  22. package/scripts/test-runner.js +344 -0
  23. package/skills/resumesession/INDEPENDENT_SKILL.md +403 -0
  24. package/skills/resumesession/README.md +381 -0
  25. package/skills/resumesession/SKILL.md +211 -0
  26. package/skills/resumesession/__init__.py +33 -0
  27. package/skills/resumesession/implementations/simple-resume.js +13 -0
  28. package/skills/resumesession/independent-resume.js +750 -0
  29. package/skills/resumesession/package.json +1 -0
  30. package/skills/resumesession/skill.json +1 -0
  31. package/src/adapters/claude/install_claude_integration.js +9 -1
  32. package/src/adapters/codebuddy/install_codebuddy_integration.js +3 -1
  33. package/src/adapters/codex/install_codex_integration.js +15 -5
  34. package/src/adapters/gemini/install_gemini_integration.js +3 -1
  35. package/src/adapters/qwen/install_qwen_integration.js +3 -1
  36. package/src/cli/commands/autoinstall.js +65 -0
  37. package/src/cli/commands/errors.js +190 -0
  38. package/src/cli/commands/independent-resume.js +395 -0
  39. package/src/cli/commands/install.js +179 -0
  40. package/src/cli/commands/permissions.js +108 -0
  41. package/src/cli/commands/project.js +485 -0
  42. package/src/cli/commands/scan.js +97 -0
  43. package/src/cli/commands/simple-resume.js +377 -0
  44. package/src/cli/commands/skills.js +158 -0
  45. package/src/cli/commands/status.js +113 -0
  46. package/src/cli/commands/stigmergy-resume.js +775 -0
  47. package/src/cli/commands/system.js +301 -0
  48. package/src/cli/commands/universal-resume.js +394 -0
  49. package/src/cli/router-beta.js +471 -0
  50. package/src/cli/utils/environment.js +75 -0
  51. package/src/cli/utils/formatters.js +47 -0
  52. package/src/cli/utils/skills_cache.js +92 -0
  53. package/src/core/cache_cleaner.js +1 -0
  54. package/src/core/cli_adapters.js +345 -0
  55. package/src/core/cli_help_analyzer.js +1236 -680
  56. package/src/core/cli_path_detector.js +702 -709
  57. package/src/core/cli_tools.js +515 -160
  58. package/src/core/coordination/nodejs/CLIIntegrationManager.js +18 -0
  59. package/src/core/coordination/nodejs/HookDeploymentManager.js +242 -412
  60. package/src/core/coordination/nodejs/HookDeploymentManager.refactored.js +323 -0
  61. package/src/core/coordination/nodejs/generators/CLIAdapterGenerator.js +363 -0
  62. package/src/core/coordination/nodejs/generators/ResumeSessionGenerator.js +932 -0
  63. package/src/core/coordination/nodejs/generators/SkillsIntegrationGenerator.js +1395 -0
  64. package/src/core/coordination/nodejs/generators/index.js +12 -0
  65. package/src/core/enhanced_cli_installer.js +1208 -608
  66. package/src/core/enhanced_cli_parameter_handler.js +402 -0
  67. package/src/core/execution_mode_detector.js +222 -0
  68. package/src/core/installer.js +151 -106
  69. package/src/core/local_skill_scanner.js +732 -0
  70. package/src/core/multilingual/language-pattern-manager.js +1 -1
  71. package/src/core/skills/BuiltinSkillsDeployer.js +188 -0
  72. package/src/core/skills/StigmergySkillManager.js +123 -16
  73. package/src/core/skills/embedded-openskills/SkillParser.js +7 -3
  74. package/src/core/smart_router.js +550 -261
  75. package/src/index.js +10 -4
  76. package/src/utils.js +66 -7
  77. package/test/cli-integration.test.js +304 -0
  78. package/test/direct_smart_router_test.js +88 -0
  79. package/test/enhanced-cli-agent-skill-test.js +485 -0
  80. package/test/simple_test.js +82 -0
  81. package/test/smart_router_test_runner.js +123 -0
  82. package/test/smart_routing_edge_cases.test.js +284 -0
  83. package/test/smart_routing_simple_verification.js +139 -0
  84. package/test/smart_routing_verification.test.js +346 -0
  85. package/test/specific-cli-agent-skill-analysis.js +385 -0
  86. package/test/unit/smart_router.test.js +295 -0
  87. package/test/very_simple_test.js +54 -0
  88. package/src/cli/router.js +0 -1783
@@ -0,0 +1,750 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ResumeSession - 跨 CLI 会话恢复工具
5
+ *
6
+ * 此工具被 AI 助手(Claude、Gemini 等)调用,用于恢复和管理会话历史
7
+ *
8
+ * 功能:
9
+ * - 恢复当前项目目录的最新会话(不论会话是哪个 CLI 产生的)
10
+ * - 显示当前项目目录相关的最近 N 个会话摘要列表
11
+ * - 显示对应 CLI 关于本项目目录相关的会话列表
12
+ * - 显示用户当前项目所有相关的会话列表
13
+ * - 显示所有 CLI 所有项目的会话分类列表
14
+ *
15
+ * 配置策略(优先级从高到低):
16
+ * 1. 工具自己的配置文件 (~/.resume-session/config.json)
17
+ * 2. Stigmergy 配置(可选增强)
18
+ * 3. 自动检测(扫描常见位置)
19
+ * 4. 默认配置
20
+ *
21
+ * 使用方式:
22
+ * node independent-resume.js # 恢复当前项目最新会话
23
+ * node independent-resume.js 5 # 显示当前项目最近 5 个会话
24
+ * node independent-resume.js iflow # 显示当前项目的 iFlow 会话
25
+ * node independent-resume.js iflow 3 # 显示当前项目最近 3 个 iFlow 会话
26
+ * node independent-resume.js --all # 显示当前项目所有 CLI 的会话
27
+ * node independent-resume.js --complete # 显示所有 CLI 所有项目的会话
28
+ * node independent-resume.js --help # 显示帮助信息
29
+ */
30
+
31
+ const fs = require('fs');
32
+ const path = require('path');
33
+ const os = require('os');
34
+
35
+ // 工具配置文件路径
36
+ const TOOL_CONFIG_PATH = path.join(os.homedir(), '.resume-session', 'config.json');
37
+
38
+ // 默认 CLI 配置(作为最后的后备)
39
+ const DEFAULT_CLI_CONFIG = {
40
+ claude: {
41
+ name: 'Claude',
42
+ path: path.join(os.homedir(), '.claude', 'projects'),
43
+ sessionPattern: /.*\.json$/,
44
+ extractProject: (filePath) => {
45
+ const match = filePath.match(/projects[\\/]([^\\/]+)/);
46
+ return match ? match[1] : null;
47
+ },
48
+ getSessionId: (filePath) => path.basename(filePath, '.json')
49
+ },
50
+ gemini: {
51
+ name: 'Gemini',
52
+ path: path.join(os.homedir(), '.config', 'gemini', 'tmp'),
53
+ sessionPattern: /.*\.json$/,
54
+ extractProject: (filePath) => {
55
+ const match = filePath.match(/tmp[\\/]([^\\/]+)/);
56
+ return match ? match[1] : null;
57
+ },
58
+ getSessionId: (filePath) => path.basename(filePath, '.json')
59
+ },
60
+ qwen: {
61
+ name: 'Qwen',
62
+ path: path.join(os.homedir(), '.qwen', 'projects'),
63
+ sessionPattern: /.*\.json$/,
64
+ extractProject: (filePath) => {
65
+ const match = filePath.match(/projects[\\/]([^\\/]+)/);
66
+ return match ? match[1] : null;
67
+ },
68
+ getSessionId: (filePath) => path.basename(filePath, '.json')
69
+ },
70
+ iflow: {
71
+ name: 'iFlow',
72
+ path: path.join(os.homedir(), '.iflow', 'projects'),
73
+ sessionPattern: /.*\.json$/,
74
+ extractProject: (filePath) => {
75
+ const match = filePath.match(/projects[\\/]([^\\/]+)/);
76
+ return match ? match[1] : null;
77
+ },
78
+ getSessionId: (filePath) => path.basename(filePath, '.json')
79
+ },
80
+ codebuddy: {
81
+ name: 'CodeBuddy',
82
+ path: path.join(os.homedir(), '.codebuddy', 'projects'),
83
+ sessionPattern: /.*\.json$/,
84
+ extractProject: (filePath) => {
85
+ const match = filePath.match(/projects[\\/]([^\\/]+)/);
86
+ return match ? match[1] : null;
87
+ },
88
+ getSessionId: (filePath) => path.basename(filePath, '.json')
89
+ },
90
+ codex: {
91
+ name: 'Codex',
92
+ path: path.join(os.homedir(), '.config', 'codex'),
93
+ sessionPattern: /.*\.json$/,
94
+ extractProject: (filePath) => {
95
+ const match = filePath.match(/codex[\\/]([^\\/]+)/);
96
+ return match ? match[1] : null;
97
+ },
98
+ getSessionId: (filePath) => path.basename(filePath, '.json')
99
+ },
100
+ qodercli: {
101
+ name: 'QoderCLI',
102
+ path: path.join(os.homedir(), '.qoder', 'projects'),
103
+ sessionPattern: /.*\.json$/,
104
+ extractProject: (filePath) => {
105
+ const match = filePath.match(/projects[\\/]([^\\/]+)/);
106
+ return match ? match[1] : null;
107
+ },
108
+ getSessionId: (filePath) => path.basename(filePath, '.json')
109
+ },
110
+ kode: {
111
+ name: 'Kode',
112
+ path: path.join(os.homedir(), '.kode', 'projects'),
113
+ sessionPattern: /.*\.json$/,
114
+ extractProject: (filePath) => {
115
+ const match = filePath.match(/projects[\\/]([^\\/]+)/);
116
+ return match ? match[1] : null;
117
+ },
118
+ getSessionId: (filePath) => path.basename(filePath, '.json')
119
+ }
120
+ };
121
+
122
+ // 常见 CLI 的候选路径配置(用于自动检测)
123
+ const CLI_CANDIDATES = {
124
+ claude: {
125
+ name: 'Claude',
126
+ candidates: [
127
+ path.join(os.homedir(), '.claude', 'projects'),
128
+ path.join(os.homedir(), '.config', 'claude', 'projects'),
129
+ path.join(os.homedir(), 'AppData', 'Roaming', 'claude', 'projects'),
130
+ ]
131
+ },
132
+ gemini: {
133
+ name: 'Gemini',
134
+ candidates: [
135
+ path.join(os.homedir(), '.config', 'gemini', 'tmp'),
136
+ path.join(os.homedir(), '.gemini', 'projects'),
137
+ path.join(os.homedir(), 'AppData', 'Roaming', 'gemini', 'tmp'),
138
+ ]
139
+ },
140
+ qwen: {
141
+ name: 'Qwen',
142
+ candidates: [
143
+ path.join(os.homedir(), '.qwen', 'projects'),
144
+ path.join(os.homedir(), '.config', 'qwen', 'projects'),
145
+ path.join(os.homedir(), 'AppData', 'Roaming', 'qwen', 'projects'),
146
+ ]
147
+ },
148
+ iflow: {
149
+ name: 'iFlow',
150
+ candidates: [
151
+ path.join(os.homedir(), '.iflow', 'projects'),
152
+ path.join(os.homedir(), '.config', 'iflow', 'projects'),
153
+ path.join(os.homedir(), 'AppData', 'Roaming', 'iflow', 'projects'),
154
+ ]
155
+ },
156
+ codebuddy: {
157
+ name: 'CodeBuddy',
158
+ candidates: [
159
+ path.join(os.homedir(), '.codebuddy', 'projects'),
160
+ path.join(os.homedir(), '.config', 'codebuddy', 'projects'),
161
+ path.join(os.homedir(), 'AppData', 'Roaming', 'codebuddy', 'projects'),
162
+ ]
163
+ },
164
+ codex: {
165
+ name: 'Codex',
166
+ candidates: [
167
+ path.join(os.homedir(), '.config', 'codex'),
168
+ path.join(os.homedir(), '.codex', 'projects'),
169
+ path.join(os.homedir(), 'AppData', 'Roaming', 'codex'),
170
+ ]
171
+ },
172
+ qodercli: {
173
+ name: 'QoderCLI',
174
+ candidates: [
175
+ path.join(os.homedir(), '.qoder', 'projects'),
176
+ path.join(os.homedir(), '.config', 'qoder', 'projects'),
177
+ path.join(os.homedir(), 'AppData', 'Roaming', 'qoder', 'projects'),
178
+ ]
179
+ },
180
+ kode: {
181
+ name: 'Kode',
182
+ candidates: [
183
+ path.join(os.homedir(), '.kode', 'projects'),
184
+ path.join(os.homedir(), '.config', 'kode', 'projects'),
185
+ path.join(os.homedir(), 'AppData', 'Roaming', 'kode', 'projects'),
186
+ ]
187
+ }
188
+ };
189
+
190
+ // 从工具配置文件读取配置
191
+ function getToolConfig() {
192
+ try {
193
+ if (fs.existsSync(TOOL_CONFIG_PATH)) {
194
+ const content = fs.readFileSync(TOOL_CONFIG_PATH, 'utf8');
195
+ const toolConfig = JSON.parse(content);
196
+
197
+ if (toolConfig.cli_paths && Object.keys(toolConfig.cli_paths).length > 0) {
198
+ return toolConfig.cli_paths;
199
+ }
200
+ }
201
+ } catch (e) {
202
+ // 忽略错误,使用默认配置
203
+ }
204
+
205
+ return null;
206
+ }
207
+
208
+ // 保存配置到工具配置文件
209
+ function saveToolConfig(cliConfig) {
210
+ try {
211
+ const configDir = path.dirname(TOOL_CONFIG_PATH);
212
+ if (!fs.existsSync(configDir)) {
213
+ fs.mkdirSync(configDir, { recursive: true });
214
+ }
215
+
216
+ const config = {
217
+ cli_paths: cliConfig,
218
+ lastUpdated: new Date().toISOString()
219
+ };
220
+
221
+ fs.writeFileSync(TOOL_CONFIG_PATH, JSON.stringify(config, null, 2));
222
+ } catch (e) {
223
+ // 忽略保存错误
224
+ }
225
+ }
226
+
227
+ // 从 stigmergy 获取配置(可选增强)
228
+ function getStigmergyConfig() {
229
+ const stigmergyConfigPath = path.join(os.homedir(), '.stigmergy', 'config.json');
230
+
231
+ try {
232
+ if (fs.existsSync(stigmergyConfigPath)) {
233
+ const content = fs.readFileSync(stigmergyConfigPath, 'utf8');
234
+ const stigmergyConfig = JSON.parse(content);
235
+
236
+ if (stigmergyConfig.cli_paths && Object.keys(stigmergyConfig.cli_paths).length > 0) {
237
+ return stigmergyConfig.cli_paths;
238
+ }
239
+ }
240
+ } catch (e) {
241
+ // 忽略错误
242
+ }
243
+
244
+ return null;
245
+ }
246
+
247
+ // 自动检测已安装的 CLI
248
+ function autoDetectCLIs() {
249
+ const detectedCLIs = {};
250
+
251
+ for (const [cliKey, cliDef] of Object.entries(CLI_CANDIDATES)) {
252
+ // 尝试所有候选路径
253
+ for (const candidatePath of cliDef.candidates) {
254
+ if (fs.existsSync(candidatePath)) {
255
+ // 找到第一个存在的路径
256
+ detectedCLIs[cliKey] = {
257
+ name: cliDef.name,
258
+ path: candidatePath,
259
+ sessionPattern: /.*\.json$/,
260
+ extractProject: DEFAULT_CLI_CONFIG[cliKey].extractProject,
261
+ getSessionId: DEFAULT_CLI_CONFIG[cliKey].getSessionId
262
+ };
263
+ break;
264
+ }
265
+ }
266
+ }
267
+
268
+ return detectedCLIs;
269
+ }
270
+
271
+ // 获取 CLI 配置(优先级:工具配置 > stigmergy > 自动检测 > 默认配置)
272
+ function getCLIConfig() {
273
+ // 1. 优先使用工具自己的配置文件
274
+ const toolConfig = getToolConfig();
275
+ if (toolConfig && Object.keys(toolConfig).length > 0) {
276
+ return toolConfig;
277
+ }
278
+
279
+ // 2. 尝试从 stigmergy 获取配置(可选增强)
280
+ const stigmergyConfig = getStigmergyConfig();
281
+ if (stigmergyConfig && Object.keys(stigmergyConfig).length > 0) {
282
+ return stigmergyConfig;
283
+ }
284
+
285
+ // 3. 使用自动检测
286
+ const detectedCLIs = autoDetectCLIs();
287
+ if (Object.keys(detectedCLIs).length > 0) {
288
+ // 保存检测到的配置
289
+ saveToolConfig(detectedCLIs);
290
+ return detectedCLIs;
291
+ }
292
+
293
+ // 4. 使用默认配置
294
+ return DEFAULT_CLI_CONFIG;
295
+ }
296
+
297
+ // 获取当前工作目录的标准化路径
298
+ function getCurrentProjectPath() {
299
+ const cwd = process.cwd();
300
+ try {
301
+ return fs.realpathSync(cwd);
302
+ } catch (e) {
303
+ return cwd;
304
+ }
305
+ }
306
+
307
+ // 递归查找目录中的所有会话文件
308
+ function findSessionFiles(dir, pattern) {
309
+ const sessions = [];
310
+
311
+ if (!fs.existsSync(dir)) {
312
+ return sessions;
313
+ }
314
+
315
+ function traverse(currentDir) {
316
+ try {
317
+ const items = fs.readdirSync(currentDir);
318
+
319
+ for (const item of items) {
320
+ try {
321
+ const itemPath = path.join(currentDir, item);
322
+ const stat = fs.statSync(itemPath);
323
+
324
+ if (stat.isDirectory()) {
325
+ traverse(itemPath);
326
+ } else if (pattern.test(item)) {
327
+ sessions.push(itemPath);
328
+ }
329
+ } catch (e) {
330
+ // 忽略无法访问的文件
331
+ }
332
+ }
333
+ } catch (e) {
334
+ // 忽略无法访问的目录
335
+ }
336
+ }
337
+
338
+ traverse(dir);
339
+ return sessions;
340
+ }
341
+
342
+ // 查找所有 CLI 的会话
343
+ function findAllSessions() {
344
+ const allSessions = [];
345
+ const cliConfig = getCLIConfig();
346
+
347
+ for (const [cliKey, config] of Object.entries(cliConfig)) {
348
+ try {
349
+ const cliPath = config.path;
350
+ const sessionFiles = findSessionFiles(cliPath, config.sessionPattern);
351
+
352
+ for (const filePath of sessionFiles) {
353
+ try {
354
+ const stats = fs.statSync(filePath);
355
+ const projectName = config.extractProject(filePath);
356
+ const sessionId = config.getSessionId(filePath);
357
+
358
+ allSessions.push({
359
+ cli: cliKey,
360
+ cliName: config.name,
361
+ filePath,
362
+ projectName,
363
+ sessionId,
364
+ lastModified: stats.mtime,
365
+ size: stats.size
366
+ });
367
+ } catch (e) {
368
+ // 忽略无法读取的会话文件
369
+ }
370
+ }
371
+ } catch (e) {
372
+ // 忽略无法访问的 CLI 目录
373
+ }
374
+ }
375
+
376
+ return allSessions;
377
+ }
378
+
379
+ // 读取会话内容
380
+ function readSessionContent(session) {
381
+ try {
382
+ const content = fs.readFileSync(session.filePath, 'utf8');
383
+ const data = JSON.parse(content);
384
+
385
+ // 尝试提取对话内容
386
+ let conversation = '';
387
+
388
+ if (data.messages && Array.isArray(data.messages)) {
389
+ conversation = data.messages.map(msg => {
390
+ const role = msg.role || 'unknown';
391
+ const content = msg.content || '';
392
+ return `[${role}]: ${content}`;
393
+ }).join('\n\n');
394
+ } else if (data.conversation && Array.isArray(data.conversation)) {
395
+ conversation = data.conversation.map(msg => {
396
+ const role = msg.role || 'unknown';
397
+ const content = msg.content || '';
398
+ return `[${role}]: ${content}`;
399
+ }).join('\n\n');
400
+ } else if (data.content) {
401
+ conversation = data.content;
402
+ } else {
403
+ conversation = JSON.stringify(data, null, 2);
404
+ }
405
+
406
+ return conversation;
407
+ } catch (e) {
408
+ return null;
409
+ }
410
+ }
411
+
412
+ // 生成会话摘要
413
+ function generateSessionSummary(session) {
414
+ const content = readSessionContent(session);
415
+ if (!content) {
416
+ return '无法读取会话内容';
417
+ }
418
+
419
+ // 取前 200 个字符作为摘要
420
+ const preview = content.substring(0, 200).replace(/\n/g, ' ');
421
+ return preview.length < content.length ? preview + '...' : preview;
422
+ }
423
+
424
+ // 按时间排序会话
425
+ function sortSessionsByTime(sessions, descending = true) {
426
+ return sessions.sort((a, b) => {
427
+ const diff = a.lastModified - b.lastModified;
428
+ return descending ? -diff : diff;
429
+ });
430
+ }
431
+
432
+ // 过滤当前项目的会话
433
+ function filterCurrentProjectSessions(sessions) {
434
+ const currentProjectPath = getCurrentProjectPath();
435
+
436
+ return sessions.filter(session => {
437
+ if (!session.projectName) return false;
438
+
439
+ // 多级匹配策略
440
+ const projectName = session.projectName.toLowerCase();
441
+ const currentPath = currentProjectPath.toLowerCase();
442
+ const currentDirName = path.basename(currentPath).toLowerCase();
443
+
444
+ // 1. 精确匹配:项目名称与当前目录名完全相同
445
+ if (projectName === currentDirName) {
446
+ return true;
447
+ }
448
+
449
+ // 2. 路径包含:当前路径包含项目名称
450
+ if (currentPath.includes(projectName)) {
451
+ return true;
452
+ }
453
+
454
+ // 3. 名称包含:项目名称包含当前目录名
455
+ if (projectName.includes(currentDirName)) {
456
+ return true;
457
+ }
458
+
459
+ // 4. 文件路径包含:会话文件路径包含当前路径
460
+ if (session.filePath.toLowerCase().includes(currentPath)) {
461
+ return true;
462
+ }
463
+
464
+ return false;
465
+ });
466
+ }
467
+
468
+ // 显示帮助信息
469
+ function showHelp() {
470
+ console.log(`
471
+ ResumeSession - 跨 CLI 会话恢复工具
472
+ ======================================
473
+
474
+ 使用方式:
475
+ node independent-resume.js [参数]
476
+
477
+ 参数说明:
478
+ 无参数 恢复当前项目目录的最新会话(不论哪个 CLI)
479
+ [数字] 显示当前项目目录相关的最近 N 个会话摘要列表
480
+ [CLI名称] 显示对应 CLI 关于本项目目录相关的会话列表
481
+ [CLI名称] [数字] 显示对应 CLI 关于本项目目录相关的最近 N 个会话
482
+ --all 显示当前项目所有 CLI 的会话列表
483
+ --complete 显示所有 CLI 所有项目的会话分类列表
484
+ --help 显示此帮助信息
485
+
486
+ 示例:
487
+ node independent-resume.js # 恢复当前项目最新会话
488
+ node independent-resume.js 5 # 显示当前项目最近 5 个会话
489
+ node independent-resume.js iflow # 显示当前项目的 iFlow 会话
490
+ node independent-resume.js iflow 3 # 显示当前项目最近 3 个 iFlow 会话
491
+ node independent-resume.js --all # 显示当前项目所有 CLI 的会话
492
+ node independent-resume.js --complete # 显示所有 CLI 所有项目的会话
493
+
494
+ 支持的 CLI:
495
+ claude, gemini, qwen, iflow, codebuddy, codex, qodercli, kode
496
+
497
+ 配置策略(优先级从高到低):
498
+ 1. 工具配置文件 (~/.resume-session/config.json)
499
+ 2. Stigmergy 配置(可选增强)
500
+ 3. 自动检测(扫描常见位置)
501
+ 4. 默认配置
502
+
503
+ 特性:
504
+ - 独立的配置管理,无需依赖 stigmergy
505
+ - 自动扫描常见 CLI 的会话存储位置
506
+ - 无需手动配置,开箱即用
507
+ - 支持自定义安装路径
508
+ - 智能项目识别
509
+ - 相对时间显示
510
+ `);
511
+ }
512
+
513
+ // 显示会话列表
514
+ function displaySessionList(sessions, limit = null) {
515
+ if (sessions.length === 0) {
516
+ console.log('未找到相关会话。');
517
+ return;
518
+ }
519
+
520
+ const displaySessions = limit ? sessions.slice(0, limit) : sessions;
521
+
522
+ console.log(`\n找到 ${sessions.length} 个会话${limit ? ` (显示前 ${limit} 个)` : ''}:\n`);
523
+ console.log('='.repeat(80));
524
+
525
+ displaySessions.forEach((session, index) => {
526
+ const summary = generateSessionSummary(session);
527
+ const timeStr = session.lastModified.toLocaleString('zh-CN');
528
+ const relativeTime = getRelativeTime(session.lastModified);
529
+
530
+ console.log(`\n[${index + 1}] ${session.cliName} - ${session.projectName || '未知项目'}`);
531
+ console.log(` 时间: ${timeStr} (${relativeTime})`);
532
+ console.log(` 会话ID: ${session.sessionId}`);
533
+ console.log(` 摘要: ${summary}`);
534
+ console.log('-'.repeat(80));
535
+ });
536
+
537
+ console.log('\n提示: 使用会话编号可以查看完整内容(功能开发中)\n');
538
+ }
539
+
540
+ // 显示完整会话内容
541
+ function displayFullSession(session) {
542
+ console.log('\n' + '='.repeat(80));
543
+ console.log(`会话来源: ${session.cliName}`);
544
+ console.log(`项目: ${session.projectName || '未知项目'}`);
545
+ console.log(`时间: ${session.lastModified.toLocaleString('zh-CN')} (${getRelativeTime(session.lastModified)})`);
546
+ console.log(`会话ID: ${session.sessionId}`);
547
+ console.log('='.repeat(80));
548
+ console.log('\n');
549
+
550
+ const content = readSessionContent(session);
551
+ if (content) {
552
+ console.log(content);
553
+ } else {
554
+ console.log('无法读取会话内容');
555
+ }
556
+
557
+ console.log('\n' + '='.repeat(80) + '\n');
558
+ }
559
+
560
+ // 显示按 CLI 分组的会话列表
561
+ function displayCliGroupedSessions(sessions) {
562
+ const grouped = {};
563
+
564
+ sessions.forEach(session => {
565
+ if (!grouped[session.cliName]) {
566
+ grouped[session.cliName] = [];
567
+ }
568
+ grouped[session.cliName].push(session);
569
+ });
570
+
571
+ console.log('\n' + '='.repeat(80));
572
+ console.log('会话列表(按 CLI 分组)');
573
+ console.log('='.repeat(80) + '\n');
574
+
575
+ for (const [cliName, cliSessions] of Object.entries(grouped)) {
576
+ console.log(`\n${cliName} (${cliSessions.length} 个会话)`);
577
+ console.log('-'.repeat(80));
578
+
579
+ cliSessions.forEach((session, index) => {
580
+ const timeStr = session.lastModified.toLocaleString('zh-CN');
581
+ const relativeTime = getRelativeTime(session.lastModified);
582
+ console.log(` [${index + 1}] ${session.projectName || '未知项目'} - ${timeStr} (${relativeTime})`);
583
+ console.log(` 会话ID: ${session.sessionId}`);
584
+ });
585
+ }
586
+
587
+ console.log('\n' + '='.repeat(80) + '\n');
588
+ }
589
+
590
+ // 显示按项目分组的会话列表
591
+ function displayProjectGroupedSessions(sessions) {
592
+ const grouped = {};
593
+
594
+ sessions.forEach(session => {
595
+ const projectName = session.projectName || '未知项目';
596
+ if (!grouped[projectName]) {
597
+ grouped[projectName] = [];
598
+ }
599
+ grouped[projectName].push(session);
600
+ });
601
+
602
+ console.log('\n' + '='.repeat(80));
603
+ console.log('会话列表(按项目分组)');
604
+ console.log('='.repeat(80) + '\n');
605
+
606
+ for (const [projectName, projectSessions] of Object.entries(grouped)) {
607
+ console.log(`\n${projectName} (${projectSessions.length} 个会话)`);
608
+ console.log('-'.repeat(80));
609
+
610
+ projectSessions.forEach((session) => {
611
+ const timeStr = session.lastModified.toLocaleString('zh-CN');
612
+ const relativeTime = getRelativeTime(session.lastModified);
613
+ console.log(` ${session.cliName} - ${timeStr} (${relativeTime})`);
614
+ console.log(` 会话ID: ${session.sessionId}`);
615
+ });
616
+ }
617
+
618
+ console.log('\n' + '='.repeat(80) + '\n');
619
+ }
620
+
621
+ // 获取相对时间
622
+ function getRelativeTime(date) {
623
+ const now = new Date();
624
+ const diff = now - date;
625
+ const seconds = Math.floor(diff / 1000);
626
+ const minutes = Math.floor(seconds / 60);
627
+ const hours = Math.floor(minutes / 60);
628
+ const days = Math.floor(hours / 24);
629
+
630
+ if (seconds < 60) {
631
+ return '刚刚';
632
+ } else if (minutes < 60) {
633
+ return `${minutes} 分钟前`;
634
+ } else if (hours < 24) {
635
+ return `${hours} 小时前`;
636
+ } else if (days < 7) {
637
+ return `${days} 天前`;
638
+ } else {
639
+ return `${date.toLocaleDateString('zh-CN')}`;
640
+ }
641
+ }
642
+
643
+ // 主函数
644
+ function main() {
645
+ const args = process.argv.slice(2);
646
+
647
+ // 显示帮助
648
+ if (args.includes('--help') || args.includes('-h')) {
649
+ showHelp();
650
+ return;
651
+ }
652
+
653
+ // 查找所有会话
654
+ const allSessions = findAllSessions();
655
+
656
+ if (allSessions.length === 0) {
657
+ console.log('未找到任何会话。');
658
+ console.log('\n提示:');
659
+ console.log('1. 确保至少使用过一个 CLI 工具并创建了会话');
660
+ console.log('2. 工具会自动扫描常见 CLI 的会话存储位置');
661
+ console.log('3. 如果您的 CLI 安装在自定义位置,请确保会话目录可访问');
662
+ console.log('4. 配置文件: ~/.resume-session/config.json');
663
+ return;
664
+ }
665
+
666
+ // 过滤当前项目的会话
667
+ const currentProjectSessions = filterCurrentProjectSessions(allSessions);
668
+
669
+ // 解析参数
670
+ const firstArg = args[0];
671
+ const secondArg = args[1];
672
+
673
+ // --complete: 显示所有 CLI 所有项目的会话分类列表
674
+ if (firstArg === '--complete') {
675
+ console.log('\n当前工作目录:', getCurrentProjectPath());
676
+ displayProjectGroupedSessions(allSessions);
677
+ return;
678
+ }
679
+
680
+ // --all: 显示当前项目所有相关的会话列表
681
+ if (firstArg === '--all') {
682
+ if (currentProjectSessions.length === 0) {
683
+ console.log('当前项目未找到相关会话。');
684
+ console.log('\n提示: 使用 --complete 查看所有项目的会话。');
685
+ return;
686
+ }
687
+
688
+ console.log('\n当前工作目录:', getCurrentProjectPath());
689
+ displaySessionList(currentProjectSessions);
690
+ return;
691
+ }
692
+
693
+ // CLI 名称参数: 显示对应 CLI 关于本项目目录相关的会话
694
+ if (DEFAULT_CLI_CONFIG[firstArg]) {
695
+ const cliSessions = currentProjectSessions.filter(s => s.cli === firstArg);
696
+
697
+ if (cliSessions.length === 0) {
698
+ console.log(`当前项目未找到 ${DEFAULT_CLI_CONFIG[firstArg].name} 的会话。`);
699
+ return;
700
+ }
701
+
702
+ // 如果有第二个参数且是数字,显示指定数量的会话
703
+ if (secondArg && /^\d+$/.test(secondArg)) {
704
+ const limit = parseInt(secondArg, 10);
705
+ displaySessionList(sortSessionsByTime(cliSessions), limit);
706
+ } else {
707
+ // 否则显示该 CLI 的所有会话
708
+ displaySessionList(sortSessionsByTime(cliSessions));
709
+ }
710
+
711
+ return;
712
+ }
713
+
714
+ // 数字参数: 显示当前项目目录相关的最近 N 个会话摘要列表
715
+ if (firstArg && /^\d+$/.test(firstArg)) {
716
+ const limit = parseInt(firstArg, 10);
717
+
718
+ if (currentProjectSessions.length === 0) {
719
+ console.log('当前项目未找到相关会话。');
720
+ console.log('\n提示: 使用 --complete 查看所有项目的会话。');
721
+ return;
722
+ }
723
+
724
+ displaySessionList(sortSessionsByTime(currentProjectSessions), limit);
725
+ return;
726
+ }
727
+
728
+ // 无参数: 恢复当前项目目录的最新会话
729
+ if (!firstArg) {
730
+ if (currentProjectSessions.length === 0) {
731
+ console.log('当前项目未找到相关会话。');
732
+ console.log('\n提示: 使用 --complete 查看所有项目的会话。');
733
+ return;
734
+ }
735
+
736
+ const sortedSessions = sortSessionsByTime(currentProjectSessions);
737
+ const latestSession = sortedSessions[0];
738
+
739
+ console.log('\n当前工作目录:', getCurrentProjectPath());
740
+ displayFullSession(latestSession);
741
+ return;
742
+ }
743
+
744
+ // 未知参数
745
+ console.log(`未知参数: ${firstArg}`);
746
+ console.log('使用 --help 查看帮助信息。');
747
+ }
748
+
749
+ // 运行主函数
750
+ main();