stigmergy 1.2.8 → 1.2.11

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 +40 -6
  2. package/STIGMERGY.md +10 -0
  3. package/package.json +19 -5
  4. package/scripts/preuninstall.js +10 -0
  5. package/src/adapters/claude/install_claude_integration.js +21 -21
  6. package/src/adapters/codebuddy/install_codebuddy_integration.js +54 -51
  7. package/src/adapters/codex/install_codex_integration.js +27 -28
  8. package/src/adapters/gemini/install_gemini_integration.js +60 -60
  9. package/src/adapters/iflow/install_iflow_integration.js +72 -72
  10. package/src/adapters/qoder/install_qoder_integration.js +64 -64
  11. package/src/adapters/qwen/install_qwen_integration.js +7 -7
  12. package/src/cli/router.js +581 -175
  13. package/src/commands/skill-bridge.js +39 -0
  14. package/src/commands/skill-handler.js +150 -0
  15. package/src/commands/skill.js +127 -0
  16. package/src/core/cli_path_detector.js +710 -0
  17. package/src/core/cli_tools.js +72 -1
  18. package/src/core/coordination/nodejs/AdapterManager.js +29 -1
  19. package/src/core/directory_permission_manager.js +568 -0
  20. package/src/core/enhanced_cli_installer.js +609 -0
  21. package/src/core/installer.js +232 -88
  22. package/src/core/multilingual/language-pattern-manager.js +78 -50
  23. package/src/core/persistent_shell_configurator.js +468 -0
  24. package/src/core/skills/StigmergySkillManager.js +357 -0
  25. package/src/core/skills/__tests__/SkillInstaller.test.js +275 -0
  26. package/src/core/skills/__tests__/SkillParser.test.js +202 -0
  27. package/src/core/skills/__tests__/SkillReader.test.js +189 -0
  28. package/src/core/skills/cli-command-test.js +201 -0
  29. package/src/core/skills/comprehensive-e2e-test.js +473 -0
  30. package/src/core/skills/e2e-test.js +267 -0
  31. package/src/core/skills/embedded-openskills/SkillInstaller.js +438 -0
  32. package/src/core/skills/embedded-openskills/SkillParser.js +123 -0
  33. package/src/core/skills/embedded-openskills/SkillReader.js +143 -0
  34. package/src/core/skills/integration-test.js +248 -0
  35. package/src/core/skills/package.json +6 -0
  36. package/src/core/skills/regression-test.js +285 -0
  37. package/src/core/skills/run-all-tests.js +129 -0
  38. package/src/core/skills/sync-test.js +210 -0
  39. package/src/core/skills/test-runner.js +242 -0
  40. package/src/utils/helpers.js +3 -20
  41. package/src/auth.js +0 -173
  42. package/src/auth_command.js +0 -208
  43. package/src/calculator.js +0 -313
  44. package/src/core/enhanced_installer.js +0 -479
  45. package/src/core/enhanced_uninstaller.js +0 -638
  46. package/src/data_encryption.js +0 -143
  47. package/src/data_structures.js +0 -440
  48. package/src/deploy.js +0 -55
@@ -0,0 +1,473 @@
1
+ /**
2
+ * Comprehensive End-to-End Test - Stigmergy Skills System
3
+ *
4
+ * Test Flow:
5
+ * 1. Clean all caches
6
+ * 2. Advanced uninstall
7
+ * 3. Uninstall partial CLI
8
+ * 4. Install stigmergy package
9
+ * 5. Test stigmergy commands one by one
10
+ * 6. Test CLI cross-calls
11
+ * 7. Test CLI skill usage
12
+ */
13
+
14
+ import { execSync, spawnSync } from 'child_process';
15
+ import fs from 'fs/promises';
16
+ import path from 'path';
17
+ import os from 'os';
18
+
19
+ class ComprehensiveE2ETestRunner {
20
+ constructor() {
21
+ this.passed = 0;
22
+ this.failed = 0;
23
+ this.skipped = 0;
24
+ this.testResults = [];
25
+ // Fix Windows path issue
26
+ const filePath = new URL(import.meta.url).pathname;
27
+ // Windows paths need to remove leading /
28
+ const normalizedPath = process.platform === 'win32' && filePath.startsWith('/')
29
+ ? filePath.substring(1)
30
+ : filePath;
31
+ this.projectRoot = path.resolve(path.dirname(normalizedPath), '../../..');
32
+ console.log(`[INFO] Project root: ${this.projectRoot}\n`);
33
+ }
34
+
35
+ async test(name, fn, options = {}) {
36
+ const startTime = Date.now();
37
+ console.log(`\n${'='.repeat(60)}`);
38
+ console.log(`[LIST] Test: ${name}`);
39
+ console.log('='.repeat(60));
40
+
41
+ try {
42
+ await fn();
43
+ const duration = Date.now() - startTime;
44
+ this.passed++;
45
+ console.log(`[OK] Passed (${duration}ms)`);
46
+ this.testResults.push({
47
+ name,
48
+ status: 'passed',
49
+ duration,
50
+ category: options.category || 'general'
51
+ });
52
+ } catch (err) {
53
+ const duration = Date.now() - startTime;
54
+
55
+ if (options.allowSkip && err.message.includes('SKIP')) {
56
+ this.skipped++;
57
+ console.log(`[INFO] Skipped: ${err.message}`);
58
+ this.testResults.push({
59
+ name,
60
+ status: 'skipped',
61
+ duration,
62
+ reason: err.message,
63
+ category: options.category || 'general'
64
+ });
65
+ } else {
66
+ this.failed++;
67
+ console.error(`[X] Failed: ${err.message}`);
68
+ if (options.verbose) {
69
+ console.error(err.stack);
70
+ }
71
+ this.testResults.push({
72
+ name,
73
+ status: 'failed',
74
+ duration,
75
+ error: err.message,
76
+ category: options.category || 'general'
77
+ });
78
+ }
79
+ }
80
+ }
81
+
82
+ exec(command, options = {}) {
83
+ console.log(` [INFO] Executing: ${command}`);
84
+ try {
85
+ const result = execSync(command, {
86
+ encoding: 'utf-8',
87
+ cwd: this.projectRoot,
88
+ timeout: options.timeout || 30000,
89
+ stdio: options.silent ? 'pipe' : 'inherit',
90
+ shell: true, // Ensure using shell on Windows
91
+ ...options
92
+ });
93
+ return result;
94
+ } catch (err) {
95
+ if (options.allowFail) {
96
+ return null;
97
+ }
98
+ throw err;
99
+ }
100
+ }
101
+
102
+ summary() {
103
+ console.log(`\n${'='.repeat(60)}`);
104
+ console.log('[LIST] Complete Test Report');
105
+ console.log('='.repeat(60));
106
+
107
+ const total = this.passed + this.failed + this.skipped;
108
+ console.log(`\nTotal: ${total} tests`);
109
+ console.log(`[OK] Passed: ${this.passed}`);
110
+ console.log(`[X] Failed: ${this.failed}`);
111
+ console.log(`[INFO] Skipped: ${this.skipped}`);
112
+
113
+ // Group by category
114
+ const byCategory = {};
115
+ for (const result of this.testResults) {
116
+ const cat = result.category;
117
+ if (!byCategory[cat]) {
118
+ byCategory[cat] = { passed: 0, failed: 0, skipped: 0 };
119
+ }
120
+ byCategory[cat][result.status]++;
121
+ }
122
+
123
+ console.log('\n[LIST] Statistics by Category:');
124
+ for (const [category, stats] of Object.entries(byCategory)) {
125
+ console.log(` ${category}: [OK]${stats.passed} [X]${stats.failed} [INFO]${stats.skipped}`);
126
+ }
127
+
128
+ console.log('\n' + '='.repeat(60));
129
+
130
+ return this.failed === 0;
131
+ }
132
+ }
133
+
134
+ async function runComprehensiveTests() {
135
+ const runner = new ComprehensiveE2ETestRunner();
136
+
137
+ console.log('[SUCCESS] Stigmergy Skills Comprehensive End-to-End Test');
138
+ console.log('Test Scope: Cache Clean -> Install -> Command Test -> Cross-Call -> Skill Usage\n');
139
+
140
+ // ==================== Phase 1: Environment Preparation ====================
141
+ console.log('\n' + '='.repeat(60));
142
+ console.log('Phase 1: Environment Preparation and Cleanup');
143
+ console.log('='.repeat(60));
144
+
145
+ await runner.test('Clean npm cache', () => {
146
+ runner.exec('npm cache clean --force', { silent: true, allowFail: true });
147
+ }, { category: 'Environment Prep' });
148
+
149
+ await runner.test('Clean project cache', () => {
150
+ runner.exec('node src/index.js clean', { silent: true, allowFail: true });
151
+ }, { category: 'Environment Prep' });
152
+
153
+ await runner.test('Clean temporary files', async () => {
154
+ const tempDirs = [
155
+ path.join(os.tmpdir(), 'stigmergy-*'),
156
+ path.join(runner.projectRoot, 'temp'),
157
+ path.join(runner.projectRoot, 'coverage')
158
+ ];
159
+
160
+ for (const pattern of tempDirs) {
161
+ try {
162
+ if (!pattern.includes('*')) {
163
+ await fs.rm(pattern, { recursive: true, force: true });
164
+ }
165
+ } catch (err) {
166
+ // Ignore non-existent directories
167
+ }
168
+ }
169
+ }, { category: 'Environment Prep' });
170
+
171
+ // ==================== Phase 2: Core Command Testing ====================
172
+ console.log('\n' + '='.repeat(60));
173
+ console.log('Phase 2: Stigmergy Core Command Testing');
174
+ console.log('='.repeat(60));
175
+
176
+ await runner.test('stigmergy --version', () => {
177
+ const output = runner.exec('node bin/stigmergy --version', { silent: true });
178
+ if (!output.includes('1.2')) {
179
+ throw new Error('Version number incorrect');
180
+ }
181
+ }, { category: 'Core Commands' });
182
+
183
+ await runner.test('stigmergy --help', () => {
184
+ const output = runner.exec('node bin/stigmergy --help', { silent: true });
185
+ if (!output.includes('skill')) {
186
+ throw new Error('Help output missing skill command');
187
+ }
188
+ }, { category: 'Core Commands' });
189
+
190
+ await runner.test('stigmergy diagnostic', () => {
191
+ runner.exec('node bin/stigmergy d', { timeout: 60000 });
192
+ }, { category: 'Core Commands' });
193
+
194
+ await runner.test('stigmergy status', () => {
195
+ runner.exec('node bin/stigmergy status', { timeout: 60000 });
196
+ }, { category: 'Core Commands' });
197
+
198
+ // ==================== Phase 3: Skill System Command Testing ====================
199
+ console.log('\n' + '='.repeat(60));
200
+ console.log('Phase 3: Skill System Command Testing');
201
+ console.log('='.repeat(60));
202
+
203
+ await runner.test('skill list (full command)', () => {
204
+ runner.exec('node bin/stigmergy skill list');
205
+ }, { category: 'Skill Commands' });
206
+
207
+ await runner.test('skill-l (short command)', () => {
208
+ runner.exec('node bin/stigmergy skill-l');
209
+ }, { category: 'Skill Commands' });
210
+
211
+ await runner.test('skill sync (full command)', () => {
212
+ runner.exec('node bin/stigmergy skill sync');
213
+ }, { category: 'Skill Commands' });
214
+
215
+ await runner.test('skill (short command - default sync)', () => {
216
+ runner.exec('node bin/stigmergy skill');
217
+ }, { category: 'Skill Commands' });
218
+
219
+ await runner.test('skill-r (read skill)', async () => {
220
+ // Get skill list first
221
+ const listOutput = runner.exec('node bin/stigmergy skill-l', { silent: true });
222
+
223
+ // Extract first skill name
224
+ const match = listOutput.match(/• ([a-z-]+)/);
225
+ if (!match) {
226
+ throw new Error('SKIP: No skills installed');
227
+ }
228
+
229
+ const skillName = match[1];
230
+ console.log(` [INFO] Reading skill: ${skillName}`);
231
+
232
+ runner.exec(`node bin/stigmergy skill-r ${skillName}`, { timeout: 10000 });
233
+ }, { category: 'Skill Commands', allowSkip: true });
234
+
235
+ await runner.test('skill-v (validate skill)', async () => {
236
+ // Find first SKILL.md file
237
+ const skillsDir = path.join(os.homedir(), '.claude/skills');
238
+ try {
239
+ const dirs = await fs.readdir(skillsDir);
240
+ if (dirs.length === 0) {
241
+ throw new Error('SKIP: No skill directories found');
242
+ }
243
+
244
+ const firstSkill = dirs[0];
245
+ const skillPath = path.join(skillsDir, firstSkill, 'SKILL.md');
246
+
247
+ runner.exec(`node bin/stigmergy skill-v ${skillPath}`, { timeout: 10000, allowFail: true });
248
+ } catch (err) {
249
+ throw new Error('SKIP: Cannot access skills directory');
250
+ }
251
+ }, { category: 'Skill Commands', allowSkip: true });
252
+
253
+ // ==================== Phase 4: Config File Sync Verification ====================
254
+ console.log('\n' + '='.repeat(60));
255
+ console.log('Phase 4: Config File Sync Verification');
256
+ console.log('='.repeat(60));
257
+
258
+ const cliConfigFiles = [
259
+ 'AGENTS.md', 'claude.md', 'qwen.md', 'gemini.md',
260
+ 'iflow.md', 'qodercli.md', 'codebuddy.md', 'copilot.md', 'codex.md'
261
+ ];
262
+
263
+ for (const fileName of cliConfigFiles) {
264
+ await runner.test(`Verify ${fileName} sync`, async () => {
265
+ const filePath = path.join(runner.projectRoot, fileName);
266
+
267
+ try {
268
+ const content = await fs.readFile(filePath, 'utf-8');
269
+
270
+ if (!content.includes('<!-- SKILLS_START -->')) {
271
+ console.log(` [INFO] ${fileName} has no skills section (may not be synced yet)`);
272
+ return;
273
+ }
274
+
275
+ if (!content.includes('<!-- SKILLS_END -->')) {
276
+ throw new Error('Missing SKILLS_END marker');
277
+ }
278
+
279
+ if (!content.includes('<available_skills>')) {
280
+ throw new Error('Missing available_skills tag');
281
+ }
282
+
283
+ console.log(` [OK] ${fileName} format correct`);
284
+ } catch (err) {
285
+ if (err.code === 'ENOENT') {
286
+ throw new Error(`SKIP: ${fileName} does not exist`);
287
+ }
288
+ throw err;
289
+ }
290
+ }, { category: 'Config Sync', allowSkip: true });
291
+ }
292
+
293
+ // ==================== Phase 5: CLI Tool Availability Check ====================
294
+ console.log('\n' + '='.repeat(60));
295
+ console.log('Phase 5: CLI Tool Availability Check');
296
+ console.log('='.repeat(60));
297
+
298
+ const cliTools = ['claude', 'qwen', 'gemini', 'iflow', 'qodercli', 'codebuddy', 'copilot', 'codex'];
299
+ const availableCLIs = [];
300
+
301
+ for (const tool of cliTools) {
302
+ await runner.test(`Check ${tool} CLI`, () => {
303
+ try {
304
+ const result = spawnSync(process.platform === 'win32' ? 'where' : 'which', [tool], {
305
+ encoding: 'utf-8',
306
+ timeout: 3000,
307
+ stdio: 'pipe'
308
+ });
309
+
310
+ if (result.status === 0 && result.stdout.trim()) {
311
+ console.log(` [OK] ${tool} installed: ${result.stdout.trim().split('\n')[0]}`);
312
+ availableCLIs.push(tool);
313
+ } else {
314
+ throw new Error(`SKIP: ${tool} not installed`);
315
+ }
316
+ } catch (err) {
317
+ throw new Error(`SKIP: ${tool} not installed`);
318
+ }
319
+ }, { category: 'CLI Availability', allowSkip: true });
320
+ }
321
+
322
+ console.log(`\n [LIST] Available CLIs: ${availableCLIs.length}/${cliTools.length}`);
323
+
324
+ // ==================== Phase 6: Cross-Call Testing ====================
325
+ console.log('\n' + '='.repeat(60));
326
+ console.log('Phase 6: CLI Cross-Call Testing');
327
+ console.log('='.repeat(60));
328
+
329
+ if (availableCLIs.length === 0) {
330
+ console.log('[INFO] No available CLI tools, skipping cross-call tests');
331
+ } else {
332
+ for (const cli of availableCLIs.slice(0, 3)) { // Test first 3 available CLIs
333
+ await runner.test(`Cross-call ${cli}`, () => {
334
+ try {
335
+ // Use simple test prompt
336
+ const testPrompt = 'hello';
337
+ console.log(` [INFO] Test prompt: "${testPrompt}"`);
338
+
339
+ runner.exec(`node bin/stigmergy ${cli} "${testPrompt}"`, {
340
+ timeout: 30000,
341
+ allowFail: true,
342
+ stdio: 'ignore'
343
+ });
344
+
345
+ console.log(` [OK] ${cli} cross-call successful`);
346
+ } catch (err) {
347
+ // Some CLIs may require authentication, not a failure
348
+ if (err.message.includes('authentication') || err.message.includes('login')) {
349
+ throw new Error(`SKIP: ${cli} requires authentication`);
350
+ }
351
+ throw err;
352
+ }
353
+ }, { category: 'CLI Cross-Call', allowSkip: true });
354
+ }
355
+ }
356
+
357
+ // ==================== Phase 7: Skill Usage Testing ====================
358
+ console.log('\n' + '='.repeat(60));
359
+ console.log('Phase 7: Skill Usage Testing');
360
+ console.log('='.repeat(60));
361
+
362
+ await runner.test('Read skill from local', async () => {
363
+ const listOutput = runner.exec('node bin/stigmergy skill-l', { silent: true });
364
+
365
+ if (!listOutput.includes('•')) {
366
+ throw new Error('SKIP: No skills installed');
367
+ }
368
+
369
+ // Extract first skill name
370
+ const match = listOutput.match(/• ([a-z-]+)/);
371
+ if (!match) {
372
+ throw new Error('SKIP: Cannot parse skill name');
373
+ }
374
+
375
+ const skillName = match[1];
376
+ console.log(` [INFO] Testing skill: ${skillName}`);
377
+
378
+ const readOutput = runner.exec(`node bin/stigmergy skill-r ${skillName}`, {
379
+ silent: true,
380
+ timeout: 15000
381
+ });
382
+
383
+ if (!readOutput.includes('---')) {
384
+ throw new Error('Skill content format incorrect');
385
+ }
386
+
387
+ console.log(` [OK] Skill read successfully, content length: ${readOutput.length} characters`);
388
+ }, { category: 'Skill Usage', allowSkip: true });
389
+
390
+ await runner.test('Skill install test (simulated)', async () => {
391
+ // Test install command syntax and parameter handling
392
+ try {
393
+ const output = runner.exec('node bin/stigmergy skill install --help', {
394
+ silent: true,
395
+ allowFail: true,
396
+ timeout: 5000
397
+ });
398
+
399
+ console.log(' [OK] skill install command available');
400
+ } catch (err) {
401
+ // If no --help, try empty parameters
402
+ try {
403
+ runner.exec('node bin/stigmergy skill-i', {
404
+ silent: true,
405
+ allowFail: true,
406
+ timeout: 5000
407
+ });
408
+ } catch (innerErr) {
409
+ // Expected to fail (due to missing parameters), but should not be syntax error
410
+ if (innerErr.message.includes('SyntaxError')) {
411
+ throw new Error('Command has syntax error');
412
+ }
413
+ }
414
+ console.log(' [OK] skill-i short command available');
415
+ }
416
+ }, { category: 'Skill Usage' });
417
+
418
+ // ==================== Phase 8: Unit Test Regression ====================
419
+ console.log('\n' + '='.repeat(60));
420
+ console.log('Phase 8: Unit Test Regression');
421
+ console.log('='.repeat(60));
422
+
423
+ await runner.test('SkillParser unit test', () => {
424
+ runner.exec('node src/core/skills/test-runner.js', { timeout: 30000 });
425
+ }, { category: 'Unit Tests' });
426
+
427
+ await runner.test('Integration test', () => {
428
+ runner.exec('node src/core/skills/integration-test.js', { timeout: 60000 });
429
+ }, { category: 'Integration Tests' });
430
+
431
+ await runner.test('Sync functionality test', () => {
432
+ runner.exec('node src/core/skills/sync-test.js', { timeout: 30000 });
433
+ }, { category: 'Integration Tests' });
434
+
435
+ // ==================== Test Summary ====================
436
+ const success = runner.summary();
437
+
438
+ // Generate test report file
439
+ const reportPath = path.join(runner.projectRoot, `comprehensive-e2e-report-${Date.now()}.json`);
440
+ await fs.writeFile(
441
+ reportPath,
442
+ JSON.stringify({
443
+ timestamp: new Date().toISOString(),
444
+ summary: {
445
+ total: runner.passed + runner.failed + runner.skipped,
446
+ passed: runner.passed,
447
+ failed: runner.failed,
448
+ skipped: runner.skipped
449
+ },
450
+ availableCLIs,
451
+ results: runner.testResults
452
+ }, null, 2)
453
+ );
454
+
455
+ console.log(`\n[INFO] Detailed report saved: ${reportPath}`);
456
+
457
+ if (success) {
458
+ console.log('\n[SUCCESS] All critical tests passed!');
459
+ return 0;
460
+ } else {
461
+ console.log('\n[INFO] Some tests failed, please review detailed report');
462
+ return 1;
463
+ }
464
+ }
465
+
466
+ // Run comprehensive tests
467
+ console.log('Starting comprehensive end-to-end tests...\n');
468
+ runComprehensiveTests()
469
+ .then(code => process.exit(code))
470
+ .catch(err => {
471
+ console.error('[X] Test execution failed:', err);
472
+ process.exit(1);
473
+ });