stigmergy 1.0.73 → 1.0.79
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 +190 -469
- package/package.json +5 -5
- package/src/core/cli_help_analyzer.js +563 -0
- package/src/index.js +9 -12
- package/src/main.js +321 -32
- package/src/main_english.js +689 -0
- package/README.de.md +0 -301
- package/README.en.md +0 -307
- package/README.es.md +0 -301
- package/README.fr.md +0 -301
- package/README.ja.md +0 -301
- package/README.ko.md +0 -301
- package/README.ru.md +0 -301
- package/README.zh.md +0 -301
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stigmergy",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.79",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"description": "Stigmergy CLI - Multi-Agents Cross-AI CLI Tools Collaboration System",
|
|
6
|
-
"main": "src/
|
|
6
|
+
"main": "src/main_english.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"stigmergy": "src/
|
|
8
|
+
"stigmergy": "src/main_english.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"start": "node src/
|
|
11
|
+
"start": "node src/main_english.js",
|
|
12
12
|
"test": "node test/real-test.js",
|
|
13
13
|
"test:all": "node test/test-all-clis.js",
|
|
14
14
|
"test:cross-cli": "node test/cross-cli-real-test.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"dev": "node --watch src/index.js",
|
|
23
23
|
"lint": "eslint src/",
|
|
24
24
|
"format": "prettier --write src/",
|
|
25
|
-
"postinstall": "node src/
|
|
25
|
+
"postinstall": "node src/main_english.js auto-install"
|
|
26
26
|
},
|
|
27
27
|
"keywords": [
|
|
28
28
|
"ai",
|
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advanced CLI Help Analyzer - International Version
|
|
3
|
+
* Analyzes CLI help information to extract command patterns and calling methods
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { spawnSync } = require('child_process');
|
|
7
|
+
const fs = require('fs/promises');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
|
|
11
|
+
class CLIHelpAnalyzer {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.configDir = path.join(os.homedir(), '.stigmergy', 'cli-patterns');
|
|
14
|
+
this.persistentConfig = path.join(this.configDir, 'cli-patterns.json');
|
|
15
|
+
this.lastAnalysisFile = path.join(this.configDir, 'last-analysis.json');
|
|
16
|
+
this.cliTools = require('../main').CLI_TOOLS || {};
|
|
17
|
+
|
|
18
|
+
// Pattern recognition rules for different CLI types
|
|
19
|
+
this.patternRules = {
|
|
20
|
+
// OpenAI style CLI (codex, chatgpt)
|
|
21
|
+
openai: {
|
|
22
|
+
commandPattern: /^(\w+)(?:\s+--?[\w-]+(?:\s+[\w-]+)?)?\s*(.*)$/,
|
|
23
|
+
optionPattern: /--?[\w-]+(?:\s+[\w-]+)?/g,
|
|
24
|
+
examplePattern: /(?:example|usage|Usage)[::]\s*\n([\s\S]*?)(?=\n\n|\n[A-Z]|\n$)/gi,
|
|
25
|
+
subcommandPattern: /^\s{2,4}(\w+)\s+.+$/gm
|
|
26
|
+
},
|
|
27
|
+
// Anthropic style CLI (claude)
|
|
28
|
+
anthropic: {
|
|
29
|
+
commandPattern: /^(\w+)(?:\s+(?:--?\w+|\[\w+\]))*\s*(.*)$/,
|
|
30
|
+
optionPattern: /--?\w+(?:\s+<\w+>)?/g,
|
|
31
|
+
examplePattern: /(?:Examples?|例子)[::]\s*\n([\s\S]*?)(?=\n\n|\n[A-Z]|\n$)/gi,
|
|
32
|
+
subcommandPattern: /^\s{2}(\w+)\s{2,}.+$/gm
|
|
33
|
+
},
|
|
34
|
+
// Google style CLI (gemini)
|
|
35
|
+
google: {
|
|
36
|
+
commandPattern: /^(\w+)(?:\s+(?:--?\w+(?:=\w+)?|<\w+>))*\s*(.*)$/,
|
|
37
|
+
optionPattern: /--?\w+(?:=\w+)?/g,
|
|
38
|
+
examplePattern: /(?:Examples?|SAMPLE)[::]\s*\n([\s\S]*?)(?=\n\n|\n[A-Z]|\n$)/gi,
|
|
39
|
+
subcommandPattern: /^\s{2,4}(\w+)\s{2,}.+$/gm
|
|
40
|
+
},
|
|
41
|
+
// Generic CLI pattern
|
|
42
|
+
generic: {
|
|
43
|
+
commandPattern: /^(\w+)(?:\s+.*)?$/,
|
|
44
|
+
optionPattern: /--?[a-zA-Z][\w-]*/g,
|
|
45
|
+
examplePattern: /(?:example|usage|用法|使用)[::]\s*\n([\s\S]*?)(?=\n\n|\n[A-Z]|\n$)/gi,
|
|
46
|
+
subcommandPattern: /^\s{2,6}([a-z][a-z0-9_-]+)\s+.+$/gm
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Initialize the analyzer and ensure config directory exists
|
|
53
|
+
*/
|
|
54
|
+
async initialize() {
|
|
55
|
+
try {
|
|
56
|
+
await fs.mkdir(this.configDir, { recursive: true });
|
|
57
|
+
|
|
58
|
+
// Initialize persistent config if not exists
|
|
59
|
+
const configExists = await this.fileExists(this.persistentConfig);
|
|
60
|
+
if (!configExists) {
|
|
61
|
+
await this.savePersistentConfig({
|
|
62
|
+
version: '1.0.0',
|
|
63
|
+
lastUpdated: new Date().toISOString(),
|
|
64
|
+
cliPatterns: {},
|
|
65
|
+
failedAttempts: {}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return true;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error('Failed to initialize CLI Help Analyzer:', error.message);
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Analyze all configured CLI tools
|
|
78
|
+
*/
|
|
79
|
+
async analyzeAllCLI() {
|
|
80
|
+
const results = {};
|
|
81
|
+
|
|
82
|
+
for (const [cliName, cliConfig] of Object.entries(this.cliTools)) {
|
|
83
|
+
try {
|
|
84
|
+
console.log(`Analyzing ${cliName}...`);
|
|
85
|
+
results[cliName] = await this.analyzeCLI(cliName);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error(`Failed to analyze ${cliName}:`, error.message);
|
|
88
|
+
results[cliName] = {
|
|
89
|
+
success: false,
|
|
90
|
+
error: error.message,
|
|
91
|
+
timestamp: new Date().toISOString()
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return results;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Analyze a specific CLI tool's help information
|
|
101
|
+
*/
|
|
102
|
+
async analyzeCLI(cliName) {
|
|
103
|
+
const cliConfig = this.cliTools[cliName];
|
|
104
|
+
if (!cliConfig) {
|
|
105
|
+
throw new Error(`CLI configuration not found for ${cliName}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Check if we have recent cached analysis
|
|
109
|
+
const cached = await this.getCachedAnalysis(cliName);
|
|
110
|
+
if (cached && !this.isCacheExpired(cached.timestamp)) {
|
|
111
|
+
return cached;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
// Get help information using multiple methods
|
|
116
|
+
const helpInfo = await this.getHelpInfo(cliName, cliConfig);
|
|
117
|
+
|
|
118
|
+
// Detect CLI type for pattern recognition
|
|
119
|
+
const cliType = this.detectCLIType(helpInfo.rawHelp, cliName);
|
|
120
|
+
|
|
121
|
+
// Extract patterns based on CLI type
|
|
122
|
+
const patterns = this.extractPatterns(helpInfo.rawHelp, cliType);
|
|
123
|
+
|
|
124
|
+
// Analyze command structure
|
|
125
|
+
const commandStructure = this.analyzeCommandStructure(patterns);
|
|
126
|
+
|
|
127
|
+
// Extract usage examples
|
|
128
|
+
const examples = this.extractUsageExamples(helpInfo.rawHelp, cliType);
|
|
129
|
+
|
|
130
|
+
// Determine CLI interaction mode
|
|
131
|
+
const interactionMode = this.determineInteractionMode(helpInfo, patterns);
|
|
132
|
+
|
|
133
|
+
const analysis = {
|
|
134
|
+
success: true,
|
|
135
|
+
cliName,
|
|
136
|
+
cliType,
|
|
137
|
+
version: helpInfo.version,
|
|
138
|
+
helpMethod: helpInfo.method,
|
|
139
|
+
patterns,
|
|
140
|
+
commandStructure,
|
|
141
|
+
examples,
|
|
142
|
+
interactionMode,
|
|
143
|
+
timestamp: new Date().toISOString()
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// Cache the analysis
|
|
147
|
+
await this.cacheAnalysis(cliName, analysis);
|
|
148
|
+
|
|
149
|
+
return analysis;
|
|
150
|
+
|
|
151
|
+
} catch (error) {
|
|
152
|
+
// Record failed attempt
|
|
153
|
+
await this.recordFailedAttempt(cliName, error);
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get help information using multiple methods
|
|
160
|
+
*/
|
|
161
|
+
async getHelpInfo(cliName, cliConfig) {
|
|
162
|
+
const helpMethods = [
|
|
163
|
+
['--help'],
|
|
164
|
+
['-h'],
|
|
165
|
+
['help'],
|
|
166
|
+
['--usage'],
|
|
167
|
+
[''],
|
|
168
|
+
['version'],
|
|
169
|
+
['--version'],
|
|
170
|
+
['-v']
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
let rawHelp = '';
|
|
174
|
+
let version = 'unknown';
|
|
175
|
+
let method = 'unknown';
|
|
176
|
+
|
|
177
|
+
// Try different help commands
|
|
178
|
+
for (const helpArgs of helpMethods) {
|
|
179
|
+
try {
|
|
180
|
+
const result = spawnSync(cliName, helpArgs, {
|
|
181
|
+
encoding: 'utf8',
|
|
182
|
+
timeout: 15000,
|
|
183
|
+
shell: true
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (result.status === 0 && result.stdout) {
|
|
187
|
+
rawHelp = result.stdout;
|
|
188
|
+
method = `${cliName} ${helpArgs.join(' ')}`;
|
|
189
|
+
break;
|
|
190
|
+
} else if (result.stderr) {
|
|
191
|
+
rawHelp = result.stderr;
|
|
192
|
+
method = `${cliName} ${helpArgs.join(' ')} (stderr)`;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
} catch (error) {
|
|
196
|
+
// Try next method
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Try to get version separately
|
|
202
|
+
if (cliConfig.version) {
|
|
203
|
+
try {
|
|
204
|
+
const versionCmd = cliConfig.version.split(' ');
|
|
205
|
+
const versionResult = spawnSync(versionCmd[0], versionCmd.slice(1), {
|
|
206
|
+
encoding: 'utf8',
|
|
207
|
+
timeout: 10000,
|
|
208
|
+
shell: true
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
if (versionResult.status === 0) {
|
|
212
|
+
version = versionResult.stdout.trim() || versionResult.stderr.trim();
|
|
213
|
+
}
|
|
214
|
+
} catch (error) {
|
|
215
|
+
// Use default version
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (!rawHelp) {
|
|
220
|
+
throw new Error(`Unable to get help information for ${cliName}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return { rawHelp, version, method };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Detect CLI type based on help content and naming
|
|
228
|
+
*/
|
|
229
|
+
detectCLIType(helpText, cliName) {
|
|
230
|
+
const text = helpText.toLowerCase();
|
|
231
|
+
const name = cliName.toLowerCase();
|
|
232
|
+
|
|
233
|
+
// Detect based on CLI name
|
|
234
|
+
if (name.includes('claude') || name.includes('anthropic')) {
|
|
235
|
+
return 'anthropic';
|
|
236
|
+
}
|
|
237
|
+
if (name.includes('gemini') || name.includes('google')) {
|
|
238
|
+
return 'google';
|
|
239
|
+
}
|
|
240
|
+
if (name.includes('codex') || name.includes('openai') || name.includes('chatgpt')) {
|
|
241
|
+
return 'openai';
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Detect based on help content patterns
|
|
245
|
+
if (text.includes('anthropic') || text.includes('claude')) {
|
|
246
|
+
return 'anthropic';
|
|
247
|
+
}
|
|
248
|
+
if (text.includes('google') || text.includes('gemini') || text.includes('vertex')) {
|
|
249
|
+
return 'google';
|
|
250
|
+
}
|
|
251
|
+
if (text.includes('openai') || text.includes('gpt') || text.includes('codex')) {
|
|
252
|
+
return 'openai';
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Default to generic
|
|
256
|
+
return 'generic';
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Extract command patterns from help text
|
|
261
|
+
*/
|
|
262
|
+
extractPatterns(helpText, cliType) {
|
|
263
|
+
const rules = this.patternRules[cliType] || this.patternRules.generic;
|
|
264
|
+
const patterns = {
|
|
265
|
+
commands: [],
|
|
266
|
+
options: [],
|
|
267
|
+
subcommands: [],
|
|
268
|
+
arguments: [],
|
|
269
|
+
flags: []
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// Extract subcommands
|
|
273
|
+
const subcommandMatches = helpText.match(rules.subcommandPattern);
|
|
274
|
+
if (subcommandMatches) {
|
|
275
|
+
patterns.subcommands = subcommandMatches.map(match => {
|
|
276
|
+
const parts = match.trim().split(/\s+/);
|
|
277
|
+
return {
|
|
278
|
+
name: parts[0],
|
|
279
|
+
description: parts.slice(1).join(' '),
|
|
280
|
+
syntax: match.trim()
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Extract options/flags
|
|
286
|
+
const optionMatches = helpText.match(rules.optionPattern);
|
|
287
|
+
if (optionMatches) {
|
|
288
|
+
patterns.options = [...new Set(optionMatches)];
|
|
289
|
+
patterns.flags = optionMatches.filter(opt => opt.startsWith('--')).map(opt => opt.replace(/^--/, ''));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Extract main commands (first level)
|
|
293
|
+
const lines = helpText.split('\n');
|
|
294
|
+
for (const line of lines) {
|
|
295
|
+
const trimmed = line.trim();
|
|
296
|
+
|
|
297
|
+
// Look for commands that start at beginning of line
|
|
298
|
+
if (/^[a-z][a-z0-9_-]+\s+.+$/.test(trimmed)) {
|
|
299
|
+
const parts = trimmed.split(/\s+/);
|
|
300
|
+
const command = parts[0];
|
|
301
|
+
const description = parts.slice(1).join(' ');
|
|
302
|
+
|
|
303
|
+
if (!patterns.commands.find(cmd => cmd.name === command)) {
|
|
304
|
+
patterns.commands.push({
|
|
305
|
+
name: command,
|
|
306
|
+
description,
|
|
307
|
+
syntax: trimmed
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return patterns;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Analyze command structure and calling patterns
|
|
318
|
+
*/
|
|
319
|
+
analyzeCommandStructure(patterns) {
|
|
320
|
+
const structure = {
|
|
321
|
+
primaryCommand: '',
|
|
322
|
+
commandFormat: '',
|
|
323
|
+
argumentStyle: '',
|
|
324
|
+
optionStyle: '',
|
|
325
|
+
interactiveMode: false,
|
|
326
|
+
hasSubcommands: patterns.subcommands.length > 0,
|
|
327
|
+
complexity: 'simple'
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// Determine complexity based on available commands
|
|
331
|
+
if (patterns.commands.length > 10 || patterns.subcommands.length > 5) {
|
|
332
|
+
structure.complexity = 'complex';
|
|
333
|
+
} else if (patterns.commands.length > 3 || patterns.options.length > 10) {
|
|
334
|
+
structure.complexity = 'moderate';
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Determine command format based on patterns
|
|
338
|
+
if (patterns.subcommands.length > 0) {
|
|
339
|
+
structure.commandFormat = 'cli <subcommand> [options] [args]';
|
|
340
|
+
} else if (patterns.options.length > 0) {
|
|
341
|
+
structure.commandFormat = 'cli [options] [args]';
|
|
342
|
+
} else {
|
|
343
|
+
structure.commandFormat = 'cli [args]';
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Check for interactive mode indicators
|
|
347
|
+
const hasInteractiveIndicators = patterns.commands.some(cmd =>
|
|
348
|
+
cmd.name.includes('chat') ||
|
|
349
|
+
cmd.name.includes('interactive') ||
|
|
350
|
+
cmd.name.includes('shell') ||
|
|
351
|
+
cmd.description.toLowerCase().includes('interactive')
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
structure.interactiveMode = hasInteractiveIndicators;
|
|
355
|
+
|
|
356
|
+
return structure;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Extract usage examples from help text
|
|
361
|
+
*/
|
|
362
|
+
extractUsageExamples(helpText, cliType) {
|
|
363
|
+
const rules = this.patternRules[cliType] || this.patternRules.generic;
|
|
364
|
+
const examples = [];
|
|
365
|
+
|
|
366
|
+
// Find example sections
|
|
367
|
+
const exampleMatches = helpText.match(rules.examplePattern);
|
|
368
|
+
|
|
369
|
+
if (exampleMatches) {
|
|
370
|
+
for (const match of exampleMatches) {
|
|
371
|
+
const exampleText = match.replace(/^(example|usage|用法|使用)[::]\s*/i, '').trim();
|
|
372
|
+
|
|
373
|
+
// Split by lines and extract command examples
|
|
374
|
+
const lines = exampleText.split('\n')
|
|
375
|
+
.map(line => line.trim())
|
|
376
|
+
.filter(line => line && !line.startsWith('#') && !line.startsWith('//'));
|
|
377
|
+
|
|
378
|
+
for (const line of lines) {
|
|
379
|
+
if (line.includes('$') || line.includes('>') || line.startsWith('cli') || /^[a-z][\w-]*\s/.test(line)) {
|
|
380
|
+
// Extract clean command
|
|
381
|
+
const command = line.replace(/^[>$\s]+/, '').replace(/^cli\s*/, '').trim();
|
|
382
|
+
if (command) {
|
|
383
|
+
examples.push({
|
|
384
|
+
command,
|
|
385
|
+
raw: line,
|
|
386
|
+
description: ''
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return examples;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Determine CLI interaction mode
|
|
399
|
+
*/
|
|
400
|
+
determineInteractionMode(helpInfo, patterns) {
|
|
401
|
+
const text = helpInfo.rawHelp.toLowerCase();
|
|
402
|
+
|
|
403
|
+
// Check for different interaction modes
|
|
404
|
+
if (text.includes('chat') || text.includes('conversation') || text.includes('interactive')) {
|
|
405
|
+
return 'chat';
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (text.includes('api') || text.includes('endpoint') || text.includes('request')) {
|
|
409
|
+
return 'api';
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (patterns.subcommands.length > 0) {
|
|
413
|
+
return 'subcommand';
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (patterns.options.length > 5) {
|
|
417
|
+
return 'option';
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return 'simple';
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Cache analysis results
|
|
425
|
+
*/
|
|
426
|
+
async cacheAnalysis(cliName, analysis) {
|
|
427
|
+
try {
|
|
428
|
+
const config = await this.loadPersistentConfig();
|
|
429
|
+
config.cliPatterns[cliName] = analysis;
|
|
430
|
+
config.lastUpdated = new Date().toISOString();
|
|
431
|
+
|
|
432
|
+
// Remove from failed attempts if it was there
|
|
433
|
+
delete config.failedAttempts[cliName];
|
|
434
|
+
|
|
435
|
+
await this.savePersistentConfig(config);
|
|
436
|
+
|
|
437
|
+
// Also save last analysis timestamp
|
|
438
|
+
await fs.writeFile(
|
|
439
|
+
this.lastAnalysisFile,
|
|
440
|
+
JSON.stringify({ [cliName]: analysis.timestamp }, null, 2)
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
} catch (error) {
|
|
444
|
+
console.error(`Failed to cache analysis for ${cliName}:`, error.message);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Get cached analysis if available
|
|
450
|
+
*/
|
|
451
|
+
async getCachedAnalysis(cliName) {
|
|
452
|
+
try {
|
|
453
|
+
const config = await this.loadPersistentConfig();
|
|
454
|
+
return config.cliPatterns[cliName] || null;
|
|
455
|
+
} catch (error) {
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Check if cache is expired (24 hours)
|
|
462
|
+
*/
|
|
463
|
+
isCacheExpired(timestamp) {
|
|
464
|
+
const cacheTime = new Date(timestamp);
|
|
465
|
+
const now = new Date();
|
|
466
|
+
const hoursDiff = (now - cacheTime) / (1000 * 60 * 60);
|
|
467
|
+
return hoursDiff > 24;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Record failed analysis attempt
|
|
472
|
+
*/
|
|
473
|
+
async recordFailedAttempt(cliName, error) {
|
|
474
|
+
try {
|
|
475
|
+
const config = await this.loadPersistentConfig();
|
|
476
|
+
config.failedAttempts[cliName] = {
|
|
477
|
+
error: error.message,
|
|
478
|
+
timestamp: new Date().toISOString(),
|
|
479
|
+
attempts: (config.failedAttempts[cliName]?.attempts || 0) + 1
|
|
480
|
+
};
|
|
481
|
+
config.lastUpdated = new Date().toISOString();
|
|
482
|
+
await this.savePersistentConfig(config);
|
|
483
|
+
} catch (err) {
|
|
484
|
+
console.error('Failed to record failed attempt:', err.message);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Load persistent configuration
|
|
490
|
+
*/
|
|
491
|
+
async loadPersistentConfig() {
|
|
492
|
+
try {
|
|
493
|
+
const data = await fs.readFile(this.persistentConfig, 'utf8');
|
|
494
|
+
return JSON.parse(data);
|
|
495
|
+
} catch (error) {
|
|
496
|
+
return {
|
|
497
|
+
version: '1.0.0',
|
|
498
|
+
lastUpdated: new Date().toISOString(),
|
|
499
|
+
cliPatterns: {},
|
|
500
|
+
failedAttempts: {}
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Save persistent configuration
|
|
507
|
+
*/
|
|
508
|
+
async savePersistentConfig(config) {
|
|
509
|
+
await fs.writeFile(this.persistentConfig, JSON.stringify(config, null, 2));
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Check if file exists
|
|
514
|
+
*/
|
|
515
|
+
async fileExists(filePath) {
|
|
516
|
+
try {
|
|
517
|
+
await fs.access(filePath);
|
|
518
|
+
return true;
|
|
519
|
+
} catch {
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Get CLI pattern for specific tool
|
|
526
|
+
*/
|
|
527
|
+
async getCLIPattern(cliName) {
|
|
528
|
+
const cached = await this.getCachedAnalysis(cliName);
|
|
529
|
+
|
|
530
|
+
if (cached && !this.isCacheExpired(cached.timestamp)) {
|
|
531
|
+
return cached;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Re-analyze if cache expired or not available
|
|
535
|
+
return await this.analyzeCLI(cliName);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Update CLI pattern when call fails
|
|
540
|
+
*/
|
|
541
|
+
async updatePatternOnFailure(cliName, error, attemptedCommand) {
|
|
542
|
+
console.log(`Updating pattern for ${cliName} due to failure:`, error.message);
|
|
543
|
+
|
|
544
|
+
try {
|
|
545
|
+
// Re-analyze the CLI
|
|
546
|
+
const newAnalysis = await this.analyzeCLI(cliName);
|
|
547
|
+
|
|
548
|
+
// Add failure context
|
|
549
|
+
newAnalysis.lastFailure = {
|
|
550
|
+
error: error.message,
|
|
551
|
+
attemptedCommand,
|
|
552
|
+
timestamp: new Date().toISOString()
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
return newAnalysis;
|
|
556
|
+
} catch (analysisError) {
|
|
557
|
+
console.error(`Failed to re-analyze ${cliName}:`, analysisError.message);
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
module.exports = CLIHelpAnalyzer;
|
package/src/index.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Stigmergy CLI -
|
|
5
|
-
*
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// Import and export the main functionality from main.js
|
|
9
|
-
|
|
10
|
-
console.error('❌ Error loading main module:', error.message);
|
|
11
|
-
process.exit(1);
|
|
12
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stigmergy CLI - Entry Point
|
|
5
|
+
* International Version - Pure ANSI Characters Only
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Import and export the main functionality from main.js
|
|
9
|
+
require('./main.js');
|