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.
- package/README.md +39 -3
- package/STIGMERGY.md +3 -0
- package/config/builtin-skills.json +43 -0
- package/config/enhanced-cli-config.json +438 -0
- package/docs/CLI_TOOLS_AGENT_SKILL_ANALYSIS.md +463 -0
- package/docs/DESIGN_CLI_HELP_ANALYZER_REFACTOR.md +726 -0
- package/docs/ENHANCED_CLI_AGENT_SKILL_CONFIG.md +285 -0
- package/docs/IMPLEMENTATION_CHECKLIST_CLI_HELP_ANALYZER_REFACTOR.md +1268 -0
- package/docs/INSTALLER_ARCHITECTURE.md +257 -0
- package/docs/LESSONS_LEARNED.md +252 -0
- package/docs/SPECS_CLI_HELP_ANALYZER_REFACTOR.md +287 -0
- package/docs/SUDO_PROBLEM_AND_SOLUTION.md +529 -0
- package/docs/correct-skillsio-implementation.md +368 -0
- package/docs/development_guidelines.md +276 -0
- package/docs/independent-resume-implementation.md +198 -0
- package/docs/resumesession-final-implementation.md +195 -0
- package/docs/resumesession-usage.md +87 -0
- package/package.json +146 -136
- package/scripts/analyze-router.js +168 -0
- package/scripts/run-comprehensive-tests.js +230 -0
- package/scripts/run-quick-tests.js +90 -0
- package/scripts/test-runner.js +344 -0
- package/skills/resumesession/INDEPENDENT_SKILL.md +403 -0
- package/skills/resumesession/README.md +381 -0
- package/skills/resumesession/SKILL.md +211 -0
- package/skills/resumesession/__init__.py +33 -0
- package/skills/resumesession/implementations/simple-resume.js +13 -0
- package/skills/resumesession/independent-resume.js +750 -0
- package/skills/resumesession/package.json +1 -0
- package/skills/resumesession/skill.json +1 -0
- package/src/adapters/claude/install_claude_integration.js +9 -1
- package/src/adapters/codebuddy/install_codebuddy_integration.js +3 -1
- package/src/adapters/codex/install_codex_integration.js +15 -5
- package/src/adapters/gemini/install_gemini_integration.js +3 -1
- package/src/adapters/qwen/install_qwen_integration.js +3 -1
- package/src/cli/commands/autoinstall.js +65 -0
- package/src/cli/commands/errors.js +190 -0
- package/src/cli/commands/independent-resume.js +395 -0
- package/src/cli/commands/install.js +179 -0
- package/src/cli/commands/permissions.js +108 -0
- package/src/cli/commands/project.js +485 -0
- package/src/cli/commands/scan.js +97 -0
- package/src/cli/commands/simple-resume.js +377 -0
- package/src/cli/commands/skills.js +158 -0
- package/src/cli/commands/status.js +113 -0
- package/src/cli/commands/stigmergy-resume.js +775 -0
- package/src/cli/commands/system.js +301 -0
- package/src/cli/commands/universal-resume.js +394 -0
- package/src/cli/router-beta.js +471 -0
- package/src/cli/utils/environment.js +75 -0
- package/src/cli/utils/formatters.js +47 -0
- package/src/cli/utils/skills_cache.js +92 -0
- package/src/core/cache_cleaner.js +1 -0
- package/src/core/cli_adapters.js +345 -0
- package/src/core/cli_help_analyzer.js +1236 -680
- package/src/core/cli_path_detector.js +702 -709
- package/src/core/cli_tools.js +515 -160
- package/src/core/coordination/nodejs/CLIIntegrationManager.js +18 -0
- package/src/core/coordination/nodejs/HookDeploymentManager.js +242 -412
- package/src/core/coordination/nodejs/HookDeploymentManager.refactored.js +323 -0
- package/src/core/coordination/nodejs/generators/CLIAdapterGenerator.js +363 -0
- package/src/core/coordination/nodejs/generators/ResumeSessionGenerator.js +932 -0
- package/src/core/coordination/nodejs/generators/SkillsIntegrationGenerator.js +1395 -0
- package/src/core/coordination/nodejs/generators/index.js +12 -0
- package/src/core/enhanced_cli_installer.js +1208 -608
- package/src/core/enhanced_cli_parameter_handler.js +402 -0
- package/src/core/execution_mode_detector.js +222 -0
- package/src/core/installer.js +151 -106
- package/src/core/local_skill_scanner.js +732 -0
- package/src/core/multilingual/language-pattern-manager.js +1 -1
- package/src/core/skills/BuiltinSkillsDeployer.js +188 -0
- package/src/core/skills/StigmergySkillManager.js +123 -16
- package/src/core/skills/embedded-openskills/SkillParser.js +7 -3
- package/src/core/smart_router.js +550 -261
- package/src/index.js +10 -4
- package/src/utils.js +66 -7
- package/test/cli-integration.test.js +304 -0
- package/test/direct_smart_router_test.js +88 -0
- package/test/enhanced-cli-agent-skill-test.js +485 -0
- package/test/simple_test.js +82 -0
- package/test/smart_router_test_runner.js +123 -0
- package/test/smart_routing_edge_cases.test.js +284 -0
- package/test/smart_routing_simple_verification.js +139 -0
- package/test/smart_routing_verification.test.js +346 -0
- package/test/specific-cli-agent-skill-analysis.js +385 -0
- package/test/unit/smart_router.test.js +295 -0
- package/test/very_simple_test.js +54 -0
- package/src/cli/router.js +0 -1783
|
@@ -0,0 +1,932 @@
|
|
|
1
|
+
// ResumeSession Extension Generator
|
|
2
|
+
// 专门负责生成各CLI的ResumeSession扩展文件
|
|
3
|
+
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
class ResumeSessionGenerator {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.supportedCLIs = [
|
|
9
|
+
'claude', 'gemini', 'qwen', 'codebuddy', 'codex',
|
|
10
|
+
'iflow', 'qodercli', 'copilot', 'kode', 'opencode',
|
|
11
|
+
'oh-my-opencode', 'resumesession'
|
|
12
|
+
];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
generateForCLI(cliName) {
|
|
16
|
+
const needsSlashPrefix = ['claude', 'codebuddy'].includes(cliName.toLowerCase());
|
|
17
|
+
const commandName = needsSlashPrefix ? '/stigmergy-resume' : 'stigmergy-resume';
|
|
18
|
+
const currentProjectPath = process.cwd();
|
|
19
|
+
|
|
20
|
+
return this.generateExtensionContent(cliName, commandName, currentProjectPath);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
generateExtensionContent(cliName, commandName, projectPath) {
|
|
24
|
+
return `// ${cliName.charAt(0).toUpperCase() + cliName.slice(1)} CLI ResumeSession Integration
|
|
25
|
+
// Auto-generated by ResumeSession v1.0.4
|
|
26
|
+
// Project: ${projectPath}
|
|
27
|
+
|
|
28
|
+
const fs = require('fs');
|
|
29
|
+
const path = require('path');
|
|
30
|
+
const os = require('os');
|
|
31
|
+
|
|
32
|
+
// Embedded path configuration for CLI session paths
|
|
33
|
+
const getAllCLISessionPaths = () => {
|
|
34
|
+
const homeDir = os.homedir();
|
|
35
|
+
return {
|
|
36
|
+
claude: [path.join(homeDir, '.claude', 'projects')],
|
|
37
|
+
gemini: [path.join(homeDir, '.config', 'gemini', 'tmp')],
|
|
38
|
+
qwen: [path.join(homeDir, '.qwen', 'projects')],
|
|
39
|
+
iflow: [path.join(homeDir, '.iflow', 'projects')],
|
|
40
|
+
qodercli: [path.join(homeDir, '.qoder', 'projects')],
|
|
41
|
+
codebuddy: [path.join(homeDir, '.codebuddy')],
|
|
42
|
+
codex: [path.join(homeDir, '.config', 'codex')],
|
|
43
|
+
kode: [path.join(homeDir, '.kode', 'projects')]
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Embedded ResumeSession Core Functionality
|
|
48
|
+
|
|
49
|
+
class SessionScanner {
|
|
50
|
+
scanSessions(cliType, sessionsPath, projectPath) {
|
|
51
|
+
const sessions = [];
|
|
52
|
+
if (!sessionsPath || !projectPath) return sessions;
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
if (!fs.existsSync(sessionsPath)) return sessions;
|
|
56
|
+
|
|
57
|
+
// For IFlow, Claude, QoderCLI, Kode: scan projects subdirectories (one level)
|
|
58
|
+
if ((cliType === 'iflow' || cliType === 'claude' || cliType === 'qodercli' || cliType === 'kode') && sessionsPath.includes('projects')) {
|
|
59
|
+
const subdirs = fs.readdirSync(sessionsPath);
|
|
60
|
+
for (const subdir of subdirs) {
|
|
61
|
+
const subdirPath = path.join(sessionsPath, subdir);
|
|
62
|
+
try {
|
|
63
|
+
const stat = fs.statSync(subdirPath);
|
|
64
|
+
if (stat.isDirectory()) {
|
|
65
|
+
sessions.push(...this.scanSessionFiles(cliType, subdirPath, projectPath));
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return sessions;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// For Gemini: scan tmp/<hash>/chats subdirectories (multiple levels)
|
|
75
|
+
if (cliType === 'gemini' && sessionsPath.includes('tmp')) {
|
|
76
|
+
const hashDirs = fs.readdirSync(sessionsPath);
|
|
77
|
+
for (const hashDir of hashDirs) {
|
|
78
|
+
const hashDirPath = path.join(sessionsPath, hashDir);
|
|
79
|
+
try {
|
|
80
|
+
const stat = fs.statSync(hashDirPath);
|
|
81
|
+
if (stat.isDirectory()) {
|
|
82
|
+
const chatsPath = path.join(hashDirPath, 'chats');
|
|
83
|
+
if (fs.existsSync(chatsPath)) {
|
|
84
|
+
sessions.push(...this.scanSessionFiles(cliType, chatsPath, projectPath));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return sessions;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// For Qwen: scan projects/<projectName>/chats subdirectories (two levels)
|
|
95
|
+
if (cliType === 'qwen' && sessionsPath.includes('projects')) {
|
|
96
|
+
const projectDirs = fs.readdirSync(sessionsPath);
|
|
97
|
+
for (const projectDir of projectDirs) {
|
|
98
|
+
const projectDirPath = path.join(sessionsPath, projectDir);
|
|
99
|
+
try {
|
|
100
|
+
const stat = fs.statSync(projectDirPath);
|
|
101
|
+
if (stat.isDirectory()) {
|
|
102
|
+
const chatsPath = path.join(projectDirPath, 'chats');
|
|
103
|
+
if (fs.existsSync(chatsPath)) {
|
|
104
|
+
sessions.push(...this.scanSessionFiles(cliType, chatsPath, projectPath));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} catch (error) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return sessions;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// For CodeBuddy: scan both projects subdirectories and root history.jsonl
|
|
115
|
+
if (cliType === 'codebuddy') {
|
|
116
|
+
const projectsPath = path.join(sessionsPath, 'projects');
|
|
117
|
+
if (fs.existsSync(projectsPath)) {
|
|
118
|
+
const projectDirs = fs.readdirSync(projectsPath);
|
|
119
|
+
for (const projectDir of projectDirs) {
|
|
120
|
+
const projectDirPath = path.join(projectsPath, projectDir);
|
|
121
|
+
try {
|
|
122
|
+
const stat = fs.statSync(projectDirPath);
|
|
123
|
+
if (stat.isDirectory()) {
|
|
124
|
+
sessions.push(...this.scanSessionFiles(cliType, projectDirPath, projectPath));
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
sessions.push(...this.scanSessionFiles(cliType, sessionsPath, projectPath));
|
|
132
|
+
return sessions;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return this.scanSessionFiles(cliType, sessionsPath, projectPath);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.warn(\`Warning: Could not scan \${cliType} sessions:\`, error.message);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return sessions;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
scanSessionFiles(cliType, sessionsPath, projectPath) {
|
|
144
|
+
const sessions = [];
|
|
145
|
+
try {
|
|
146
|
+
const files = fs.readdirSync(sessionsPath);
|
|
147
|
+
for (const file of files) {
|
|
148
|
+
if (file.endsWith('.json') || file.endsWith('.session') || file.endsWith('.jsonl')) {
|
|
149
|
+
try {
|
|
150
|
+
const filePath = path.join(sessionsPath, file);
|
|
151
|
+
let sessionData;
|
|
152
|
+
|
|
153
|
+
if (file.endsWith('.jsonl')) {
|
|
154
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
155
|
+
const lines = content.trim().split('\\n').filter(line => line.trim());
|
|
156
|
+
const messages = lines.map(line => JSON.parse(line));
|
|
157
|
+
|
|
158
|
+
if (messages.length === 0) continue;
|
|
159
|
+
sessionData = this.parseJSONLSession(messages, file);
|
|
160
|
+
} else {
|
|
161
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
162
|
+
sessionData = JSON.parse(content);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (this.isProjectSession(sessionData, projectPath)) {
|
|
166
|
+
sessions.push({
|
|
167
|
+
cliType,
|
|
168
|
+
sessionId: sessionData.id || sessionData.sessionId || file.replace(/\\.(json|session|jsonl)$/, ''),
|
|
169
|
+
title: sessionData.title || sessionData.topic || 'Untitled',
|
|
170
|
+
content: this.extractContent(sessionData),
|
|
171
|
+
updatedAt: new Date(sessionData.updatedAt || sessionData.timestamp || fs.statSync(filePath).mtime),
|
|
172
|
+
messageCount: sessionData.messageCount || this.countMessages(sessionData),
|
|
173
|
+
projectPath
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.warn(\`Warning: Could not parse \${file}:\`, error.message);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.warn('Warning: Could not scan files:', error.message);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return sessions;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
parseJSONLSession(messages, filename) {
|
|
189
|
+
const firstMsg = messages[0];
|
|
190
|
+
const lastMsg = messages[messages.length - 1];
|
|
191
|
+
const userMessages = messages.filter(m => m.type === 'user' || m.role === 'user');
|
|
192
|
+
|
|
193
|
+
let title = 'Untitled Session';
|
|
194
|
+
if (userMessages.length > 0) {
|
|
195
|
+
const firstUserMsg = userMessages[0];
|
|
196
|
+
let content = firstUserMsg.message?.content || firstUserMsg.content || '';
|
|
197
|
+
if (typeof content === 'object') {
|
|
198
|
+
content = JSON.stringify(content);
|
|
199
|
+
}
|
|
200
|
+
if (typeof content === 'string' && content.trim()) {
|
|
201
|
+
title = content.substring(0, 100) || title;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const contentParts = messages
|
|
206
|
+
.map(m => {
|
|
207
|
+
if (m.message && typeof m.message === 'object') {
|
|
208
|
+
return m.message.content || m.message.text || '';
|
|
209
|
+
}
|
|
210
|
+
return m.content || m.text || '';
|
|
211
|
+
})
|
|
212
|
+
.filter(text => text && typeof text === 'string' && text.trim());
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
sessionId: firstMsg.sessionId || filename.replace('.jsonl', ''),
|
|
216
|
+
title: title,
|
|
217
|
+
content: contentParts.join(' '),
|
|
218
|
+
timestamp: lastMsg.timestamp || new Date().toISOString(),
|
|
219
|
+
projectPath: firstMsg.cwd || firstMsg.workingDirectory,
|
|
220
|
+
messageCount: messages.filter(m => m.type === 'user' || m.type === 'assistant' || m.role === 'user' || m.role === 'assistant').length,
|
|
221
|
+
messages: messages
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
scanAllCLISessions(projectPath) {
|
|
226
|
+
const allSessions = [];
|
|
227
|
+
const cliPathsMap = getAllCLISessionPaths();
|
|
228
|
+
|
|
229
|
+
for (const [cliType, sessionsPaths] of Object.entries(cliPathsMap)) {
|
|
230
|
+
for (const sessionsPath of sessionsPaths) {
|
|
231
|
+
const sessions = this.scanSessions(cliType, sessionsPath, projectPath);
|
|
232
|
+
allSessions.push(...sessions);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return allSessions;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
isProjectSession(session, projectPath) {
|
|
240
|
+
const sessionProject = session.projectPath || session.workingDirectory;
|
|
241
|
+
if (!sessionProject) return true;
|
|
242
|
+
|
|
243
|
+
return sessionProject === projectPath ||
|
|
244
|
+
sessionProject.startsWith(projectPath) ||
|
|
245
|
+
projectPath.startsWith(sessionProject);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
extractContent(sessionData) {
|
|
249
|
+
if (sessionData.content && typeof sessionData.content === 'string') {
|
|
250
|
+
return sessionData.content;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (sessionData.messages && Array.isArray(sessionData.messages)) {
|
|
254
|
+
return sessionData.messages
|
|
255
|
+
.map(msg => {
|
|
256
|
+
if (msg.message && typeof msg.message === 'object') {
|
|
257
|
+
const content = msg.message.content || msg.message.text || '';
|
|
258
|
+
return this.extractTextFromContent(content);
|
|
259
|
+
}
|
|
260
|
+
const content = msg.content || msg.text || '';
|
|
261
|
+
return this.extractTextFromContent(content);
|
|
262
|
+
})
|
|
263
|
+
.filter(text => text && typeof text === 'string' && text.trim())
|
|
264
|
+
.join(' ');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (Array.isArray(sessionData)) {
|
|
268
|
+
return sessionData
|
|
269
|
+
.map(item => {
|
|
270
|
+
if (item.message && typeof item.message === 'object') {
|
|
271
|
+
const content = item.message.content || item.message.text || '';
|
|
272
|
+
return this.extractTextFromContent(content);
|
|
273
|
+
}
|
|
274
|
+
const content = item.content || item.text || '';
|
|
275
|
+
return this.extractTextFromContent(content);
|
|
276
|
+
})
|
|
277
|
+
.filter(text => text && typeof text === 'string' && text.trim())
|
|
278
|
+
.join(' ');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return '';
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
extractTextFromContent(content) {
|
|
285
|
+
if (typeof content === 'string') {
|
|
286
|
+
return content;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (Array.isArray(content)) {
|
|
290
|
+
return content
|
|
291
|
+
.map(item => {
|
|
292
|
+
if (typeof item === 'string') return item;
|
|
293
|
+
if (item && typeof item === 'object') {
|
|
294
|
+
return item.text || item.content || '';
|
|
295
|
+
}
|
|
296
|
+
return '';
|
|
297
|
+
})
|
|
298
|
+
.filter(text => text && typeof text === 'string')
|
|
299
|
+
.join(' ');
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (content && typeof content === 'object') {
|
|
303
|
+
return content.text || content.content || JSON.stringify(content);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return '';
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
countMessages(sessionData) {
|
|
310
|
+
if (sessionData.messages) {
|
|
311
|
+
return Array.isArray(sessionData.messages) ? sessionData.messages.length : 0;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (Array.isArray(sessionData)) {
|
|
315
|
+
return sessionData.length;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return 0;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
class SessionFilter {
|
|
323
|
+
filterByCLI(sessions, cliType) {
|
|
324
|
+
if (!cliType) return sessions;
|
|
325
|
+
return sessions.filter(session => session.cliType === cliType);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
filterBySearch(sessions, searchTerm) {
|
|
329
|
+
if (!searchTerm) return sessions;
|
|
330
|
+
|
|
331
|
+
const lowerSearch = searchTerm.toLowerCase();
|
|
332
|
+
return sessions.filter(session =>
|
|
333
|
+
session.title.toLowerCase().includes(lowerSearch) ||
|
|
334
|
+
session.content.toLowerCase().includes(lowerSearch)
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
filterByDateRange(sessions, timeRange = 'all') {
|
|
339
|
+
if (timeRange === 'all') return sessions;
|
|
340
|
+
|
|
341
|
+
const now = new Date();
|
|
342
|
+
return sessions.filter(session => {
|
|
343
|
+
const sessionDate = new Date(session.updatedAt);
|
|
344
|
+
|
|
345
|
+
switch (timeRange) {
|
|
346
|
+
case 'today':
|
|
347
|
+
return sessionDate.toDateString() === now.toDateString();
|
|
348
|
+
case 'week': {
|
|
349
|
+
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
350
|
+
return sessionDate >= weekAgo;
|
|
351
|
+
}
|
|
352
|
+
case 'month': {
|
|
353
|
+
const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
|
|
354
|
+
return sessionDate >= monthAgo;
|
|
355
|
+
}
|
|
356
|
+
default:
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
sortByDate(sessions) {
|
|
363
|
+
return [...sessions].sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
filterByProject(sessions, projectPath) {
|
|
367
|
+
return sessions.filter(session => session.projectPath === projectPath);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
applyFilters(sessions, options, projectPath) {
|
|
371
|
+
let filteredSessions = [...sessions];
|
|
372
|
+
|
|
373
|
+
filteredSessions = this.filterByProject(filteredSessions, projectPath);
|
|
374
|
+
|
|
375
|
+
if (options.cli) {
|
|
376
|
+
filteredSessions = this.filterByCLI(filteredSessions, options.cli);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (options.search) {
|
|
380
|
+
filteredSessions = this.filterBySearch(filteredSessions, options.search);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (options.timeRange) {
|
|
384
|
+
filteredSessions = this.filterByDateRange(filteredSessions, options.timeRange);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
filteredSessions = this.sortByDate(filteredSessions);
|
|
388
|
+
|
|
389
|
+
if (options.limit && options.limit > 0) {
|
|
390
|
+
filteredSessions = filteredSessions.slice(0, options.limit);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return filteredSessions;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
class HistoryFormatter {
|
|
398
|
+
formatSummary(sessions, context) {
|
|
399
|
+
if (sessions.length === 0) {
|
|
400
|
+
return \`📭 当前项目暂无历史会话\\n\\n💡 **提示:** 尝试: ${commandName} --search <关键词>\`;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
let response = \`📁 **项目历史会话**\\n\\n📊 共找到 \${sessions.length} 个会话\\n\\n\`;
|
|
404
|
+
|
|
405
|
+
const byCLI = {};
|
|
406
|
+
sessions.forEach(session => {
|
|
407
|
+
if (!byCLI[session.cliType]) byCLI[session.cliType] = [];
|
|
408
|
+
byCLI[session.cliType].push(session);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
Object.entries(byCLI).forEach(([cli, cliSessions]) => {
|
|
412
|
+
const icon = this.getCLIIcon(cli);
|
|
413
|
+
response += \`\${icon} **\${cli.toUpperCase()}** (\${cliSessions.length}个)\\n\`;
|
|
414
|
+
|
|
415
|
+
cliSessions.slice(0, 3).forEach((session, i) => {
|
|
416
|
+
const date = this.formatDate(session.updatedAt);
|
|
417
|
+
const title = session.title.substring(0, 50);
|
|
418
|
+
response += \` \${i + 1}. \${title}...\\n\`;
|
|
419
|
+
response += \` 📅 \${date} • 💬 \${session.messageCount}条消息\\n\`;
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
if (cliSessions.length > 3) {
|
|
423
|
+
response += \` ... 还有 \${cliSessions.length - 3} 个会话\\n\`;
|
|
424
|
+
}
|
|
425
|
+
response += '\\n';
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
response += \`💡 **使用方法:**\\n\`;
|
|
429
|
+
response += \`• '${commandName} --cli <工具>' - 查看特定CLI\\n\`;
|
|
430
|
+
response += \`• '${commandName} --search <关键词>' - 搜索内容\\n\`;
|
|
431
|
+
response += \`• '${commandName} --format timeline' - 时间线视图\`;
|
|
432
|
+
|
|
433
|
+
return response;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
getCLIIcon(cliType) {
|
|
437
|
+
const icons = {
|
|
438
|
+
'claude': '🟢',
|
|
439
|
+
'gemini': '🔵',
|
|
440
|
+
'qwen': '🟡',
|
|
441
|
+
'iflow': '🔴',
|
|
442
|
+
'codebuddy': '🟣',
|
|
443
|
+
'codex': '🟪',
|
|
444
|
+
'qodercli': '🟠',
|
|
445
|
+
'kode': '⚡'
|
|
446
|
+
};
|
|
447
|
+
return icons[cliType] || '🔹';
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
formatDate(date) {
|
|
451
|
+
const now = new Date();
|
|
452
|
+
const diff = now.getTime() - date.getTime();
|
|
453
|
+
const days = Math.floor(diff / (24 * 60 * 60 * 1000));
|
|
454
|
+
|
|
455
|
+
if (days === 0) {
|
|
456
|
+
return date.toLocaleTimeString();
|
|
457
|
+
} else if (days === 1) {
|
|
458
|
+
return '昨天';
|
|
459
|
+
} else if (days < 7) {
|
|
460
|
+
return \`\${days}天前\`;
|
|
461
|
+
} else if (days < 30) {
|
|
462
|
+
return \`\${Math.floor(days / 7)}周前\`;
|
|
463
|
+
} else {
|
|
464
|
+
return \`\${Math.floor(days / 30)}个月前\`;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
class HistoryQuery {
|
|
470
|
+
constructor() {
|
|
471
|
+
this.scanner = new SessionScanner();
|
|
472
|
+
this.filter = new SessionFilter();
|
|
473
|
+
this.formatter = new HistoryFormatter();
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
queryHistory(options, projectPath) {
|
|
477
|
+
try {
|
|
478
|
+
const allSessions = this.scanner.scanAllCLISessions(projectPath);
|
|
479
|
+
const filteredSessions = this.filter.applyFilters(allSessions, options, projectPath);
|
|
480
|
+
|
|
481
|
+
const response = this.formatter.formatSummary(filteredSessions);
|
|
482
|
+
const suggestions = this.generateSuggestions(filteredSessions, options);
|
|
483
|
+
|
|
484
|
+
return {
|
|
485
|
+
response,
|
|
486
|
+
suggestions
|
|
487
|
+
};
|
|
488
|
+
} catch (error) {
|
|
489
|
+
return {
|
|
490
|
+
response: \`❌ 历史查询失败: \${error.message}\`,
|
|
491
|
+
suggestions: ['${commandName} --help']
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
generateSuggestions(sessions, query) {
|
|
497
|
+
const suggestions = [];
|
|
498
|
+
|
|
499
|
+
if (sessions.length > 0) {
|
|
500
|
+
suggestions.push(\`${commandName} --format timeline\`);
|
|
501
|
+
|
|
502
|
+
if (sessions[0]?.cliType) {
|
|
503
|
+
suggestions.push(\`${commandName} --cli \${sessions[0].cliType}\`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
suggestions.push(\`${commandName} --search "react"\`);
|
|
508
|
+
suggestions.push(\`${commandName} --today\`);
|
|
509
|
+
|
|
510
|
+
return suggestions.slice(0, 5);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* 处理 ${commandName} 命令
|
|
516
|
+
*/
|
|
517
|
+
async function handleHistoryCommand(input, context) {
|
|
518
|
+
try {
|
|
519
|
+
console.log('🔍 Searching cross-CLI history...');
|
|
520
|
+
|
|
521
|
+
const query = buildQuery(input);
|
|
522
|
+
const historyQuery = new HistoryQuery();
|
|
523
|
+
const result = historyQuery.queryHistory(query, '${projectPath}');
|
|
524
|
+
|
|
525
|
+
return {
|
|
526
|
+
response: result.response,
|
|
527
|
+
suggestions: result.suggestions
|
|
528
|
+
};
|
|
529
|
+
} catch (error) {
|
|
530
|
+
console.error('History command error:', error);
|
|
531
|
+
return {
|
|
532
|
+
response: \`❌ 历史查询失败: \${error.message}\`,
|
|
533
|
+
suggestions: ['${commandName} --help']
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* 构建查询参数
|
|
540
|
+
*/
|
|
541
|
+
function buildQuery(input) {
|
|
542
|
+
const options = {
|
|
543
|
+
limit: 10,
|
|
544
|
+
format: 'summary',
|
|
545
|
+
timeRange: 'all',
|
|
546
|
+
cli: null,
|
|
547
|
+
search: null
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
const cleanInput = input.replace(new RegExp('^\\\\\\\\/?' + '${commandName}' + '\\\\\\s*', 'i'), '').trim();
|
|
551
|
+
const parts = cleanInput.split(/\\\\s+/).filter(p => p.length > 0);
|
|
552
|
+
|
|
553
|
+
for (let i = 0; i < parts.length; i++) {
|
|
554
|
+
const part = parts[i].toLowerCase();
|
|
555
|
+
|
|
556
|
+
if (part === '--cli' && i + 1 < parts.length) {
|
|
557
|
+
options.cli = parts[++i];
|
|
558
|
+
} else if (part === '--search' && i + 1 < parts.length) {
|
|
559
|
+
options.search = parts[++i];
|
|
560
|
+
} else if (part === '--limit' && i + 1 < parts.length) {
|
|
561
|
+
options.limit = parseInt(parts[++i]);
|
|
562
|
+
} else if (part === '--format' && i + 1 < parts.length) {
|
|
563
|
+
const format = parts[++i]?.toLowerCase();
|
|
564
|
+
if (['summary', 'timeline', 'detailed', 'context'].includes(format)) {
|
|
565
|
+
options.format = format;
|
|
566
|
+
}
|
|
567
|
+
} else if (part === '--today') {
|
|
568
|
+
options.timeRange = 'today';
|
|
569
|
+
} else if (part === '--week') {
|
|
570
|
+
options.timeRange = 'week';
|
|
571
|
+
} else if (part === '--month') {
|
|
572
|
+
options.timeRange = 'month';
|
|
573
|
+
} else if (!part.startsWith('--') && !options.search) {
|
|
574
|
+
options.search = part;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return options;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// CLI-specific registration logic
|
|
582
|
+
|
|
583
|
+
${this.generateCLIRegistrationCode(cliName, commandName, projectPath)}
|
|
584
|
+
`;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
generateCLIRegistrationCode(cliName, commandName, projectPath) {
|
|
588
|
+
switch (cliName.toLowerCase()) {
|
|
589
|
+
case 'claude':
|
|
590
|
+
case 'codebuddy':
|
|
591
|
+
return `
|
|
592
|
+
// ${cliName.charAt(0).toUpperCase() + cliName.slice(1)} CLI integration
|
|
593
|
+
|
|
594
|
+
// Command name constant
|
|
595
|
+
const commandName = '${commandName}';
|
|
596
|
+
|
|
597
|
+
// Main command handler
|
|
598
|
+
function handleHistoryCommand(input) {
|
|
599
|
+
if (!input || typeof input !== 'string') {
|
|
600
|
+
return {
|
|
601
|
+
response: 'Usage: ' + commandName + ' [options]\\n' +
|
|
602
|
+
'Options:\\n' +
|
|
603
|
+
' --cli <name> Filter by CLI (claude, gemini, qwen, etc.)\\n' +
|
|
604
|
+
' --search <term> Search in session content\\n' +
|
|
605
|
+
' --limit <num> Maximum sessions to show (default: 10)\\n' +
|
|
606
|
+
' --format <type> Output format: summary, detailed, json (default: summary)\\n' +
|
|
607
|
+
' --time <range> Time range: today, week, month, all (default: all)',
|
|
608
|
+
suggestions: []
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
try {
|
|
613
|
+
const options = parseCommandOptions(input);
|
|
614
|
+
const historyQuery = new HistoryQuery();
|
|
615
|
+
const result = historyQuery.queryHistory(options, projectPath);
|
|
616
|
+
|
|
617
|
+
return {
|
|
618
|
+
response: result.response,
|
|
619
|
+
suggestions: result.suggestions || []
|
|
620
|
+
};
|
|
621
|
+
} catch (error) {
|
|
622
|
+
return {
|
|
623
|
+
response: 'Error: ' + error.message,
|
|
624
|
+
suggestions: []
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Parse command options
|
|
630
|
+
function parseCommandOptions(input) {
|
|
631
|
+
const options = {
|
|
632
|
+
limit: 10,
|
|
633
|
+
format: 'summary',
|
|
634
|
+
timeRange: 'all',
|
|
635
|
+
cli: null,
|
|
636
|
+
search: null
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
const cleanInput = input.replace(new RegExp('^\\\\/?' + commandName + '\\\\\s*', 'i'), '').trim();
|
|
640
|
+
const parts = cleanInput.split(/\\\s+/).filter(p => p.length > 0);
|
|
641
|
+
|
|
642
|
+
for (let i = 0; i < parts.length; i++) {
|
|
643
|
+
const part = parts[i].toLowerCase();
|
|
644
|
+
if (part === '--cli' && i + 1 < parts.length) {
|
|
645
|
+
options.cli = parts[++i];
|
|
646
|
+
} else if (part === '--search' && i + 1 < parts.length) {
|
|
647
|
+
options.search = parts[++i];
|
|
648
|
+
} else if (part === '--limit' && i + 1 < parts.length) {
|
|
649
|
+
options.limit = parseInt(parts[++i]);
|
|
650
|
+
} else if (part === '--format' && i + 1 < parts.length) {
|
|
651
|
+
options.format = parts[++i];
|
|
652
|
+
} else if (part === '--time' && i + 1 < parts.length) {
|
|
653
|
+
options.timeRange = parts[++i];
|
|
654
|
+
} else if (!part.startsWith('--') && !options.search) {
|
|
655
|
+
options.search = part;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
return options;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// Register command
|
|
663
|
+
if (typeof ${cliName} !== 'undefined') {
|
|
664
|
+
${cliName}.addCommand('${commandName}', handleHistoryCommand);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// 导出处理器
|
|
668
|
+
module.exports = {
|
|
669
|
+
handleHistoryCommand,
|
|
670
|
+
parseCommandOptions
|
|
671
|
+
};`;
|
|
672
|
+
|
|
673
|
+
case 'gemini':
|
|
674
|
+
return `
|
|
675
|
+
class GeminiHistoryHandler {
|
|
676
|
+
constructor() {
|
|
677
|
+
this.commandName = '${commandName}';
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
async handleCommand(input, session) {
|
|
681
|
+
if (!input.startsWith('${commandName}')) return null;
|
|
682
|
+
|
|
683
|
+
try {
|
|
684
|
+
const options = this.buildQuery(input);
|
|
685
|
+
const historyQuery = new HistoryQuery();
|
|
686
|
+
const result = historyQuery.queryHistory(options, '${projectPath}');
|
|
687
|
+
|
|
688
|
+
return {
|
|
689
|
+
text: result.response,
|
|
690
|
+
continue: true,
|
|
691
|
+
suggestions: result.suggestions
|
|
692
|
+
};
|
|
693
|
+
} catch (error) {
|
|
694
|
+
return {
|
|
695
|
+
text: \`History command failed: \${error.message}\`,
|
|
696
|
+
continue: true,
|
|
697
|
+
suggestions: []
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
buildQuery(input) {
|
|
703
|
+
const options = {
|
|
704
|
+
limit: 10,
|
|
705
|
+
format: 'summary',
|
|
706
|
+
timeRange: 'all',
|
|
707
|
+
cli: null,
|
|
708
|
+
search: null
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
const cleanInput = input.replace(new RegExp('^\\\\/?' + this.commandName + '\\\\\s*', 'i'), '').trim();
|
|
712
|
+
const parts = cleanInput.split(/\\\s+/).filter(p => p.length > 0);
|
|
713
|
+
|
|
714
|
+
for (let i = 0; i < parts.length; i++) {
|
|
715
|
+
const part = parts[i].toLowerCase();
|
|
716
|
+
if (part === '--cli' && i + 1 < parts.length) {
|
|
717
|
+
options.cli = parts[++i];
|
|
718
|
+
} else if (part === '--search' && i + 1 < parts.length) {
|
|
719
|
+
options.search = parts[++i];
|
|
720
|
+
} else if (part === '--limit' && i + 1 < parts.length) {
|
|
721
|
+
options.limit = parseInt(parts[++i]);
|
|
722
|
+
} else if (part === '--format' && i + 1 < parts.length) {
|
|
723
|
+
options.format = parts[++i];
|
|
724
|
+
} else if (part === '--time' && i + 1 < parts.length) {
|
|
725
|
+
options.timeRange = parts[++i];
|
|
726
|
+
} else if (!part.startsWith('--') && !options.search) {
|
|
727
|
+
options.search = part;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
return options;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// 注册处理器
|
|
736
|
+
const handler = new GeminiHistoryHandler();
|
|
737
|
+
|
|
738
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
739
|
+
module.exports = { GeminiHistoryHandler, handler };
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
if (typeof geminiCLI !== 'undefined') {
|
|
743
|
+
const cmdName = '${commandName}'.replace(/^\\\//, '');
|
|
744
|
+
geminiCLI.addCommandHandler(cmdName, handler.handleCommand.bind(handler));
|
|
745
|
+
}`;
|
|
746
|
+
|
|
747
|
+
case 'qwen':
|
|
748
|
+
case 'qodercli':
|
|
749
|
+
case 'kode':
|
|
750
|
+
return `
|
|
751
|
+
// ${cliName.charAt(0).toUpperCase() + cliName.slice(1)} CLI integration
|
|
752
|
+
|
|
753
|
+
// Command name constant
|
|
754
|
+
const commandName = '${commandName}';
|
|
755
|
+
|
|
756
|
+
// Main command handler
|
|
757
|
+
function handleHistoryCommand(input) {
|
|
758
|
+
if (!input || typeof input !== 'string') {
|
|
759
|
+
return {
|
|
760
|
+
response: 'Usage: ' + commandName + ' [options]\\n' +
|
|
761
|
+
'Options:\\n' +
|
|
762
|
+
' --cli <name> Filter by CLI (claude, gemini, qwen, etc.)\\n' +
|
|
763
|
+
' --search <term> Search in session content\\n' +
|
|
764
|
+
' --limit <num> Maximum sessions to show (default: 10)\\n' +
|
|
765
|
+
' --format <type> Output format: summary, detailed, json (default: summary)\\n' +
|
|
766
|
+
' --time <range> Time range: today, week, month, all (default: all)',
|
|
767
|
+
suggestions: []
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
try {
|
|
772
|
+
const options = parseCommandOptions(input);
|
|
773
|
+
const historyQuery = new HistoryQuery();
|
|
774
|
+
const result = historyQuery.queryHistory(options, projectPath);
|
|
775
|
+
|
|
776
|
+
return {
|
|
777
|
+
response: result.response,
|
|
778
|
+
suggestions: result.suggestions || []
|
|
779
|
+
};
|
|
780
|
+
} catch (error) {
|
|
781
|
+
return {
|
|
782
|
+
response: 'Error: ' + error.message,
|
|
783
|
+
suggestions: []
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// Parse command options
|
|
789
|
+
function parseCommandOptions(input) {
|
|
790
|
+
const options = {
|
|
791
|
+
limit: 10,
|
|
792
|
+
format: 'summary',
|
|
793
|
+
timeRange: 'all',
|
|
794
|
+
cli: null,
|
|
795
|
+
search: null
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
const cleanInput = input.replace(new RegExp('^\\\\/?' + commandName + '\\\\\s*', 'i'), '').trim();
|
|
799
|
+
const parts = cleanInput.split(/\\\s+/).filter(p => p.length > 0);
|
|
800
|
+
|
|
801
|
+
for (let i = 0; i < parts.length; i++) {
|
|
802
|
+
const part = parts[i].toLowerCase();
|
|
803
|
+
if (part === '--cli' && i + 1 < parts.length) {
|
|
804
|
+
options.cli = parts[++i];
|
|
805
|
+
} else if (part === '--search' && i + 1 < parts.length) {
|
|
806
|
+
options.search = parts[++i];
|
|
807
|
+
} else if (part === '--limit' && i + 1 < parts.length) {
|
|
808
|
+
options.limit = parseInt(parts[++i]);
|
|
809
|
+
} else if (part === '--format' && i + 1 < parts.length) {
|
|
810
|
+
options.format = parts[++i];
|
|
811
|
+
} else if (part === '--time' && i + 1 < parts.length) {
|
|
812
|
+
options.timeRange = parts[++i];
|
|
813
|
+
} else if (!part.startsWith('--') && !options.search) {
|
|
814
|
+
options.search = part;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
return options;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// Register extension
|
|
822
|
+
if (typeof ${cliName} !== 'undefined' && ${cliName}.addExtension) {
|
|
823
|
+
${cliName}.addExtension('history', handleHistoryCommand);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// 导出处理器
|
|
827
|
+
module.exports = {
|
|
828
|
+
handleHistoryCommand,
|
|
829
|
+
parseCommandOptions
|
|
830
|
+
};`;
|
|
831
|
+
|
|
832
|
+
default:
|
|
833
|
+
return `
|
|
834
|
+
// ${cliName.charAt(0).toUpperCase() + cliName.slice(1)} CLI integration
|
|
835
|
+
// Generic registration - may need customization for specific CLI
|
|
836
|
+
|
|
837
|
+
// Command name constant
|
|
838
|
+
const commandName = '${commandName}';
|
|
839
|
+
|
|
840
|
+
// Main command handler
|
|
841
|
+
function handleHistoryCommand(input) {
|
|
842
|
+
if (!input || typeof input !== 'string') {
|
|
843
|
+
return {
|
|
844
|
+
response: 'Usage: ' + commandName + ' [options]\\n' +
|
|
845
|
+
'Options:\\n' +
|
|
846
|
+
' --cli <name> Filter by CLI (claude, gemini, qwen, etc.)\\n' +
|
|
847
|
+
' --search <term> Search in session content\\n' +
|
|
848
|
+
' --limit <num> Maximum sessions to show (default: 10)\\n' +
|
|
849
|
+
' --format <type> Output format: summary, detailed, json (default: summary)\\n' +
|
|
850
|
+
' --time <range> Time range: today, week, month, all (default: all)',
|
|
851
|
+
suggestions: []
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
try {
|
|
856
|
+
const options = parseCommandOptions(input);
|
|
857
|
+
const historyQuery = new HistoryQuery();
|
|
858
|
+
const result = historyQuery.queryHistory(options, projectPath);
|
|
859
|
+
|
|
860
|
+
return {
|
|
861
|
+
response: result.response,
|
|
862
|
+
suggestions: result.suggestions || []
|
|
863
|
+
};
|
|
864
|
+
} catch (error) {
|
|
865
|
+
return {
|
|
866
|
+
response: 'Error: ' + error.message,
|
|
867
|
+
suggestions: []
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// Parse command options
|
|
873
|
+
function parseCommandOptions(input) {
|
|
874
|
+
const options = {
|
|
875
|
+
limit: 10,
|
|
876
|
+
format: 'summary',
|
|
877
|
+
timeRange: 'all',
|
|
878
|
+
cli: null,
|
|
879
|
+
search: null
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
const cleanInput = input.replace(new RegExp('^\\\\/?' + commandName + '\\\\\s*', 'i'), '').trim();
|
|
883
|
+
const parts = cleanInput.split(/\\\s+/).filter(p => p.length > 0);
|
|
884
|
+
|
|
885
|
+
for (let i = 0; i < parts.length; i++) {
|
|
886
|
+
const part = parts[i].toLowerCase();
|
|
887
|
+
if (part === '--cli' && i + 1 < parts.length) {
|
|
888
|
+
options.cli = parts[++i];
|
|
889
|
+
} else if (part === '--search' && i + 1 < parts.length) {
|
|
890
|
+
options.search = parts[++i];
|
|
891
|
+
} else if (part === '--limit' && i + 1 < parts.length) {
|
|
892
|
+
options.limit = parseInt(parts[++i]);
|
|
893
|
+
} else if (part === '--format' && i + 1 < parts.length) {
|
|
894
|
+
options.format = parts[++i];
|
|
895
|
+
} else if (part === '--time' && i + 1 < parts.length) {
|
|
896
|
+
options.timeRange = parts[++i];
|
|
897
|
+
} else if (!part.startsWith('--') && !options.search) {
|
|
898
|
+
options.search = part;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
return options;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
906
|
+
module.exports = {
|
|
907
|
+
handleHistoryCommand,
|
|
908
|
+
parseCommandOptions
|
|
909
|
+
};
|
|
910
|
+
}`;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
getFileName(cliName) {
|
|
915
|
+
switch (cliName.toLowerCase()) {
|
|
916
|
+
case 'claude':
|
|
917
|
+
case 'gemini':
|
|
918
|
+
case 'qwen':
|
|
919
|
+
case 'codebuddy':
|
|
920
|
+
case 'codex':
|
|
921
|
+
case 'kode':
|
|
922
|
+
case 'resumesession':
|
|
923
|
+
return 'resumesession-history.js';
|
|
924
|
+
case 'qodercli':
|
|
925
|
+
return 'history.js';
|
|
926
|
+
default:
|
|
927
|
+
return 'resumesession-history.js';
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
module.exports = ResumeSessionGenerator;
|