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.
Files changed (125) hide show
  1. package/LICENSE +18 -18
  2. package/README.md +28 -223
  3. package/STIGMERGY.md +61 -61
  4. package/docs/PROJECT_CONSTITUTION.md +433 -433
  5. package/docs/PROJECT_STRUCTURE_CURRENT.md +80 -80
  6. package/examples/calculator-example.js +72 -72
  7. package/examples/cline_usage_examples.md +364 -364
  8. package/examples/encryption-example.js +67 -67
  9. package/examples/json-parser-example.js +120 -120
  10. package/examples/json-validation-example.js +64 -64
  11. package/examples/rest-client-example.js +52 -52
  12. package/examples/rest_client_example.js +54 -54
  13. package/package.json +36 -15
  14. package/scripts/build.js +74 -74
  15. package/scripts/post-deployment-config.js +296 -296
  16. package/scripts/preinstall-check.js +173 -173
  17. package/scripts/publish.js +58 -268
  18. package/scripts/run-layered-tests.js +247 -0
  19. package/scripts/safe-install.js +139 -139
  20. package/scripts/simple-publish.js +57 -59
  21. package/src/adapters/claude/install_claude_integration.js +292 -0
  22. package/src/adapters/codebuddy/install_codebuddy_integration.js +349 -0
  23. package/src/adapters/codex/install_codex_integration.js +395 -0
  24. package/src/adapters/copilot/install_copilot_integration.js +716 -0
  25. package/src/adapters/gemini/install_gemini_integration.js +304 -0
  26. package/src/adapters/iflow/install_iflow_integration.js +304 -0
  27. package/src/adapters/qoder/install_qoder_integration.js +1090 -0
  28. package/src/adapters/qwen/install_qwen_integration.js +285 -0
  29. package/src/auth.js +173 -173
  30. package/src/auth_command.js +208 -208
  31. package/src/calculator.js +313 -313
  32. package/src/cli/router.js +417 -38
  33. package/src/core/cache_cleaner.js +767 -744
  34. package/src/core/cli_help_analyzer.js +680 -674
  35. package/src/core/cli_parameter_handler.js +132 -127
  36. package/src/core/cli_tools.js +89 -89
  37. package/src/core/coordination/index.js +16 -16
  38. package/src/core/coordination/nodejs/AdapterManager.js +102 -89
  39. package/src/core/coordination/nodejs/CLCommunication.js +132 -124
  40. package/src/core/coordination/nodejs/CLIIntegrationManager.js +272 -236
  41. package/src/core/coordination/nodejs/HealthChecker.js +76 -77
  42. package/src/core/coordination/nodejs/HookDeploymentManager.js +263 -190
  43. package/src/core/coordination/nodejs/StatisticsCollector.js +71 -71
  44. package/src/core/coordination/nodejs/index.js +90 -72
  45. package/src/core/coordination/nodejs/utils/Logger.js +29 -29
  46. package/src/core/enhanced_installer.js +479 -456
  47. package/src/core/enhanced_uninstaller.js +638 -618
  48. package/src/core/error_handler.js +406 -406
  49. package/src/core/installer.js +815 -294
  50. package/src/core/memory_manager.js +83 -83
  51. package/src/core/rest_client.js +160 -160
  52. package/src/core/smart_router.js +249 -146
  53. package/src/core/upgrade_manager.js +76 -59
  54. package/src/data_encryption.js +143 -143
  55. package/src/data_structures.js +440 -440
  56. package/src/deploy.js +55 -55
  57. package/src/index.js +30 -30
  58. package/src/test/cli-availability-checker.js +194 -0
  59. package/src/test/test-environment.js +289 -0
  60. package/src/utils/helpers.js +35 -35
  61. package/src/utils.js +921 -915
  62. package/src/weatherProcessor.js +228 -228
  63. package/test/cache-cleaner-implemented.test.js +0 -328
  64. package/test/cache-cleaner.test.js +0 -390
  65. package/test/calculator.test.js +0 -215
  66. package/test/collision-test.js +0 -26
  67. package/test/comprehensive-enhanced-features.test.js +0 -252
  68. package/test/comprehensive-execution-test.js +0 -428
  69. package/test/conflict-prevention-test.js +0 -95
  70. package/test/cross-cli-detection-test.js +0 -33
  71. package/test/csv-processing-test.js +0 -36
  72. package/test/deploy-hooks-test.js +0 -250
  73. package/test/e2e/claude-cli-test.js +0 -128
  74. package/test/e2e/collaboration-test.js +0 -75
  75. package/test/e2e/comprehensive-test.js +0 -431
  76. package/test/e2e/error-handling-test.js +0 -90
  77. package/test/e2e/individual-tool-test.js +0 -143
  78. package/test/e2e/other-cli-test.js +0 -130
  79. package/test/e2e/qoder-cli-test.js +0 -128
  80. package/test/e2e/run-e2e-tests.js +0 -73
  81. package/test/e2e/test-data.js +0 -88
  82. package/test/e2e/test-utils.js +0 -222
  83. package/test/encryption-simple-test.js +0 -110
  84. package/test/encryption.test.js +0 -129
  85. package/test/enhanced-main-alignment.test.js +0 -298
  86. package/test/enhanced-uninstaller-implemented.test.js +0 -271
  87. package/test/enhanced-uninstaller.test.js +0 -284
  88. package/test/error-handling-test.js +0 -341
  89. package/test/fibonacci.test.js +0 -178
  90. package/test/final-deploy-test.js +0 -221
  91. package/test/final-install-test.js +0 -226
  92. package/test/hash-table-demo.js +0 -33
  93. package/test/hash-table-test.js +0 -26
  94. package/test/hash_table_test.js +0 -114
  95. package/test/hook-system-integration-test.js +0 -307
  96. package/test/iflow-integration-test.js +0 -292
  97. package/test/improved-install-test.js +0 -362
  98. package/test/install-command-test.js +0 -370
  99. package/test/json-parser-test.js +0 -161
  100. package/test/json-validation-test.js +0 -164
  101. package/test/natural-language-skills-test.js +0 -320
  102. package/test/nl-integration-test.js +0 -179
  103. package/test/parameter-parsing-test.js +0 -143
  104. package/test/plugin-deployment-test.js +0 -316
  105. package/test/postinstall-test.js +0 -269
  106. package/test/python-plugins-test.js +0 -259
  107. package/test/real-test.js +0 -435
  108. package/test/remaining-adapters-test.js +0 -256
  109. package/test/rest-client-test.js +0 -56
  110. package/test/rest_client.test.js +0 -85
  111. package/test/safe-installation-cleaner.test.js +0 -343
  112. package/test/simple-iflow-hook-test.js +0 -137
  113. package/test/stigmergy-upgrade-test.js +0 -243
  114. package/test/system-compatibility-test.js +0 -467
  115. package/test/tdd-deploy-fix-test.js +0 -324
  116. package/test/tdd-fixes-test.js +0 -211
  117. package/test/third-party-skills-test.js +0 -321
  118. package/test/tool-selection-integration-test.js +0 -158
  119. package/test/unit/calculator-full.test.js +0 -191
  120. package/test/unit/calculator-simple.test.js +0 -96
  121. package/test/unit/calculator.test.js +0 -97
  122. package/test/unit/cli-scanner.test.js +0 -291
  123. package/test/unit/cli_parameter_handler.test.js +0 -116
  124. package/test/unit/cross-cli-executor.test.js +0 -399
  125. package/test/weather-processor.test.js +0 -104
@@ -1,618 +1,638 @@
1
- /**
2
- * Enhanced Stigmergy Uninstaller
3
- *
4
- * Comprehensive uninstallation with complete cleanup of all Stigmergy-related files,
5
- * configurations, caches, and integrations across all supported CLI tools.
6
- */
7
-
8
- const fs = require('fs');
9
- const path = require('path');
10
- const os = require('os');
11
- const { spawnSync } = require('child_process');
12
-
13
- class EnhancedUninstaller {
14
- constructor(options = {}) {
15
- this.options = {
16
- dryRun: options.dryRun || false,
17
- force: options.force || false,
18
- verbose: options.verbose || false,
19
- preserveUserConfigs: options.preserveUserConfigs || false,
20
- ...options
21
- };
22
-
23
- this.homeDir = os.homedir();
24
- this.stigmergyDir = path.join(this.homeDir, '.stigmergy');
25
- this.stigmergyTestDir = path.join(this.homeDir, '.stigmergy-test');
26
-
27
- this.supportedCLIs = [
28
- 'claude', 'gemini', 'qwen', 'iflow', 'qodercli',
29
- 'codebuddy', 'codex', 'copilot', 'qwencode'
30
- ];
31
-
32
- this.results = {
33
- filesRemoved: 0,
34
- directoriesRemoved: 0,
35
- errors: [],
36
- skipped: []
37
- };
38
- }
39
-
40
- /**
41
- * Perform complete uninstallation
42
- */
43
- async completeUninstall() {
44
- console.log('šŸ—‘ļø Starting Enhanced Stigmergy Uninstall...\n');
45
-
46
- if (this.options.dryRun) {
47
- console.log('šŸ” DRY RUN MODE - No files will be deleted\n');
48
- }
49
-
50
- try {
51
- // 1. Clean Stigmergy main directory
52
- await this.cleanStigmergyDirectory();
53
-
54
- // 2. Clean test directory
55
- await this.cleanTestDirectory();
56
-
57
- // 3. Clean CLI configurations
58
- await this.cleanCLIConfigurations();
59
-
60
- // 4. Clean NPX cache
61
- await this.cleanNPXCache();
62
-
63
- // 5. Clean temporary files
64
- await this.cleanTemporaryFiles();
65
-
66
- // 6. Uninstall global packages (if requested)
67
- if (this.options.uninstallGlobal) {
68
- await this.uninstallGlobalPackages();
69
- }
70
-
71
- // 7. Print summary
72
- this.printSummary();
73
-
74
- return this.results;
75
-
76
- } catch (error) {
77
- console.error('āŒ Uninstall failed:', error.message);
78
- this.results.errors.push(error.message);
79
- return this.results;
80
- }
81
- }
82
-
83
- /**
84
- * Create uninstall plan without executing
85
- */
86
- async createUninstallPlan() {
87
- console.log('šŸ“‹ Creating Uninstall Plan...\n');
88
-
89
- const plan = {
90
- directories: [],
91
- files: [],
92
- globalPackages: [],
93
- cliConfigurations: [],
94
- estimatedSize: 0
95
- };
96
-
97
- // Scan Stigmergy directory
98
- if (fs.existsSync(this.stigmergyDir)) {
99
- await this.scanDirectory(this.stigmergyDir, plan);
100
- }
101
-
102
- // Scan test directory
103
- if (fs.existsSync(this.stigmergyTestDir)) {
104
- await this.scanDirectory(this.stigmergyTestDir, plan);
105
- }
106
-
107
- // Scan CLI configurations
108
- for (const cli of this.supportedCLIs) {
109
- const cliConfig = path.join(this.homeDir, `.${cli}`);
110
- if (fs.existsSync(cliConfig)) {
111
- const stigmergyFiles = await this.findStigmergyFiles(cliConfig);
112
- plan.cliConfigurations.push({
113
- cli,
114
- files: stigmergyFiles
115
- });
116
- }
117
- }
118
-
119
- // Calculate estimated size
120
- plan.estimatedSize = await this.calculateDirectorySize([
121
- this.stigmergyDir,
122
- this.stigmergyTestDir,
123
- ...plan.cliConfigurations.flatMap(c => c.files)
124
- ]);
125
-
126
- return plan;
127
- }
128
-
129
- /**
130
- * Clean Stigmergy main directory
131
- */
132
- async cleanStigmergyDirectory() {
133
- console.log('šŸ“ Cleaning Stigmergy directory...');
134
-
135
- if (!fs.existsSync(this.stigmergyDir)) {
136
- console.log(' ā„¹ļø Stigmergy directory not found');
137
- return;
138
- }
139
-
140
- const files = await this.getAllFiles(this.stigmergyDir);
141
-
142
- if (this.options.dryRun) {
143
- console.log(` šŸ” Would remove ${files.length} files and directories`);
144
- this.logFiles(files, ' ');
145
- return;
146
- }
147
-
148
- try {
149
- await this.removeDirectory(this.stigmergyDir);
150
- console.log(` āœ… Removed ${files.length} files and directories`);
151
- this.results.filesRemoved += files.length;
152
- this.results.directoriesRemoved++;
153
- } catch (error) {
154
- console.error(` āŒ Failed to remove Stigmergy directory: ${error.message}`);
155
- this.results.errors.push(`Stigmergy directory: ${error.message}`);
156
- }
157
- }
158
-
159
- /**
160
- * Clean test directory
161
- */
162
- async cleanTestDirectory() {
163
- console.log('🧪 Cleaning test directory...');
164
-
165
- if (!fs.existsSync(this.stigmergyTestDir)) {
166
- console.log(' ā„¹ļø Test directory not found');
167
- return;
168
- }
169
-
170
- if (this.options.dryRun) {
171
- console.log(' šŸ” Would remove test directory');
172
- return;
173
- }
174
-
175
- try {
176
- await this.removeDirectory(this.stigmergyTestDir);
177
- console.log(' āœ… Removed test directory');
178
- this.results.directoriesRemoved++;
179
- } catch (error) {
180
- console.error(` āŒ Failed to remove test directory: ${error.message}`);
181
- this.results.errors.push(`Test directory: ${error.message}`);
182
- }
183
- }
184
-
185
- /**
186
- * Clean CLI configurations
187
- */
188
- async cleanCLIConfigurations() {
189
- console.log('āš™ļø Cleaning CLI configurations...');
190
-
191
- let totalCleaned = 0;
192
-
193
- for (const cli of this.supportedCLIs) {
194
- const cliConfig = path.join(this.homeDir, `.${cli}`);
195
-
196
- if (!fs.existsSync(cliConfig)) {
197
- continue;
198
- }
199
-
200
- const stigmergyFiles = await this.findStigmergyFiles(cliConfig);
201
-
202
- if (stigmergyFiles.length === 0) {
203
- continue;
204
- }
205
-
206
- console.log(` šŸ“‚ ${cli}: ${stigmergyFiles.length} Stigmergy files`);
207
-
208
- if (this.options.dryRun) {
209
- this.logFiles(stigmergyFiles, ' šŸ” ');
210
- continue;
211
- }
212
-
213
- try {
214
- for (const file of stigmergyFiles) {
215
- await this.removeFile(file);
216
- totalCleaned++;
217
- }
218
- console.log(` āœ… Cleaned ${stigmergyFiles.length} files`);
219
- } catch (error) {
220
- console.error(` āŒ Failed to clean ${cli}: ${error.message}`);
221
- this.results.errors.push(`${cli} config: ${error.message}`);
222
- }
223
- }
224
-
225
- if (!this.options.dryRun && totalCleaned > 0) {
226
- console.log(` āœ… Cleaned ${totalCleaned} CLI configuration files`);
227
- this.results.filesRemoved += totalCleaned;
228
- }
229
- }
230
-
231
- /**
232
- * Clean NPX cache
233
- */
234
- async cleanNPXCache() {
235
- console.log('šŸ“¦ Cleaning NPX cache...');
236
-
237
- const npxCacheDirs = await this.findNPXCacheDirectories();
238
-
239
- if (npxCacheDirs.length === 0) {
240
- console.log(' ā„¹ļø No Stigmergy entries in NPX cache');
241
- return;
242
- }
243
-
244
- console.log(` šŸ“¦ Found ${npxCacheDirs.length} Stigmergy entries in NPX cache`);
245
-
246
- if (this.options.dryRun) {
247
- this.logFiles(npxCacheDirs, ' šŸ” ');
248
- return;
249
- }
250
-
251
- let removed = 0;
252
- for (const cacheDir of npxCacheDirs) {
253
- try {
254
- await this.removeDirectory(cacheDir);
255
- removed++;
256
- } catch (error) {
257
- console.error(` āŒ Failed to remove ${cacheDir}: ${error.message}`);
258
- this.results.errors.push(`NPX cache: ${error.message}`);
259
- }
260
- }
261
-
262
- if (removed > 0) {
263
- console.log(` āœ… Removed ${removed} NPX cache entries`);
264
- this.results.directoriesRemoved += removed;
265
- }
266
- }
267
-
268
- /**
269
- * Clean temporary files
270
- */
271
- async cleanTemporaryFiles() {
272
- console.log('šŸ—‘ļø Cleaning temporary files...');
273
-
274
- const tempDirs = [
275
- path.join(os.tmpdir()),
276
- path.join(this.homeDir, 'AppData', 'Local', 'Temp')
277
- ];
278
-
279
- let totalRemoved = 0;
280
-
281
- for (const tempDir of tempDirs) {
282
- if (!fs.existsSync(tempDir)) {
283
- continue;
284
- }
285
-
286
- try {
287
- const tempFiles = await this.findStigmergyTempFiles(tempDir);
288
-
289
- if (this.options.dryRun) {
290
- console.log(` šŸ” ${tempDir}: ${tempFiles.length} temporary files`);
291
- continue;
292
- }
293
-
294
- for (const file of tempFiles) {
295
- await this.removeFile(file);
296
- totalRemoved++;
297
- }
298
-
299
- if (tempFiles.length > 0) {
300
- console.log(` āœ… ${path.basename(tempDir)}: removed ${tempFiles.length} files`);
301
- }
302
-
303
- } catch (error) {
304
- console.error(` āŒ Failed to clean ${tempDir}: ${error.message}`);
305
- this.results.errors.push(`Temp files: ${error.message}`);
306
- }
307
- }
308
-
309
- if (!this.options.dryRun && totalRemoved > 0) {
310
- console.log(` āœ… Removed ${totalRemoved} temporary files`);
311
- this.results.filesRemoved += totalRemoved;
312
- }
313
- }
314
-
315
- /**
316
- * Uninstall global packages
317
- */
318
- async uninstallGlobalPackages() {
319
- console.log('🌐 Uninstalling global packages...');
320
-
321
- const globalPackages = [
322
- 'stigmergy-cli',
323
- 'stigmergy'
324
- ];
325
-
326
- for (const pkg of globalPackages) {
327
- try {
328
- const result = spawnSync('npm', ['list', '-g', pkg], {
329
- encoding: 'utf8',
330
- shell: true
331
- });
332
-
333
- if (result.status === 0 && result.stdout.includes(pkg)) {
334
- console.log(` šŸ“¦ Found global package: ${pkg}`);
335
-
336
- if (this.options.dryRun) {
337
- console.log(` šŸ” Would uninstall: ${pkg}`);
338
- continue;
339
- }
340
-
341
- const uninstallResult = spawnSync('npm', ['uninstall', '-g', pkg], {
342
- encoding: 'utf8',
343
- shell: true,
344
- stdio: 'inherit'
345
- });
346
-
347
- if (uninstallResult.status === 0) {
348
- console.log(` āœ… Uninstalled: ${pkg}`);
349
- } else {
350
- console.log(` āš ļø Failed to uninstall: ${pkg}`);
351
- }
352
- }
353
- } catch (error) {
354
- console.error(` āŒ Error checking ${pkg}: ${error.message}`);
355
- }
356
- }
357
- }
358
-
359
- /**
360
- * Helper methods
361
- */
362
- async removeDirectory(dirPath) {
363
- try {
364
- if (!fs.existsSync(dirPath)) {
365
- return false;
366
- }
367
-
368
- if (this.options.verbose) {
369
- console.log(` Removing directory: ${dirPath}`);
370
- }
371
-
372
- fs.rmSync(dirPath, { recursive: true, force: true, maxRetries: 3 });
373
- return true;
374
- } catch (error) {
375
- if (!this.options.force) {
376
- throw error;
377
- }
378
- this.results.skipped.push(`Directory: ${dirPath} (${error.message})`);
379
- return false;
380
- }
381
- }
382
-
383
- async removeFile(filePath) {
384
- try {
385
- if (!fs.existsSync(filePath)) {
386
- return false;
387
- }
388
-
389
- if (this.options.verbose) {
390
- console.log(` Removing file: ${filePath}`);
391
- }
392
-
393
- fs.unlinkSync(filePath);
394
- return true;
395
- } catch (error) {
396
- if (!this.options.force) {
397
- throw error;
398
- }
399
- this.results.skipped.push(`File: ${filePath} (${error.message})`);
400
- return false;
401
- }
402
- }
403
-
404
- async getAllFiles(dirPath) {
405
- const files = [];
406
-
407
- try {
408
- const items = fs.readdirSync(dirPath);
409
-
410
- for (const item of items) {
411
- const fullPath = path.join(dirPath, item);
412
- const stat = fs.statSync(fullPath);
413
-
414
- if (stat.isDirectory()) {
415
- files.push(...await this.getAllFiles(fullPath));
416
- files.push(fullPath);
417
- } else {
418
- files.push(fullPath);
419
- }
420
- }
421
-
422
- files.push(dirPath);
423
- } catch (error) {
424
- console.warn(`Warning: Could not read ${dirPath}: ${error.message}`);
425
- }
426
-
427
- return files;
428
- }
429
-
430
- async findStigmergyFiles(dirPath) {
431
- const stigmergyFiles = [];
432
-
433
- try {
434
- const files = fs.readdirSync(dirPath, { withFileTypes: true });
435
-
436
- for (const file of files) {
437
- const fullPath = path.join(dirPath, file.name);
438
-
439
- if (file.isDirectory()) {
440
- stigmergyFiles.push(...await this.findStigmergyFiles(fullPath));
441
- } else if (this.isStigmergyFile(file.name)) {
442
- stigmergyFiles.push(fullPath);
443
- }
444
- }
445
- } catch (error) {
446
- // Skip directories we can't read
447
- }
448
-
449
- return stigmergyFiles;
450
- }
451
-
452
- isStigmergyFile(fileName) {
453
- const stigmergyPatterns = [
454
- 'stigmergy',
455
- 'cross-cli',
456
- 'hook',
457
- 'integration',
458
- '.stigmergy'
459
- ];
460
-
461
- const lowerFileName = fileName.toLowerCase();
462
- return stigmergyPatterns.some(pattern =>
463
- lowerFileName.includes(pattern.toLowerCase())
464
- );
465
- }
466
-
467
- async findNPXCacheDirectories() {
468
- const cacheDirs = [];
469
- const npxCacheBase = path.join(this.homeDir, 'AppData', 'Local', 'npm-cache', '_npx');
470
-
471
- if (!fs.existsSync(npxCacheBase)) {
472
- // Try alternative locations
473
- const alternatives = [
474
- path.join(this.homeDir, '.npm', '_npx'),
475
- path.join(os.tmpdir(), 'npm-cache', '_npx')
476
- ];
477
-
478
- for (const alt of alternatives) {
479
- if (fs.existsSync(alt)) {
480
- npxCacheBase = alt;
481
- break;
482
- }
483
- }
484
- }
485
-
486
- if (fs.existsSync(npxCacheBase)) {
487
- try {
488
- const entries = fs.readdirSync(npxCacheBase);
489
-
490
- for (const entry of entries) {
491
- const entryPath = path.join(npxCacheBase, entry);
492
- const stigmergyPath = path.join(entryPath, 'node_modules', 'stigmergy');
493
-
494
- if (fs.existsSync(stigmergyPath)) {
495
- cacheDirs.push(entryPath);
496
- }
497
- }
498
- } catch (error) {
499
- console.warn(`Warning: Could not scan NPX cache: ${error.message}`);
500
- }
501
- }
502
-
503
- return cacheDirs;
504
- }
505
-
506
- async findStigmergyTempFiles(tempDir) {
507
- const tempFiles = [];
508
-
509
- try {
510
- const files = fs.readdirSync(tempDir, { withFileTypes: true });
511
-
512
- for (const file of files) {
513
- if (this.isStigmergyFile(file.name) ||
514
- file.name.startsWith('stigmergy-') ||
515
- file.name.includes('stigmergy')) {
516
- const fullPath = path.join(tempDir, file.name);
517
-
518
- if (file.isDirectory()) {
519
- tempFiles.push(fullPath);
520
- } else {
521
- tempFiles.push(fullPath);
522
- }
523
- }
524
- }
525
- } catch (error) {
526
- // Skip temp directories we can't read
527
- }
528
-
529
- return tempFiles;
530
- }
531
-
532
- async scanDirectory(dirPath, plan) {
533
- try {
534
- const stat = fs.statSync(dirPath);
535
-
536
- if (stat.isDirectory()) {
537
- const files = fs.readdirSync(dirPath, { withFileTypes: true });
538
-
539
- for (const file of files) {
540
- const fullPath = path.join(dirPath, file.name);
541
-
542
- if (file.isDirectory()) {
543
- plan.directories.push(fullPath);
544
- await this.scanDirectory(fullPath, plan);
545
- } else {
546
- plan.files.push(fullPath);
547
- }
548
- }
549
- } else {
550
- plan.files.push(dirPath);
551
- }
552
- } catch (error) {
553
- console.warn(`Warning: Could not scan ${dirPath}: ${error.message}`);
554
- }
555
- }
556
-
557
- async calculateDirectorySize(paths) {
558
- let totalSize = 0;
559
-
560
- for (const filePath of paths) {
561
- try {
562
- if (fs.existsSync(filePath)) {
563
- const stat = fs.statSync(filePath);
564
- totalSize += stat.size;
565
- }
566
- } catch (error) {
567
- // Skip files we can't stat
568
- }
569
- }
570
-
571
- return totalSize;
572
- }
573
-
574
- formatBytes(bytes) {
575
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
576
- if (bytes === 0) return '0 Bytes';
577
- const i = Math.floor(Math.log(bytes) / Math.log(1024));
578
- return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
579
- }
580
-
581
- logFiles(files, prefix = '') {
582
- files.forEach(file => {
583
- console.log(`${prefix}${path.basename(file)}`);
584
- });
585
- }
586
-
587
- printSummary() {
588
- console.log('\nšŸ“Š UNINSTALL SUMMARY:');
589
- console.log('=' .repeat(50));
590
-
591
- if (this.options.dryRun) {
592
- console.log('šŸ” DRY RUN MODE - No files were actually deleted');
593
- } else {
594
- console.log(`šŸ“ Directories removed: ${this.results.directoriesRemoved}`);
595
- console.log(`šŸ“„ Files removed: ${this.results.filesRemoved}`);
596
- }
597
-
598
- if (this.results.skipped.length > 0) {
599
- console.log(`ā­ļø Items skipped: ${this.results.skipped.length}`);
600
- if (this.options.verbose) {
601
- this.results.skipped.forEach(item => {
602
- console.log(` ${item}`);
603
- });
604
- }
605
- }
606
-
607
- if (this.results.errors.length > 0) {
608
- console.log(`āŒ Errors: ${this.results.errors.length}`);
609
- this.results.errors.forEach(error => {
610
- console.log(` ${error}`);
611
- });
612
- }
613
-
614
- console.log('\nāœ… Enhanced uninstall completed!');
615
- }
616
- }
617
-
618
- module.exports = EnhancedUninstaller;
1
+ /**
2
+ * Enhanced Stigmergy Uninstaller
3
+ *
4
+ * Comprehensive uninstallation with complete cleanup of all Stigmergy-related files,
5
+ * configurations, caches, and integrations across all supported CLI tools.
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const os = require('os');
11
+ const { spawnSync } = require('child_process');
12
+
13
+ class EnhancedUninstaller {
14
+ constructor(options = {}) {
15
+ this.options = {
16
+ dryRun: options.dryRun || false,
17
+ force: options.force || false,
18
+ verbose: options.verbose || false,
19
+ preserveUserConfigs: options.preserveUserConfigs || false,
20
+ ...options,
21
+ };
22
+
23
+ this.homeDir = os.homedir();
24
+ this.stigmergyDir = path.join(this.homeDir, '.stigmergy');
25
+ this.stigmergyTestDir = path.join(this.homeDir, '.stigmergy-test');
26
+
27
+ this.supportedCLIs = [
28
+ 'claude',
29
+ 'gemini',
30
+ 'qwen',
31
+ 'iflow',
32
+ 'qodercli',
33
+ 'codebuddy',
34
+ 'codex',
35
+ 'copilot',
36
+ 'qwencode',
37
+ ];
38
+
39
+ this.results = {
40
+ filesRemoved: 0,
41
+ directoriesRemoved: 0,
42
+ errors: [],
43
+ skipped: [],
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Perform complete uninstallation
49
+ */
50
+ async completeUninstall() {
51
+ console.log('šŸ—‘ļæ½? Starting Enhanced Stigmergy Uninstall...\n');
52
+
53
+ if (this.options.dryRun) {
54
+ console.log('šŸ” DRY RUN MODE - No files will be deleted\n');
55
+ }
56
+
57
+ try {
58
+ // 1. Clean Stigmergy main directory
59
+ await this.cleanStigmergyDirectory();
60
+
61
+ // 2. Clean test directory
62
+ await this.cleanTestDirectory();
63
+
64
+ // 3. Clean CLI configurations
65
+ await this.cleanCLIConfigurations();
66
+
67
+ // 4. Clean NPX cache
68
+ await this.cleanNPXCache();
69
+
70
+ // 5. Clean temporary files
71
+ await this.cleanTemporaryFiles();
72
+
73
+ // 6. Uninstall global packages (if requested)
74
+ if (this.options.uninstallGlobal) {
75
+ await this.uninstallGlobalPackages();
76
+ }
77
+
78
+ // 7. Print summary
79
+ this.printSummary();
80
+
81
+ return this.results;
82
+ } catch (error) {
83
+ console.error('ļæ½?Uninstall failed:', error.message);
84
+ this.results.errors.push(error.message);
85
+ return this.results;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Create uninstall plan without executing
91
+ */
92
+ async createUninstallPlan() {
93
+ console.log('šŸ“‹ Creating Uninstall Plan...\n');
94
+
95
+ const plan = {
96
+ directories: [],
97
+ files: [],
98
+ globalPackages: [],
99
+ cliConfigurations: [],
100
+ estimatedSize: 0,
101
+ };
102
+
103
+ // Scan Stigmergy directory
104
+ if (fs.existsSync(this.stigmergyDir)) {
105
+ await this.scanDirectory(this.stigmergyDir, plan);
106
+ }
107
+
108
+ // Scan test directory
109
+ if (fs.existsSync(this.stigmergyTestDir)) {
110
+ await this.scanDirectory(this.stigmergyTestDir, plan);
111
+ }
112
+
113
+ // Scan CLI configurations
114
+ for (const cli of this.supportedCLIs) {
115
+ const cliConfig = path.join(this.homeDir, `.${cli}`);
116
+ if (fs.existsSync(cliConfig)) {
117
+ const stigmergyFiles = await this.findStigmergyFiles(cliConfig);
118
+ plan.cliConfigurations.push({
119
+ cli,
120
+ files: stigmergyFiles,
121
+ });
122
+ }
123
+ }
124
+
125
+ // Calculate estimated size
126
+ plan.estimatedSize = await this.calculateDirectorySize([
127
+ this.stigmergyDir,
128
+ this.stigmergyTestDir,
129
+ ...plan.cliConfigurations.flatMap((c) => c.files),
130
+ ]);
131
+
132
+ return plan;
133
+ }
134
+
135
+ /**
136
+ * Clean Stigmergy main directory
137
+ */
138
+ async cleanStigmergyDirectory() {
139
+ console.log('šŸ“ Cleaning Stigmergy directory...');
140
+
141
+ if (!fs.existsSync(this.stigmergyDir)) {
142
+ console.log(' ā„¹ļø Stigmergy directory not found');
143
+ return;
144
+ }
145
+
146
+ const files = await this.getAllFiles(this.stigmergyDir);
147
+
148
+ if (this.options.dryRun) {
149
+ console.log(` šŸ” Would remove ${files.length} files and directories`);
150
+ this.logFiles(files, ' ');
151
+ return;
152
+ }
153
+
154
+ try {
155
+ await this.removeDirectory(this.stigmergyDir);
156
+ console.log(` ļæ½?Removed ${files.length} files and directories`);
157
+ this.results.filesRemoved += files.length;
158
+ this.results.directoriesRemoved++;
159
+ } catch (error) {
160
+ console.error(
161
+ ` ļæ½?Failed to remove Stigmergy directory: ${error.message}`,
162
+ );
163
+ this.results.errors.push(`Stigmergy directory: ${error.message}`);
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Clean test directory
169
+ */
170
+ async cleanTestDirectory() {
171
+ console.log('🧪 Cleaning test directory...');
172
+
173
+ if (!fs.existsSync(this.stigmergyTestDir)) {
174
+ console.log(' ā„¹ļø Test directory not found');
175
+ return;
176
+ }
177
+
178
+ if (this.options.dryRun) {
179
+ console.log(' šŸ” Would remove test directory');
180
+ return;
181
+ }
182
+
183
+ try {
184
+ await this.removeDirectory(this.stigmergyTestDir);
185
+ console.log(' ļæ½?Removed test directory');
186
+ this.results.directoriesRemoved++;
187
+ } catch (error) {
188
+ console.error(` ļæ½?Failed to remove test directory: ${error.message}`);
189
+ this.results.errors.push(`Test directory: ${error.message}`);
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Clean CLI configurations
195
+ */
196
+ async cleanCLIConfigurations() {
197
+ console.log('āš™ļø Cleaning CLI configurations...');
198
+
199
+ let totalCleaned = 0;
200
+
201
+ for (const cli of this.supportedCLIs) {
202
+ const cliConfig = path.join(this.homeDir, `.${cli}`);
203
+
204
+ if (!fs.existsSync(cliConfig)) {
205
+ continue;
206
+ }
207
+
208
+ const stigmergyFiles = await this.findStigmergyFiles(cliConfig);
209
+
210
+ if (stigmergyFiles.length === 0) {
211
+ continue;
212
+ }
213
+
214
+ console.log(` šŸ“‚ ${cli}: ${stigmergyFiles.length} Stigmergy files`);
215
+
216
+ if (this.options.dryRun) {
217
+ this.logFiles(stigmergyFiles, ' šŸ” ');
218
+ continue;
219
+ }
220
+
221
+ try {
222
+ for (const file of stigmergyFiles) {
223
+ await this.removeFile(file);
224
+ totalCleaned++;
225
+ }
226
+ console.log(` ļæ½?Cleaned ${stigmergyFiles.length} files`);
227
+ } catch (error) {
228
+ console.error(` ļæ½?Failed to clean ${cli}: ${error.message}`);
229
+ this.results.errors.push(`${cli} config: ${error.message}`);
230
+ }
231
+ }
232
+
233
+ if (!this.options.dryRun && totalCleaned > 0) {
234
+ console.log(` ļæ½?Cleaned ${totalCleaned} CLI configuration files`);
235
+ this.results.filesRemoved += totalCleaned;
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Clean NPX cache
241
+ */
242
+ async cleanNPXCache() {
243
+ console.log('šŸ“¦ Cleaning NPX cache...');
244
+
245
+ const npxCacheDirs = await this.findNPXCacheDirectories();
246
+
247
+ if (npxCacheDirs.length === 0) {
248
+ console.log(' ā„¹ļø No Stigmergy entries in NPX cache');
249
+ return;
250
+ }
251
+
252
+ console.log(
253
+ ` šŸ“¦ Found ${npxCacheDirs.length} Stigmergy entries in NPX cache`,
254
+ );
255
+
256
+ if (this.options.dryRun) {
257
+ this.logFiles(npxCacheDirs, ' šŸ” ');
258
+ return;
259
+ }
260
+
261
+ let removed = 0;
262
+ for (const cacheDir of npxCacheDirs) {
263
+ try {
264
+ await this.removeDirectory(cacheDir);
265
+ removed++;
266
+ } catch (error) {
267
+ console.error(` ļæ½?Failed to remove ${cacheDir}: ${error.message}`);
268
+ this.results.errors.push(`NPX cache: ${error.message}`);
269
+ }
270
+ }
271
+
272
+ if (removed > 0) {
273
+ console.log(` ļæ½?Removed ${removed} NPX cache entries`);
274
+ this.results.directoriesRemoved += removed;
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Clean temporary files
280
+ */
281
+ async cleanTemporaryFiles() {
282
+ console.log('šŸ—‘ļæ½? Cleaning temporary files...');
283
+
284
+ const tempDirs = [
285
+ path.join(os.tmpdir()),
286
+ path.join(this.homeDir, 'AppData', 'Local', 'Temp'),
287
+ ];
288
+
289
+ let totalRemoved = 0;
290
+
291
+ for (const tempDir of tempDirs) {
292
+ if (!fs.existsSync(tempDir)) {
293
+ continue;
294
+ }
295
+
296
+ try {
297
+ const tempFiles = await this.findStigmergyTempFiles(tempDir);
298
+
299
+ if (this.options.dryRun) {
300
+ console.log(` šŸ” ${tempDir}: ${tempFiles.length} temporary files`);
301
+ continue;
302
+ }
303
+
304
+ for (const file of tempFiles) {
305
+ await this.removeFile(file);
306
+ totalRemoved++;
307
+ }
308
+
309
+ if (tempFiles.length > 0) {
310
+ console.log(
311
+ ` ļæ½?${path.basename(tempDir)}: removed ${tempFiles.length} files`,
312
+ );
313
+ }
314
+ } catch (error) {
315
+ console.error(` ļæ½?Failed to clean ${tempDir}: ${error.message}`);
316
+ this.results.errors.push(`Temp files: ${error.message}`);
317
+ }
318
+ }
319
+
320
+ if (!this.options.dryRun && totalRemoved > 0) {
321
+ console.log(` ļæ½?Removed ${totalRemoved} temporary files`);
322
+ this.results.filesRemoved += totalRemoved;
323
+ }
324
+ }
325
+
326
+ /**
327
+ * Uninstall global packages
328
+ */
329
+ async uninstallGlobalPackages() {
330
+ console.log('🌐 Uninstalling global packages...');
331
+
332
+ const globalPackages = ['stigmergy-cli', 'stigmergy'];
333
+
334
+ for (const pkg of globalPackages) {
335
+ try {
336
+ const result = spawnSync('npm', ['list', '-g', pkg], {
337
+ encoding: 'utf8',
338
+ shell: true,
339
+ });
340
+
341
+ if (result.status === 0 && result.stdout.includes(pkg)) {
342
+ console.log(` šŸ“¦ Found global package: ${pkg}`);
343
+
344
+ if (this.options.dryRun) {
345
+ console.log(` šŸ” Would uninstall: ${pkg}`);
346
+ continue;
347
+ }
348
+
349
+ const uninstallResult = spawnSync('npm', ['uninstall', '-g', pkg], {
350
+ encoding: 'utf8',
351
+ shell: true,
352
+ stdio: 'inherit',
353
+ });
354
+
355
+ if (uninstallResult.status === 0) {
356
+ console.log(` ļæ½?Uninstalled: ${pkg}`);
357
+ } else {
358
+ console.log(` āš ļø Failed to uninstall: ${pkg}`);
359
+ }
360
+ }
361
+ } catch (error) {
362
+ console.error(` ļæ½?Error checking ${pkg}: ${error.message}`);
363
+ }
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Helper methods
369
+ */
370
+ async removeDirectory(dirPath) {
371
+ try {
372
+ if (!fs.existsSync(dirPath)) {
373
+ return false;
374
+ }
375
+
376
+ if (this.options.verbose) {
377
+ console.log(` Removing directory: ${dirPath}`);
378
+ }
379
+
380
+ fs.rmSync(dirPath, { recursive: true, force: true, maxRetries: 3 });
381
+ return true;
382
+ } catch (error) {
383
+ if (!this.options.force) {
384
+ throw error;
385
+ }
386
+ this.results.skipped.push(`Directory: ${dirPath} (${error.message})`);
387
+ return false;
388
+ }
389
+ }
390
+
391
+ async removeFile(filePath) {
392
+ try {
393
+ if (!fs.existsSync(filePath)) {
394
+ return false;
395
+ }
396
+
397
+ if (this.options.verbose) {
398
+ console.log(` Removing file: ${filePath}`);
399
+ }
400
+
401
+ fs.unlinkSync(filePath);
402
+ return true;
403
+ } catch (error) {
404
+ if (!this.options.force) {
405
+ throw error;
406
+ }
407
+ this.results.skipped.push(`File: ${filePath} (${error.message})`);
408
+ return false;
409
+ }
410
+ }
411
+
412
+ async getAllFiles(dirPath) {
413
+ const files = [];
414
+
415
+ try {
416
+ const items = fs.readdirSync(dirPath);
417
+
418
+ for (const item of items) {
419
+ const fullPath = path.join(dirPath, item);
420
+ const stat = fs.statSync(fullPath);
421
+
422
+ if (stat.isDirectory()) {
423
+ files.push(...(await this.getAllFiles(fullPath)));
424
+ files.push(fullPath);
425
+ } else {
426
+ files.push(fullPath);
427
+ }
428
+ }
429
+
430
+ files.push(dirPath);
431
+ } catch (error) {
432
+ console.warn(`Warning: Could not read ${dirPath}: ${error.message}`);
433
+ }
434
+
435
+ return files;
436
+ }
437
+
438
+ async findStigmergyFiles(dirPath) {
439
+ const stigmergyFiles = [];
440
+
441
+ try {
442
+ const files = fs.readdirSync(dirPath, { withFileTypes: true });
443
+
444
+ for (const file of files) {
445
+ const fullPath = path.join(dirPath, file.name);
446
+
447
+ if (file.isDirectory()) {
448
+ stigmergyFiles.push(...(await this.findStigmergyFiles(fullPath)));
449
+ } else if (this.isStigmergyFile(file.name)) {
450
+ stigmergyFiles.push(fullPath);
451
+ }
452
+ }
453
+ } catch (error) {
454
+ // Skip directories we can't read
455
+ }
456
+
457
+ return stigmergyFiles;
458
+ }
459
+
460
+ isStigmergyFile(fileName) {
461
+ const stigmergyPatterns = [
462
+ 'stigmergy',
463
+ 'cross-cli',
464
+ 'hook',
465
+ 'integration',
466
+ '.stigmergy',
467
+ ];
468
+
469
+ const lowerFileName = fileName.toLowerCase();
470
+ return stigmergyPatterns.some((pattern) =>
471
+ lowerFileName.includes(pattern.toLowerCase()),
472
+ );
473
+ }
474
+
475
+ async findNPXCacheDirectories() {
476
+ const cacheDirs = [];
477
+ const npxCacheBase = path.join(
478
+ this.homeDir,
479
+ 'AppData',
480
+ 'Local',
481
+ 'npm-cache',
482
+ '_npx',
483
+ );
484
+
485
+ if (!fs.existsSync(npxCacheBase)) {
486
+ // Try alternative locations
487
+ const alternatives = [
488
+ path.join(this.homeDir, '.npm', '_npx'),
489
+ path.join(os.tmpdir(), 'npm-cache', '_npx'),
490
+ ];
491
+
492
+ for (const alt of alternatives) {
493
+ if (fs.existsSync(alt)) {
494
+ npxCacheBase = alt;
495
+ break;
496
+ }
497
+ }
498
+ }
499
+
500
+ if (fs.existsSync(npxCacheBase)) {
501
+ try {
502
+ const entries = fs.readdirSync(npxCacheBase);
503
+
504
+ for (const entry of entries) {
505
+ const entryPath = path.join(npxCacheBase, entry);
506
+ const stigmergyPath = path.join(
507
+ entryPath,
508
+ 'node_modules',
509
+ 'stigmergy',
510
+ );
511
+
512
+ if (fs.existsSync(stigmergyPath)) {
513
+ cacheDirs.push(entryPath);
514
+ }
515
+ }
516
+ } catch (error) {
517
+ console.warn(`Warning: Could not scan NPX cache: ${error.message}`);
518
+ }
519
+ }
520
+
521
+ return cacheDirs;
522
+ }
523
+
524
+ async findStigmergyTempFiles(tempDir) {
525
+ const tempFiles = [];
526
+
527
+ try {
528
+ const files = fs.readdirSync(tempDir, { withFileTypes: true });
529
+
530
+ for (const file of files) {
531
+ if (
532
+ this.isStigmergyFile(file.name) ||
533
+ file.name.startsWith('stigmergy-') ||
534
+ file.name.includes('stigmergy')
535
+ ) {
536
+ const fullPath = path.join(tempDir, file.name);
537
+
538
+ if (file.isDirectory()) {
539
+ tempFiles.push(fullPath);
540
+ } else {
541
+ tempFiles.push(fullPath);
542
+ }
543
+ }
544
+ }
545
+ } catch (error) {
546
+ // Skip temp directories we can't read
547
+ }
548
+
549
+ return tempFiles;
550
+ }
551
+
552
+ async scanDirectory(dirPath, plan) {
553
+ try {
554
+ const stat = fs.statSync(dirPath);
555
+
556
+ if (stat.isDirectory()) {
557
+ const files = fs.readdirSync(dirPath, { withFileTypes: true });
558
+
559
+ for (const file of files) {
560
+ const fullPath = path.join(dirPath, file.name);
561
+
562
+ if (file.isDirectory()) {
563
+ plan.directories.push(fullPath);
564
+ await this.scanDirectory(fullPath, plan);
565
+ } else {
566
+ plan.files.push(fullPath);
567
+ }
568
+ }
569
+ } else {
570
+ plan.files.push(dirPath);
571
+ }
572
+ } catch (error) {
573
+ console.warn(`Warning: Could not scan ${dirPath}: ${error.message}`);
574
+ }
575
+ }
576
+
577
+ async calculateDirectorySize(paths) {
578
+ let totalSize = 0;
579
+
580
+ for (const filePath of paths) {
581
+ try {
582
+ if (fs.existsSync(filePath)) {
583
+ const stat = fs.statSync(filePath);
584
+ totalSize += stat.size;
585
+ }
586
+ } catch (error) {
587
+ // Skip files we can't stat
588
+ }
589
+ }
590
+
591
+ return totalSize;
592
+ }
593
+
594
+ formatBytes(bytes) {
595
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
596
+ if (bytes === 0) return '0 Bytes';
597
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
598
+ return Math.round((bytes / Math.pow(1024, i)) * 100) / 100 + ' ' + sizes[i];
599
+ }
600
+
601
+ logFiles(files, prefix = '') {
602
+ files.forEach((file) => {
603
+ console.log(`${prefix}${path.basename(file)}`);
604
+ });
605
+ }
606
+
607
+ printSummary() {
608
+ console.log('\nšŸ“Š UNINSTALL SUMMARY:');
609
+ console.log('='.repeat(50));
610
+
611
+ if (this.options.dryRun) {
612
+ console.log('šŸ” DRY RUN MODE - No files were actually deleted');
613
+ } else {
614
+ console.log(`šŸ“ Directories removed: ${this.results.directoriesRemoved}`);
615
+ console.log(`šŸ“„ Files removed: ${this.results.filesRemoved}`);
616
+ }
617
+
618
+ if (this.results.skipped.length > 0) {
619
+ console.log(`ā­ļø Items skipped: ${this.results.skipped.length}`);
620
+ if (this.options.verbose) {
621
+ this.results.skipped.forEach((item) => {
622
+ console.log(` ${item}`);
623
+ });
624
+ }
625
+ }
626
+
627
+ if (this.results.errors.length > 0) {
628
+ console.log(`ļæ½?Errors: ${this.results.errors.length}`);
629
+ this.results.errors.forEach((error) => {
630
+ console.log(` ${error}`);
631
+ });
632
+ }
633
+
634
+ console.log('\nļæ½?Enhanced uninstall completed!');
635
+ }
636
+ }
637
+
638
+ module.exports = EnhancedUninstaller;