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,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced CLI Parameter Handler with Agent/Skill Support and Multi-Mode Retry
|
|
3
|
+
*
|
|
4
|
+
* This module extends the basic CLI parameter handling with:
|
|
5
|
+
* 1. TWO-STAGE agent and skill detection (fast pre-check + detailed matching)
|
|
6
|
+
* 2. Multiple parameter format support with retry mechanism
|
|
7
|
+
* 3. Fallback strategies for different CLI tools
|
|
8
|
+
*
|
|
9
|
+
* OPTIMIZATION: Stage 1 (quickDetectMention) avoids cache I/O if no keywords found
|
|
10
|
+
*
|
|
11
|
+
* 📚 参考文档:
|
|
12
|
+
* - CLI Help Analyzer 重构:REFACTORING_CLI_HELP_ANALYZER.md
|
|
13
|
+
*
|
|
14
|
+
* 🔗 依赖关系:
|
|
15
|
+
* - 依赖 CLIHelpAnalyzer 的 getCLIPattern() 方法
|
|
16
|
+
* - 重构后调用方式保持不变,完全向后兼容
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const CLIHelpAnalyzer = require('./cli_help_analyzer');
|
|
20
|
+
const { CLI_TOOLS } = require('./cli_tools');
|
|
21
|
+
const LocalSkillScanner = require('./local_skill_scanner');
|
|
22
|
+
|
|
23
|
+
class EnhancedCLIParameterHandler {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.analyzer = new CLIHelpAnalyzer();
|
|
26
|
+
this.skillScanner = new LocalSkillScanner();
|
|
27
|
+
this.retryHistory = new Map(); // Track failed parameter formats
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Initialize the analyzer
|
|
32
|
+
*/
|
|
33
|
+
async initialize() {
|
|
34
|
+
await this.analyzer.initialize();
|
|
35
|
+
// Note: skillScanner is initialized lazily (only when needed)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generate optimized arguments with agent/skill support and retry capability
|
|
40
|
+
*
|
|
41
|
+
* OPTIMIZED FLOW:
|
|
42
|
+
* 1. Stage 1: Quick keyword detection (<1ms, no I/O)
|
|
43
|
+
* - No keywords? Skip all skill/agent processing
|
|
44
|
+
* - Has keywords? Proceed to Stage 2
|
|
45
|
+
* 2. Stage 2: Load cache and detailed matching
|
|
46
|
+
* - Load skills/agents from cache
|
|
47
|
+
* - Perform precise matching
|
|
48
|
+
* 3. Generate parameters based on CLI type
|
|
49
|
+
*
|
|
50
|
+
* @param {string} toolName - Name of the CLI tool
|
|
51
|
+
* @param {string} prompt - User prompt
|
|
52
|
+
* @param {Object} options - Options
|
|
53
|
+
* @param {number} options.maxRetries - Maximum number of format retries (default: 3)
|
|
54
|
+
* @param {boolean} options.enableAgentSkillOptimization - Enable agent/skill optimization (default: true)
|
|
55
|
+
* @param {Array} options.preferredFormats - Preferred parameter formats to try first
|
|
56
|
+
* @returns {Promise<Object>} Result with arguments, format used, and optimization info
|
|
57
|
+
*/
|
|
58
|
+
async generateArgumentsWithRetry(toolName, prompt, options = {}) {
|
|
59
|
+
const {
|
|
60
|
+
maxRetries = 3,
|
|
61
|
+
enableAgentSkillOptimization = true,
|
|
62
|
+
preferredFormats = null
|
|
63
|
+
} = options;
|
|
64
|
+
|
|
65
|
+
await this.initialize();
|
|
66
|
+
|
|
67
|
+
// === STAGE 1: Quick Pre-Check (Fast, No I/O) ===
|
|
68
|
+
let detectedMentions = { hasAgent: false, hasSkill: false, confidence: 0 };
|
|
69
|
+
let optimizationApplied = false;
|
|
70
|
+
let optimizedPrompt = prompt;
|
|
71
|
+
let skillMatches = [];
|
|
72
|
+
let agentMatches = [];
|
|
73
|
+
|
|
74
|
+
if (enableAgentSkillOptimization) {
|
|
75
|
+
// Fast keyword detection (<1ms)
|
|
76
|
+
const quickDetection = this.skillScanner.quickDetectMention(prompt);
|
|
77
|
+
|
|
78
|
+
if (!quickDetection.shouldLoadCache) {
|
|
79
|
+
// No agent/skill keywords detected - skip all skill/agent processing
|
|
80
|
+
if (process.env.DEBUG === 'true') {
|
|
81
|
+
console.log(`[AGENT/SKILL] No keywords detected, skipping skill/agent processing`);
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
// Keywords detected - proceed to Stage 2
|
|
85
|
+
if (process.env.DEBUG === 'true') {
|
|
86
|
+
console.log(`[AGENT/SKILL] Stage 1: Keywords detected (agent=${quickDetection.hasAgentKeyword}, skill=${quickDetection.hasSkillKeyword})`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// === STAGE 2: Load Cache and Detailed Matching ===
|
|
90
|
+
await this.skillScanner.initialize(); // Load cache
|
|
91
|
+
|
|
92
|
+
// Get detailed matches using CLIHelpAnalyzer
|
|
93
|
+
detectedMentions = this.analyzer.detectAgentSkillMentions(prompt, toolName);
|
|
94
|
+
|
|
95
|
+
// Get local skill/agent matches
|
|
96
|
+
skillMatches = this.skillScanner.matchSkills(prompt, toolName);
|
|
97
|
+
agentMatches = this.skillScanner.matchAgents(prompt, toolName);
|
|
98
|
+
|
|
99
|
+
// Combine detection results
|
|
100
|
+
if (skillMatches.length > 0 || agentMatches.length > 0) {
|
|
101
|
+
detectedMentions.hasSkill = detectedMentions.hasSkill || skillMatches.length > 0;
|
|
102
|
+
detectedMentions.hasAgent = detectedMentions.hasAgent || agentMatches.length > 0;
|
|
103
|
+
detectedMentions.confidence = Math.min(1.0, detectedMentions.confidence + 0.2);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Optimize prompt if matches found
|
|
107
|
+
if (detectedMentions.hasAgent || detectedMentions.hasSkill) {
|
|
108
|
+
optimizedPrompt = this.analyzer.optimizePromptForCLI(prompt, toolName, detectedMentions);
|
|
109
|
+
optimizationApplied = (optimizedPrompt !== prompt);
|
|
110
|
+
|
|
111
|
+
if (process.env.DEBUG === 'true') {
|
|
112
|
+
console.log(`[AGENT/SKILL] Stage 2: Detailed matching complete`);
|
|
113
|
+
console.log(`[AGENT/SKILL] Skill matches: ${skillMatches.length}, Agent matches: ${agentMatches.length}`);
|
|
114
|
+
if (optimizationApplied) {
|
|
115
|
+
console.log(`[AGENT/SKILL] Original: ${prompt}`);
|
|
116
|
+
console.log(`[AGENT/SKILL] Optimized: ${optimizedPrompt}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Step 3: Get parameter formats to try (in priority order)
|
|
124
|
+
const formatsToTry = await this.getParameterFormats(toolName, preferredFormats, detectedMentions);
|
|
125
|
+
|
|
126
|
+
// Step 4: Try each format until one succeeds (or return first for non-execution context)
|
|
127
|
+
const results = {
|
|
128
|
+
toolName,
|
|
129
|
+
originalPrompt: prompt,
|
|
130
|
+
optimizedPrompt,
|
|
131
|
+
optimizationApplied,
|
|
132
|
+
detectedMentions,
|
|
133
|
+
formats: []
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
for (let i = 0; i < Math.min(formatsToTry.length, maxRetries); i++) {
|
|
137
|
+
const format = formatsToTry[i];
|
|
138
|
+
const args = this.generateArgumentsForFormat(toolName, optimizedPrompt, format);
|
|
139
|
+
|
|
140
|
+
results.formats.push({
|
|
141
|
+
format: format.name,
|
|
142
|
+
args,
|
|
143
|
+
priority: format.priority,
|
|
144
|
+
attempted: true
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// For non-execution context, return all formats
|
|
148
|
+
// For execution, the caller should handle retry
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Select the best format based on priority and detection
|
|
152
|
+
const selectedFormat = this.selectBestFormat(results.formats, detectedMentions);
|
|
153
|
+
results.selectedFormat = selectedFormat;
|
|
154
|
+
results.arguments = selectedFormat.args;
|
|
155
|
+
|
|
156
|
+
return results;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get parameter formats to try for a specific tool
|
|
161
|
+
*
|
|
162
|
+
* @param {string} toolName - Name of the CLI tool
|
|
163
|
+
* @param {Array} preferredFormats - User-preferred formats
|
|
164
|
+
* @param {Object} detectedMentions - Detected agent/skill mentions
|
|
165
|
+
* @returns {Promise<Array>} Array of format objects to try
|
|
166
|
+
*/
|
|
167
|
+
async getParameterFormats(toolName, preferredFormats, detectedMentions) {
|
|
168
|
+
const formats = [];
|
|
169
|
+
|
|
170
|
+
// Get CLI pattern from analyzer
|
|
171
|
+
const cliPattern = await this.analyzer.getCLIPattern(toolName);
|
|
172
|
+
const enhancedPattern = this.analyzer.enhancedPatterns[toolName];
|
|
173
|
+
|
|
174
|
+
if (process.env.DEBUG === 'true') {
|
|
175
|
+
console.log(`[PARAM_FORMAT] Getting formats for ${toolName}`);
|
|
176
|
+
console.log(`[PARAM_FORMAT] cliPattern exists: ${!!cliPattern}`);
|
|
177
|
+
if (cliPattern && cliPattern.commandStructure) {
|
|
178
|
+
console.log(`[PARAM_FORMAT] executionPattern: ${cliPattern.commandStructure.executionPattern}`);
|
|
179
|
+
console.log(`[PARAM_FORMAT] nonInteractiveFlag: ${cliPattern.commandStructure.nonInteractiveFlag}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Define format priority based on tool characteristics
|
|
184
|
+
if (enhancedPattern) {
|
|
185
|
+
// Use enhanced pattern information
|
|
186
|
+
if (enhancedPattern.positionalArgs) {
|
|
187
|
+
// Check if CLI has flag-based non-interactive execution
|
|
188
|
+
// For tools like Kode that have flag-based execution, don't use positional args
|
|
189
|
+
const hasFlagBasedExecution = cliPattern?.commandStructure?.executionPattern === 'flag-based' &&
|
|
190
|
+
cliPattern?.commandStructure?.nonInteractiveFlag;
|
|
191
|
+
|
|
192
|
+
if (!hasFlagBasedExecution) {
|
|
193
|
+
// Qwen, Copilot, etc. support positional arguments
|
|
194
|
+
formats.push({
|
|
195
|
+
name: 'positional',
|
|
196
|
+
priority: enhancedPattern.positionalArgs ? 10 : 5,
|
|
197
|
+
description: 'Positional arguments (natural language)',
|
|
198
|
+
template: (prompt) => [prompt]
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (enhancedPattern.naturalLanguageSupport) {
|
|
204
|
+
// CLIs with good natural language support
|
|
205
|
+
formats.push({
|
|
206
|
+
name: 'prompt-flag',
|
|
207
|
+
priority: 8,
|
|
208
|
+
description: 'Standard -p flag',
|
|
209
|
+
template: (prompt) => ['-p', `"${prompt}"`]
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (enhancedPattern.skillPrefixRequired) {
|
|
214
|
+
// CodeBuddy requires skill: prefix
|
|
215
|
+
formats.push({
|
|
216
|
+
name: 'skill-prefix',
|
|
217
|
+
priority: 9,
|
|
218
|
+
description: 'Skill prefix with -y flag',
|
|
219
|
+
template: (prompt) => ['-y', '-p', `"${prompt}"`]
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Add standard formats based on CLI pattern analysis
|
|
225
|
+
if (cliPattern && cliPattern.commandStructure) {
|
|
226
|
+
const structure = cliPattern.commandStructure;
|
|
227
|
+
|
|
228
|
+
if (structure.promptFlag) {
|
|
229
|
+
formats.push({
|
|
230
|
+
name: 'detected-prompt-flag',
|
|
231
|
+
priority: 7,
|
|
232
|
+
description: `Detected prompt flag: ${structure.promptFlag}`,
|
|
233
|
+
template: (prompt) => [structure.promptFlag, `"${prompt}"`]
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (structure.executionPattern === 'argument-based') {
|
|
238
|
+
formats.push({
|
|
239
|
+
name: 'argument-based',
|
|
240
|
+
priority: 6,
|
|
241
|
+
description: 'Argument-based execution',
|
|
242
|
+
template: (prompt) => [`"${prompt}"`]
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (structure.executionPattern === 'subcommand-based') {
|
|
247
|
+
// For tools like Codex that need subcommand
|
|
248
|
+
formats.push({
|
|
249
|
+
name: 'subcommand-exec',
|
|
250
|
+
priority: 7,
|
|
251
|
+
description: 'Subcommand-based with exec',
|
|
252
|
+
template: (prompt) => ['exec', '-p', `"${prompt}"`]
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (structure.executionPattern === 'flag-based') {
|
|
257
|
+
// For tools like Kode that use flag-based execution (e.g., --print)
|
|
258
|
+
if (structure.nonInteractiveFlag) {
|
|
259
|
+
formats.push({
|
|
260
|
+
name: 'flag-based-non-interactive',
|
|
261
|
+
priority: 9,
|
|
262
|
+
description: `Flag-based non-interactive: ${structure.nonInteractiveFlag}`,
|
|
263
|
+
template: (prompt) => [structure.nonInteractiveFlag, `"${prompt}"`]
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Add fallback formats
|
|
270
|
+
formats.push({
|
|
271
|
+
name: 'standard-p-flag',
|
|
272
|
+
priority: 4,
|
|
273
|
+
description: 'Standard -p flag (fallback)',
|
|
274
|
+
template: (prompt) => ['-p', `"${prompt}"`]
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Filter out failed formats from retry history
|
|
278
|
+
const failedFormats = this.retryHistory.get(toolName) || new Set();
|
|
279
|
+
const validFormats = formats.filter(f => !failedFormats.has(f.name));
|
|
280
|
+
|
|
281
|
+
if (process.env.DEBUG === 'true') {
|
|
282
|
+
console.log(`[PARAM_FORMAT] Total formats: ${formats.length}`);
|
|
283
|
+
formats.forEach(f => {
|
|
284
|
+
console.log(`[PARAM_FORMAT] - ${f.name} (priority: ${f.priority}): ${f.description}`);
|
|
285
|
+
});
|
|
286
|
+
console.log(`[PARAM_FORMAT] Valid formats: ${validFormats.length}`);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// If all formats failed, clear history and try again
|
|
290
|
+
if (validFormats.length === 0) {
|
|
291
|
+
if (process.env.DEBUG === 'true') {
|
|
292
|
+
console.log(`[RETRY] All formats failed for ${toolName}, clearing retry history`);
|
|
293
|
+
}
|
|
294
|
+
this.retryHistory.delete(toolName);
|
|
295
|
+
return formats;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Sort by priority (descending)
|
|
299
|
+
return validFormats.sort((a, b) => b.priority - a.priority);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Generate arguments for a specific format
|
|
304
|
+
*
|
|
305
|
+
* @param {string} toolName - Name of the CLI tool
|
|
306
|
+
* @param {string} prompt - Optimized prompt
|
|
307
|
+
* @param {Object} format - Format object
|
|
308
|
+
* @returns {Array} Arguments array
|
|
309
|
+
*/
|
|
310
|
+
generateArgumentsForFormat(toolName, prompt, format) {
|
|
311
|
+
try {
|
|
312
|
+
return format.template(prompt);
|
|
313
|
+
} catch (error) {
|
|
314
|
+
if (process.env.DEBUG === 'true') {
|
|
315
|
+
console.log(`[ERROR] Failed to generate args for format ${format.name}:`, error.message);
|
|
316
|
+
}
|
|
317
|
+
// Fallback to standard format
|
|
318
|
+
return ['-p', `"${prompt}"`];
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Select the best format from available options
|
|
324
|
+
*
|
|
325
|
+
* @param {Array} formats - Available formats
|
|
326
|
+
* @param {Object} detectedMentions - Detected agent/skill mentions
|
|
327
|
+
* @returns {Object} Selected format
|
|
328
|
+
*/
|
|
329
|
+
selectBestFormat(formats, detectedMentions) {
|
|
330
|
+
// Prioritize formats based on detection
|
|
331
|
+
if (detectedMentions.hasSkill || detectedMentions.hasAgent) {
|
|
332
|
+
// For agent/skill requests, prefer formats with higher priority
|
|
333
|
+
const skillOptimizedFormats = formats.filter(f => f.priority >= 8);
|
|
334
|
+
if (skillOptimizedFormats.length > 0) {
|
|
335
|
+
return skillOptimizedFormats[0];
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Return highest priority format
|
|
340
|
+
return formats[0] || {
|
|
341
|
+
name: 'fallback',
|
|
342
|
+
priority: 1,
|
|
343
|
+
args: ['-p', '"{prompt}"'],
|
|
344
|
+
description: 'Fallback format'
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Record a failed format for a tool
|
|
350
|
+
*
|
|
351
|
+
* @param {string} toolName - Name of the CLI tool
|
|
352
|
+
* @param {string} formatName - Name of the failed format
|
|
353
|
+
*/
|
|
354
|
+
recordFailedFormat(toolName, formatName) {
|
|
355
|
+
if (!this.retryHistory.has(toolName)) {
|
|
356
|
+
this.retryHistory.set(toolName, new Set());
|
|
357
|
+
}
|
|
358
|
+
this.retryHistory.get(toolName).add(formatName);
|
|
359
|
+
|
|
360
|
+
if (process.env.DEBUG === 'true') {
|
|
361
|
+
console.log(`[RETRY] Recorded failed format ${formatName} for ${toolName}`);
|
|
362
|
+
console.log(`[RETRY] Failed formats:`, Array.from(this.retryHistory.get(toolName)));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Clear retry history for a tool
|
|
368
|
+
*
|
|
369
|
+
* @param {string} toolName - Name of the CLI tool
|
|
370
|
+
*/
|
|
371
|
+
clearRetryHistory(toolName) {
|
|
372
|
+
this.retryHistory.delete(toolName);
|
|
373
|
+
|
|
374
|
+
if (process.env.DEBUG === 'true') {
|
|
375
|
+
console.log(`[RETRY] Cleared retry history for ${toolName}`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Get compatibility score for a tool with the given prompt
|
|
381
|
+
*
|
|
382
|
+
* @param {string} toolName - Name of the CLI tool
|
|
383
|
+
* @param {string} prompt - User prompt
|
|
384
|
+
* @returns {Object} Compatibility score and reasons
|
|
385
|
+
*/
|
|
386
|
+
getCompatibilityScore(toolName, prompt) {
|
|
387
|
+
return this.analyzer.getAgentSkillCompatibilityScore(toolName, prompt);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Generate optimized call command for a tool
|
|
392
|
+
*
|
|
393
|
+
* @param {string} toolName - Name of the CLI tool
|
|
394
|
+
* @param {string} prompt - User prompt
|
|
395
|
+
* @returns {Object|null} Optimized call command
|
|
396
|
+
*/
|
|
397
|
+
generateOptimizedCall(toolName, prompt) {
|
|
398
|
+
return this.analyzer.generateOptimizedCall(toolName, prompt);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
module.exports = EnhancedCLIParameterHandler;
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution Mode Detector
|
|
3
|
+
* Intelligently detects whether to run in interactive or one-time mode
|
|
4
|
+
* based on execution context and user preferences
|
|
5
|
+
*
|
|
6
|
+
* Modes:
|
|
7
|
+
* - 'interactive': Keep CLI tool running for continuous conversation
|
|
8
|
+
* - 'one-time': Execute prompt and exit (return control to caller)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
class ExecutionModeDetector {
|
|
12
|
+
constructor() {
|
|
13
|
+
// Cached detection result
|
|
14
|
+
this._cachedMode = null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Detect the appropriate execution mode
|
|
19
|
+
* @param {Object} options - Detection options
|
|
20
|
+
* @param {boolean} options.interactive - Force interactive mode
|
|
21
|
+
* @param {boolean} options.print - Force one-time (print) mode
|
|
22
|
+
* @param {boolean} options.verbose - Enable verbose output
|
|
23
|
+
* @returns {string} 'interactive' or 'one-time'
|
|
24
|
+
*/
|
|
25
|
+
detect(options = {}) {
|
|
26
|
+
// Return cached result if available
|
|
27
|
+
if (this._cachedMode && !options.interactive && !options.print) {
|
|
28
|
+
return this._cachedMode;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const mode = this._detectMode(options);
|
|
32
|
+
|
|
33
|
+
// Cache the result if not forced
|
|
34
|
+
if (!options.interactive && !options.print) {
|
|
35
|
+
this._cachedMode = mode;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return mode;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Internal detection logic
|
|
43
|
+
* @private
|
|
44
|
+
*/
|
|
45
|
+
_detectMode(options) {
|
|
46
|
+
const verbose = options.verbose || process.env.DEBUG === 'true';
|
|
47
|
+
|
|
48
|
+
// Priority 1: Explicit user flags (highest priority)
|
|
49
|
+
if (options.interactive) {
|
|
50
|
+
if (verbose) {
|
|
51
|
+
console.log('[MODE] Interactive mode forced by --interactive flag');
|
|
52
|
+
}
|
|
53
|
+
return 'interactive';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (options.print) {
|
|
57
|
+
if (verbose) {
|
|
58
|
+
console.log('[MODE] One-time mode forced by --print flag');
|
|
59
|
+
}
|
|
60
|
+
return 'one-time';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Priority 2: Environment variable
|
|
64
|
+
const envMode = process.env.STIGMERGY_MODE;
|
|
65
|
+
if (envMode) {
|
|
66
|
+
if (envMode === 'interactive') {
|
|
67
|
+
if (verbose) {
|
|
68
|
+
console.log('[MODE] Interactive mode from STIGMERGY_MODE environment variable');
|
|
69
|
+
}
|
|
70
|
+
return 'interactive';
|
|
71
|
+
}
|
|
72
|
+
if (envMode === 'one-time' || envMode === 'onetime') {
|
|
73
|
+
if (verbose) {
|
|
74
|
+
console.log('[MODE] One-time mode from STIGMERGY_MODE environment variable');
|
|
75
|
+
}
|
|
76
|
+
return 'one-time';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Priority 3: Intelligent context detection
|
|
81
|
+
const context = this._analyzeContext();
|
|
82
|
+
|
|
83
|
+
if (verbose) {
|
|
84
|
+
console.log('[MODE] Context Analysis:');
|
|
85
|
+
console.log(` - stdin.isTTY: ${context.stdinIsTTY}`);
|
|
86
|
+
console.log(` - stdout.isTTY: ${context.stdoutIsTTY}`);
|
|
87
|
+
console.log(` - hasPipedInput: ${context.hasPipedInput}`);
|
|
88
|
+
console.log(` - isFromCI: ${context.isFromCI}`);
|
|
89
|
+
console.log(` - isFromScript: ${context.isFromScript}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Decision logic
|
|
93
|
+
const isInteractiveTerminal = context.stdinIsTTY && context.stdoutIsTTY;
|
|
94
|
+
|
|
95
|
+
if (isInteractiveTerminal && !context.isFromCI && !context.isFromScript) {
|
|
96
|
+
if (verbose) {
|
|
97
|
+
console.log('[MODE] Detected interactive terminal → Interactive mode');
|
|
98
|
+
}
|
|
99
|
+
return 'interactive';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// All other cases: use one-time mode
|
|
103
|
+
if (verbose) {
|
|
104
|
+
console.log('[MODE] Detected non-interactive context → One-time mode');
|
|
105
|
+
}
|
|
106
|
+
return 'one-time';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Analyze execution context
|
|
111
|
+
* @private
|
|
112
|
+
*/
|
|
113
|
+
_analyzeContext() {
|
|
114
|
+
const context = {
|
|
115
|
+
// Check if stdin is a terminal (interactive input)
|
|
116
|
+
stdinIsTTY: process.stdin.isTTY,
|
|
117
|
+
|
|
118
|
+
// Check if stdout is a terminal (interactive output)
|
|
119
|
+
stdoutIsTTY: process.stdout.isTTY,
|
|
120
|
+
|
|
121
|
+
// Check if there's piped input
|
|
122
|
+
hasPipedInput: !process.stdin.isTTY,
|
|
123
|
+
|
|
124
|
+
// Check if running in CI environment
|
|
125
|
+
isFromCI: this._isCIEnvironment(),
|
|
126
|
+
|
|
127
|
+
// Check if running from a script
|
|
128
|
+
isFromScript: this._isRunningFromScript(),
|
|
129
|
+
|
|
130
|
+
// Get parent process name
|
|
131
|
+
parentProcess: this._getParentProcessName()
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return context;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Check if running in CI environment
|
|
139
|
+
* @private
|
|
140
|
+
*/
|
|
141
|
+
_isCIEnvironment() {
|
|
142
|
+
// Common CI environment variables
|
|
143
|
+
const ciVars = [
|
|
144
|
+
'CI',
|
|
145
|
+
'CONTINUOUS_INTEGRATION',
|
|
146
|
+
'JENKINS_URL',
|
|
147
|
+
'BUILD_NUMBER',
|
|
148
|
+
'GITHUB_ACTIONS',
|
|
149
|
+
'GITLAB_CI',
|
|
150
|
+
'TRAVIS',
|
|
151
|
+
'CIRCLECI',
|
|
152
|
+
'APPVEYOR',
|
|
153
|
+
'TEAMCITY_VERSION'
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
return ciVars.some(varName => process.env[varName]);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Check if running from a script
|
|
161
|
+
* @private
|
|
162
|
+
*/
|
|
163
|
+
_isRunningFromScript() {
|
|
164
|
+
// Check if called from node script
|
|
165
|
+
const invokedAsScript = process.argv[1] &&
|
|
166
|
+
!process.argv[1].endsWith('stigmergy') &&
|
|
167
|
+
(process.argv[1].endsWith('.js') ||
|
|
168
|
+
process.argv[1].endsWith('.cjs') ||
|
|
169
|
+
process.argv[1].endsWith('.mjs'));
|
|
170
|
+
|
|
171
|
+
return invokedAsScript;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Get parent process name (if available)
|
|
176
|
+
* @private
|
|
177
|
+
*/
|
|
178
|
+
_getParentProcessName() {
|
|
179
|
+
try {
|
|
180
|
+
// Try to get parent process info
|
|
181
|
+
if (process.ppid) {
|
|
182
|
+
return `PID:${process.ppid}`;
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
} catch (error) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Clear cached detection result
|
|
192
|
+
* Call this if execution context changes
|
|
193
|
+
*/
|
|
194
|
+
clearCache() {
|
|
195
|
+
this._cachedMode = null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get human-readable mode description
|
|
200
|
+
* @param {string} mode - 'interactive' or 'one-time'
|
|
201
|
+
* @returns {string} Description
|
|
202
|
+
*/
|
|
203
|
+
getModeDescription(mode) {
|
|
204
|
+
const descriptions = {
|
|
205
|
+
'interactive': 'Continuous conversation mode (CLI tool stays running)',
|
|
206
|
+
'one-time': 'Execute and exit mode (returns control after completion)'
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
return descriptions[mode] || 'Unknown mode';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Check if a specific mode is forced by user
|
|
214
|
+
* @param {Object} options - Detection options
|
|
215
|
+
* @returns {boolean} True if mode is forced
|
|
216
|
+
*/
|
|
217
|
+
isModeForced(options) {
|
|
218
|
+
return !!(options.interactive || options.print);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = ExecutionModeDetector;
|