stigmergy 1.0.95 → 1.0.98
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 +20 -0
- package/bin/stigmergy +11 -0
- package/docs/http-request-handler.md +289 -0
- package/docs/json-parser.md +102 -0
- package/docs/rest_client.md +144 -0
- package/examples/encryption-example.js +67 -0
- package/examples/json-parser-example.js +120 -0
- package/examples/rest_client_example.js +54 -0
- package/package.json +6 -5
- package/src/auth.js +19 -17
- package/src/auth_command.js +47 -34
- package/src/calculator.js +96 -3
- package/src/core/cli_help_analyzer.js +615 -567
- package/src/core/cli_parameter_handler.js +109 -103
- package/src/core/cli_tools.js +73 -73
- package/src/core/error_handler.js +136 -37
- package/src/core/memory_manager.js +67 -60
- package/src/core/rest_client.js +160 -0
- package/src/core/smart_router.js +127 -114
- package/src/data_encryption.js +143 -0
- package/src/data_structures.js +440 -0
- package/src/deploy.js +34 -28
- package/src/index.js +9 -9
- package/src/main.js +877 -740
- package/src/main_english.js +1275 -875
- package/src/utils.js +441 -54
- package/src/weatherProcessor.js +51 -28
- package/test/encryption-simple-test.js +110 -0
- package/test/encryption.test.js +129 -0
- package/test/hash_table_test.js +114 -0
- package/test/json-parser-test.js +161 -0
- package/test/rest_client.test.js +85 -0
- package/src/main_fixed.js +0 -1035
|
@@ -7,620 +7,668 @@ const { CLI_TOOLS } = require('./cli_tools');
|
|
|
7
7
|
const { errorHandler, ERROR_TYPES } = require('./error_handler');
|
|
8
8
|
|
|
9
9
|
class CLIHelpAnalyzer {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
10
|
+
constructor() {
|
|
11
|
+
this.configDir = path.join(os.homedir(), '.stigmergy', 'cli-patterns');
|
|
12
|
+
this.persistentConfig = path.join(this.configDir, 'cli-patterns.json');
|
|
13
|
+
this.lastAnalysisFile = path.join(this.configDir, 'last-analysis.json');
|
|
14
|
+
this.cliTools = CLI_TOOLS;
|
|
15
|
+
|
|
16
|
+
// Pattern recognition rules for different CLI types
|
|
17
|
+
this.patternRules = {
|
|
18
|
+
// OpenAI style CLI (codex, chatgpt)
|
|
19
|
+
openai: {
|
|
20
|
+
commandPattern: /^(\w+)(?:\s+--?[\w-]+(?:\s+[\w-]+)?)?\s*(.*)$/,
|
|
21
|
+
optionPattern: /--?[\w-]+(?:\s+[\w-]+)?/g,
|
|
22
|
+
examplePattern:
|
|
23
|
+
/(?:example|usage|Usage)[::]\s*\n([\s\S]*?)(?=\n\n|\n[A-Z]|\n$)/gi,
|
|
24
|
+
subcommandPattern: /^\s{2,4}(\w+)\s+.+$/gm,
|
|
25
|
+
},
|
|
26
|
+
// Anthropic style CLI (claude)
|
|
27
|
+
anthropic: {
|
|
28
|
+
commandPattern: /^(\w+)(?:\s+(?:--?\w+|\[\w+\]))*\s*(.*)$/,
|
|
29
|
+
optionPattern: /--?\w+(?:\s+<\w+>)?/g,
|
|
30
|
+
examplePattern:
|
|
31
|
+
/(?: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:
|
|
39
|
+
/(?:Examples?|SAMPLE)[::]\s*\n([\s\S]*?)(?=\n\n|\n[A-Z]|\n$)/gi,
|
|
40
|
+
subcommandPattern: /^\s{2,4}(\w+)\s{2,}.+$/gm,
|
|
41
|
+
},
|
|
42
|
+
// Generic CLI pattern
|
|
43
|
+
generic: {
|
|
44
|
+
commandPattern: /^(\w+)(?:\s+.*)?$/,
|
|
45
|
+
optionPattern: /--?[a-zA-Z][\w-]*/g,
|
|
46
|
+
examplePattern:
|
|
47
|
+
/(?:example|usage|用法|使用)[::]\s*\n([\s\S]*?)(?=\n\n|\n[A-Z]|\n$)/gi,
|
|
48
|
+
subcommandPattern: /^\s{2,6}([a-z][a-z0-9_-]+)\s+.+$/gm,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Set CLI tools configuration
|
|
55
|
+
* @param {Object} cliTools - CLI tools configuration
|
|
56
|
+
*/
|
|
57
|
+
setCLITools(cliTools) {
|
|
58
|
+
this.cliTools = cliTools;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Initialize the analyzer and ensure config directory exists
|
|
63
|
+
*/
|
|
64
|
+
async initialize() {
|
|
65
|
+
try {
|
|
66
|
+
await fs.mkdir(this.configDir, { recursive: true });
|
|
67
|
+
|
|
68
|
+
// Initialize persistent config if not exists
|
|
69
|
+
const configExists = await this.fileExists(this.persistentConfig);
|
|
70
|
+
if (!configExists) {
|
|
71
|
+
await this.savePersistentConfig({
|
|
72
|
+
version: '1.0.0',
|
|
73
|
+
lastUpdated: new Date().toISOString(),
|
|
74
|
+
cliPatterns: {},
|
|
75
|
+
failedAttempts: {},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return true;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
await errorHandler.logError(error, 'ERROR', 'CLIHelpAnalyzer.initialize');
|
|
82
|
+
return false;
|
|
47
83
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
cliPatterns: {},
|
|
71
|
-
failedAttempts: {}
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return true;
|
|
76
|
-
} catch (error) {
|
|
77
|
-
await errorHandler.logError(
|
|
78
|
-
error,
|
|
79
|
-
'ERROR',
|
|
80
|
-
'CLIHelpAnalyzer.initialize'
|
|
81
|
-
);
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Analyze all configured CLI tools
|
|
88
|
+
*/
|
|
89
|
+
async analyzeAllCLI() {
|
|
90
|
+
const results = {};
|
|
91
|
+
|
|
92
|
+
for (const [cliName, cliConfig] of Object.entries(this.cliTools)) {
|
|
93
|
+
try {
|
|
94
|
+
if (process.env.DEBUG === 'true') {
|
|
95
|
+
console.log(`Analyzing ${cliName}...`);
|
|
96
|
+
}
|
|
97
|
+
results[cliName] = await this.analyzeCLI(cliName);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
await errorHandler.logError(
|
|
100
|
+
error,
|
|
101
|
+
'WARN',
|
|
102
|
+
`CLIHelpAnalyzer.analyzeAllCLI.${cliName}`,
|
|
103
|
+
);
|
|
104
|
+
results[cliName] = { success: false, error: error.message };
|
|
105
|
+
}
|
|
84
106
|
}
|
|
85
107
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
*/
|
|
89
|
-
async analyzeAllCLI() {
|
|
90
|
-
const results = {};
|
|
91
|
-
|
|
92
|
-
for (const [cliName, cliConfig] of Object.entries(this.cliTools)) {
|
|
93
|
-
try {
|
|
94
|
-
console.log(`Analyzing ${cliName}...`);
|
|
95
|
-
results[cliName] = await this.analyzeCLI(cliName);
|
|
96
|
-
} catch (error) {
|
|
97
|
-
await errorHandler.logError(
|
|
98
|
-
error,
|
|
99
|
-
'WARN',
|
|
100
|
-
`CLIHelpAnalyzer.analyzeAllCLI.${cliName}`
|
|
101
|
-
);
|
|
102
|
-
results[cliName] = { success: false, error: error.message };
|
|
103
|
-
}
|
|
104
|
-
}
|
|
108
|
+
return results;
|
|
109
|
+
}
|
|
105
110
|
|
|
106
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Analyze specific CLI tool
|
|
113
|
+
*/
|
|
114
|
+
async analyzeCLI(cliName) {
|
|
115
|
+
const cliConfig = this.cliTools[cliName];
|
|
116
|
+
if (!cliConfig) {
|
|
117
|
+
throw new Error(`CLI tool ${cliName} not found in configuration`);
|
|
107
118
|
}
|
|
108
119
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
try {
|
|
121
|
+
// Get help information
|
|
122
|
+
const helpInfo = await this.getHelpInfo(cliName, cliConfig);
|
|
123
|
+
|
|
124
|
+
// Detect CLI type
|
|
125
|
+
const cliType = this.detectCLIType(helpInfo.rawHelp, cliName);
|
|
126
|
+
|
|
127
|
+
// Extract patterns
|
|
128
|
+
const patterns = this.extractPatterns(helpInfo.rawHelp, cliType);
|
|
129
|
+
|
|
130
|
+
// Analyze command structure
|
|
131
|
+
const commandStructure = this.analyzeCommandStructure(patterns);
|
|
132
|
+
|
|
133
|
+
// Extract usage examples
|
|
134
|
+
const examples = this.extractUsageExamples(helpInfo.rawHelp, cliType);
|
|
135
|
+
|
|
136
|
+
// Determine interaction mode
|
|
137
|
+
const interactionMode = this.determineInteractionMode(helpInfo, patterns);
|
|
138
|
+
|
|
139
|
+
const analysis = {
|
|
140
|
+
success: true,
|
|
141
|
+
cliName,
|
|
142
|
+
cliType,
|
|
143
|
+
version: helpInfo.version,
|
|
144
|
+
helpMethod: helpInfo.method,
|
|
145
|
+
patterns,
|
|
146
|
+
commandStructure,
|
|
147
|
+
examples,
|
|
148
|
+
interactionMode,
|
|
149
|
+
timestamp: new Date().toISOString(),
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// Cache the analysis
|
|
153
|
+
await this.cacheAnalysis(cliName, analysis);
|
|
154
|
+
|
|
155
|
+
return analysis;
|
|
156
|
+
} catch (error) {
|
|
157
|
+
// Record failed attempt
|
|
158
|
+
await this.recordFailedAttempt(cliName, error);
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get help information using multiple methods
|
|
165
|
+
*/
|
|
166
|
+
async getHelpInfo(cliName, cliConfig) {
|
|
167
|
+
const helpMethods = [
|
|
168
|
+
['--help'],
|
|
169
|
+
['-h'],
|
|
170
|
+
['help'],
|
|
171
|
+
['--usage'],
|
|
172
|
+
[''],
|
|
173
|
+
['version'],
|
|
174
|
+
['--version'],
|
|
175
|
+
['-v'],
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
let rawHelp = '';
|
|
179
|
+
let version = 'unknown';
|
|
180
|
+
let method = 'unknown';
|
|
181
|
+
|
|
182
|
+
// Try different help commands
|
|
183
|
+
for (const helpArgs of helpMethods) {
|
|
184
|
+
try {
|
|
185
|
+
const result = spawnSync(cliName, helpArgs, {
|
|
186
|
+
encoding: 'utf8',
|
|
187
|
+
timeout: 15000,
|
|
188
|
+
shell: true,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
if (result.status === 0 && result.stdout) {
|
|
192
|
+
rawHelp = result.stdout;
|
|
193
|
+
method = `${cliName} ${helpArgs.join(' ')}`;
|
|
194
|
+
break;
|
|
195
|
+
} else if (result.stderr) {
|
|
196
|
+
rawHelp = result.stderr;
|
|
197
|
+
method = `${cliName} ${helpArgs.join(' ')} (stderr)`;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
} catch (error) {
|
|
201
|
+
// Try next method
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
117
205
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// Determine interaction mode
|
|
135
|
-
const interactionMode = this.determineInteractionMode(helpInfo, patterns);
|
|
136
|
-
|
|
137
|
-
const analysis = {
|
|
138
|
-
success: true,
|
|
139
|
-
cliName,
|
|
140
|
-
cliType,
|
|
141
|
-
version: helpInfo.version,
|
|
142
|
-
helpMethod: helpInfo.method,
|
|
143
|
-
patterns,
|
|
144
|
-
commandStructure,
|
|
145
|
-
examples,
|
|
146
|
-
interactionMode,
|
|
147
|
-
timestamp: new Date().toISOString()
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
// Cache the analysis
|
|
151
|
-
await this.cacheAnalysis(cliName, analysis);
|
|
152
|
-
|
|
153
|
-
return analysis;
|
|
154
|
-
|
|
155
|
-
} catch (error) {
|
|
156
|
-
// Record failed attempt
|
|
157
|
-
await this.recordFailedAttempt(cliName, error);
|
|
158
|
-
throw error;
|
|
159
|
-
}
|
|
206
|
+
// Try to get version separately
|
|
207
|
+
if (cliConfig.version) {
|
|
208
|
+
try {
|
|
209
|
+
const versionCmd = cliConfig.version.split(' ');
|
|
210
|
+
const versionResult = spawnSync(versionCmd[0], versionCmd.slice(1), {
|
|
211
|
+
encoding: 'utf8',
|
|
212
|
+
timeout: 10000,
|
|
213
|
+
shell: true,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
if (versionResult.status === 0) {
|
|
217
|
+
version = versionResult.stdout.trim() || versionResult.stderr.trim();
|
|
218
|
+
}
|
|
219
|
+
} catch (error) {
|
|
220
|
+
// Use default version
|
|
221
|
+
}
|
|
160
222
|
}
|
|
161
223
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
async getHelpInfo(cliName, cliConfig) {
|
|
166
|
-
const helpMethods = [
|
|
167
|
-
['--help'],
|
|
168
|
-
['-h'],
|
|
169
|
-
['help'],
|
|
170
|
-
['--usage'],
|
|
171
|
-
[''],
|
|
172
|
-
['version'],
|
|
173
|
-
['--version'],
|
|
174
|
-
['-v']
|
|
175
|
-
];
|
|
176
|
-
|
|
177
|
-
let rawHelp = '';
|
|
178
|
-
let version = 'unknown';
|
|
179
|
-
let method = 'unknown';
|
|
180
|
-
|
|
181
|
-
// Try different help commands
|
|
182
|
-
for (const helpArgs of helpMethods) {
|
|
183
|
-
try {
|
|
184
|
-
const result = spawnSync(cliName, helpArgs, {
|
|
185
|
-
encoding: 'utf8',
|
|
186
|
-
timeout: 15000,
|
|
187
|
-
shell: true
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
if (result.status === 0 && result.stdout) {
|
|
191
|
-
rawHelp = result.stdout;
|
|
192
|
-
method = `${cliName} ${helpArgs.join(' ')}`;
|
|
193
|
-
break;
|
|
194
|
-
} else if (result.stderr) {
|
|
195
|
-
rawHelp = result.stderr;
|
|
196
|
-
method = `${cliName} ${helpArgs.join(' ')} (stderr)`;
|
|
197
|
-
break;
|
|
198
|
-
}
|
|
199
|
-
} catch (error) {
|
|
200
|
-
// Try next method
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
224
|
+
if (!rawHelp) {
|
|
225
|
+
throw new Error(`Unable to get help information for ${cliName}`);
|
|
226
|
+
}
|
|
204
227
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
try {
|
|
208
|
-
const versionCmd = cliConfig.version.split(' ');
|
|
209
|
-
const versionResult = spawnSync(versionCmd[0], versionCmd.slice(1), {
|
|
210
|
-
encoding: 'utf8',
|
|
211
|
-
timeout: 10000,
|
|
212
|
-
shell: true
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
if (versionResult.status === 0) {
|
|
216
|
-
version = versionResult.stdout.trim() || versionResult.stderr.trim();
|
|
217
|
-
}
|
|
218
|
-
} catch (error) {
|
|
219
|
-
// Use default version
|
|
220
|
-
}
|
|
221
|
-
}
|
|
228
|
+
return { rawHelp, version, method };
|
|
229
|
+
}
|
|
222
230
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
231
|
+
/**
|
|
232
|
+
* Detect CLI type based on help content and naming
|
|
233
|
+
*/
|
|
234
|
+
detectCLIType(helpText, cliName) {
|
|
235
|
+
const text = helpText.toLowerCase();
|
|
236
|
+
const name = cliName.toLowerCase();
|
|
226
237
|
|
|
227
|
-
|
|
238
|
+
// Detect based on CLI name
|
|
239
|
+
if (name.includes('claude') || name.includes('anthropic')) {
|
|
240
|
+
return 'anthropic';
|
|
241
|
+
}
|
|
242
|
+
if (name.includes('gemini') || name.includes('google')) {
|
|
243
|
+
return 'google';
|
|
244
|
+
}
|
|
245
|
+
if (
|
|
246
|
+
name.includes('codex') ||
|
|
247
|
+
name.includes('openai') ||
|
|
248
|
+
name.includes('chatgpt')
|
|
249
|
+
) {
|
|
250
|
+
return 'openai';
|
|
228
251
|
}
|
|
229
252
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
// Detect based on help content patterns
|
|
249
|
-
if (text.includes('anthropic') || text.includes('claude')) {
|
|
250
|
-
return 'anthropic';
|
|
251
|
-
}
|
|
252
|
-
if (text.includes('google') || text.includes('gemini') || text.includes('vertex')) {
|
|
253
|
-
return 'google';
|
|
254
|
-
}
|
|
255
|
-
if (text.includes('openai') || text.includes('gpt') || text.includes('codex')) {
|
|
256
|
-
return 'openai';
|
|
257
|
-
}
|
|
253
|
+
// Detect based on help content patterns
|
|
254
|
+
if (text.includes('anthropic') || text.includes('claude')) {
|
|
255
|
+
return 'anthropic';
|
|
256
|
+
}
|
|
257
|
+
if (
|
|
258
|
+
text.includes('google') ||
|
|
259
|
+
text.includes('gemini') ||
|
|
260
|
+
text.includes('vertex')
|
|
261
|
+
) {
|
|
262
|
+
return 'google';
|
|
263
|
+
}
|
|
264
|
+
if (
|
|
265
|
+
text.includes('openai') ||
|
|
266
|
+
text.includes('gpt') ||
|
|
267
|
+
text.includes('codex')
|
|
268
|
+
) {
|
|
269
|
+
return 'openai';
|
|
270
|
+
}
|
|
258
271
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
272
|
+
// Default to generic
|
|
273
|
+
return 'generic';
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Extract command patterns from help text
|
|
278
|
+
*/
|
|
279
|
+
extractPatterns(helpText, cliType) {
|
|
280
|
+
const rules = this.patternRules[cliType] || this.patternRules.generic;
|
|
281
|
+
const patterns = {
|
|
282
|
+
commands: [],
|
|
283
|
+
options: [],
|
|
284
|
+
subcommands: [],
|
|
285
|
+
arguments: [],
|
|
286
|
+
flags: [],
|
|
287
|
+
// New fields for better parameter handling
|
|
288
|
+
nonInteractiveFlag: null,
|
|
289
|
+
promptFlag: null,
|
|
290
|
+
requiredFlags: [],
|
|
291
|
+
commonPatterns: [],
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// Extract subcommands
|
|
295
|
+
const subcommandMatches = helpText.match(rules.subcommandPattern);
|
|
296
|
+
if (subcommandMatches) {
|
|
297
|
+
patterns.subcommands = subcommandMatches.map((match) => {
|
|
298
|
+
const parts = match.trim().split(/\s+/);
|
|
299
|
+
return {
|
|
300
|
+
name: parts[0],
|
|
301
|
+
description: parts.slice(1).join(' '),
|
|
302
|
+
syntax: match.trim(),
|
|
279
303
|
};
|
|
280
|
-
|
|
281
|
-
// Extract subcommands
|
|
282
|
-
const subcommandMatches = helpText.match(rules.subcommandPattern);
|
|
283
|
-
if (subcommandMatches) {
|
|
284
|
-
patterns.subcommands = subcommandMatches.map(match => {
|
|
285
|
-
const parts = match.trim().split(/\s+/);
|
|
286
|
-
return {
|
|
287
|
-
name: parts[0],
|
|
288
|
-
description: parts.slice(1).join(' '),
|
|
289
|
-
syntax: match.trim()
|
|
290
|
-
};
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Extract options/flags
|
|
295
|
-
const optionMatches = helpText.match(rules.optionPattern);
|
|
296
|
-
if (optionMatches) {
|
|
297
|
-
patterns.options = [...new Set(optionMatches)];
|
|
298
|
-
patterns.flags = optionMatches.filter(opt => opt.startsWith('--')).map(opt => opt.replace(/^--/, ''));
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// Extract main commands (first level)
|
|
302
|
-
const lines = helpText.split('\n');
|
|
303
|
-
for (const line of lines) {
|
|
304
|
-
const trimmed = line.trim();
|
|
305
|
-
|
|
306
|
-
// Look for commands that start at beginning of line
|
|
307
|
-
if (/^[a-z][a-z0-9_-]+\s+.+$/.test(trimmed)) {
|
|
308
|
-
const parts = trimmed.split(/\s+/);
|
|
309
|
-
const command = parts[0];
|
|
310
|
-
const description = parts.slice(1).join(' ');
|
|
311
|
-
|
|
312
|
-
if (!patterns.commands.find(cmd => cmd.name === command)) {
|
|
313
|
-
patterns.commands.push({
|
|
314
|
-
name: command,
|
|
315
|
-
description,
|
|
316
|
-
syntax: trimmed
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Enhanced pattern extraction for non-interactive mode and prompt handling
|
|
323
|
-
this.extractEnhancedPatterns(helpText, patterns);
|
|
324
|
-
|
|
325
|
-
return patterns;
|
|
304
|
+
});
|
|
326
305
|
}
|
|
327
306
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
patterns.nonInteractiveFlag = '--print';
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Look for prompt-related flags
|
|
340
|
-
if (text.includes('prompt') || text.includes('-p ')) {
|
|
341
|
-
patterns.promptFlag = '-p';
|
|
342
|
-
}
|
|
307
|
+
// Extract options/flags
|
|
308
|
+
const optionMatches = helpText.match(rules.optionPattern);
|
|
309
|
+
if (optionMatches) {
|
|
310
|
+
patterns.options = [...new Set(optionMatches)];
|
|
311
|
+
patterns.flags = optionMatches
|
|
312
|
+
.filter((opt) => opt.startsWith('--'))
|
|
313
|
+
.map((opt) => opt.replace(/^--/, ''));
|
|
314
|
+
}
|
|
343
315
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
316
|
+
// Extract main commands (first level)
|
|
317
|
+
const lines = helpText.split('\n');
|
|
318
|
+
for (const line of lines) {
|
|
319
|
+
const trimmed = line.trim();
|
|
320
|
+
|
|
321
|
+
// Look for commands that start at beginning of line
|
|
322
|
+
if (/^[a-z][a-z0-9_-]+\s+.+$/.test(trimmed)) {
|
|
323
|
+
const parts = trimmed.split(/\s+/);
|
|
324
|
+
const command = parts[0];
|
|
325
|
+
const description = parts.slice(1).join(' ');
|
|
326
|
+
|
|
327
|
+
if (!patterns.commands.find((cmd) => cmd.name === command)) {
|
|
328
|
+
patterns.commands.push({
|
|
329
|
+
name: command,
|
|
330
|
+
description,
|
|
331
|
+
syntax: trimmed,
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
348
336
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
337
|
+
// Enhanced pattern extraction for non-interactive mode and prompt handling
|
|
338
|
+
this.extractEnhancedPatterns(helpText, patterns);
|
|
339
|
+
|
|
340
|
+
return patterns;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Extract enhanced patterns for better parameter handling
|
|
345
|
+
*/
|
|
346
|
+
extractEnhancedPatterns(helpText, patterns) {
|
|
347
|
+
const text = helpText.toLowerCase();
|
|
348
|
+
|
|
349
|
+
// Look for non-interactive mode flags
|
|
350
|
+
if (
|
|
351
|
+
text.includes('print') ||
|
|
352
|
+
text.includes('non-interactive') ||
|
|
353
|
+
text.includes('output')
|
|
354
|
+
) {
|
|
355
|
+
patterns.nonInteractiveFlag = '--print';
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
const structure = {
|
|
363
|
-
primaryCommand: '',
|
|
364
|
-
commandFormat: '',
|
|
365
|
-
argumentStyle: '',
|
|
366
|
-
optionStyle: '',
|
|
367
|
-
interactiveMode: false,
|
|
368
|
-
hasSubcommands: patterns.subcommands.length > 0,
|
|
369
|
-
complexity: 'simple',
|
|
370
|
-
// Fields for better execution
|
|
371
|
-
nonInteractiveSupport: !!patterns.nonInteractiveFlag,
|
|
372
|
-
promptStyle: patterns.promptFlag ? 'flag' : 'argument',
|
|
373
|
-
executionPattern: '',
|
|
374
|
-
// Additional fields for CLI parameter handling
|
|
375
|
-
nonInteractiveFlag: patterns.nonInteractiveFlag,
|
|
376
|
-
promptFlag: patterns.promptFlag,
|
|
377
|
-
requiredFlags: patterns.requiredFlags,
|
|
378
|
-
commonPatterns: patterns.commonPatterns
|
|
379
|
-
};
|
|
358
|
+
// Look for prompt-related flags
|
|
359
|
+
if (text.includes('prompt') || text.includes('-p ')) {
|
|
360
|
+
patterns.promptFlag = '-p';
|
|
361
|
+
}
|
|
380
362
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
structure.complexity = 'moderate';
|
|
386
|
-
}
|
|
363
|
+
// Look for required flags for non-interactive mode
|
|
364
|
+
if (text.includes('non-interactive') && text.includes('prompt')) {
|
|
365
|
+
patterns.requiredFlags.push('-p');
|
|
366
|
+
}
|
|
387
367
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
368
|
+
// Extract common usage patterns from examples
|
|
369
|
+
const exampleLines = helpText.split('\n');
|
|
370
|
+
for (const line of exampleLines) {
|
|
371
|
+
if (
|
|
372
|
+
line.includes('-p "') ||
|
|
373
|
+
line.includes('--prompt') ||
|
|
374
|
+
line.includes(' -p ')
|
|
375
|
+
) {
|
|
376
|
+
patterns.commonPatterns.push(line.trim());
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Analyze command structure and calling patterns
|
|
383
|
+
*/
|
|
384
|
+
analyzeCommandStructure(patterns) {
|
|
385
|
+
const structure = {
|
|
386
|
+
primaryCommand: '',
|
|
387
|
+
commandFormat: '',
|
|
388
|
+
argumentStyle: '',
|
|
389
|
+
optionStyle: '',
|
|
390
|
+
interactiveMode: false,
|
|
391
|
+
hasSubcommands: patterns.subcommands.length > 0,
|
|
392
|
+
complexity: 'simple',
|
|
393
|
+
// Fields for better execution
|
|
394
|
+
nonInteractiveSupport: !!patterns.nonInteractiveFlag,
|
|
395
|
+
promptStyle: patterns.promptFlag ? 'flag' : 'argument',
|
|
396
|
+
executionPattern: '',
|
|
397
|
+
// Additional fields for CLI parameter handling
|
|
398
|
+
nonInteractiveFlag: patterns.nonInteractiveFlag,
|
|
399
|
+
promptFlag: patterns.promptFlag,
|
|
400
|
+
requiredFlags: patterns.requiredFlags,
|
|
401
|
+
commonPatterns: patterns.commonPatterns,
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// Determine complexity based on available commands
|
|
405
|
+
if (patterns.commands.length > 10 || patterns.subcommands.length > 5) {
|
|
406
|
+
structure.complexity = 'complex';
|
|
407
|
+
} else if (patterns.commands.length > 3 || patterns.options.length > 10) {
|
|
408
|
+
structure.complexity = 'moderate';
|
|
409
|
+
}
|
|
396
410
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
411
|
+
// Determine command format based on patterns
|
|
412
|
+
if (patterns.subcommands.length > 0) {
|
|
413
|
+
structure.commandFormat = 'cli <subcommand> [options] [args]';
|
|
414
|
+
} else if (patterns.options.length > 0) {
|
|
415
|
+
structure.commandFormat = 'cli [options] [args]';
|
|
416
|
+
} else {
|
|
417
|
+
structure.commandFormat = 'cli [args]';
|
|
418
|
+
}
|
|
404
419
|
|
|
405
|
-
|
|
420
|
+
// Check for interactive mode indicators
|
|
421
|
+
const hasInteractiveIndicators = patterns.commands.some(
|
|
422
|
+
(cmd) =>
|
|
423
|
+
cmd.name.includes('chat') ||
|
|
424
|
+
cmd.name.includes('interactive') ||
|
|
425
|
+
cmd.name.includes('shell') ||
|
|
426
|
+
(cmd.description &&
|
|
427
|
+
cmd.description.toLowerCase().includes('interactive')),
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
structure.interactiveMode = hasInteractiveIndicators;
|
|
431
|
+
|
|
432
|
+
// Determine execution pattern
|
|
433
|
+
if (patterns.nonInteractiveFlag && patterns.promptFlag) {
|
|
434
|
+
structure.executionPattern = 'flag-based';
|
|
435
|
+
} else if (patterns.nonInteractiveFlag) {
|
|
436
|
+
structure.executionPattern = 'argument-based';
|
|
437
|
+
} else {
|
|
438
|
+
structure.executionPattern = 'interactive-default';
|
|
439
|
+
}
|
|
406
440
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
441
|
+
return structure;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Extract usage examples from help text
|
|
446
|
+
*/
|
|
447
|
+
extractUsageExamples(helpText, cliType) {
|
|
448
|
+
const rules = this.patternRules[cliType] || this.patternRules.generic;
|
|
449
|
+
const examples = [];
|
|
450
|
+
|
|
451
|
+
// Find example sections
|
|
452
|
+
const exampleMatches = helpText.match(rules.examplePattern);
|
|
453
|
+
|
|
454
|
+
if (exampleMatches) {
|
|
455
|
+
for (const match of exampleMatches) {
|
|
456
|
+
const exampleText = match
|
|
457
|
+
.replace(/^(example|usage|用法|使用)[::]\s*/i, '')
|
|
458
|
+
.trim();
|
|
459
|
+
|
|
460
|
+
// Split by lines and extract command examples
|
|
461
|
+
const lines = exampleText
|
|
462
|
+
.split('\n')
|
|
463
|
+
.map((line) => line.trim())
|
|
464
|
+
.filter(
|
|
465
|
+
(line) => line && !line.startsWith('#') && !line.startsWith('//'),
|
|
466
|
+
);
|
|
415
467
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
const lines = exampleText.split('\n')
|
|
435
|
-
.map(line => line.trim())
|
|
436
|
-
.filter(line => line && !line.startsWith('#') && !line.startsWith('//'));
|
|
437
|
-
|
|
438
|
-
for (const line of lines) {
|
|
439
|
-
if (line.includes('$') || line.includes('>') || line.startsWith('cli') || /^[a-z][\w-]*\s/.test(line)) {
|
|
440
|
-
// Extract clean command
|
|
441
|
-
const command = line.replace(/^[>$\s]+/, '').replace(/^cli\s*/, '').trim();
|
|
442
|
-
if (command) {
|
|
443
|
-
examples.push({
|
|
444
|
-
command,
|
|
445
|
-
raw: line,
|
|
446
|
-
description: ''
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
}
|
|
468
|
+
for (const line of lines) {
|
|
469
|
+
if (
|
|
470
|
+
line.includes('$') ||
|
|
471
|
+
line.includes('>') ||
|
|
472
|
+
line.startsWith('cli') ||
|
|
473
|
+
/^[a-z][\w-]*\s/.test(line)
|
|
474
|
+
) {
|
|
475
|
+
// Extract clean command
|
|
476
|
+
const command = line
|
|
477
|
+
.replace(/^[>$\s]+/, '')
|
|
478
|
+
.replace(/^cli\s*/, '')
|
|
479
|
+
.trim();
|
|
480
|
+
if (command) {
|
|
481
|
+
examples.push({
|
|
482
|
+
command,
|
|
483
|
+
raw: line,
|
|
484
|
+
description: '',
|
|
485
|
+
});
|
|
451
486
|
}
|
|
487
|
+
}
|
|
452
488
|
}
|
|
453
|
-
|
|
454
|
-
return examples;
|
|
489
|
+
}
|
|
455
490
|
}
|
|
456
491
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
return 'subcommand';
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
if (patterns.options.length > 5) {
|
|
477
|
-
return 'option';
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
return 'simple';
|
|
492
|
+
return examples;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Determine CLI interaction mode
|
|
497
|
+
*/
|
|
498
|
+
determineInteractionMode(helpInfo, patterns) {
|
|
499
|
+
const text = helpInfo.rawHelp.toLowerCase();
|
|
500
|
+
|
|
501
|
+
// Check for different interaction modes
|
|
502
|
+
if (
|
|
503
|
+
text.includes('chat') ||
|
|
504
|
+
text.includes('conversation') ||
|
|
505
|
+
text.includes('interactive')
|
|
506
|
+
) {
|
|
507
|
+
return 'chat';
|
|
481
508
|
}
|
|
482
509
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
config.cliPatterns[cliName] = analysis;
|
|
490
|
-
config.lastUpdated = new Date().toISOString();
|
|
491
|
-
|
|
492
|
-
// Remove from failed attempts if it was there
|
|
493
|
-
delete config.failedAttempts[cliName];
|
|
494
|
-
|
|
495
|
-
await this.savePersistentConfig(config);
|
|
496
|
-
|
|
497
|
-
// Also save last analysis timestamp
|
|
498
|
-
await fs.writeFile(
|
|
499
|
-
this.lastAnalysisFile,
|
|
500
|
-
JSON.stringify({ [cliName]: analysis.timestamp }, null, 2)
|
|
501
|
-
);
|
|
502
|
-
|
|
503
|
-
} catch (error) {
|
|
504
|
-
console.error(`Failed to cache analysis for ${cliName}:`, error.message);
|
|
505
|
-
}
|
|
510
|
+
if (
|
|
511
|
+
text.includes('api') ||
|
|
512
|
+
text.includes('endpoint') ||
|
|
513
|
+
text.includes('request')
|
|
514
|
+
) {
|
|
515
|
+
return 'api';
|
|
506
516
|
}
|
|
507
517
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
*/
|
|
511
|
-
async getCachedAnalysis(cliName) {
|
|
512
|
-
try {
|
|
513
|
-
const config = await this.loadPersistentConfig();
|
|
514
|
-
return config.cliPatterns[cliName] || null;
|
|
515
|
-
} catch (error) {
|
|
516
|
-
return null;
|
|
517
|
-
}
|
|
518
|
+
if (patterns.subcommands.length > 0) {
|
|
519
|
+
return 'subcommand';
|
|
518
520
|
}
|
|
519
521
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
*/
|
|
523
|
-
isCacheExpired(timestamp) {
|
|
524
|
-
const cacheTime = new Date(timestamp);
|
|
525
|
-
const now = new Date();
|
|
526
|
-
const hoursDiff = (now - cacheTime) / (1000 * 60 * 60);
|
|
527
|
-
return hoursDiff > 24;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
/**
|
|
531
|
-
* Record failed analysis attempt
|
|
532
|
-
*/
|
|
533
|
-
async recordFailedAttempt(cliName, error) {
|
|
534
|
-
try {
|
|
535
|
-
const config = await this.loadPersistentConfig();
|
|
536
|
-
config.failedAttempts[cliName] = {
|
|
537
|
-
error: error.message,
|
|
538
|
-
timestamp: new Date().toISOString(),
|
|
539
|
-
attempts: (config.failedAttempts[cliName]?.attempts || 0) + 1
|
|
540
|
-
};
|
|
541
|
-
config.lastUpdated = new Date().toISOString();
|
|
542
|
-
await this.savePersistentConfig(config);
|
|
543
|
-
} catch (err) {
|
|
544
|
-
console.error('Failed to record failed attempt:', err.message);
|
|
545
|
-
}
|
|
522
|
+
if (patterns.options.length > 5) {
|
|
523
|
+
return 'option';
|
|
546
524
|
}
|
|
547
525
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
526
|
+
return 'simple';
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Cache analysis results
|
|
531
|
+
*/
|
|
532
|
+
async cacheAnalysis(cliName, analysis) {
|
|
533
|
+
try {
|
|
534
|
+
const config = await this.loadPersistentConfig();
|
|
535
|
+
config.cliPatterns[cliName] = analysis;
|
|
536
|
+
config.lastUpdated = new Date().toISOString();
|
|
537
|
+
|
|
538
|
+
// Remove from failed attempts if it was there
|
|
539
|
+
delete config.failedAttempts[cliName];
|
|
540
|
+
|
|
541
|
+
await this.savePersistentConfig(config);
|
|
542
|
+
|
|
543
|
+
// Also save last analysis timestamp
|
|
544
|
+
await fs.writeFile(
|
|
545
|
+
this.lastAnalysisFile,
|
|
546
|
+
JSON.stringify({ [cliName]: analysis.timestamp }, null, 2),
|
|
547
|
+
);
|
|
548
|
+
} catch (error) {
|
|
549
|
+
console.error(`Failed to cache analysis for ${cliName}:`, error.message);
|
|
563
550
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Get cached analysis if available
|
|
555
|
+
*/
|
|
556
|
+
async getCachedAnalysis(cliName) {
|
|
557
|
+
try {
|
|
558
|
+
const config = await this.loadPersistentConfig();
|
|
559
|
+
return config.cliPatterns[cliName] || null;
|
|
560
|
+
} catch (error) {
|
|
561
|
+
return null;
|
|
570
562
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Check if cache is expired (24 hours)
|
|
567
|
+
*/
|
|
568
|
+
isCacheExpired(timestamp) {
|
|
569
|
+
const cacheTime = new Date(timestamp);
|
|
570
|
+
const now = new Date();
|
|
571
|
+
const hoursDiff = (now - cacheTime) / (1000 * 60 * 60);
|
|
572
|
+
return hoursDiff > 24;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Record failed analysis attempt
|
|
577
|
+
*/
|
|
578
|
+
async recordFailedAttempt(cliName, error) {
|
|
579
|
+
try {
|
|
580
|
+
const config = await this.loadPersistentConfig();
|
|
581
|
+
config.failedAttempts[cliName] = {
|
|
582
|
+
error: error.message,
|
|
583
|
+
timestamp: new Date().toISOString(),
|
|
584
|
+
attempts: (config.failedAttempts[cliName]?.attempts || 0) + 1,
|
|
585
|
+
};
|
|
586
|
+
config.lastUpdated = new Date().toISOString();
|
|
587
|
+
await this.savePersistentConfig(config);
|
|
588
|
+
} catch (err) {
|
|
589
|
+
console.error('Failed to record failed attempt:', err.message);
|
|
582
590
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Load persistent configuration
|
|
595
|
+
*/
|
|
596
|
+
async loadPersistentConfig() {
|
|
597
|
+
try {
|
|
598
|
+
const data = await fs.readFile(this.persistentConfig, 'utf8');
|
|
599
|
+
return JSON.parse(data);
|
|
600
|
+
} catch (error) {
|
|
601
|
+
return {
|
|
602
|
+
version: '1.0.0',
|
|
603
|
+
lastUpdated: new Date().toISOString(),
|
|
604
|
+
cliPatterns: {},
|
|
605
|
+
failedAttempts: {},
|
|
606
|
+
};
|
|
596
607
|
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Save persistent configuration
|
|
612
|
+
*/
|
|
613
|
+
async savePersistentConfig(config) {
|
|
614
|
+
await fs.writeFile(this.persistentConfig, JSON.stringify(config, null, 2));
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Check if file exists
|
|
619
|
+
*/
|
|
620
|
+
async fileExists(filePath) {
|
|
621
|
+
try {
|
|
622
|
+
await fs.access(filePath);
|
|
623
|
+
return true;
|
|
624
|
+
} catch {
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
597
628
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
try {
|
|
605
|
-
// Re-analyze the CLI
|
|
606
|
-
const newAnalysis = await this.analyzeCLI(cliName);
|
|
607
|
-
|
|
608
|
-
// Add failure context
|
|
609
|
-
newAnalysis.lastFailure = {
|
|
610
|
-
error: error.message,
|
|
611
|
-
attemptedCommand,
|
|
612
|
-
timestamp: new Date().toISOString()
|
|
613
|
-
};
|
|
629
|
+
/**
|
|
630
|
+
* Get CLI pattern for specific tool
|
|
631
|
+
*/
|
|
632
|
+
async getCLIPattern(cliName) {
|
|
633
|
+
const cached = await this.getCachedAnalysis(cliName);
|
|
614
634
|
|
|
615
|
-
|
|
616
|
-
|
|
635
|
+
if (cached && !this.isCacheExpired(cached.timestamp)) {
|
|
636
|
+
return cached;
|
|
637
|
+
}
|
|
617
638
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
639
|
+
// Re-analyze if cache expired or not available
|
|
640
|
+
return await this.analyzeCLI(cliName);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Update CLI pattern when call fails
|
|
645
|
+
*/
|
|
646
|
+
async updatePatternOnFailure(cliName, error, attemptedCommand) {
|
|
647
|
+
console.log(
|
|
648
|
+
`Updating pattern for ${cliName} due to failure:`,
|
|
649
|
+
error.message,
|
|
650
|
+
);
|
|
651
|
+
|
|
652
|
+
try {
|
|
653
|
+
// Re-analyze the CLI
|
|
654
|
+
const newAnalysis = await this.analyzeCLI(cliName);
|
|
655
|
+
|
|
656
|
+
// Add failure context
|
|
657
|
+
newAnalysis.lastFailure = {
|
|
658
|
+
error: error.message,
|
|
659
|
+
attemptedCommand,
|
|
660
|
+
timestamp: new Date().toISOString(),
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
// Update the cached analysis
|
|
664
|
+
await this.cacheAnalysis(cliName, newAnalysis);
|
|
665
|
+
|
|
666
|
+
return newAnalysis;
|
|
667
|
+
} catch (analysisError) {
|
|
668
|
+
console.error(`Failed to re-analyze ${cliName}:`, analysisError.message);
|
|
669
|
+
return null;
|
|
623
670
|
}
|
|
671
|
+
}
|
|
624
672
|
}
|
|
625
673
|
|
|
626
|
-
module.exports = CLIHelpAnalyzer;
|
|
674
|
+
module.exports = CLIHelpAnalyzer;
|