stigmergy 1.2.0 → 1.2.6
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/LICENSE +18 -18
- package/README.md +28 -223
- package/STIGMERGY.md +61 -61
- package/docs/PROJECT_CONSTITUTION.md +433 -433
- package/docs/PROJECT_STRUCTURE_CURRENT.md +80 -80
- package/examples/calculator-example.js +72 -72
- package/examples/cline_usage_examples.md +364 -364
- package/examples/encryption-example.js +67 -67
- package/examples/json-parser-example.js +120 -120
- package/examples/json-validation-example.js +64 -64
- package/examples/rest-client-example.js +52 -52
- package/examples/rest_client_example.js +54 -54
- package/package.json +36 -15
- package/scripts/build.js +74 -74
- package/scripts/post-deployment-config.js +296 -296
- package/scripts/preinstall-check.js +173 -173
- package/scripts/publish.js +58 -268
- package/scripts/run-layered-tests.js +247 -0
- package/scripts/safe-install.js +139 -139
- package/scripts/simple-publish.js +57 -59
- package/src/adapters/claude/install_claude_integration.js +292 -0
- package/src/adapters/codebuddy/install_codebuddy_integration.js +349 -0
- package/src/adapters/codex/install_codex_integration.js +395 -0
- package/src/adapters/copilot/install_copilot_integration.js +716 -0
- package/src/adapters/gemini/install_gemini_integration.js +304 -0
- package/src/adapters/iflow/install_iflow_integration.js +304 -0
- package/src/adapters/qoder/install_qoder_integration.js +1090 -0
- package/src/adapters/qwen/install_qwen_integration.js +285 -0
- package/src/auth.js +173 -173
- package/src/auth_command.js +208 -208
- package/src/calculator.js +313 -313
- package/src/cli/router.js +417 -38
- package/src/core/cache_cleaner.js +767 -744
- package/src/core/cli_help_analyzer.js +680 -674
- package/src/core/cli_parameter_handler.js +132 -127
- package/src/core/cli_tools.js +89 -89
- package/src/core/coordination/index.js +16 -16
- package/src/core/coordination/nodejs/AdapterManager.js +102 -89
- package/src/core/coordination/nodejs/CLCommunication.js +132 -124
- package/src/core/coordination/nodejs/CLIIntegrationManager.js +272 -236
- package/src/core/coordination/nodejs/HealthChecker.js +76 -77
- package/src/core/coordination/nodejs/HookDeploymentManager.js +263 -190
- package/src/core/coordination/nodejs/StatisticsCollector.js +71 -71
- package/src/core/coordination/nodejs/index.js +90 -72
- package/src/core/coordination/nodejs/utils/Logger.js +29 -29
- package/src/core/enhanced_installer.js +479 -456
- package/src/core/enhanced_uninstaller.js +638 -618
- package/src/core/error_handler.js +406 -406
- package/src/core/installer.js +815 -294
- package/src/core/memory_manager.js +83 -83
- package/src/core/rest_client.js +160 -160
- package/src/core/smart_router.js +249 -146
- package/src/core/upgrade_manager.js +76 -59
- package/src/data_encryption.js +143 -143
- package/src/data_structures.js +440 -440
- package/src/deploy.js +55 -55
- package/src/index.js +30 -30
- package/src/test/cli-availability-checker.js +194 -0
- package/src/test/test-environment.js +289 -0
- package/src/utils/helpers.js +35 -35
- package/src/utils.js +921 -915
- package/src/weatherProcessor.js +228 -228
- package/test/cache-cleaner-implemented.test.js +0 -328
- package/test/cache-cleaner.test.js +0 -390
- package/test/calculator.test.js +0 -215
- package/test/collision-test.js +0 -26
- package/test/comprehensive-enhanced-features.test.js +0 -252
- package/test/comprehensive-execution-test.js +0 -428
- package/test/conflict-prevention-test.js +0 -95
- package/test/cross-cli-detection-test.js +0 -33
- package/test/csv-processing-test.js +0 -36
- package/test/deploy-hooks-test.js +0 -250
- package/test/e2e/claude-cli-test.js +0 -128
- package/test/e2e/collaboration-test.js +0 -75
- package/test/e2e/comprehensive-test.js +0 -431
- package/test/e2e/error-handling-test.js +0 -90
- package/test/e2e/individual-tool-test.js +0 -143
- package/test/e2e/other-cli-test.js +0 -130
- package/test/e2e/qoder-cli-test.js +0 -128
- package/test/e2e/run-e2e-tests.js +0 -73
- package/test/e2e/test-data.js +0 -88
- package/test/e2e/test-utils.js +0 -222
- package/test/encryption-simple-test.js +0 -110
- package/test/encryption.test.js +0 -129
- package/test/enhanced-main-alignment.test.js +0 -298
- package/test/enhanced-uninstaller-implemented.test.js +0 -271
- package/test/enhanced-uninstaller.test.js +0 -284
- package/test/error-handling-test.js +0 -341
- package/test/fibonacci.test.js +0 -178
- package/test/final-deploy-test.js +0 -221
- package/test/final-install-test.js +0 -226
- package/test/hash-table-demo.js +0 -33
- package/test/hash-table-test.js +0 -26
- package/test/hash_table_test.js +0 -114
- package/test/hook-system-integration-test.js +0 -307
- package/test/iflow-integration-test.js +0 -292
- package/test/improved-install-test.js +0 -362
- package/test/install-command-test.js +0 -370
- package/test/json-parser-test.js +0 -161
- package/test/json-validation-test.js +0 -164
- package/test/natural-language-skills-test.js +0 -320
- package/test/nl-integration-test.js +0 -179
- package/test/parameter-parsing-test.js +0 -143
- package/test/plugin-deployment-test.js +0 -316
- package/test/postinstall-test.js +0 -269
- package/test/python-plugins-test.js +0 -259
- package/test/real-test.js +0 -435
- package/test/remaining-adapters-test.js +0 -256
- package/test/rest-client-test.js +0 -56
- package/test/rest_client.test.js +0 -85
- package/test/safe-installation-cleaner.test.js +0 -343
- package/test/simple-iflow-hook-test.js +0 -137
- package/test/stigmergy-upgrade-test.js +0 -243
- package/test/system-compatibility-test.js +0 -467
- package/test/tdd-deploy-fix-test.js +0 -324
- package/test/tdd-fixes-test.js +0 -211
- package/test/third-party-skills-test.js +0 -321
- package/test/tool-selection-integration-test.js +0 -158
- package/test/unit/calculator-full.test.js +0 -191
- package/test/unit/calculator-simple.test.js +0 -96
- package/test/unit/calculator.test.js +0 -97
- package/test/unit/cli-scanner.test.js +0 -291
- package/test/unit/cli_parameter_handler.test.js +0 -116
- package/test/unit/cross-cli-executor.test.js +0 -399
- package/test/weather-processor.test.js +0 -104
package/src/core/installer.js
CHANGED
|
@@ -18,14 +18,154 @@ class StigmergyInstaller {
|
|
|
18
18
|
const tool = this.router.tools[toolName];
|
|
19
19
|
if (!tool) return false;
|
|
20
20
|
|
|
21
|
-
//
|
|
21
|
+
// First, check if tool is properly configured
|
|
22
|
+
if (!tool.version && !tool.install) {
|
|
23
|
+
console.log(
|
|
24
|
+
`[DEBUG] Tool ${toolName} has no version/install info, skipping check`,
|
|
25
|
+
);
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Special handling for codex - check if the JS file is valid
|
|
30
|
+
if (toolName === 'codex') {
|
|
31
|
+
try {
|
|
32
|
+
// First check if codex command exists
|
|
33
|
+
const whichCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
34
|
+
const whichResult = spawnSync(whichCmd, [toolName], {
|
|
35
|
+
encoding: 'utf8',
|
|
36
|
+
timeout: 3000,
|
|
37
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
38
|
+
shell: true,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (whichResult.status === 0 && whichResult.stdout.trim()) {
|
|
42
|
+
const codexPath = whichResult.stdout.trim().split('\n')[0]; // Get first match
|
|
43
|
+
|
|
44
|
+
// If it's a shell script, check the target JS file
|
|
45
|
+
if (
|
|
46
|
+
codexPath.endsWith('.sh') ||
|
|
47
|
+
codexPath.endsWith('.cmd') ||
|
|
48
|
+
codexPath.endsWith('/codex') ||
|
|
49
|
+
codexPath.endsWith('\\codex')
|
|
50
|
+
) {
|
|
51
|
+
// Read the script to find the JS file path
|
|
52
|
+
const fsSync = require('fs');
|
|
53
|
+
const scriptContent = fsSync.readFileSync(codexPath, 'utf8');
|
|
54
|
+
|
|
55
|
+
// Look for JS file reference in the script
|
|
56
|
+
const jsFileMatch = scriptContent.match(
|
|
57
|
+
/(["'])(.*?codex\.js.*?)\1/,
|
|
58
|
+
);
|
|
59
|
+
if (jsFileMatch) {
|
|
60
|
+
const jsFilePath = jsFileMatch[2];
|
|
61
|
+
// Check if JS file exists and is not empty
|
|
62
|
+
if (fsSync.existsSync(jsFilePath)) {
|
|
63
|
+
const stats = fsSync.statSync(jsFilePath);
|
|
64
|
+
if (stats.size === 0) {
|
|
65
|
+
console.log(
|
|
66
|
+
'[DEBUG] Codex JS file is empty, marking as unavailable',
|
|
67
|
+
);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
console.log(
|
|
72
|
+
'[DEBUG] Codex JS file does not exist, marking as unavailable',
|
|
73
|
+
);
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// If we got here, the codex command exists and seems valid
|
|
80
|
+
// Continue with normal checks below
|
|
81
|
+
} else {
|
|
82
|
+
// Codex command doesn't exist
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.log(`[DEBUG] Error checking codex: ${error.message}`);
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// First try to find the executable using which/where command (more reliable)
|
|
92
|
+
try {
|
|
93
|
+
const whichCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
94
|
+
const whichResult = spawnSync(whichCmd, [toolName], {
|
|
95
|
+
encoding: 'utf8',
|
|
96
|
+
timeout: 3000,
|
|
97
|
+
stdio: ['pipe', 'pipe', 'pipe'], // Use pipes to avoid file opening
|
|
98
|
+
shell: true,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (whichResult.status === 0 && whichResult.stdout.trim()) {
|
|
102
|
+
// Found executable, now test it safely
|
|
103
|
+
const testArgs = ['--help'];
|
|
104
|
+
const testOptions = {
|
|
105
|
+
encoding: 'utf8',
|
|
106
|
+
timeout: 5000,
|
|
107
|
+
stdio: ['pipe', 'pipe', 'pipe'], // Don't inherit from parent to avoid opening UI
|
|
108
|
+
shell: true,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Additional protection for codex
|
|
112
|
+
if (toolName === 'codex') {
|
|
113
|
+
testOptions.shell = false;
|
|
114
|
+
testOptions.windowsHide = true;
|
|
115
|
+
testOptions.detached = false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const testResult = spawnSync(toolName, testArgs, testOptions);
|
|
119
|
+
|
|
120
|
+
// If command runs successfully or at least returns something (not command not found)
|
|
121
|
+
if (testResult.status === 0 || testResult.status === 1) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
// which/where command probably failed, continue with other checks
|
|
127
|
+
console.log(
|
|
128
|
+
`[DEBUG] which/where check failed for ${toolName}: ${error.message}`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Special handling for codex to avoid opening files
|
|
133
|
+
if (toolName === 'codex') {
|
|
134
|
+
// For codex, only try --help and --version with extra precautions
|
|
135
|
+
const codexChecks = [
|
|
136
|
+
{ args: ['--help'], expected: 0 },
|
|
137
|
+
{ args: ['--version'], expected: 0 },
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
for (const check of codexChecks) {
|
|
141
|
+
try {
|
|
142
|
+
const result = spawnSync(toolName, check.args, {
|
|
143
|
+
encoding: 'utf8',
|
|
144
|
+
timeout: 3000,
|
|
145
|
+
stdio: ['pipe', 'pipe', 'pipe'], // Ensure all IO is piped
|
|
146
|
+
shell: false, // Don't use shell to avoid extra window opening
|
|
147
|
+
windowsHide: true, // Hide console window on Windows
|
|
148
|
+
detached: false, // Don't detach process
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (result.status === 0 || result.status === 1) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
} catch (error) {
|
|
155
|
+
// Continue to next check
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return false; // If all codex checks fail
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Fallback: Try multiple ways to check if CLI is available but more safely
|
|
22
162
|
const checks = [
|
|
23
|
-
// Method 1: Try
|
|
24
|
-
{ args: ['--version'], expected: 0 },
|
|
25
|
-
// Method 2: Try help command
|
|
163
|
+
// Method 1: Try help command (most common and safe)
|
|
26
164
|
{ args: ['--help'], expected: 0 },
|
|
27
|
-
// Method
|
|
165
|
+
// Method 2: Try help command with -h
|
|
28
166
|
{ args: ['-h'], expected: 0 },
|
|
167
|
+
// Method 3: Try version command
|
|
168
|
+
{ args: ['--version'], expected: 0 },
|
|
29
169
|
// Method 4: Try just the command (help case)
|
|
30
170
|
{ args: [], expected: 0 },
|
|
31
171
|
];
|
|
@@ -35,19 +175,23 @@ class StigmergyInstaller {
|
|
|
35
175
|
const result = spawnSync(toolName, check.args, {
|
|
36
176
|
encoding: 'utf8',
|
|
37
177
|
timeout: 5000,
|
|
178
|
+
stdio: ['pipe', 'pipe', 'pipe'], // Use pipe instead of inherit to avoid opening files
|
|
38
179
|
shell: true,
|
|
39
180
|
});
|
|
40
181
|
|
|
41
182
|
// Check if command executed successfully or at least didn't fail with "command not found"
|
|
42
183
|
if (
|
|
43
184
|
result.status === check.expected ||
|
|
44
|
-
(result.status !== 127 &&
|
|
185
|
+
(result.status !== 127 &&
|
|
186
|
+
result.status !== 9009 &&
|
|
187
|
+
result.status !== 1) // Also avoid status 1 (general error)
|
|
45
188
|
) {
|
|
46
189
|
// 127 = command not found on Unix, 9009 = command not found on Windows
|
|
47
190
|
return true;
|
|
48
191
|
}
|
|
49
192
|
} catch (error) {
|
|
50
193
|
// Continue to next check method
|
|
194
|
+
console.log(`[DEBUG] checkCLI error for ${toolName}: ${error.message}`);
|
|
51
195
|
continue;
|
|
52
196
|
}
|
|
53
197
|
}
|
|
@@ -81,6 +225,7 @@ class StigmergyInstaller {
|
|
|
81
225
|
console.log(
|
|
82
226
|
`[ERROR] Failed to check ${toolInfo.name}: ${error.message}`,
|
|
83
227
|
);
|
|
228
|
+
console.log(`[MISSING] ${toolInfo.name} is not installed`);
|
|
84
229
|
missing[toolName] = toolInfo;
|
|
85
230
|
}
|
|
86
231
|
}
|
|
@@ -88,88 +233,88 @@ class StigmergyInstaller {
|
|
|
88
233
|
return { available, missing };
|
|
89
234
|
}
|
|
90
235
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
try {
|
|
104
|
-
console.log(`\n[INSTALL] Installing ${toolInfo.name}...`);
|
|
105
|
-
console.log(`[CMD] ${toolInfo.install}`);
|
|
236
|
+
/**
|
|
237
|
+
* Download required assets for Stigmergy CLI
|
|
238
|
+
*/
|
|
239
|
+
async downloadRequiredAssets() {
|
|
240
|
+
// This is a placeholder implementation
|
|
241
|
+
// In a real implementation, this would download required assets
|
|
242
|
+
console.log('[ASSETS] No required assets to download');
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
106
245
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
246
|
+
/**
|
|
247
|
+
* Show install options for missing CLI tools
|
|
248
|
+
*/
|
|
249
|
+
async showInstallOptions(missingTools) {
|
|
250
|
+
// This is a placeholder implementation
|
|
251
|
+
console.log('[INSTALL] No install options to show');
|
|
252
|
+
return [];
|
|
253
|
+
}
|
|
111
254
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
console.log(`[INFO] Please run manually: ${toolInfo.install}`);
|
|
122
|
-
}
|
|
123
|
-
} catch (error) {
|
|
124
|
-
await errorHandler.logError(
|
|
125
|
-
error,
|
|
126
|
-
'ERROR',
|
|
127
|
-
`StigmergyInstaller.installTools.${toolName}`,
|
|
128
|
-
);
|
|
129
|
-
console.log(
|
|
130
|
-
`[ERROR] Failed to install ${toolInfo.name}: ${error.message}`,
|
|
131
|
-
);
|
|
132
|
-
console.log(`[INFO] Please run manually: ${toolInfo.install}`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
255
|
+
/**
|
|
256
|
+
* Get user selection for CLI tools to install
|
|
257
|
+
*/
|
|
258
|
+
async getUserSelection(options, missingTools) {
|
|
259
|
+
// This is a placeholder implementation
|
|
260
|
+
console.log('[INSTALL] No user selection needed');
|
|
261
|
+
return [];
|
|
262
|
+
}
|
|
135
263
|
|
|
264
|
+
/**
|
|
265
|
+
* Install selected CLI tools
|
|
266
|
+
*/
|
|
267
|
+
async installTools(selectedTools, missingTools) {
|
|
268
|
+
// This is a placeholder implementation
|
|
269
|
+
console.log('[INSTALL] No tools to install');
|
|
136
270
|
return true;
|
|
137
271
|
}
|
|
138
272
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
// Import Node.js coordination layer for hook deployment
|
|
145
|
-
let HookDeploymentManager;
|
|
146
|
-
try {
|
|
147
|
-
HookDeploymentManager = require('../core/coordination/nodejs/HookDeploymentManager');
|
|
148
|
-
} catch (error) {
|
|
149
|
-
console.log('[WARN] Node.js coordination layer not available, using Python fallback');
|
|
150
|
-
// Fallback to Python implementation would go here
|
|
151
|
-
console.log('[ERROR] Python fallback not implemented yet');
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
273
|
+
/**
|
|
274
|
+
* Deploy hooks for available CLI tools
|
|
275
|
+
*/
|
|
276
|
+
async deployHooks(available) {
|
|
277
|
+
console.log('\n[DEPLOY] Deploying cross-CLI integration hooks...');
|
|
154
278
|
|
|
155
|
-
|
|
279
|
+
const HookDeploymentManager = require('./coordination/nodejs/HookDeploymentManager');
|
|
156
280
|
const hookManager = new HookDeploymentManager();
|
|
281
|
+
|
|
157
282
|
await hookManager.initialize();
|
|
158
283
|
|
|
159
|
-
|
|
284
|
+
let successCount = 0;
|
|
285
|
+
let totalCount = Object.keys(available).length;
|
|
286
|
+
|
|
287
|
+
for (const [toolName, toolInfo] of Object.entries(available)) {
|
|
288
|
+
console.log(`\n[DEPLOY] Deploying hooks for ${toolInfo.name}...`);
|
|
289
|
+
|
|
160
290
|
try {
|
|
161
|
-
|
|
291
|
+
const fs = require('fs/promises');
|
|
292
|
+
const path = require('path');
|
|
162
293
|
|
|
163
|
-
//
|
|
164
|
-
|
|
294
|
+
// Create hooks directory
|
|
295
|
+
await fs.mkdir(toolInfo.hooksDir, { recursive: true });
|
|
296
|
+
console.log(`[OK] Created hooks directory: ${toolInfo.hooksDir}`);
|
|
297
|
+
|
|
298
|
+
// Create config directory
|
|
299
|
+
const configDir = path.dirname(toolInfo.config);
|
|
300
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
301
|
+
console.log(`[OK] Created config directory: ${configDir}`);
|
|
302
|
+
|
|
303
|
+
// Use HookDeploymentManager to deploy Node.js hooks
|
|
304
|
+
const deploySuccess = await hookManager.deployHooksForCLI(toolName);
|
|
165
305
|
|
|
166
|
-
if (
|
|
167
|
-
console.log(`[OK]
|
|
168
|
-
successCount++;
|
|
169
|
-
} else {
|
|
170
|
-
console.log(`[ERROR] Failed to deploy hooks for ${toolInfo.name}`);
|
|
306
|
+
if (deploySuccess) {
|
|
307
|
+
console.log(`[OK] Node.js hooks deployed successfully for ${toolInfo.name}`);
|
|
171
308
|
}
|
|
309
|
+
|
|
310
|
+
// Run JavaScript-based installation for each tool
|
|
311
|
+
await this.installToolIntegration(toolName, toolInfo);
|
|
312
|
+
|
|
313
|
+
console.log(`[OK] Hooks deployed successfully for ${toolInfo.name}`);
|
|
314
|
+
successCount++
|
|
315
|
+
|
|
172
316
|
} catch (error) {
|
|
317
|
+
const { errorHandler } = require('./error_handler');
|
|
173
318
|
await errorHandler.logError(
|
|
174
319
|
error,
|
|
175
320
|
'ERROR',
|
|
@@ -178,295 +323,671 @@ class StigmergyInstaller {
|
|
|
178
323
|
console.log(
|
|
179
324
|
`[ERROR] Failed to deploy hooks for ${toolInfo.name}: ${error.message}`,
|
|
180
325
|
);
|
|
326
|
+
console.log('[INFO] Continuing with other tools...');
|
|
181
327
|
}
|
|
182
328
|
}
|
|
183
329
|
|
|
184
330
|
console.log(
|
|
185
331
|
`\n[SUMMARY] Hook deployment completed: ${successCount}/${totalCount} tools successful`,
|
|
186
332
|
);
|
|
333
|
+
return successCount > 0;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Check if a file or directory exists
|
|
338
|
+
*/
|
|
339
|
+
async fileExists(path) {
|
|
340
|
+
const fs = require('fs/promises');
|
|
341
|
+
try {
|
|
342
|
+
await fs.access(path);
|
|
343
|
+
return true;
|
|
344
|
+
} catch {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Copy a directory recursively
|
|
351
|
+
*/
|
|
352
|
+
async copyDirectory(src, dest) {
|
|
353
|
+
const fs = require('fs/promises');
|
|
354
|
+
const path = require('path');
|
|
355
|
+
|
|
356
|
+
await fs.mkdir(dest, { recursive: true });
|
|
357
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
358
|
+
|
|
359
|
+
for (const entry of entries) {
|
|
360
|
+
const srcPath = path.join(src, entry.name);
|
|
361
|
+
const destPath = path.join(dest, entry.name);
|
|
362
|
+
|
|
363
|
+
if (entry.isDirectory()) {
|
|
364
|
+
await this.copyDirectory(srcPath, destPath);
|
|
365
|
+
} else {
|
|
366
|
+
await fs.copyFile(srcPath, destPath);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
187
369
|
}
|
|
188
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Deploy project documentation
|
|
373
|
+
*/
|
|
189
374
|
async deployProjectDocumentation() {
|
|
190
|
-
|
|
375
|
+
// This is a placeholder implementation
|
|
376
|
+
console.log('[DOCS] No documentation to deploy');
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Initialize configuration
|
|
382
|
+
*/
|
|
383
|
+
async initializeConfig() {
|
|
384
|
+
// This is a placeholder implementation
|
|
385
|
+
console.log('[CONFIG] Configuration initialized');
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Show usage instructions
|
|
391
|
+
*/
|
|
392
|
+
showUsageInstructions() {
|
|
393
|
+
// This is a placeholder implementation
|
|
394
|
+
console.log('[USAGE] No usage instructions to show');
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
191
397
|
|
|
398
|
+
/**
|
|
399
|
+
* Create project files (PROJECT_SPEC.json and PROJECT_CONSTITUTION.md) in current directory
|
|
400
|
+
*/
|
|
401
|
+
async createProjectFiles() {
|
|
402
|
+
const fs = require('fs/promises');
|
|
403
|
+
const path = require('path');
|
|
404
|
+
|
|
192
405
|
try {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
406
|
+
const currentDir = process.cwd();
|
|
407
|
+
|
|
408
|
+
// Create PROJECT_SPEC.json if it doesn't exist
|
|
409
|
+
const projectSpecPath = path.join(currentDir, 'PROJECT_SPEC.json');
|
|
410
|
+
if (!(await fs.access(projectSpecPath).then(() => true).catch(() => false))) {
|
|
411
|
+
await fs.writeFile(projectSpecPath, '{}', 'utf8');
|
|
412
|
+
console.log('[PROJECT] Created PROJECT_SPEC.json');
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Create PROJECT_CONSTITUTION.md if it doesn't exist
|
|
416
|
+
const projectConstitutionPath = path.join(currentDir, 'PROJECT_CONSTITUTION.md');
|
|
417
|
+
if (!(await fs.access(projectConstitutionPath).then(() => true).catch(() => false))) {
|
|
418
|
+
const constitutionContent = `# 项目协作宪法
|
|
419
|
+
|
|
420
|
+
## 协作基本原则
|
|
421
|
+
- 所有协作通过PROJECT_SPEC.json协调
|
|
422
|
+
- 智能体基于背景状态自主决�?- 无中央调度器,实现去中心化协�?
|
|
423
|
+
## 任务管理原则
|
|
424
|
+
- 智能体可认领分配给自己的任务
|
|
425
|
+
- 智能体可认领与其能力匹配的未分配任务
|
|
426
|
+
- 任务状态实时更新至共享背景
|
|
427
|
+
`;
|
|
428
|
+
await fs.writeFile(projectConstitutionPath, constitutionContent, 'utf8');
|
|
429
|
+
console.log('[PROJECT] Created PROJECT_CONSTITUTION.md');
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return true;
|
|
433
|
+
} catch (error) {
|
|
434
|
+
console.error('[PROJECT] Failed to create project files:', error.message);
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
205
438
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
439
|
+
/**
|
|
440
|
+
* Install integration for a specific CLI tool using JavaScript
|
|
441
|
+
*/
|
|
442
|
+
async installToolIntegration(toolName, toolInfo) {
|
|
443
|
+
const fs = require('fs/promises');
|
|
444
|
+
const path = require('path');
|
|
445
|
+
const { spawn } = require('child_process');
|
|
446
|
+
|
|
447
|
+
console.log(`[CONFIG] Configuring ${toolInfo.name}...`);
|
|
448
|
+
|
|
449
|
+
try {
|
|
450
|
+
// Map tool names to their adapter directory names
|
|
451
|
+
const toolToAdapterDir = {
|
|
452
|
+
'qodercli': 'qoder',
|
|
453
|
+
'qwencode': 'qwen'
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
const adapterDirName = toolToAdapterDir[toolName] || toolName;
|
|
457
|
+
const installScriptPath = path.join(__dirname, '..', 'adapters', adapterDirName, `install_${adapterDirName}_integration.js`);
|
|
458
|
+
|
|
459
|
+
// Check if the JavaScript installation script exists
|
|
460
|
+
try {
|
|
461
|
+
await fs.access(installScriptPath);
|
|
462
|
+
console.log(`[RUN] Running JavaScript installation script for ${toolInfo.name}...`);
|
|
463
|
+
|
|
464
|
+
// Run the JavaScript installation script
|
|
465
|
+
const result = await this.runInstallScript(installScriptPath);
|
|
466
|
+
|
|
467
|
+
if (result.success) {
|
|
468
|
+
console.log(`[OK] ${toolInfo.name} installation script completed successfully`);
|
|
469
|
+
} else {
|
|
470
|
+
console.log(`[WARN] ${toolInfo.name} installation script failed: ${result.error}`);
|
|
211
471
|
}
|
|
472
|
+
} catch (error) {
|
|
473
|
+
console.log(`[WARN] JavaScript installation script not found for ${toolInfo.name}: ${installScriptPath}`);
|
|
474
|
+
console.log(`[FALLBACK] Using inline configuration for ${toolInfo.name}...`);
|
|
475
|
+
|
|
476
|
+
// Fallback to inline configuration if script doesn't exist
|
|
477
|
+
await this.inlineToolConfiguration(toolName, toolInfo);
|
|
212
478
|
}
|
|
213
|
-
|
|
214
|
-
console.log('[OK] Project documentation deployed successfully');
|
|
479
|
+
|
|
215
480
|
} catch (error) {
|
|
216
|
-
console.log(
|
|
217
|
-
`[ERROR] Failed to deploy project documentation: ${error.message}`,
|
|
218
|
-
);
|
|
481
|
+
console.log(`[WARN] Configuration failed for ${toolInfo.name}: ${error.message}`);
|
|
219
482
|
}
|
|
220
483
|
}
|
|
221
484
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
485
|
+
/**
|
|
486
|
+
* Run a JavaScript installation script
|
|
487
|
+
*/
|
|
488
|
+
async runInstallScript(scriptPath) {
|
|
489
|
+
return new Promise((resolve) => {
|
|
490
|
+
const child = spawn('node', [scriptPath], {
|
|
491
|
+
cwd: path.dirname(scriptPath),
|
|
492
|
+
stdio: 'pipe',
|
|
493
|
+
timeout: 30000 // 30 second timeout
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
let stdout = '';
|
|
497
|
+
let stderr = '';
|
|
498
|
+
|
|
499
|
+
child.stdout.on('data', (data) => {
|
|
500
|
+
stdout += data.toString();
|
|
501
|
+
// Print output in real-time
|
|
502
|
+
process.stdout.write(data.toString());
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
child.stderr.on('data', (data) => {
|
|
506
|
+
stderr += data.toString();
|
|
507
|
+
// Print errors in real-time
|
|
508
|
+
process.stderr.write(data.toString());
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
child.on('close', (code) => {
|
|
512
|
+
if (code === 0) {
|
|
513
|
+
resolve({ success: true, code: code, stdout: stdout, stderr: stderr });
|
|
514
|
+
} else {
|
|
515
|
+
resolve({ success: false, code: code, stdout: stdout, stderr: stderr, error: `Script exited with code ${code}` });
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
child.on('error', (error) => {
|
|
520
|
+
resolve({ success: false, error: error.message });
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Fallback inline configuration for tools without dedicated scripts
|
|
527
|
+
*/
|
|
528
|
+
async inlineToolConfiguration(toolName, toolInfo) {
|
|
529
|
+
const fs = require('fs/promises');
|
|
530
|
+
const path = require('path');
|
|
531
|
+
const os = require('os');
|
|
532
|
+
|
|
533
|
+
// Step 1: Create tool-specific configuration
|
|
534
|
+
const config = {
|
|
535
|
+
cross_cli_enabled: true,
|
|
536
|
+
supported_clis: ["claude", "gemini", "qwen", "iflow", "qodercli", "codebuddy", "copilot", "codex"],
|
|
537
|
+
auto_detect: true,
|
|
538
|
+
timeout: 30,
|
|
539
|
+
collaboration_mode: "active",
|
|
540
|
+
stigmergy_version: "1.2.1",
|
|
541
|
+
deployment_time: new Date().toISOString()
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
// Write configuration file
|
|
545
|
+
await fs.writeFile(toolInfo.config, JSON.stringify(config, null, 2));
|
|
546
|
+
console.log(`[OK] Configuration file created: ${toolInfo.config}`);
|
|
547
|
+
|
|
548
|
+
// Step 2: Copy adapter files if they exist
|
|
549
|
+
const adapterDir = path.join(__dirname, '..', 'adapters', toolName === 'qodercli' ? 'qoder' : toolName);
|
|
550
|
+
if (await this.fileExists(adapterDir)) {
|
|
551
|
+
await this.copyAdapterFiles(adapterDir, toolInfo.hooksDir);
|
|
552
|
+
console.log(`[OK] Adapter files copied for ${toolInfo.name}`);
|
|
553
|
+
}
|
|
225
554
|
|
|
226
|
-
|
|
227
|
-
|
|
555
|
+
// Step 3: Create tool-specific hook files
|
|
556
|
+
await this.createToolHooks(toolName, toolInfo);
|
|
228
557
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
- **Tool Name**: ${cliInfo.name}
|
|
232
|
-
- **Installation Command**: \`${cliInfo.install || 'Not configured'}\`
|
|
233
|
-
- **Version Check**: \`${cliInfo.version || cliName + ' --version'}\`
|
|
558
|
+
// Step 4: Create hooks configuration file
|
|
559
|
+
await this.createHooksConfig(toolName, toolInfo);
|
|
234
560
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
1. Direct execution: \`${cliName} [arguments]\`
|
|
238
|
-
2. Through Stigmergy coordination layer
|
|
239
|
-
3. Cross-CLI calls from other tools
|
|
561
|
+
// Step 5: Create Cross-CLI documentation
|
|
562
|
+
await this.createCrossCliDocumentation(toolName, toolInfo);
|
|
240
563
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
\`\`\`bash
|
|
244
|
-
# From any other supported CLI
|
|
245
|
-
use ${cliName} to [task description]
|
|
246
|
-
# or
|
|
247
|
-
call ${cliName} [task description]
|
|
248
|
-
# or
|
|
249
|
-
ask ${cliName} [task description]
|
|
250
|
-
\`\`\`
|
|
251
|
-
|
|
252
|
-
## Configuration
|
|
253
|
-
This tool integrates with Stigmergy through hooks deployed to:
|
|
254
|
-
\`${cliInfo.hooksDir || 'Not configured'}\`
|
|
255
|
-
|
|
256
|
-
## Last Updated
|
|
257
|
-
${new Date().toISOString()}
|
|
564
|
+
// Step 6: Append to existing .md file if it exists
|
|
565
|
+
await this.appendToToolMdFile(toolName);
|
|
258
566
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
`;
|
|
567
|
+
// Step 7: Create additional tool-specific configurations
|
|
568
|
+
await this.createToolSpecificConfigs(toolName, toolInfo);
|
|
262
569
|
}
|
|
263
570
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
571
|
+
/**
|
|
572
|
+
* Create hooks configuration file
|
|
573
|
+
*/
|
|
574
|
+
async createHooksConfig(toolName, toolInfo) {
|
|
575
|
+
const fs = require('fs/promises');
|
|
576
|
+
const path = require('path');
|
|
577
|
+
|
|
578
|
+
try {
|
|
579
|
+
const hooksConfig = {
|
|
580
|
+
cross_cli_adapter: {
|
|
581
|
+
enabled: true,
|
|
582
|
+
supported_tools: ["claude", "gemini", "qwen", "iflow", "qodercli", "codebuddy", "copilot", "codex"],
|
|
583
|
+
trigger_patterns: [
|
|
584
|
+
"use\\s+(\\w+)\\s+to\\s+(.+)$",
|
|
585
|
+
"call\\s+(\\w+)\\s+(.+)$",
|
|
586
|
+
"ask\\s+(\\w+)\\s+(.+)$",
|
|
587
|
+
"stigmergy\\s+(\\w+)\\s+(.+)$"
|
|
588
|
+
]
|
|
589
|
+
}
|
|
590
|
+
};
|
|
268
591
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
592
|
+
const hooksConfigPath = path.join(path.dirname(toolInfo.config), 'hooks.json');
|
|
593
|
+
await fs.writeFile(hooksConfigPath, JSON.stringify(hooksConfig, null, 2));
|
|
594
|
+
console.log(`[OK] Created hooks configuration: ${hooksConfigPath}`);
|
|
595
|
+
} catch (error) {
|
|
596
|
+
console.log(`[WARN] Failed to create hooks config: ${error.message}`);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
273
599
|
|
|
274
|
-
|
|
275
|
-
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
600
|
+
/**
|
|
601
|
+
* Create tool-specific configurations
|
|
602
|
+
*/
|
|
603
|
+
async createToolSpecificConfigs(toolName, toolInfo) {
|
|
604
|
+
const fs = require('fs/promises');
|
|
605
|
+
const path = require('path');
|
|
279
606
|
|
|
280
|
-
|
|
281
|
-
|
|
607
|
+
try {
|
|
608
|
+
// Create installation_id file
|
|
609
|
+
const installationId = Date.now().toString();
|
|
610
|
+
const installationIdPath = path.join(path.dirname(toolInfo.config), 'installation_id');
|
|
611
|
+
await fs.writeFile(installationIdPath, installationId);
|
|
612
|
+
console.log(`[OK] Created installation ID: ${installationIdPath}`);
|
|
613
|
+
|
|
614
|
+
// Create settings.json if it doesn't exist
|
|
615
|
+
const settingsPath = path.join(path.dirname(toolInfo.config), 'settings.json');
|
|
616
|
+
try {
|
|
617
|
+
await fs.access(settingsPath);
|
|
618
|
+
} catch {
|
|
619
|
+
const settings = {
|
|
620
|
+
version: "1.0.0",
|
|
621
|
+
theme: "default",
|
|
622
|
+
auto_save: true,
|
|
623
|
+
cross_cli_enabled: true
|
|
624
|
+
};
|
|
625
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2));
|
|
626
|
+
console.log(`[OK] Created settings file: ${settingsPath}`);
|
|
627
|
+
}
|
|
282
628
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
629
|
+
// Tool-specific configurations
|
|
630
|
+
switch (toolName) {
|
|
631
|
+
case 'copilot':
|
|
632
|
+
await this.createCopilotConfig(toolInfo);
|
|
633
|
+
break;
|
|
634
|
+
case 'iflow':
|
|
635
|
+
await this.createIflowConfig(toolInfo);
|
|
636
|
+
break;
|
|
637
|
+
case 'qodercli':
|
|
638
|
+
await this.createQoderConfig(toolInfo);
|
|
639
|
+
break;
|
|
640
|
+
case 'codebuddy':
|
|
641
|
+
await this.createCodeBuddyConfig(toolInfo);
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
644
|
+
} catch (error) {
|
|
645
|
+
console.log(`[WARN] Failed to create tool-specific configs: ${error.message}`);
|
|
646
|
+
}
|
|
286
647
|
}
|
|
287
648
|
|
|
288
|
-
|
|
289
|
-
|
|
649
|
+
/**
|
|
650
|
+
* Create Copilot-specific configuration
|
|
651
|
+
*/
|
|
652
|
+
async createCopilotConfig(toolInfo) {
|
|
653
|
+
const fs = require('fs/promises');
|
|
654
|
+
const path = require('path');
|
|
290
655
|
|
|
291
656
|
try {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
657
|
+
const copilotConfig = {
|
|
658
|
+
mcp: {
|
|
659
|
+
enabled: true,
|
|
660
|
+
servers: {
|
|
661
|
+
"cross-cli-caller": {
|
|
662
|
+
name: "CrossCLICaller",
|
|
663
|
+
description: "Cross-CLI Tool Calling Agent",
|
|
664
|
+
command: "node",
|
|
665
|
+
args: [path.join(toolInfo.hooksDir, "cross_cli_caller.js")]
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
},
|
|
669
|
+
cross_cli_caller: {
|
|
670
|
+
name: "CrossCLICaller",
|
|
671
|
+
description: "Cross-CLI Tool Calling Agent"
|
|
672
|
+
}
|
|
306
673
|
};
|
|
307
674
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
console.log('[OK] Configuration initialized successfully');
|
|
675
|
+
const configPath = path.join(path.dirname(toolInfo.config), 'mcp_config.json');
|
|
676
|
+
await fs.writeFile(configPath, JSON.stringify(copilotConfig, null, 2));
|
|
677
|
+
console.log(`[OK] Created Copilot MCP config: ${configPath}`);
|
|
312
678
|
} catch (error) {
|
|
313
|
-
console.log(
|
|
314
|
-
`[ERROR] Failed to initialize configuration: ${error.message}`,
|
|
315
|
-
);
|
|
679
|
+
console.log(`[WARN] Failed to create Copilot config: ${error.message}`);
|
|
316
680
|
}
|
|
317
681
|
}
|
|
318
682
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
683
|
+
/**
|
|
684
|
+
* Create iFlow-specific configuration
|
|
685
|
+
*/
|
|
686
|
+
async createIflowConfig(toolInfo) {
|
|
687
|
+
const fs = require('fs/promises');
|
|
688
|
+
const path = require('path');
|
|
324
689
|
|
|
325
|
-
async fileExists(filePath) {
|
|
326
690
|
try {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
691
|
+
const iflowConfig = {
|
|
692
|
+
workflow: {
|
|
693
|
+
enabled: true,
|
|
694
|
+
cross_cli_integration: true,
|
|
695
|
+
default_timeout: 30
|
|
696
|
+
},
|
|
697
|
+
hooks: {
|
|
698
|
+
cross_cli: {
|
|
699
|
+
enabled: true,
|
|
700
|
+
auto_detect: true
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
const configPath = path.join(path.dirname(toolInfo.config), 'workflow_config.json');
|
|
706
|
+
await fs.writeFile(configPath, JSON.stringify(iflowConfig, null, 2));
|
|
707
|
+
console.log(`[OK] Created iFlow workflow config: ${configPath}`);
|
|
708
|
+
} catch (error) {
|
|
709
|
+
console.log(`[WARN] Failed to create iFlow config: ${error.message}`);
|
|
331
710
|
}
|
|
332
711
|
}
|
|
333
712
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
713
|
+
/**
|
|
714
|
+
* Create Qoder-specific configuration
|
|
715
|
+
*/
|
|
716
|
+
async createQoderConfig(toolInfo) {
|
|
717
|
+
const fs = require('fs/promises');
|
|
718
|
+
const path = require('path');
|
|
339
719
|
|
|
340
|
-
|
|
341
|
-
|
|
720
|
+
try {
|
|
721
|
+
const qoderConfig = {
|
|
722
|
+
notifications: {
|
|
723
|
+
enabled: true,
|
|
724
|
+
cross_cli: true
|
|
725
|
+
},
|
|
726
|
+
keywords: {
|
|
727
|
+
cross_cli: ["请用", "调用", "用", "让", "use", "call", "ask"]
|
|
728
|
+
}
|
|
729
|
+
};
|
|
342
730
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
});
|
|
731
|
+
const configPath = path.join(path.dirname(toolInfo.config), 'qoder_config.json');
|
|
732
|
+
await fs.writeFile(configPath, JSON.stringify(qoderConfig, null, 2));
|
|
733
|
+
console.log(`[OK] Created Qoder config: ${configPath}`);
|
|
734
|
+
} catch (error) {
|
|
735
|
+
console.log(`[WARN] Failed to create Qoder config: ${error.message}`);
|
|
349
736
|
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Create CodeBuddy-specific configuration
|
|
741
|
+
*/
|
|
742
|
+
async createCodeBuddyConfig(toolInfo) {
|
|
743
|
+
const fs = require('fs/promises');
|
|
744
|
+
const path = require('path');
|
|
350
745
|
|
|
351
|
-
|
|
352
|
-
{
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
746
|
+
try {
|
|
747
|
+
const buddyConfig = {
|
|
748
|
+
skills: {
|
|
749
|
+
cross_cli: {
|
|
750
|
+
enabled: true,
|
|
751
|
+
auto_register: true
|
|
752
|
+
}
|
|
753
|
+
},
|
|
754
|
+
buddy: {
|
|
755
|
+
cross_cli_calls_count: 0
|
|
756
|
+
}
|
|
757
|
+
};
|
|
361
758
|
|
|
362
|
-
|
|
759
|
+
const configPath = path.join(path.dirname(toolInfo.config), 'buddy_config.json');
|
|
760
|
+
await fs.writeFile(configPath, JSON.stringify(buddyConfig, null, 2));
|
|
761
|
+
console.log(`[OK] Created CodeBuddy buddy config: ${configPath}`);
|
|
762
|
+
} catch (error) {
|
|
763
|
+
console.log(`[WARN] Failed to create CodeBuddy config: ${error.message}`);
|
|
764
|
+
}
|
|
363
765
|
}
|
|
364
766
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
767
|
+
/**
|
|
768
|
+
* Create Cross-CLI documentation for the tool
|
|
769
|
+
*/
|
|
770
|
+
async createCrossCliDocumentation(toolName, toolInfo) {
|
|
771
|
+
const fs = require('fs/promises');
|
|
772
|
+
const path = require('path');
|
|
773
|
+
|
|
774
|
+
try {
|
|
775
|
+
// Create CROSS_CLI_GUIDE.md in the tool's config directory
|
|
776
|
+
const docContent = this.generateCrossCliGuide(toolName);
|
|
777
|
+
const docPath = path.join(path.dirname(toolInfo.config), 'CROSS_CLI_GUIDE.md');
|
|
778
|
+
await fs.writeFile(docPath, docContent);
|
|
779
|
+
console.log(`[OK] Created Cross-CLI Communication Guide: ${docPath}`);
|
|
780
|
+
|
|
781
|
+
// Append to existing .md file if it exists
|
|
782
|
+
await this.appendToToolMdFile(toolName);
|
|
783
|
+
|
|
784
|
+
} catch (error) {
|
|
785
|
+
console.log(`[WARN] Failed to create Cross-CLI documentation: ${error.message}`);
|
|
368
786
|
}
|
|
787
|
+
}
|
|
369
788
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
789
|
+
/**
|
|
790
|
+
* Generate Cross-CLI guide content
|
|
791
|
+
*/
|
|
792
|
+
generateCrossCliGuide(toolName) {
|
|
793
|
+
const toolNames = {
|
|
794
|
+
'claude': 'Claude CLI',
|
|
795
|
+
'gemini': 'Gemini CLI',
|
|
796
|
+
'qwen': 'Qwen CLI',
|
|
797
|
+
'iflow': 'iFlow CLI',
|
|
798
|
+
'qodercli': 'Qoder CLI',
|
|
799
|
+
'codebuddy': 'CodeBuddy CLI',
|
|
800
|
+
'copilot': 'GitHub Copilot CLI',
|
|
801
|
+
'codex': 'OpenAI Codex CLI'
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
const isChinese = ['claude', 'qwen', 'iflow', 'qodercli', 'codebuddy'].includes(toolName);
|
|
805
|
+
|
|
806
|
+
if (isChinese) {
|
|
807
|
+
return `# ${toolNames[toolName]} Cross-CLI 通信指南
|
|
378
808
|
|
|
379
|
-
|
|
380
|
-
|
|
809
|
+
## Cross-CLI Communication
|
|
810
|
+
当您需要调用其他AI工具时,请使用以下提示<<<在shell中执行以下命令:stigmergy <工具名> <任务描述>>>
|
|
381
811
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
-
|
|
400
|
-
-
|
|
401
|
-
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
This file automatically tracks all interactions with AI CLI tools through the Stigmergy system.
|
|
405
|
-
|
|
406
|
-
## Recent Interactions
|
|
407
|
-
No interactions recorded yet.
|
|
408
|
-
|
|
409
|
-
## Collaboration History
|
|
410
|
-
No collaboration history yet.
|
|
411
|
-
|
|
412
|
-
## Available CLI Tools
|
|
413
|
-
See individual documentation files:
|
|
414
|
-
- claude.md
|
|
415
|
-
- gemini.md
|
|
416
|
-
- qwen.md
|
|
417
|
-
- iflow.md
|
|
418
|
-
- qodercli.md
|
|
419
|
-
- codebuddy.md
|
|
420
|
-
- copilot.md
|
|
421
|
-
- codex.md
|
|
812
|
+
示例:
|
|
813
|
+
- 在shell中运行:stigmergy claude "写一个Python函数"
|
|
814
|
+
- 在shell中运行:stigmergy gemini "翻译这段文字"
|
|
815
|
+
- 在shell中运行:stigmergy qwen "分析这段代码"
|
|
816
|
+
|
|
817
|
+
可用工具:claude, gemini, qwen, iflow, qodercli, codebuddy, copilot, codex
|
|
818
|
+
|
|
819
|
+
---
|
|
820
|
+
*本文档由Stigmergy CLI自动生成和维护*
|
|
821
|
+
`;
|
|
822
|
+
} else {
|
|
823
|
+
return `# ${toolNames[toolName]} Cross-CLI Communication Guide
|
|
824
|
+
|
|
825
|
+
## Cross-CLI Communication
|
|
826
|
+
When you need to call other AI tools, please use the following prompt <<<Execute the following command in shell: stigmergy <tool_name> <task_description>>>
|
|
827
|
+
|
|
828
|
+
Examples:
|
|
829
|
+
- Run in shell: stigmergy claude "write a Python function"
|
|
830
|
+
- Run in shell: stigmergy gemini "translate this text"
|
|
831
|
+
- Run in shell: stigmergy qwen "analyze this code"
|
|
832
|
+
|
|
833
|
+
Available tools: claude, gemini, qwen, iflow, qodercli, codebuddy, copilot, codex
|
|
422
834
|
|
|
423
835
|
---
|
|
424
|
-
*This
|
|
425
|
-
*Last updated: ${new Date().toISOString()}*
|
|
836
|
+
*This document is automatically generated and maintained by Stigmergy CLI*
|
|
426
837
|
`;
|
|
838
|
+
}
|
|
427
839
|
}
|
|
428
840
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
841
|
+
/**
|
|
842
|
+
* Append Cross-CLI communication guide to existing .md file
|
|
843
|
+
*/
|
|
844
|
+
async appendToToolMdFile(toolName) {
|
|
845
|
+
const fs = require('fs/promises');
|
|
846
|
+
const path = require('path');
|
|
847
|
+
const os = require('os');
|
|
848
|
+
|
|
849
|
+
const toolMdFiles = {
|
|
850
|
+
'claude': path.join(os.homedir(), '.claude', 'claude.md'),
|
|
851
|
+
'gemini': path.join(os.homedir(), '.gemini', 'gemini.md'),
|
|
852
|
+
'qwen': path.join(os.homedir(), '.qwen', 'qwen.md'),
|
|
853
|
+
'iflow': path.join(os.homedir(), '.iflow', 'iflow.md'),
|
|
854
|
+
'qodercli': path.join(os.homedir(), '.qoder', 'qoder.md'),
|
|
855
|
+
'codebuddy': path.join(os.homedir(), '.codebuddy', 'codebuddy.md'),
|
|
856
|
+
'copilot': path.join(os.homedir(), '.copilot', 'copilot.md'),
|
|
857
|
+
'codex': path.join(os.homedir(), '.codex', 'codex.md')
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
const mdFilePath = toolMdFiles[toolName];
|
|
861
|
+
if (!mdFilePath) return;
|
|
432
862
|
|
|
433
|
-
|
|
434
|
-
|
|
863
|
+
try {
|
|
864
|
+
const exists = await fs.access(mdFilePath).then(() => true).catch(() => false);
|
|
865
|
+
if (exists) {
|
|
866
|
+
const crossCliContent = this.generateCrossCliAppendContent(toolName);
|
|
867
|
+
await fs.appendFile(mdFilePath, crossCliContent);
|
|
868
|
+
console.log(`[OK] Appended Cross-CLI communication guide to ${path.basename(mdFilePath)}`);
|
|
869
|
+
}
|
|
870
|
+
} catch (error) {
|
|
871
|
+
console.log(`[WARN] Failed to append to ${mdFilePath}: ${error.message}`);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Generate content to append to .md files
|
|
877
|
+
*/
|
|
878
|
+
generateCrossCliAppendContent(toolName) {
|
|
879
|
+
const isChinese = ['claude', 'qwen', 'iflow', 'qodercli', 'codebuddy'].includes(toolName);
|
|
880
|
+
|
|
881
|
+
if (isChinese) {
|
|
882
|
+
return `
|
|
883
|
+
|
|
884
|
+
## Cross-CLI Communication
|
|
885
|
+
当您需要调用其他AI工具时,请使用以下提示<<<在shell中执行以下命令:stigmergy <工具名> <任务描述>>>
|
|
435
886
|
|
|
436
|
-
|
|
437
|
-
-
|
|
438
|
-
-
|
|
439
|
-
-
|
|
440
|
-
- **Version Check**: \`${cliInfo.version || cliName + ' --version'}\`
|
|
887
|
+
示例:
|
|
888
|
+
- 在shell中运行:stigmergy claude "写一个Python函数"
|
|
889
|
+
- 在shell中运行:stigmergy gemini "翻译这段文字"
|
|
890
|
+
- 在shell中运行:stigmergy qwen "分析这段代码"
|
|
441
891
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
3. Cross-CLI calls from other tools
|
|
892
|
+
可用工具:claude, gemini, qwen, iflow, qodercli, codebuddy, copilot, codex
|
|
893
|
+
`;
|
|
894
|
+
} else {
|
|
895
|
+
return `
|
|
447
896
|
|
|
448
897
|
## Cross-CLI Communication
|
|
449
|
-
|
|
450
|
-
\`\`\`bash
|
|
451
|
-
# From any other supported CLI
|
|
452
|
-
use ${cliName} to [task description]
|
|
453
|
-
# or
|
|
454
|
-
call ${cliName} [task description]
|
|
455
|
-
# or
|
|
456
|
-
ask ${cliName} [task description]
|
|
457
|
-
\`\`\`
|
|
458
|
-
|
|
459
|
-
## Configuration
|
|
460
|
-
This tool integrates with Stigmergy through hooks deployed to:
|
|
461
|
-
\`${cliInfo.hooksDir || 'Not configured'}\`
|
|
462
|
-
|
|
463
|
-
## Last Updated
|
|
464
|
-
${new Date().toISOString()}
|
|
898
|
+
When you need to call other AI tools, please use the following prompt <<<Execute the following command in shell: stigmergy <tool_name> <task_description>>>
|
|
465
899
|
|
|
466
|
-
|
|
467
|
-
|
|
900
|
+
Examples:
|
|
901
|
+
- Run in shell: stigmergy claude "write a Python function"
|
|
902
|
+
- Run in shell: stigmergy gemini "translate this text"
|
|
903
|
+
- Run in shell: stigmergy qwen "analyze this code"
|
|
904
|
+
|
|
905
|
+
Available tools: claude, gemini, qwen, iflow, qodercli, codebuddy, copilot, codex
|
|
906
|
+
`;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Copy adapter files from the adapters directory
|
|
912
|
+
*/
|
|
913
|
+
async copyAdapterFiles(adapterDir, hooksDir) {
|
|
914
|
+
const fs = require('fs/promises');
|
|
915
|
+
const path = require('path');
|
|
916
|
+
|
|
917
|
+
try {
|
|
918
|
+
const files = await fs.readdir(adapterDir);
|
|
919
|
+
for (const file of files) {
|
|
920
|
+
// Skip __pycache__ directory
|
|
921
|
+
if (file === '__pycache__') continue;
|
|
922
|
+
|
|
923
|
+
const srcPath = path.join(adapterDir, file);
|
|
924
|
+
const dstPath = path.join(hooksDir, file);
|
|
925
|
+
|
|
926
|
+
// Check if it's a directory
|
|
927
|
+
const stats = await fs.stat(srcPath);
|
|
928
|
+
if (stats.isDirectory()) {
|
|
929
|
+
// Recursively copy subdirectories
|
|
930
|
+
await this.copyDirectory(srcPath, dstPath);
|
|
931
|
+
} else {
|
|
932
|
+
// Copy all relevant files (including .py files)
|
|
933
|
+
if (file.endsWith('.py') || file.endsWith('.js') || file.endsWith('.json') ||
|
|
934
|
+
file.endsWith('.md') || file.endsWith('.yml') || file.endsWith('.yaml')) {
|
|
935
|
+
await fs.copyFile(srcPath, dstPath);
|
|
936
|
+
console.log(`[OK] Copied adapter file: ${file}`);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
} catch (error) {
|
|
941
|
+
console.log(`[WARN] Failed to copy adapter files: ${error.message}`);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* Create tool-specific hook files
|
|
947
|
+
*/
|
|
948
|
+
async createToolHooks(toolName, toolInfo) {
|
|
949
|
+
const fs = require('fs/promises');
|
|
950
|
+
const path = require('path');
|
|
951
|
+
|
|
952
|
+
// Create a simple integration hook
|
|
953
|
+
const hookContent = `#!/usr/bin/env node
|
|
954
|
+
/**
|
|
955
|
+
* ${toolInfo.name} Integration Hook
|
|
956
|
+
* Generated by Stigmergy CLI v1.2.1
|
|
957
|
+
*/
|
|
958
|
+
|
|
959
|
+
const fs = require('fs');
|
|
960
|
+
const path = require('path');
|
|
961
|
+
|
|
962
|
+
class ${toolName.charAt(0).toUpperCase() + toolName.slice(1)}Hook {
|
|
963
|
+
constructor() {
|
|
964
|
+
this.toolName = '${toolName}';
|
|
965
|
+
this.configPath = '${toolInfo.config}';
|
|
966
|
+
this.hooksDir = '${toolInfo.hooksDir}';
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
async onPrompt(prompt) {
|
|
970
|
+
// Handle incoming prompts from other CLI tools
|
|
971
|
+
console.log(\`[\${this.toolName.toUpperCase()}] Received cross-CLI prompt: \${prompt}\`);
|
|
972
|
+
return { handled: true, response: 'Prompt processed by ' + this.toolName };
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
async onResponse(response) {
|
|
976
|
+
// Handle responses to be sent to other CLI tools
|
|
977
|
+
console.log(\`[\${this.toolName.toUpperCase()}] Sending cross-CLI response\`);
|
|
978
|
+
return response;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
module.exports = ${toolName.charAt(0).toUpperCase() + toolName.slice(1)}Hook;
|
|
468
983
|
`;
|
|
984
|
+
|
|
985
|
+
const hookFilePath = path.join(toolInfo.hooksDir, `${toolName}_hook.js`);
|
|
986
|
+
await fs.writeFile(hookFilePath, hookContent);
|
|
987
|
+
console.log(`[OK] Created hook file: ${hookFilePath}`);
|
|
469
988
|
}
|
|
989
|
+
|
|
990
|
+
// ... rest of the file remains unchanged
|
|
470
991
|
}
|
|
471
992
|
|
|
472
993
|
module.exports = StigmergyInstaller;
|