stigmergy 1.2.13 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -3
- package/STIGMERGY.md +3 -0
- package/config/builtin-skills.json +43 -0
- package/config/enhanced-cli-config.json +438 -0
- package/docs/CLI_TOOLS_AGENT_SKILL_ANALYSIS.md +463 -0
- package/docs/DESIGN_CLI_HELP_ANALYZER_REFACTOR.md +726 -0
- package/docs/ENHANCED_CLI_AGENT_SKILL_CONFIG.md +285 -0
- package/docs/IMPLEMENTATION_CHECKLIST_CLI_HELP_ANALYZER_REFACTOR.md +1268 -0
- package/docs/INSTALLER_ARCHITECTURE.md +257 -0
- package/docs/LESSONS_LEARNED.md +252 -0
- package/docs/SPECS_CLI_HELP_ANALYZER_REFACTOR.md +287 -0
- package/docs/SUDO_PROBLEM_AND_SOLUTION.md +529 -0
- package/docs/correct-skillsio-implementation.md +368 -0
- package/docs/development_guidelines.md +276 -0
- package/docs/independent-resume-implementation.md +198 -0
- package/docs/resumesession-final-implementation.md +195 -0
- package/docs/resumesession-usage.md +87 -0
- package/package.json +146 -136
- package/scripts/analyze-router.js +168 -0
- package/scripts/run-comprehensive-tests.js +230 -0
- package/scripts/run-quick-tests.js +90 -0
- package/scripts/test-runner.js +344 -0
- package/skills/resumesession/INDEPENDENT_SKILL.md +403 -0
- package/skills/resumesession/README.md +381 -0
- package/skills/resumesession/SKILL.md +211 -0
- package/skills/resumesession/__init__.py +33 -0
- package/skills/resumesession/implementations/simple-resume.js +13 -0
- package/skills/resumesession/independent-resume.js +750 -0
- package/skills/resumesession/package.json +1 -0
- package/skills/resumesession/skill.json +1 -0
- package/src/adapters/claude/install_claude_integration.js +9 -1
- package/src/adapters/codebuddy/install_codebuddy_integration.js +3 -1
- package/src/adapters/codex/install_codex_integration.js +15 -5
- package/src/adapters/gemini/install_gemini_integration.js +3 -1
- package/src/adapters/qwen/install_qwen_integration.js +3 -1
- package/src/cli/commands/autoinstall.js +65 -0
- package/src/cli/commands/errors.js +190 -0
- package/src/cli/commands/independent-resume.js +395 -0
- package/src/cli/commands/install.js +179 -0
- package/src/cli/commands/permissions.js +108 -0
- package/src/cli/commands/project.js +485 -0
- package/src/cli/commands/scan.js +97 -0
- package/src/cli/commands/simple-resume.js +377 -0
- package/src/cli/commands/skills.js +158 -0
- package/src/cli/commands/status.js +113 -0
- package/src/cli/commands/stigmergy-resume.js +775 -0
- package/src/cli/commands/system.js +301 -0
- package/src/cli/commands/universal-resume.js +394 -0
- package/src/cli/router-beta.js +471 -0
- package/src/cli/utils/environment.js +75 -0
- package/src/cli/utils/formatters.js +47 -0
- package/src/cli/utils/skills_cache.js +92 -0
- package/src/core/cache_cleaner.js +1 -0
- package/src/core/cli_adapters.js +345 -0
- package/src/core/cli_help_analyzer.js +1236 -680
- package/src/core/cli_path_detector.js +702 -709
- package/src/core/cli_tools.js +515 -160
- package/src/core/coordination/nodejs/CLIIntegrationManager.js +18 -0
- package/src/core/coordination/nodejs/HookDeploymentManager.js +242 -412
- package/src/core/coordination/nodejs/HookDeploymentManager.refactored.js +323 -0
- package/src/core/coordination/nodejs/generators/CLIAdapterGenerator.js +363 -0
- package/src/core/coordination/nodejs/generators/ResumeSessionGenerator.js +932 -0
- package/src/core/coordination/nodejs/generators/SkillsIntegrationGenerator.js +1395 -0
- package/src/core/coordination/nodejs/generators/index.js +12 -0
- package/src/core/enhanced_cli_installer.js +1208 -608
- package/src/core/enhanced_cli_parameter_handler.js +402 -0
- package/src/core/execution_mode_detector.js +222 -0
- package/src/core/installer.js +151 -106
- package/src/core/local_skill_scanner.js +732 -0
- package/src/core/multilingual/language-pattern-manager.js +1 -1
- package/src/core/skills/BuiltinSkillsDeployer.js +188 -0
- package/src/core/skills/StigmergySkillManager.js +123 -16
- package/src/core/skills/embedded-openskills/SkillParser.js +7 -3
- package/src/core/smart_router.js +550 -261
- package/src/index.js +10 -4
- package/src/utils.js +66 -7
- package/test/cli-integration.test.js +304 -0
- package/test/direct_smart_router_test.js +88 -0
- package/test/enhanced-cli-agent-skill-test.js +485 -0
- package/test/simple_test.js +82 -0
- package/test/smart_router_test_runner.js +123 -0
- package/test/smart_routing_edge_cases.test.js +284 -0
- package/test/smart_routing_simple_verification.js +139 -0
- package/test/smart_routing_verification.test.js +346 -0
- package/test/specific-cli-agent-skill-analysis.js +385 -0
- package/test/unit/smart_router.test.js +295 -0
- package/test/very_simple_test.js +54 -0
- package/src/cli/router.js +0 -1783
|
@@ -1,710 +1,703 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI Path Detector and Auto-Configurator
|
|
3
|
-
*
|
|
4
|
-
* This module automatically detects CLI tool paths and configures them
|
|
5
|
-
* to resolve common installation issues on different platforms.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { spawnSync } = require('child_process');
|
|
9
|
-
const fs = require('fs/promises');
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const os = require('os');
|
|
12
|
-
|
|
13
|
-
class CLIPathDetector {
|
|
14
|
-
constructor() {
|
|
15
|
-
this.configDir = path.join(os.homedir(), '.stigmergy', 'cli-paths');
|
|
16
|
-
this.pathCacheFile = path.join(this.configDir, 'detected-paths.json');
|
|
17
|
-
this.detectedPaths = {};
|
|
18
|
-
this.platform = os.platform();
|
|
19
|
-
|
|
20
|
-
// Common npm global directories by platform
|
|
21
|
-
this.npmGlobalPaths = this.getNPMGlobalPaths();
|
|
22
|
-
|
|
23
|
-
// CLI tool name mappings (actual command vs expected)
|
|
24
|
-
this.cliNameMap = {
|
|
25
|
-
'claude': ['claude'],
|
|
26
|
-
'gemini': ['gemini'],
|
|
27
|
-
'qwen': ['qwen'],
|
|
28
|
-
'iflow': ['iflow'],
|
|
29
|
-
'qodercli': ['qodercli'],
|
|
30
|
-
'codebuddy': ['codebuddy'],
|
|
31
|
-
'copilot': ['copilot'],
|
|
32
|
-
'codex': ['codex']
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
'
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
path.join(os.homedir(), '
|
|
57
|
-
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
'/
|
|
62
|
-
'/
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
'/root/
|
|
68
|
-
'/root/.
|
|
69
|
-
|
|
70
|
-
// NVM
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
'
|
|
82
|
-
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
'
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
'
|
|
249
|
-
'
|
|
250
|
-
|
|
251
|
-
'/
|
|
252
|
-
'
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
binDir =
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
$
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
$
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
if
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
fi
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
await fs.
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
return
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
/**
|
|
685
|
-
* Get
|
|
686
|
-
*/
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
}
|
|
704
|
-
};
|
|
705
|
-
|
|
706
|
-
return report;
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
|
|
1
|
+
/**
|
|
2
|
+
* CLI Path Detector and Auto-Configurator
|
|
3
|
+
*
|
|
4
|
+
* This module automatically detects CLI tool paths and configures them
|
|
5
|
+
* to resolve common installation issues on different platforms.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { spawnSync } = require('child_process');
|
|
9
|
+
const fs = require('fs/promises');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
|
|
13
|
+
class CLIPathDetector {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.configDir = path.join(os.homedir(), '.stigmergy', 'cli-paths');
|
|
16
|
+
this.pathCacheFile = path.join(this.configDir, 'detected-paths.json');
|
|
17
|
+
this.detectedPaths = {};
|
|
18
|
+
this.platform = os.platform();
|
|
19
|
+
|
|
20
|
+
// Common npm global directories by platform
|
|
21
|
+
this.npmGlobalPaths = this.getNPMGlobalPaths();
|
|
22
|
+
|
|
23
|
+
// CLI tool name mappings (actual command vs expected)
|
|
24
|
+
this.cliNameMap = {
|
|
25
|
+
'claude': ['claude'],
|
|
26
|
+
'gemini': ['gemini'],
|
|
27
|
+
'qwen': ['qwen'],
|
|
28
|
+
'iflow': ['iflow'],
|
|
29
|
+
'qodercli': ['qodercli'],
|
|
30
|
+
'codebuddy': ['codebuddy'],
|
|
31
|
+
'copilot': ['copilot'],
|
|
32
|
+
'codex': ['codex'],
|
|
33
|
+
'kode': ['kode'],
|
|
34
|
+
'resumesession': ['resumesession']
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get all possible npm global directories for current platform
|
|
40
|
+
*/
|
|
41
|
+
getNPMGlobalPaths() {
|
|
42
|
+
const paths = [];
|
|
43
|
+
|
|
44
|
+
if (this.platform === 'win32') {
|
|
45
|
+
// Windows paths
|
|
46
|
+
paths.push(
|
|
47
|
+
path.join(os.homedir(), 'AppData', 'Roaming', 'npm'), // User npm
|
|
48
|
+
'C:/npm_global', // Custom global
|
|
49
|
+
'C:/Program Files/nodejs/npm', // System npm
|
|
50
|
+
path.join(process.env.ProgramFiles || 'C:/Program Files', 'npm')
|
|
51
|
+
);
|
|
52
|
+
} else {
|
|
53
|
+
// Unix-like paths - comprehensive coverage
|
|
54
|
+
paths.push(
|
|
55
|
+
// User-specific npm global paths
|
|
56
|
+
path.join(os.homedir(), '.npm-global', 'bin'), // User local with custom prefix
|
|
57
|
+
path.join(os.homedir(), '.npm', 'bin'), // User npm
|
|
58
|
+
path.join(os.homedir(), 'node_modules', '.bin'), // Local node_modules bin
|
|
59
|
+
|
|
60
|
+
// System-wide paths
|
|
61
|
+
'/usr/local/bin', // Common system location
|
|
62
|
+
'/usr/bin', // System binaries
|
|
63
|
+
'/opt/node/bin', // Node.js installed to /opt
|
|
64
|
+
'/opt/nodejs/bin', // Alternative system installation
|
|
65
|
+
|
|
66
|
+
// Root-specific paths (when running as root)
|
|
67
|
+
'/root/.npm-global/bin', // Root user custom prefix
|
|
68
|
+
'/root/.npm/bin', // Root user npm
|
|
69
|
+
'/root/node_modules/.bin', // Root local node_modules
|
|
70
|
+
'/root/.nvm/versions/node/*/bin', // NVM installations for root
|
|
71
|
+
|
|
72
|
+
// NVM (Node Version Manager) paths for regular users
|
|
73
|
+
path.join(os.homedir(), '.nvm', 'versions', 'node', '*', 'bin'), // NVM user installations
|
|
74
|
+
path.join(os.homedir(), '.nvm', 'current', 'bin'), // NVM current version
|
|
75
|
+
|
|
76
|
+
// NodeSource installation paths
|
|
77
|
+
'/usr/bin/nodejs', // NodeSource package installations
|
|
78
|
+
'/usr/local/share/npm/bin', // npm share location
|
|
79
|
+
|
|
80
|
+
// Homebrew (macOS) paths
|
|
81
|
+
path.join(os.homedir(), '.brew', 'node', 'bin'), // Custom Homebrew
|
|
82
|
+
'/opt/homebrew/bin', // Apple Silicon Homebrew
|
|
83
|
+
'/usr/local/bin', // Intel Homebrew
|
|
84
|
+
|
|
85
|
+
// pkg-config and other package managers
|
|
86
|
+
path.join(os.homedir(), '.local', 'bin'), // User local binaries
|
|
87
|
+
'/snap/bin', // Snap packages (Ubuntu)
|
|
88
|
+
'/var/lib/snapd/snap/bin' // Snap system
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Filter paths, handling wildcards for NVM
|
|
93
|
+
return paths.filter(p => {
|
|
94
|
+
try {
|
|
95
|
+
// Handle wildcard paths (NVM versions)
|
|
96
|
+
if (p.includes('*')) {
|
|
97
|
+
return this.expandWildcardPath(p);
|
|
98
|
+
}
|
|
99
|
+
return fs.existsSync(p);
|
|
100
|
+
} catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Expand wildcard paths (e.g., NVM version paths)
|
|
108
|
+
*/
|
|
109
|
+
expandWildcardPath(wildcardPath) {
|
|
110
|
+
try {
|
|
111
|
+
const { spawnSync } = require('child_process');
|
|
112
|
+
|
|
113
|
+
// Use shell to expand wildcards
|
|
114
|
+
const result = spawnSync('bash', ['-c', `ls -d ${wildcardPath} 2>/dev/null`], {
|
|
115
|
+
encoding: 'utf8',
|
|
116
|
+
shell: true
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (result.status === 0 && result.stdout.trim()) {
|
|
120
|
+
// Check if any of the expanded paths exist
|
|
121
|
+
const expandedPaths = result.stdout.trim().split('\n');
|
|
122
|
+
return expandedPaths.some(p => {
|
|
123
|
+
try {
|
|
124
|
+
return fs.existsSync(p.trim());
|
|
125
|
+
} catch {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
} catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get current PATH environment variable
|
|
138
|
+
*/
|
|
139
|
+
getCurrentPath() {
|
|
140
|
+
return (process.env.PATH || process.env.Path || '').split(path.delimiter);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Check if a command exists in a specific directory
|
|
145
|
+
*/
|
|
146
|
+
checkCommandInDir(command, dir) {
|
|
147
|
+
const fullPath = path.join(dir, command);
|
|
148
|
+
|
|
149
|
+
// Windows: check .cmd, .ps1, .exe files
|
|
150
|
+
if (this.platform === 'win32') {
|
|
151
|
+
const extensions = ['.cmd', '.ps1', '.exe', ''];
|
|
152
|
+
for (const ext of extensions) {
|
|
153
|
+
const fileWithExt = fullPath + ext;
|
|
154
|
+
try {
|
|
155
|
+
if (fs.existsSync(fileWithExt)) {
|
|
156
|
+
return fileWithExt;
|
|
157
|
+
}
|
|
158
|
+
} catch {}
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
// Unix-like: check direct executable
|
|
162
|
+
try {
|
|
163
|
+
if (fs.existsSync(fullPath)) {
|
|
164
|
+
return fullPath;
|
|
165
|
+
}
|
|
166
|
+
} catch {}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Find command in system PATH
|
|
174
|
+
*/
|
|
175
|
+
findCommandInPath(command) {
|
|
176
|
+
try {
|
|
177
|
+
const result = spawnSync('where', [command], {
|
|
178
|
+
encoding: 'utf8',
|
|
179
|
+
shell: true
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
if (result.status === 0 && result.stdout.trim()) {
|
|
183
|
+
return result.stdout.trim().split('\n')[0]; // Return first match
|
|
184
|
+
}
|
|
185
|
+
} catch {}
|
|
186
|
+
|
|
187
|
+
// Fallback: manually search PATH
|
|
188
|
+
for (const dir of this.getCurrentPath()) {
|
|
189
|
+
const found = this.checkCommandInDir(command, dir);
|
|
190
|
+
if (found) return found;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Find command in npm global directories
|
|
198
|
+
*/
|
|
199
|
+
findCommandInNPMGlobal(command) {
|
|
200
|
+
for (const dir of this.npmGlobalPaths) {
|
|
201
|
+
const found = this.checkCommandInDir(command, dir);
|
|
202
|
+
if (found) return found;
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Detect CLI tool path using multiple methods
|
|
209
|
+
*/
|
|
210
|
+
async detectCLIPath(toolName) {
|
|
211
|
+
console.log(`[DETECTOR] Detecting path for ${toolName}...`);
|
|
212
|
+
|
|
213
|
+
const commandNames = this.cliNameMap[toolName] || [toolName];
|
|
214
|
+
|
|
215
|
+
for (const command of commandNames) {
|
|
216
|
+
// Method 0: Use npm to get actual global installation path
|
|
217
|
+
let pathFound = await this.findCommandViaNPM(command);
|
|
218
|
+
if (pathFound) {
|
|
219
|
+
console.log(`[DETECTOR] Found ${toolName} via npm: ${pathFound}`);
|
|
220
|
+
return pathFound;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Method 1: Check system PATH (most common)
|
|
224
|
+
pathFound = this.findCommandInPath(command);
|
|
225
|
+
if (pathFound) {
|
|
226
|
+
console.log(`[DETECTOR] Found ${toolName} in PATH: ${pathFound}`);
|
|
227
|
+
return pathFound;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Method 2: Check npm global directories
|
|
231
|
+
pathFound = this.findCommandInNPMGlobal(command);
|
|
232
|
+
if (pathFound) {
|
|
233
|
+
console.log(`[DETECTOR] Found ${toolName} in npm global: ${pathFound}`);
|
|
234
|
+
return pathFound;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Method 3: Check common installation locations
|
|
238
|
+
if (this.platform === 'win32') {
|
|
239
|
+
const userNPMPath = path.join(os.homedir(), 'AppData', 'Roaming', 'npm');
|
|
240
|
+
pathFound = this.checkCommandInDir(command, userNPMPath);
|
|
241
|
+
if (pathFound) {
|
|
242
|
+
console.log(`[DETECTOR] Found ${toolName} in user npm: ${pathFound}`);
|
|
243
|
+
return pathFound;
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
// Check multiple Unix-like locations
|
|
247
|
+
const unixPaths = [
|
|
248
|
+
path.join(os.homedir(), '.npm-global', 'bin'),
|
|
249
|
+
path.join(os.homedir(), '.npm', 'bin'),
|
|
250
|
+
'/usr/local/bin',
|
|
251
|
+
'/usr/bin',
|
|
252
|
+
path.join(os.homedir(), '.local', 'bin'),
|
|
253
|
+
'/root/.npm-global/bin',
|
|
254
|
+
'/root/.npm/bin'
|
|
255
|
+
];
|
|
256
|
+
|
|
257
|
+
for (const dir of unixPaths) {
|
|
258
|
+
pathFound = this.checkCommandInDir(command, dir);
|
|
259
|
+
if (pathFound) {
|
|
260
|
+
console.log(`[DETECTOR] Found ${toolName} in ${dir}: ${pathFound}`);
|
|
261
|
+
return pathFound;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
console.log(`[DETECTOR] ${toolName} not found`);
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Find command using npm's actual global installation path
|
|
273
|
+
*/
|
|
274
|
+
async findCommandViaNPM(command) {
|
|
275
|
+
try {
|
|
276
|
+
const { spawnSync } = require('child_process');
|
|
277
|
+
|
|
278
|
+
// Get npm global prefix
|
|
279
|
+
const npmPrefixResult = spawnSync('npm', ['config', 'get', 'prefix'], {
|
|
280
|
+
encoding: 'utf8',
|
|
281
|
+
shell: true
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
if (npmPrefixResult.status === 0 && npmPrefixResult.stdout.trim()) {
|
|
285
|
+
const npmPrefix = npmPrefixResult.stdout.trim();
|
|
286
|
+
let binDir;
|
|
287
|
+
|
|
288
|
+
if (this.platform === 'win32') {
|
|
289
|
+
binDir = npmPrefix; // Windows: prefix already points to the directory with executables
|
|
290
|
+
} else {
|
|
291
|
+
binDir = path.join(npmPrefix, 'bin'); // Unix: bin subdirectory
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const commandPath = this.checkCommandInDir(command, binDir);
|
|
295
|
+
if (commandPath) {
|
|
296
|
+
return commandPath;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
} catch (error) {
|
|
301
|
+
console.log(`[DETECTOR] npm query failed: ${error.message}`);
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Detect all CLI tool paths
|
|
308
|
+
*/
|
|
309
|
+
async detectAllCLIPaths() {
|
|
310
|
+
console.log('[DETECTOR] Starting comprehensive CLI path detection...');
|
|
311
|
+
|
|
312
|
+
const allPaths = {};
|
|
313
|
+
|
|
314
|
+
for (const toolName of Object.keys(this.cliNameMap)) {
|
|
315
|
+
allPaths[toolName] = await this.detectCLIPath(toolName);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
this.detectedPaths = allPaths;
|
|
319
|
+
await this.saveDetectedPaths();
|
|
320
|
+
|
|
321
|
+
return allPaths;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Save detected paths to cache
|
|
326
|
+
*/
|
|
327
|
+
async saveDetectedPaths() {
|
|
328
|
+
try {
|
|
329
|
+
await fs.mkdir(this.configDir, { recursive: true });
|
|
330
|
+
|
|
331
|
+
const cacheData = {
|
|
332
|
+
version: '1.0.0',
|
|
333
|
+
timestamp: new Date().toISOString(),
|
|
334
|
+
platform: this.platform,
|
|
335
|
+
npmGlobalPaths: this.npmGlobalPaths,
|
|
336
|
+
detectedPaths: this.detectedPaths
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
await fs.writeFile(
|
|
340
|
+
this.pathCacheFile,
|
|
341
|
+
JSON.stringify(cacheData, null, 2),
|
|
342
|
+
'utf8'
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
console.log(`[DETECTOR] Saved path cache to: ${this.pathCacheFile}`);
|
|
346
|
+
} catch (error) {
|
|
347
|
+
console.log(`[DETECTOR] Warning: Could not save path cache: ${error.message}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Load detected paths from cache
|
|
353
|
+
*/
|
|
354
|
+
async loadDetectedPaths() {
|
|
355
|
+
try {
|
|
356
|
+
if (await fs.access(this.pathCacheFile).then(() => true).catch(() => false)) {
|
|
357
|
+
const data = await fs.readFile(this.pathCacheFile, 'utf8');
|
|
358
|
+
const cacheData = JSON.parse(data);
|
|
359
|
+
|
|
360
|
+
// Check if cache is too old (older than 1 hour) and skip loading if so
|
|
361
|
+
const cacheAge = Date.now() - new Date(cacheData.timestamp).getTime();
|
|
362
|
+
const maxCacheAge = 60 * 60 * 1000; // 1 hour in milliseconds
|
|
363
|
+
|
|
364
|
+
if (cacheAge < maxCacheAge) {
|
|
365
|
+
this.detectedPaths = cacheData.detectedPaths || {};
|
|
366
|
+
console.log(`[DETECTOR] Loaded ${Object.keys(this.detectedPaths).length} paths from cache (age: ${Math.floor(cacheAge/1000)}s)`);
|
|
367
|
+
return this.detectedPaths;
|
|
368
|
+
} else {
|
|
369
|
+
console.log(`[DETECTOR] Cache is too old (${Math.floor(cacheAge/1000)}s), skipping cache`);
|
|
370
|
+
return {};
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
} catch (error) {
|
|
374
|
+
console.log(`[DETECTOR] Warning: Could not load path cache: ${error.message}`);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return {};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Update PATH environment variable if needed
|
|
382
|
+
*/
|
|
383
|
+
async updatePATHIfMissing() {
|
|
384
|
+
console.log('[DETECTOR] Checking PATH configuration...');
|
|
385
|
+
|
|
386
|
+
const currentPath = this.getCurrentPath();
|
|
387
|
+
const missingPaths = [];
|
|
388
|
+
|
|
389
|
+
for (const dir of this.npmGlobalPaths) {
|
|
390
|
+
if (!currentPath.includes(dir)) {
|
|
391
|
+
missingPaths.push(dir);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (missingPaths.length > 0) {
|
|
396
|
+
console.log(`[DETECTOR] Found ${missingPaths.length} missing npm global directories in PATH`);
|
|
397
|
+
console.log('[DETECTOR] Automatically updating PATH for persistent access...');
|
|
398
|
+
|
|
399
|
+
// Create PATH update script first (as backup)
|
|
400
|
+
await this.createPATHUpdateScript(missingPaths);
|
|
401
|
+
|
|
402
|
+
// Perform automatic PATH update
|
|
403
|
+
const updateResult = await this.performAutoPATHUpdate(missingPaths);
|
|
404
|
+
|
|
405
|
+
return {
|
|
406
|
+
updated: updateResult.success,
|
|
407
|
+
missingPaths,
|
|
408
|
+
message: updateResult.success ? 'PATH automatically updated' : `PATH update failed: ${updateResult.error}`,
|
|
409
|
+
scriptCreated: true,
|
|
410
|
+
autoUpdateAttempted: true,
|
|
411
|
+
scriptPath: path.join(this.configDir, 'setup-scripts')
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return {
|
|
416
|
+
updated: true,
|
|
417
|
+
message: 'PATH already contains all npm global directories'
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Create script to update PATH
|
|
423
|
+
*/
|
|
424
|
+
async createPATHUpdateScript(missingPaths) {
|
|
425
|
+
const scriptDir = path.join(this.configDir, 'setup-scripts');
|
|
426
|
+
await fs.mkdir(scriptDir, { recursive: true });
|
|
427
|
+
|
|
428
|
+
if (this.platform === 'win32') {
|
|
429
|
+
// Windows PowerShell script
|
|
430
|
+
const ps1Script = `
|
|
431
|
+
# Stigmergy CLI PATH Update Script
|
|
432
|
+
# Run this script in PowerShell as Administrator
|
|
433
|
+
|
|
434
|
+
Write-Host "Adding npm global directories to PATH..." -ForegroundColor Green
|
|
435
|
+
|
|
436
|
+
$missingPaths = @(
|
|
437
|
+
${missingPaths.map(p => ` "${p}"`).join(',\n')}
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
$currentPath = [Environment]::GetEnvironmentVariable("PATH", "User")
|
|
441
|
+
$newPath = $currentPath + ";" + ($missingPaths -join ";")
|
|
442
|
+
|
|
443
|
+
[Environment]::SetEnvironmentVariable("PATH", $newPath, "User")
|
|
444
|
+
Write-Host "PATH updated successfully!" -ForegroundColor Green
|
|
445
|
+
Write-Host "Please restart your terminal or run 'refreshenv' to apply changes" -ForegroundColor Yellow
|
|
446
|
+
`;
|
|
447
|
+
|
|
448
|
+
await fs.writeFile(
|
|
449
|
+
path.join(scriptDir, 'update-path.ps1'),
|
|
450
|
+
ps1Script,
|
|
451
|
+
'utf8'
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
// Windows CMD script
|
|
455
|
+
const cmdScript = `
|
|
456
|
+
@echo off
|
|
457
|
+
REM Stigmergy CLI PATH Update Script
|
|
458
|
+
REM Run this as Administrator
|
|
459
|
+
|
|
460
|
+
echo Adding npm global directories to PATH...
|
|
461
|
+
setx PATH "%PATH%;${missingPaths.join(';')}"
|
|
462
|
+
echo PATH updated successfully!
|
|
463
|
+
echo Please restart your terminal to apply changes
|
|
464
|
+
pause
|
|
465
|
+
`;
|
|
466
|
+
|
|
467
|
+
await fs.writeFile(
|
|
468
|
+
path.join(scriptDir, 'update-path.bat'),
|
|
469
|
+
cmdScript,
|
|
470
|
+
'utf8'
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
} else {
|
|
474
|
+
// Unix/Linux/Mac script
|
|
475
|
+
const missingPathsArray = missingPaths.map(p => `"${p}"`).join('\n');
|
|
476
|
+
const shScript = `#!/bin/bash
|
|
477
|
+
# Stigmergy CLI PATH Update Script
|
|
478
|
+
# Run this script: source update-path.sh
|
|
479
|
+
|
|
480
|
+
echo "Adding npm global directories to PATH..."
|
|
481
|
+
|
|
482
|
+
missing_paths=(
|
|
483
|
+
${missingPathsArray}
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
# Add to shell profile
|
|
487
|
+
shell_rc="$HOME/.bashrc"
|
|
488
|
+
if [ -f "$HOME/.zshrc" ]; then
|
|
489
|
+
shell_rc="$HOME/.zshrc"
|
|
490
|
+
elif [ -f "$HOME/.profile" ]; then
|
|
491
|
+
shell_rc="$HOME/.profile"
|
|
492
|
+
fi
|
|
493
|
+
|
|
494
|
+
for path in "\${missing_paths[@]}"; do
|
|
495
|
+
if ! echo "$PATH" | grep -q "$path"; then
|
|
496
|
+
echo "Adding $path to PATH in $shell_rc"
|
|
497
|
+
echo 'export PATH="$PATH:'"$path'"' >> "$shell_rc"
|
|
498
|
+
export PATH="$PATH:$path"
|
|
499
|
+
fi
|
|
500
|
+
done
|
|
501
|
+
|
|
502
|
+
echo "PATH updated successfully!"
|
|
503
|
+
echo "Please restart your terminal or run 'source $shell_rc' to apply changes"
|
|
504
|
+
`;
|
|
505
|
+
|
|
506
|
+
await fs.writeFile(
|
|
507
|
+
path.join(scriptDir, 'update-path.sh'),
|
|
508
|
+
shScript,
|
|
509
|
+
'utf8'
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
// Make script executable
|
|
513
|
+
await fs.chmod(path.join(scriptDir, 'update-path.sh'), '755');
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
console.log(`[DETECTOR] Created PATH update scripts in: ${scriptDir}`);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Perform automatic PATH update
|
|
521
|
+
*/
|
|
522
|
+
async performAutoPATHUpdate(missingPaths) {
|
|
523
|
+
try {
|
|
524
|
+
if (this.platform === 'win32') {
|
|
525
|
+
return await this.performWindowsPATHUpdate(missingPaths);
|
|
526
|
+
} else {
|
|
527
|
+
return await this.performUnixPATHUpdate(missingPaths);
|
|
528
|
+
}
|
|
529
|
+
} catch (error) {
|
|
530
|
+
return {
|
|
531
|
+
success: false,
|
|
532
|
+
error: error.message
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Perform Windows PATH update
|
|
539
|
+
*/
|
|
540
|
+
async performWindowsPATHUpdate(missingPaths) {
|
|
541
|
+
try {
|
|
542
|
+
const { spawnSync } = require('child_process');
|
|
543
|
+
|
|
544
|
+
console.log('[DETECTOR] Windows: Updating user PATH environment variable...');
|
|
545
|
+
|
|
546
|
+
// Use PowerShell to update user PATH permanently
|
|
547
|
+
const pathsToAdd = missingPaths.join(';');
|
|
548
|
+
const psCommand = `
|
|
549
|
+
$currentPath = [Environment]::GetEnvironmentVariable("PATH", "User")
|
|
550
|
+
$newPaths = @(${missingPaths.map(p => `"${p}"`).join(',')})
|
|
551
|
+
foreach ($path in $newPaths) {
|
|
552
|
+
if ($currentPath -notlike "*$path*") {
|
|
553
|
+
$currentPath = $currentPath + ";" + $path
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
[Environment]::SetEnvironmentVariable("PATH", $currentPath, "User")
|
|
557
|
+
Write-Output "PATH updated successfully"
|
|
558
|
+
`;
|
|
559
|
+
|
|
560
|
+
const result = spawnSync('powershell', ['-Command', psCommand], {
|
|
561
|
+
stdio: 'pipe',
|
|
562
|
+
shell: true,
|
|
563
|
+
encoding: 'utf8',
|
|
564
|
+
timeout: 30000
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
if (result.status === 0) {
|
|
568
|
+
console.log('[DETECTOR] ✓ Windows PATH updated successfully');
|
|
569
|
+
console.log('[DETECTOR] ℹ Note: Restart terminal or run refreshenv to apply changes');
|
|
570
|
+
return { success: true };
|
|
571
|
+
} else {
|
|
572
|
+
console.log('[DETECTOR] ✗ Windows PATH update failed');
|
|
573
|
+
console.log(`[DETECTOR] Error: ${result.stderr || result.stdout}`);
|
|
574
|
+
return {
|
|
575
|
+
success: false,
|
|
576
|
+
error: result.stderr || result.stdout || 'Unknown PowerShell error'
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
} catch (error) {
|
|
580
|
+
return { success: false, error: error.message };
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Perform Unix/Linux/macOS PATH update
|
|
586
|
+
*/
|
|
587
|
+
async performUnixPATHUpdate(missingPaths) {
|
|
588
|
+
try {
|
|
589
|
+
const fs = require('fs').promises;
|
|
590
|
+
const { spawnSync } = require('child_process');
|
|
591
|
+
|
|
592
|
+
console.log('[DETECTOR] Unix: Updating shell profile...');
|
|
593
|
+
|
|
594
|
+
// Determine which shell profile to update
|
|
595
|
+
const shellProfile = await this.determineShellProfile();
|
|
596
|
+
|
|
597
|
+
if (!shellProfile) {
|
|
598
|
+
return {
|
|
599
|
+
success: false,
|
|
600
|
+
error: 'Could not determine shell profile to update'
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Read existing profile
|
|
605
|
+
let profileContent = '';
|
|
606
|
+
try {
|
|
607
|
+
profileContent = await fs.readFile(shellProfile, 'utf8');
|
|
608
|
+
} catch (error) {
|
|
609
|
+
// File doesn't exist, create it
|
|
610
|
+
profileContent = '';
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Add missing paths to profile
|
|
614
|
+
const pathExports = missingPaths.map(path => `export PATH="$PATH:${path}"`).join('\n');
|
|
615
|
+
|
|
616
|
+
// Check if paths are already in the profile
|
|
617
|
+
const pathsToAdd = missingPaths.filter(path => !profileContent.includes(path));
|
|
618
|
+
|
|
619
|
+
if (pathsToAdd.length === 0) {
|
|
620
|
+
console.log('[DETECTOR] ✓ All paths already present in shell profile');
|
|
621
|
+
return { success: true };
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const newPathExports = pathsToAdd.map(path => `export PATH="$PATH:${path}"`).join('\n');
|
|
625
|
+
const contentToAdd = `\n# Added by Stigmergy CLI - ${new Date().toISOString()}\n${newPathExports}\n`;
|
|
626
|
+
|
|
627
|
+
await fs.writeFile(shellProfile, profileContent + contentToAdd, 'utf8');
|
|
628
|
+
|
|
629
|
+
console.log(`[DETECTOR] ✓ Updated ${shellProfile} with PATH additions`);
|
|
630
|
+
console.log('[DETECTOR] ℹ Note: Restart terminal or run source ~/.bashrc to apply changes');
|
|
631
|
+
|
|
632
|
+
return { success: true };
|
|
633
|
+
} catch (error) {
|
|
634
|
+
return { success: false, error: error.message };
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Determine which shell profile to update
|
|
640
|
+
*/
|
|
641
|
+
async determineShellProfile() {
|
|
642
|
+
const fs = require('fs').promises;
|
|
643
|
+
const os = require('os');
|
|
644
|
+
|
|
645
|
+
const homeDir = os.homedir();
|
|
646
|
+
const possibleProfiles = [
|
|
647
|
+
path.join(homeDir, '.bashrc'),
|
|
648
|
+
path.join(homeDir, '.zshrc'),
|
|
649
|
+
path.join(homeDir, '.profile'),
|
|
650
|
+
path.join(homeDir, '.bash_profile')
|
|
651
|
+
];
|
|
652
|
+
|
|
653
|
+
// First check which shell is currently being used
|
|
654
|
+
const shellEnv = process.env.SHELL;
|
|
655
|
+
if (shellEnv) {
|
|
656
|
+
if (shellEnv.includes('zsh')) {
|
|
657
|
+
return path.join(homeDir, '.zshrc');
|
|
658
|
+
} else if (shellEnv.includes('bash')) {
|
|
659
|
+
return path.join(homeDir, '.bashrc');
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Fall back to checking which profile exists
|
|
664
|
+
for (const profile of possibleProfiles) {
|
|
665
|
+
try {
|
|
666
|
+
await fs.access(profile);
|
|
667
|
+
return profile;
|
|
668
|
+
} catch {
|
|
669
|
+
// File doesn't exist, continue checking
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// Default to .bashrc for most systems
|
|
674
|
+
return path.join(homeDir, '.bashrc');
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Get detected path for a specific tool
|
|
679
|
+
*/
|
|
680
|
+
getDetectedPath(toolName) {
|
|
681
|
+
return this.detectedPaths[toolName] || null;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Get status report of detected paths
|
|
686
|
+
*/
|
|
687
|
+
getPathStatusReport() {
|
|
688
|
+
const report = {
|
|
689
|
+
platform: this.platform,
|
|
690
|
+
npmGlobalPaths: this.npmGlobalPaths,
|
|
691
|
+
detectedPaths: this.detectedPaths,
|
|
692
|
+
summary: {
|
|
693
|
+
total: Object.keys(this.cliNameMap).length,
|
|
694
|
+
found: Object.values(this.detectedPaths).filter(Boolean).length,
|
|
695
|
+
missing: Object.values(this.detectedPaths).filter(v => !v).length
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
return report;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
710
703
|
module.exports = CLIPathDetector;
|