stigmergy 1.2.13 → 1.3.2-beta.0

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.
Files changed (48) hide show
  1. package/README.md +39 -3
  2. package/STIGMERGY.md +3 -0
  3. package/config/enhanced-cli-config.json +438 -0
  4. package/docs/CLI_TOOLS_AGENT_SKILL_ANALYSIS.md +463 -0
  5. package/docs/ENHANCED_CLI_AGENT_SKILL_CONFIG.md +285 -0
  6. package/docs/INSTALLER_ARCHITECTURE.md +257 -0
  7. package/docs/SUDO_PROBLEM_AND_SOLUTION.md +529 -0
  8. package/package.json +14 -5
  9. package/scripts/analyze-router.js +168 -0
  10. package/scripts/test-runner.js +344 -0
  11. package/src/cli/commands/autoinstall.js +158 -0
  12. package/src/cli/commands/errors.js +190 -0
  13. package/src/cli/commands/install.js +142 -0
  14. package/src/cli/commands/permissions.js +108 -0
  15. package/src/cli/commands/project.js +449 -0
  16. package/src/cli/commands/resume.js +136 -0
  17. package/src/cli/commands/scan.js +97 -0
  18. package/src/cli/commands/skills.js +158 -0
  19. package/src/cli/commands/status.js +106 -0
  20. package/src/cli/commands/system.js +301 -0
  21. package/src/cli/router-beta.js +477 -0
  22. package/src/cli/utils/environment.js +75 -0
  23. package/src/cli/utils/formatters.js +47 -0
  24. package/src/cli/utils/skills_cache.js +92 -0
  25. package/src/core/cache_cleaner.js +1 -0
  26. package/src/core/cli_adapters.js +345 -0
  27. package/src/core/cli_help_analyzer.js +473 -1
  28. package/src/core/cli_path_detector.js +2 -1
  29. package/src/core/cli_tools.js +107 -0
  30. package/src/core/coordination/nodejs/HookDeploymentManager.js +185 -422
  31. package/src/core/coordination/nodejs/HookDeploymentManager.refactored.js +323 -0
  32. package/src/core/coordination/nodejs/generators/CLIAdapterGenerator.js +363 -0
  33. package/src/core/coordination/nodejs/generators/ResumeSessionGenerator.js +701 -0
  34. package/src/core/coordination/nodejs/generators/SkillsIntegrationGenerator.js +1210 -0
  35. package/src/core/coordination/nodejs/generators/index.js +12 -0
  36. package/src/core/enhanced_cli_installer.js +220 -30
  37. package/src/core/enhanced_cli_parameter_handler.js +395 -0
  38. package/src/core/execution_mode_detector.js +222 -0
  39. package/src/core/installer.js +51 -70
  40. package/src/core/local_skill_scanner.js +732 -0
  41. package/src/core/multilingual/language-pattern-manager.js +1 -1
  42. package/src/core/skills/StigmergySkillManager.js +26 -8
  43. package/src/core/smart_router.js +279 -2
  44. package/src/index.js +10 -4
  45. package/test/cli-integration.test.js +304 -0
  46. package/test/enhanced-cli-agent-skill-test.js +485 -0
  47. package/test/specific-cli-agent-skill-analysis.js +385 -0
  48. package/src/cli/router.js +0 -1783
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Router.js Structure Analysis Tool
5
+ * Analyzes the router.js file to identify modularization opportunities
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ console.log('šŸ” Router.js Structure Analysis');
12
+ console.log('='.repeat(50));
13
+
14
+ const routerPath = path.join(__dirname, '../src/cli/router.js');
15
+
16
+ if (!fs.existsSync(routerPath)) {
17
+ console.error('āŒ router.js file not found');
18
+ process.exit(1);
19
+ }
20
+
21
+ const content = fs.readFileSync(routerPath, 'utf8');
22
+ const lines = content.split('\n');
23
+
24
+ console.log(`šŸ“„ Total Lines: ${lines.length}`);
25
+ console.log(`šŸ“ File Size: ${(fs.statSync(routerPath).size / 1024).toFixed(2)} KB`);
26
+ console.log('');
27
+
28
+ // Analyze imports
29
+ console.log('šŸ“¦ Import Analysis:');
30
+ const importRegex = /const\s+(.+?)\s*=\s*require\(['"](.+?)['"]\)/g;
31
+ const imports = [];
32
+ let match;
33
+
34
+ while ((match = importRegex.exec(content)) !== null) {
35
+ imports.push({
36
+ name: match[1],
37
+ path: match[2],
38
+ line: content.substring(0, match.index).split('\n').length
39
+ });
40
+ }
41
+
42
+ console.log(`Found ${imports.length} imports:`);
43
+ imports.forEach(imp => {
44
+ console.log(` šŸ“‹ ${imp.name} <- ${imp.path} (line ${imp.line})`);
45
+ });
46
+
47
+ // Analyze functions
48
+ console.log('\nšŸ”§ Function Analysis:');
49
+ const functionRegex = /(?:function\s+(\w+)|const\s+(\w+)\s*=\s*(?:async\s+)?(?:function|\([^)]*\)\s*=>))/g;
50
+ const functions = [];
51
+ let funcMatch;
52
+
53
+ while ((funcMatch = functionRegex.exec(content)) !== null) {
54
+ const funcName = funcMatch[1] || funcMatch[2];
55
+ const funcStart = content.substring(0, funcMatch.index).split('\n').length;
56
+ functions.push({
57
+ name: funcName,
58
+ line: funcStart,
59
+ isAsync: content.includes('async') && content.substring(funcMatch.index - 50, funcMatch.index).includes('async')
60
+ });
61
+ }
62
+
63
+ console.log(`Found ${functions.length} functions:`);
64
+ functions.forEach(func => {
65
+ console.log(` āš™ļø ${func.name}${func.isAsync ? ' (async)' : ''} (line ${func.line})`);
66
+ });
67
+
68
+ // Analyze main sections
69
+ console.log('\nšŸ“‚ Section Analysis:');
70
+
71
+ // Look for main sections
72
+ const sections = [
73
+ { name: 'Import Section', pattern: /const.*require/ },
74
+ { name: 'Setup Section', pattern: /setupGlobalErrorHandlers|program\.version/ },
75
+ { name: 'Command Definitions', pattern: /program\.command/ },
76
+ { name: 'CLI Tools Routing', pattern: /SmartRouter|routeToCLI/ },
77
+ { name: 'Error Handling', pattern: /errorHandler|catch.*error/ },
78
+ { name: 'Helper Functions', pattern: /function formatBytes|function getWorkingDirectory/ },
79
+ { name: 'Main Execution', pattern: /async function main|if \(require\.main/ }
80
+ ];
81
+
82
+ sections.forEach(section => {
83
+ const sectionMatch = content.match(section.pattern);
84
+ if (sectionMatch) {
85
+ const lineNum = content.substring(0, sectionMatch.index).split('\n').length;
86
+ console.log(` šŸ“‘ ${section.name} (around line ${lineNum})`);
87
+ }
88
+ });
89
+
90
+ // Look for command definitions
91
+ console.log('\nšŸŽÆ Command Definitions:');
92
+ const commandRegex = /program\.command\(['"]([^'"]+)['"]\)/g;
93
+ const commands = [];
94
+ let cmdMatch;
95
+
96
+ while ((cmdMatch = commandRegex.exec(content)) !== null) {
97
+ commands.push(cmdMatch[1]);
98
+ }
99
+
100
+ console.log(`Found ${commands.length} CLI commands:`);
101
+ commands.forEach(cmd => {
102
+ console.log(` šŸ’» ${cmd}`);
103
+ });
104
+
105
+ // Look for CLI tools routing
106
+ console.log('\nšŸ›£ļø CLI Tools Routing:');
107
+ const toolRegex = /(?:case|if).*['"]([^'"]+)['"].*?:/g;
108
+ const tools = [];
109
+ let toolMatch;
110
+
111
+ while ((toolMatch = toolRegex.exec(content)) !== null) {
112
+ const tool = toolMatch[1];
113
+ if (!tools.includes(tool) && ['claude', 'gemini', 'qwen', 'codebuddy', 'codex', 'iflow', 'qodercli', 'copilot'].includes(tool)) {
114
+ tools.push(tool);
115
+ }
116
+ }
117
+
118
+ console.log(`Found routing for ${tools.length} CLI tools:`);
119
+ tools.forEach(tool => {
120
+ console.log(` šŸ”— ${tool}`);
121
+ });
122
+
123
+ // Suggest modularization strategy
124
+ console.log('\nšŸ’” Modularization Suggestions:');
125
+ console.log('');
126
+
127
+ console.log('šŸ—ļø Recommended Module Structure:');
128
+ console.log(' src/cli/');
129
+ console.log(' ā”œā”€ā”€ router.js (main entry, ~200 lines)');
130
+ console.log(' ā”œā”€ā”€ commands/');
131
+ console.log(' │ ā”œā”€ā”€ index.js (command registry)');
132
+ console.log(' │ ā”œā”€ā”€ install.js (install commands)');
133
+ console.log(' │ ā”œā”€ā”€ status.js (status commands)');
134
+ console.log(' │ ā”œā”€ā”€ scan.js (scan commands)');
135
+ console.log(' │ └── deploy.js (deploy commands)');
136
+ console.log(' ā”œā”€ā”€ routing/');
137
+ console.log(' │ ā”œā”€ā”€ index.js (routing coordinator)');
138
+ console.log(' │ ā”œā”€ā”€ cli-router.js (CLI tools routing)');
139
+ console.log(' │ └── command-router.js (command routing)');
140
+ console.log(' ā”œā”€ā”€ utils/');
141
+ console.log(' │ ā”œā”€ā”€ formatters.js (format helpers)');
142
+ console.log(' │ ā”œā”€ā”€ validators.js (input validation)');
143
+ console.log(' │ └── executors.js (command execution)');
144
+ console.log(' └── config/');
145
+ console.log(' ā”œā”€ā”€ program-setup.js (commander setup)');
146
+ console.log(' └── environment.js (environment setup)');
147
+
148
+ console.log('');
149
+ console.log('šŸŽÆ TDD Migration Strategy:');
150
+ console.log(' 1. āœ… Create comprehensive test suite for current router.js');
151
+ console.log(' 2. šŸ”„ Extract helper functions first (low risk)');
152
+ console.log(' 3. šŸ”„ Extract command definitions (medium risk)');
153
+ console.log(' 4. šŸ”„ Extract CLI routing logic (medium risk)');
154
+ console.log(' 5. šŸ”„ Create modular command handlers (high risk)');
155
+ console.log(' 6. āœ… Maintain backward compatibility');
156
+ console.log(' 7. āœ… Create rollback mechanism');
157
+
158
+ console.log('');
159
+ console.log('āš ļø Risk Assessment:');
160
+ console.log(' 🟢 LOW: Helper functions (formatBytes, etc.)');
161
+ console.log(' 🟔 MEDIUM: Command definitions and routing');
162
+ console.log(' šŸ”“ HIGH: Core CLI execution logic');
163
+ console.log('');
164
+ console.log('šŸ›”ļø Safety Measures:');
165
+ console.log(' āœ… Backup original file');
166
+ console.log(' āœ… Create feature branch');
167
+ console.log(' āœ… Test before and after each extraction');
168
+ console.log(' āœ… Gradual migration with rollback points');
@@ -0,0 +1,344 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Enhanced Test Runner for HookDeploymentManager Test Suite
5
+ * Provides comprehensive testing with reporting and analysis
6
+ */
7
+
8
+ const { execSync } = require('child_process');
9
+ const fs = require('fs-extra');
10
+ const path = require('path');
11
+ const chalk = require('chalk');
12
+
13
+ class TestRunner {
14
+ constructor() {
15
+ this.testResults = {
16
+ unit: { passed: 0, failed: 0, total: 0, duration: 0 },
17
+ integration: { passed: 0, failed: 0, total: 0, duration: 0 },
18
+ regression: { passed: 0, failed: 0, total: 0, duration: 0 },
19
+ performance: { passed: 0, failed: 0, total: 0, duration: 0 }
20
+ };
21
+ this.startTime = Date.now();
22
+ }
23
+
24
+ log(message, type = 'info') {
25
+ const colors = {
26
+ info: chalk.blue,
27
+ success: chalk.green,
28
+ warning: chalk.yellow,
29
+ error: chalk.red
30
+ };
31
+
32
+ console.log(colors[type](`[${new Date().toLocaleTimeString()}] ${message}`));
33
+ }
34
+
35
+ async runCommand(command, description) {
36
+ this.log(`Running: ${description}...`);
37
+ const startTime = Date.now();
38
+
39
+ try {
40
+ const output = execSync(command, {
41
+ encoding: 'utf8',
42
+ stdio: 'pipe'
43
+ });
44
+
45
+ const duration = Date.now() - startTime;
46
+ this.log(`${description} completed successfully (${duration}ms)`, 'success');
47
+
48
+ return {
49
+ success: true,
50
+ output,
51
+ duration
52
+ };
53
+ } catch (error) {
54
+ const duration = Date.now() - startTime;
55
+ this.log(`${description} failed after ${duration}ms`, 'error');
56
+
57
+ return {
58
+ success: false,
59
+ error: error.message,
60
+ output: error.stdout,
61
+ duration
62
+ };
63
+ }
64
+ }
65
+
66
+ parseJestOutput(output) {
67
+ const lines = output.split('\n');
68
+ const results = {
69
+ passed: 0,
70
+ failed: 0,
71
+ total: 0,
72
+ suites: 0
73
+ };
74
+
75
+ lines.forEach(line => {
76
+ const match = line.match(/Tests:\s+(\d+)\s+passed,\s+(\d+)\s+failed/);
77
+ if (match) {
78
+ results.passed = parseInt(match[1]);
79
+ results.failed = parseInt(match[2]);
80
+ results.total = results.passed + results.failed;
81
+ }
82
+
83
+ const suiteMatch = line.match(/Test Suites:\s+(\d+)\s+passed,\s+(\d+)\s+failed/);
84
+ if (suiteMatch) {
85
+ results.suites = parseInt(suiteMatch[1]) + parseInt(suiteMatch[2]);
86
+ }
87
+ });
88
+
89
+ return results;
90
+ }
91
+
92
+ async runUnitTests() {
93
+ this.log('\n🧪 Running Unit Tests', 'info');
94
+
95
+ const result = await this.runCommand(
96
+ 'npm run test:unit -- --verbose --json',
97
+ 'Unit Tests'
98
+ );
99
+
100
+ if (result.success) {
101
+ const jestOutput = result.output;
102
+ try {
103
+ const jsonMatch = jestOutput.match(/\{[\s\S]*\}/);
104
+ if (jsonMatch) {
105
+ const jestResults = JSON.parse(jsonMatch[0]);
106
+ this.testResults.unit.passed = jestResults.numPassedTests || 0;
107
+ this.testResults.unit.failed = jestResults.numFailedTests || 0;
108
+ this.testResults.unit.total = jestResults.numTotalTests || 0;
109
+ this.testResults.unit.duration = jestResults.testResults?.reduce(
110
+ (total, test) => total + (test.duration || 0), 0
111
+ ) || 0;
112
+ } else {
113
+ const parsed = this.parseJestOutput(result.output);
114
+ Object.assign(this.testResults.unit, parsed);
115
+ }
116
+ } catch (parseError) {
117
+ this.log('Failed to parse Jest output, using fallback parsing', 'warning');
118
+ const parsed = this.parseJestOutput(result.output);
119
+ Object.assign(this.testResults.unit, parsed);
120
+ }
121
+ } else {
122
+ this.testResults.unit.failed = 1;
123
+ }
124
+
125
+ return result.success;
126
+ }
127
+
128
+ async runIntegrationTests() {
129
+ this.log('\nšŸ”— Running Integration Tests', 'info');
130
+
131
+ const result = await this.runCommand(
132
+ 'npm run test:integration -- --verbose',
133
+ 'Integration Tests'
134
+ );
135
+
136
+ if (result.success) {
137
+ const parsed = this.parseJestOutput(result.output);
138
+ Object.assign(this.testResults.integration, parsed);
139
+ this.testResults.integration.duration = result.duration;
140
+ } else {
141
+ this.testResults.integration.failed = 1;
142
+ }
143
+
144
+ return result.success;
145
+ }
146
+
147
+ async runRegressionTests() {
148
+ this.log('\nšŸ”„ Running Regression Tests', 'info');
149
+
150
+ const result = await this.runCommand(
151
+ 'npm run test:regression -- --verbose',
152
+ 'Regression Tests'
153
+ );
154
+
155
+ if (result.success) {
156
+ const parsed = this.parseJestOutput(result.output);
157
+ Object.assign(this.testResults.regression, parsed);
158
+ this.testResults.regression.duration = result.duration;
159
+ } else {
160
+ this.testResults.regression.failed = 1;
161
+ }
162
+
163
+ return result.success;
164
+ }
165
+
166
+ async runPerformanceTests() {
167
+ this.log('\n⚔ Running Performance Tests', 'info');
168
+
169
+ const result = await this.runCommand(
170
+ 'npm run test:performance -- --verbose',
171
+ 'Performance Tests'
172
+ );
173
+
174
+ if (result.success) {
175
+ const parsed = this.parseJestOutput(result.output);
176
+ Object.assign(this.testResults.performance, parsed);
177
+ this.testResults.performance.duration = result.duration;
178
+ } else {
179
+ this.testResults.performance.failed = 1;
180
+ }
181
+
182
+ return result.success;
183
+ }
184
+
185
+ generateReport() {
186
+ const totalDuration = Date.now() - this.startTime;
187
+ const allTests = Object.values(this.testResults);
188
+ const totalPassed = allTests.reduce((sum, cat) => sum + cat.passed, 0);
189
+ const totalFailed = allTests.reduce((sum, cat) => sum + cat.failed, 0);
190
+ const totalTests = totalPassed + totalFailed;
191
+
192
+ console.log('\n' + '='.repeat(80));
193
+ console.log(chalk.bold.blue('šŸ“Š TEST SUITE REPORT'));
194
+ console.log('='.repeat(80));
195
+
196
+ // Summary
197
+ console.log(chalk.bold('\nšŸ“‹ SUMMARY:'));
198
+ console.log(`Total Duration: ${(totalDuration / 1000).toFixed(2)}s`);
199
+ console.log(`Total Tests: ${totalTests}`);
200
+ console.log(`Passed: ${chalk.green(totalPassed)}`);
201
+ console.log(`Failed: ${chalk.red(totalFailed)}`);
202
+ console.log(`Success Rate: ${((totalPassed / totalTests) * 100).toFixed(1)}%`);
203
+
204
+ // Category breakdown
205
+ console.log(chalk.bold('\nšŸ“‚ TEST CATEGORIES:'));
206
+
207
+ const categories = [
208
+ { name: 'Unit Tests', key: 'unit', icon: '🧪' },
209
+ { name: 'Integration Tests', key: 'integration', icon: 'šŸ”—' },
210
+ { name: 'Regression Tests', key: 'regression', icon: 'šŸ”„' },
211
+ { name: 'Performance Tests', key: 'performance', icon: '⚔' }
212
+ ];
213
+
214
+ categories.forEach(category => {
215
+ const results = this.testResults[category.key];
216
+ const success = results.failed === 0;
217
+ const status = success ? 'āœ…' : 'āŒ';
218
+ const color = success ? chalk.green : chalk.red;
219
+
220
+ console.log(`\n${category.icon} ${category.name}: ${status}`);
221
+ console.log(` Tests: ${results.total}`);
222
+ console.log(` Passed: ${color(results.passed)}`);
223
+ console.log(` Failed: ${results.failed > 0 ? chalk.red(results.failed) : results.failed}`);
224
+ console.log(` Duration: ${(results.duration / 1000).toFixed(2)}s`);
225
+ });
226
+
227
+ // Coverage information if available
228
+ const coveragePath = path.join(process.cwd(), 'coverage', 'coverage-summary.json');
229
+ if (fs.existsSync(coveragePath)) {
230
+ try {
231
+ const coverage = fs.readJsonSync(coveragePath);
232
+ console.log(chalk.bold('\nšŸ“ˆ COVERAGE SUMMARY:'));
233
+ console.log(`Lines: ${coverage.total.lines.pct}%`);
234
+ console.log(`Functions: ${coverage.total.functions.pct}%`);
235
+ console.log(`Branches: ${coverage.total.branches.pct}%`);
236
+ console.log(`Statements: ${coverage.total.statements.pct}%`);
237
+ } catch (error) {
238
+ this.log('Could not read coverage information', 'warning');
239
+ }
240
+ }
241
+
242
+ console.log('\n' + '='.repeat(80));
243
+
244
+ // Exit with appropriate code
245
+ const exitCode = totalFailed > 0 ? 1 : 0;
246
+
247
+ if (exitCode === 0) {
248
+ console.log(chalk.green.bold('\nšŸŽ‰ ALL TESTS PASSED!'));
249
+ } else {
250
+ console.log(chalk.red.bold(`\nāŒ ${totalFailed} TEST(S) FAILED!`));
251
+ }
252
+
253
+ return exitCode;
254
+ }
255
+
256
+ async runTestSuite() {
257
+ console.log(chalk.bold.blue('šŸš€ Starting HookDeploymentManager Test Suite'));
258
+ console.log(`Node.js version: ${process.version}`);
259
+ console.log(`Platform: ${process.platform}`);
260
+ console.log(`Working directory: ${process.cwd()}`);
261
+
262
+ try {
263
+ // Clean previous results
264
+ await fs.remove('coverage');
265
+ await fs.remove('test-results');
266
+
267
+ const unitSuccess = await this.runUnitTests();
268
+ const integrationSuccess = await this.runIntegrationTests();
269
+ const regressionSuccess = await this.runRegressionTests();
270
+
271
+ // Run performance tests only if others pass (to save time)
272
+ let performanceSuccess = true;
273
+ if (unitSuccess && integrationSuccess && regressionSuccess) {
274
+ performanceSuccess = await this.runPerformanceTests();
275
+ } else {
276
+ this.log('Skipping performance tests due to failures in other categories', 'warning');
277
+ }
278
+
279
+ // Generate report and exit
280
+ const exitCode = this.generateReport();
281
+ process.exit(exitCode);
282
+
283
+ } catch (error) {
284
+ this.log(`Test runner error: ${error.message}`, 'error');
285
+ console.error(error.stack);
286
+ process.exit(1);
287
+ }
288
+ }
289
+ }
290
+
291
+ // CLI interface
292
+ if (require.main === module) {
293
+ const runner = new TestRunner();
294
+
295
+ // Parse command line arguments
296
+ const args = process.argv.slice(2);
297
+
298
+ if (args.includes('--help') || args.includes('-h')) {
299
+ console.log(`
300
+ HookDeploymentManager Test Runner
301
+
302
+ Usage: node scripts/test-runner.js [options]
303
+
304
+ Options:
305
+ --help, -h Show this help message
306
+ --unit-only Run only unit tests
307
+ --integration-only Run only integration tests
308
+ --regression-only Run only regression tests
309
+ --performance-only Run only performance tests
310
+ --no-performance Skip performance tests
311
+ --verbose Enable verbose output
312
+
313
+ Examples:
314
+ node scripts/test-runner.js # Run all tests
315
+ node scripts/test-runner.js --unit-only # Run only unit tests
316
+ node scripts/test-runner.js --no-performance # Skip performance tests
317
+ `);
318
+ process.exit(0);
319
+ }
320
+
321
+ // Handle specific test category requests
322
+ if (args.includes('--unit-only')) {
323
+ runner.runUnitTests().then(success => {
324
+ process.exit(success ? 0 : 1);
325
+ });
326
+ } else if (args.includes('--integration-only')) {
327
+ runner.runIntegrationTests().then(success => {
328
+ process.exit(success ? 0 : 1);
329
+ });
330
+ } else if (args.includes('--regression-only')) {
331
+ runner.runRegressionTests().then(success => {
332
+ process.exit(success ? 0 : 1);
333
+ });
334
+ } else if (args.includes('--performance-only')) {
335
+ runner.runPerformanceTests().then(success => {
336
+ process.exit(success ? 0 : 1);
337
+ });
338
+ } else {
339
+ // Run all tests
340
+ runner.runTestSuite();
341
+ }
342
+ }
343
+
344
+ module.exports = TestRunner;
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Auto-install Command
3
+ * Modular implementation for automated CLI tool installation (npm postinstall)
4
+ */
5
+
6
+ const chalk = require('chalk');
7
+ const { handleInstallCommand } = require('./install');
8
+
9
+ /**
10
+ * Handle auto-install command - Automated installation for npm postinstall
11
+ * @param {Object} options - Command options
12
+ */
13
+ async function handleAutoInstallCommand(options = {}) {
14
+ try {
15
+ // Detect npm environment for better output visibility
16
+ const isNpmPostinstall = process.env.npm_lifecycle_event === 'postinstall';
17
+
18
+ // Use stderr for critical messages in npm environment (more likely to be shown)
19
+ const criticalLog = isNpmPostinstall ? console.error : console.log;
20
+
21
+ criticalLog(chalk.cyan('šŸš€ STIGMERGY CLI AUTO-INSTALL STARTING'));
22
+ criticalLog('='.repeat(60));
23
+ criticalLog('Installing cross-CLI integration and scanning for AI tools...');
24
+ criticalLog('='.repeat(60));
25
+
26
+ console.log(chalk.blue('[AUTO-INSTALL] Stigmergy CLI automated setup'));
27
+ console.log('='.repeat(60));
28
+
29
+ // Check if we're in npm postinstall environment
30
+ if (isNpmPostinstall) {
31
+ console.log(chalk.yellow('šŸ“¦ Detected npm postinstall environment'));
32
+ console.log(chalk.gray('Setting up CLI integrations automatically...'));
33
+ }
34
+
35
+ // Auto-install options - non-interactive mode
36
+ const autoInstallOptions = {
37
+ cli: 'all', // Install all available CLI tools
38
+ verbose: options.verbose || process.env.DEBUG === 'true',
39
+ force: options.force || false,
40
+ nonInteractive: true, // Critical for automated installation
41
+ autoDetect: true, // Auto-detect available tools
42
+ skipPermissionCheck: process.env.STIGMERGY_SKIP_PERMISSION_CHECK === 'true'
43
+ };
44
+
45
+ console.log(chalk.blue('šŸ” Scanning for available CLI tools...'));
46
+
47
+ // Run the installation
48
+ console.log(chalk.blue('šŸ› ļø Starting automated CLI tool installation...'));
49
+
50
+ const installResult = await handleInstallCommand(autoInstallOptions);
51
+
52
+ if (installResult.success) {
53
+ console.log(chalk.green('\nāœ… Auto-install completed successfully!'));
54
+
55
+ if (isNpmPostinstall) {
56
+ criticalLog(chalk.green('āœ… STIGMERGY CLI SETUP COMPLETE'));
57
+ criticalLog(chalk.gray('You can now use: stigmergy <tool> <command>'));
58
+ }
59
+
60
+ // Show summary of what was installed
61
+ if (installResult.installed && installResult.installed.length > 0) {
62
+ console.log(chalk.blue('\nšŸ“‹ Installation Summary:'));
63
+ installResult.installed.forEach(tool => {
64
+ console.log(` ${chalk.green('āœ…')} ${tool}`);
65
+ });
66
+ }
67
+
68
+ if (installResult.failed && installResult.failed.length > 0) {
69
+ console.log(chalk.blue('\nāŒ Failed Tools:'));
70
+ installResult.failed.forEach(tool => {
71
+ console.log(` ${chalk.red('āŒ')} ${tool} (installation failed)`);
72
+ });
73
+ }
74
+
75
+ if (installResult.existing && installResult.existing.length > 0) {
76
+ console.log(chalk.blue('\nšŸ”§ Already Available:'));
77
+ installResult.existing.forEach(tool => {
78
+ console.log(` ${chalk.yellow('āš ļø')} ${tool} (already installed)`);
79
+ });
80
+ }
81
+
82
+ } else {
83
+ console.log(chalk.red('\nāŒ Auto-install encountered issues'));
84
+
85
+ if (isNpmPostinstall) {
86
+ criticalLog(chalk.red('āŒ STIGMERGY CLI SETUP INCOMPLETE'));
87
+ criticalLog(chalk.yellow('Run: stigmergy install to complete setup manually'));
88
+ }
89
+
90
+ if (installResult.error) {
91
+ console.log(chalk.red(`Error: ${installResult.error}`));
92
+ }
93
+ }
94
+
95
+ // Setup verification
96
+ console.log(chalk.blue('\nšŸ” Verifying installation...'));
97
+
98
+ // Quick verification of core functionality
99
+ const verificationChecks = [
100
+ { name: 'Core modules accessible', check: () => require('../utils/formatters') && require('../utils/environment') },
101
+ { name: 'Error handler available', check: () => require('../../core/error_handler') },
102
+ { name: 'Smart router available', check: () => require('../../core/smart_router') }
103
+ ];
104
+
105
+ let verificationPassed = 0;
106
+ for (const check of verificationChecks) {
107
+ try {
108
+ check.check();
109
+ console.log(` ${chalk.green('āœ…')} ${check.name}`);
110
+ verificationPassed++;
111
+ } catch (error) {
112
+ console.log(` ${chalk.red('āŒ')} ${check.name}`);
113
+ }
114
+ }
115
+
116
+ if (verificationPassed === verificationChecks.length) {
117
+ console.log(chalk.green('\nāœ… All verification checks passed!'));
118
+
119
+ if (isNpmPostinstall) {
120
+ criticalLog(chalk.green('šŸŽ‰ STIGMERGY CLI IS READY TO USE!'));
121
+ }
122
+ } else {
123
+ console.log(chalk.yellow('\nāš ļø Some verification checks failed'));
124
+ console.log(chalk.yellow('Run: stigmergy diagnostic for full system check'));
125
+ }
126
+
127
+ // Final summary for npm environment
128
+ if (isNpmPostinstall) {
129
+ criticalLog('='.repeat(60));
130
+ criticalLog(chalk.cyan('šŸŽÆ STIGMERGY CLI AUTO-INSTALL FINISHED'));
131
+ criticalLog('='.repeat(60));
132
+ }
133
+
134
+ return {
135
+ success: installResult.success,
136
+ verificationPassed,
137
+ installed: installResult.installed || [],
138
+ existing: installResult.existing || []
139
+ };
140
+
141
+ } catch (error) {
142
+ const isNpmPostinstall = process.env.npm_lifecycle_event === 'postinstall';
143
+ const criticalLog = isNpmPostinstall ? console.error : console.log;
144
+
145
+ console.error(chalk.red('[ERROR] Auto-install failed:'), error.message);
146
+
147
+ if (isNpmPostinstall) {
148
+ criticalLog(chalk.red('šŸ’„ AUTO-INSTALL FAILED'));
149
+ criticalLog(chalk.yellow('Run: stigmergy install --verbose for detailed installation'));
150
+ }
151
+
152
+ return { success: false, error: error.message };
153
+ }
154
+ }
155
+
156
+ module.exports = {
157
+ handleAutoInstallCommand
158
+ };