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,285 @@
1
+ /**
2
+ * Regression Test - Ensure new skill system doesn't affect existing functionality
3
+ *
4
+ * Test Strategy:
5
+ * 1. Verify existing commands still work
6
+ * 2. Verify existing config files are not corrupted
7
+ * 3. Verify existing adapters are not affected
8
+ * 4. Verify existing AGENTS.md format remains compatible
9
+ */
10
+
11
+ import { execSync } from 'child_process';
12
+ import fs from 'fs/promises';
13
+ import path from 'path';
14
+ import os from 'os';
15
+
16
+ class RegressionTestRunner {
17
+ constructor() {
18
+ this.passed = 0;
19
+ this.failed = 0;
20
+ this.testDir = path.join(os.tmpdir(), `regression-test-${Date.now()}`);
21
+ }
22
+
23
+ async test(name, fn) {
24
+ try {
25
+ await fn();
26
+ this.passed++;
27
+ console.log(`[OK] ${name}`);
28
+ } catch (err) {
29
+ this.failed++;
30
+ console.error(`[X] ${name}`);
31
+ console.error(` ${err.message}`);
32
+ }
33
+ }
34
+
35
+ summary() {
36
+ console.log(`\n${'='.repeat(60)}`);
37
+ console.log(`Regression Test Total: ${this.passed + this.failed}`);
38
+ console.log(`[OK] Passed: ${this.passed}`);
39
+ console.log(`[X] Failed: ${this.failed}`);
40
+ console.log('='.repeat(60));
41
+ return this.failed === 0;
42
+ }
43
+ }
44
+
45
+ async function runRegressionTests() {
46
+ const runner = new RegressionTestRunner();
47
+
48
+ // Get project root directory (from src/core/skills to root)
49
+ const projectRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '../../..');
50
+
51
+ console.log('[INFO] Regression Test - Ensure existing functionality is not affected\n');
52
+ console.log(`Project root: ${projectRoot}\n`);
53
+
54
+ // ===== Test 1: Verify stigmergy main command still executable =====
55
+ await runner.test('stigmergy main command executable', () => {
56
+ try {
57
+ execSync('node src/index.js --help', {
58
+ stdio: 'pipe',
59
+ cwd: path.join(process.cwd(), '../../..')
60
+ });
61
+ } catch (err) {
62
+ throw new Error('stigmergy command execution failed');
63
+ }
64
+ });
65
+
66
+ // ===== Test 2: Verify existing subcommands not affected =====
67
+ await runner.test('status command still works', () => {
68
+ try {
69
+ const output = execSync('node src/index.js status', {
70
+ stdio: 'pipe',
71
+ cwd: path.join(process.cwd(), '../../..'),
72
+ encoding: 'utf-8'
73
+ });
74
+ // Pass if executes without error
75
+ } catch (err) {
76
+ // status command may fail due to missing config, but should not be syntax error
77
+ if (err.message.includes('SyntaxError') || err.message.includes('MODULE_NOT_FOUND')) {
78
+ throw new Error('status command has syntax or module error');
79
+ }
80
+ }
81
+ });
82
+
83
+ // ===== Test 3: Verify AGENTS.md format backward compatible =====
84
+ await runner.test('AGENTS.md format backward compatible', async () => {
85
+ const agentsMdPath = path.join(process.cwd(), '../../../AGENTS.md');
86
+
87
+ try {
88
+ const content = await fs.readFile(agentsMdPath, 'utf-8');
89
+
90
+ // Verify no breaking changes
91
+ assert(!content.includes('BREAKING_CHANGE'), 'AGENTS.md contains breaking changes');
92
+
93
+ // If skills section exists, verify format is correct
94
+ if (content.includes('<available_skills>')) {
95
+ assert(content.includes('</available_skills>'), 'Missing closing tag');
96
+ }
97
+ } catch (err) {
98
+ if (err.code === 'ENOENT') {
99
+ // AGENTS.md not existing is acceptable
100
+ } else {
101
+ throw err;
102
+ }
103
+ }
104
+ });
105
+
106
+ // ===== Test 3.5: Verify all CLI config files sync =====
107
+ await runner.test('All CLI config files sync integrity', async () => {
108
+ const cliFiles = [
109
+ 'AGENTS.md', 'claude.md', 'qwen.md', 'gemini.md',
110
+ 'iflow.md', 'qodercli.md', 'codebuddy.md', 'copilot.md', 'codex.md'
111
+ ];
112
+
113
+ for (const fileName of cliFiles) {
114
+ const filePath = path.join(process.cwd(), '../../../', fileName);
115
+
116
+ try {
117
+ const content = await fs.readFile(filePath, 'utf-8');
118
+
119
+ // Verify skills section exists and format is correct
120
+ if (content.includes('<available_skills>')) {
121
+ assert(content.includes('</available_skills>'), `${fileName} missing closing tag`);
122
+ assert(content.includes('<!-- SKILLS_START -->'), `${fileName} missing start marker`);
123
+ assert(content.includes('<!-- SKILLS_END -->'), `${fileName} missing end marker`);
124
+ }
125
+ } catch (err) {
126
+ if (err.code === 'ENOENT') {
127
+ // File not existing is acceptable (user may not have that CLI installed)
128
+ continue;
129
+ } else {
130
+ throw err;
131
+ }
132
+ }
133
+ }
134
+ });
135
+
136
+ // ===== Test 4: Verify existing config files not corrupted =====
137
+ await runner.test('Existing config files integrity', async () => {
138
+ const configPaths = [
139
+ path.join(os.homedir(), '.stigmergy', 'config.json'),
140
+ path.join(process.cwd(), '../../../.stigmergy-project', 'stigmergy-config.json')
141
+ ];
142
+
143
+ for (const configPath of configPaths) {
144
+ try {
145
+ const content = await fs.readFile(configPath, 'utf-8');
146
+ JSON.parse(content); // Verify JSON format
147
+ } catch (err) {
148
+ if (err.code !== 'ENOENT') {
149
+ throw new Error(`Config file corrupted: ${configPath}`);
150
+ }
151
+ }
152
+ }
153
+ });
154
+
155
+ // ===== Test 5: Verify adapter directory structure unchanged =====
156
+ await runner.test('Adapter directory structure intact', async () => {
157
+ const adaptersDir = path.join(process.cwd(), '../../../src/adapters');
158
+
159
+ try {
160
+ const entries = await fs.readdir(adaptersDir);
161
+
162
+ // Verify key adapters exist
163
+ const requiredAdapters = ['claude', 'qwen', 'gemini', 'iflow'];
164
+ for (const adapter of requiredAdapters) {
165
+ assert(entries.includes(adapter), `Missing adapter: ${adapter}`);
166
+ }
167
+ } catch (err) {
168
+ throw new Error('Adapter directory structure changed');
169
+ }
170
+ });
171
+
172
+ // ===== Test 6: Verify no interference with existing skills directory =====
173
+ await runner.test('No interference with existing .claude/skills directory', async () => {
174
+ const claudeSkillsDir = path.join(os.homedir(), '.claude', 'skills');
175
+
176
+ try {
177
+ // Check if exists
178
+ await fs.access(claudeSkillsDir);
179
+
180
+ // List existing skills
181
+ const before = await fs.readdir(claudeSkillsDir);
182
+
183
+ // New system should not modify existing directory (unless explicitly installed)
184
+ // Only verify directory is still accessible
185
+ assert(Array.isArray(before), 'Existing skills directory not accessible');
186
+ } catch (err) {
187
+ if (err.code !== 'ENOENT') {
188
+ throw err;
189
+ }
190
+ // Directory not existing is normal
191
+ }
192
+ });
193
+
194
+ // ===== Test 7: Verify package.json integrity =====
195
+ await runner.test('package.json integrity', async () => {
196
+ const pkgPath = path.join(process.cwd(), '../../../package.json');
197
+ const content = await fs.readFile(pkgPath, 'utf-8');
198
+ const pkg = JSON.parse(content);
199
+
200
+ // Verify key fields exist
201
+ assert(pkg.name === 'stigmergy-cli', 'package name changed');
202
+ assert(pkg.bin && pkg.bin.stigmergy, 'Missing bin configuration');
203
+ assert(pkg.scripts && pkg.scripts.test, 'Missing test script');
204
+ });
205
+
206
+ // ===== Test 8: Verify existing commands not conflicting =====
207
+ await runner.test('Command names not conflicting', () => {
208
+ const newCommand = 'skill';
209
+ const existingCommands = [
210
+ 'status', 'scan', 'init', 'deploy',
211
+ 'clean', 'validate', 'register', 'login', 'logout'
212
+ ];
213
+
214
+ assert(!existingCommands.includes(newCommand),
215
+ `New command '${newCommand}' conflicts with existing commands`);
216
+ });
217
+
218
+ // ===== Test 9: Verify not affecting existing tests =====
219
+ await runner.test('Existing tests still runnable', () => {
220
+ try {
221
+ // Check test directories exist
222
+ const testDirs = [
223
+ path.join(process.cwd(), '../../../test'),
224
+ path.join(process.cwd(), '../../../tests')
225
+ ];
226
+
227
+ // At least one test directory should exist
228
+ let hasTestDir = false;
229
+ for (const dir of testDirs) {
230
+ try {
231
+ execSync(`test -d "${dir}"`, { stdio: 'ignore' });
232
+ hasTestDir = true;
233
+ break;
234
+ } catch {}
235
+ }
236
+
237
+ assert(hasTestDir, 'Test directory missing');
238
+ } catch (err) {
239
+ // Windows environment may not have test command, skip
240
+ }
241
+ });
242
+
243
+ // ===== Test 10: Verify module imports not breaking existing code =====
244
+ await runner.test('Module imports backward compatible', async () => {
245
+ const indexPath = path.join(process.cwd(), '../../../src/index.js');
246
+
247
+ try {
248
+ const content = await fs.readFile(indexPath, 'utf-8');
249
+
250
+ // Verify no syntax errors (being able to read normally means no obvious errors)
251
+ assert(content.length > 0, 'index.js is empty');
252
+
253
+ // Verify no incompatible ES module imports added (if project is CommonJS)
254
+ if (!content.includes('import ')) {
255
+ // Project is CommonJS, should not add ES imports
256
+ // Our skill system is a standalone ES module, does not affect main project
257
+ }
258
+ } catch (err) {
259
+ throw new Error('index.js read failed');
260
+ }
261
+ });
262
+
263
+ return runner.summary();
264
+ }
265
+
266
+ function assert(condition, message) {
267
+ if (!condition) {
268
+ throw new Error(message || 'Assertion failed');
269
+ }
270
+ }
271
+
272
+ // Run regression tests
273
+ runRegressionTests()
274
+ .then(success => {
275
+ if (success) {
276
+ console.log('\n[OK] All regression tests passed! Existing functionality intact.');
277
+ } else {
278
+ console.error('\n[X] Regression tests failed! Please fix before continuing integration.');
279
+ }
280
+ process.exit(success ? 0 : 1);
281
+ })
282
+ .catch(err => {
283
+ console.error('[X] Regression test execution failed:', err);
284
+ process.exit(1);
285
+ });
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Complete Test Suite Runner - TDD-driven development
3
+ *
4
+ * Test Levels:
5
+ * 1. Unit Tests (14) - Basic functionality
6
+ * 2. Integration Tests (7) - Module cooperation
7
+ * 3. Regression Tests (10) - Existing functionality protection
8
+ *
9
+ * Total: 31 tests
10
+ */
11
+
12
+ import { execSync, spawn } from 'child_process';
13
+ import path from 'path';
14
+
15
+ class TestSuiteRunner {
16
+ constructor() {
17
+ this.results = {
18
+ unit: { passed: 0, failed: 0, name: 'Unit Tests' },
19
+ integration: { passed: 0, failed: 0, name: 'Integration Tests' },
20
+ regression: { passed: 0, failed: 0, name: 'Regression Tests' }
21
+ };
22
+ }
23
+
24
+ async runTest(name, scriptPath) {
25
+ console.log(`\n${'='.repeat(70)}`);
26
+ console.log(`[LIST] Running ${name}`);
27
+ console.log('='.repeat(70));
28
+
29
+ return new Promise((resolve) => {
30
+ const child = spawn('node', [scriptPath], {
31
+ cwd: process.cwd(),
32
+ stdio: 'inherit',
33
+ shell: true
34
+ });
35
+
36
+ child.on('close', (code) => {
37
+ resolve(code === 0);
38
+ });
39
+
40
+ child.on('error', (err) => {
41
+ console.error(`[X] Execution failed: ${err.message}`);
42
+ resolve(false);
43
+ });
44
+ });
45
+ }
46
+
47
+ printSummary() {
48
+ console.log(`\n\n${'='.repeat(70)}`);
49
+ console.log('[LIST] Complete Test Suite Summary');
50
+ console.log('='.repeat(70));
51
+
52
+ let totalPassed = 0;
53
+ let totalFailed = 0;
54
+
55
+ for (const [key, result] of Object.entries(this.results)) {
56
+ const icon = result.failed === 0 ? '[OK]' : '[X]';
57
+ console.log(`${icon} ${result.name}: ${result.passed} passed, ${result.failed} failed`);
58
+ totalPassed += result.passed;
59
+ totalFailed += result.failed;
60
+ }
61
+
62
+ console.log('='.repeat(70));
63
+ console.log(`[LIST] Total: ${totalPassed + totalFailed} tests`);
64
+ console.log(`[OK] Passed: ${totalPassed}`);
65
+ console.log(`[X] Failed: ${totalFailed}`);
66
+ console.log('='.repeat(70));
67
+
68
+ if (totalFailed === 0) {
69
+ console.log('\n[SUCCESS] All tests passed! System ready for integration into main command.');
70
+ console.log('\n[OK] TDD Verification Complete:');
71
+ console.log(' [OK] Basic functionality tests passed (14 unit tests)');
72
+ console.log(' [OK] Module integration tests passed (7 integration tests)');
73
+ console.log(' [OK] Existing functionality protection passed (10 regression tests)');
74
+ console.log('\n[SUCCESS] Safe to integrate into stigmergy main command!');
75
+ } else {
76
+ console.log('\n[X] Some tests failed, please fix before integration.');
77
+ }
78
+
79
+ return totalFailed === 0;
80
+ }
81
+ }
82
+
83
+ async function runAllTests() {
84
+ const runner = new TestSuiteRunner();
85
+
86
+ console.log('[SUCCESS] Stigmergy Skills - Complete Test Suite');
87
+ console.log('TDD-driven, ensuring quality\n');
88
+
89
+ const currentDir = process.cwd();
90
+
91
+ // 1. Unit Tests
92
+ const unitTestSuccess = await runner.runTest(
93
+ 'Unit Tests (SkillParser, SkillReader, SkillInstaller)',
94
+ path.join(currentDir, 'test-runner.js')
95
+ );
96
+ runner.results.unit.passed = unitTestSuccess ? 14 : 0;
97
+ runner.results.unit.failed = unitTestSuccess ? 0 : 14;
98
+
99
+ // 2. Integration Tests
100
+ const integrationTestSuccess = await runner.runTest(
101
+ 'Integration Tests (StigmergySkillManager)',
102
+ path.join(currentDir, 'integration-test.js')
103
+ );
104
+ runner.results.integration.passed = integrationTestSuccess ? 7 : 0;
105
+ runner.results.integration.failed = integrationTestSuccess ? 0 : 7;
106
+
107
+ // 3. Regression Tests
108
+ const regressionTestSuccess = await runner.runTest(
109
+ 'Regression Tests (Existing Functionality Protection)',
110
+ path.join(currentDir, 'regression-test.js')
111
+ );
112
+ runner.results.regression.passed = regressionTestSuccess ? 10 : 0;
113
+ runner.results.regression.failed = regressionTestSuccess ? 0 : 10;
114
+
115
+ // Print summary
116
+ const success = runner.printSummary();
117
+
118
+ return success;
119
+ }
120
+
121
+ // Run all tests
122
+ runAllTests()
123
+ .then(success => {
124
+ process.exit(success ? 0 : 1);
125
+ })
126
+ .catch(err => {
127
+ console.error('\n[X] Test suite execution failed:', err);
128
+ process.exit(1);
129
+ });
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Sync Functionality Test - Verify multi-CLI file sync
3
+ *
4
+ * Test Objectives:
5
+ * 1. Verify sync() updates all 8 CLI config files
6
+ * 2. Verify creation of non-existent files
7
+ * 3. Verify skill content is correctly inserted
8
+ */
9
+
10
+ import { StigmergySkillManager } from './StigmergySkillManager.js';
11
+ import fs from 'fs/promises';
12
+ import path from 'path';
13
+ import os from 'os';
14
+
15
+ class SyncTestRunner {
16
+ constructor() {
17
+ this.testDir = path.join(os.tmpdir(), `stigmergy-sync-test-${Date.now()}`);
18
+ this.manager = new StigmergySkillManager({
19
+ skillsDir: path.join(this.testDir, 'skills')
20
+ });
21
+ this.passed = 0;
22
+ this.failed = 0;
23
+ }
24
+
25
+ async test(name, fn) {
26
+ try {
27
+ await fn();
28
+ this.passed++;
29
+ console.log(`[OK] ${name}`);
30
+ } catch (err) {
31
+ this.failed++;
32
+ console.error(`[X] ${name}`);
33
+ console.error(` Error: ${err.message}`);
34
+ }
35
+ }
36
+
37
+ async setup() {
38
+ console.log('[INFO] Setting up test environment...\n');
39
+
40
+ // Create test directory
41
+ await fs.mkdir(this.testDir, { recursive: true });
42
+
43
+ // Create test skill
44
+ const skillDir = path.join(this.testDir, 'skills', 'test-skill');
45
+ await fs.mkdir(skillDir, { recursive: true });
46
+ await fs.writeFile(
47
+ path.join(skillDir, 'SKILL.md'),
48
+ `---
49
+ name: test-skill
50
+ description: Test skill for sync testing
51
+ version: 1.0.0
52
+ ---
53
+
54
+ # Test Skill
55
+
56
+ This is a test skill.
57
+ `
58
+ );
59
+
60
+ // Create some CLI config files (simulate real scenario)
61
+ const existingFiles = ['AGENTS.md', 'claude.md', 'qwen.md'];
62
+ for (const file of existingFiles) {
63
+ await fs.writeFile(
64
+ path.join(this.testDir, file),
65
+ `# ${file}\n\nExisting content\n`
66
+ );
67
+ }
68
+
69
+ console.log(`[OK] Test environment created at ${this.testDir}\n`);
70
+ }
71
+
72
+ async cleanup() {
73
+ console.log('\n[INFO] Cleaning up...');
74
+ await fs.rm(this.testDir, { recursive: true, force: true });
75
+ console.log('[OK] Cleanup complete');
76
+ }
77
+
78
+ summary() {
79
+ console.log(`\n${'='.repeat(60)}`);
80
+ console.log(`Sync Test Total: ${this.passed + this.failed} tests`);
81
+ console.log(`[OK] Passed: ${this.passed}`);
82
+ console.log(`[X] Failed: ${this.failed}`);
83
+ console.log('='.repeat(60));
84
+ return this.failed === 0;
85
+ }
86
+ }
87
+
88
+ async function runSyncTests() {
89
+ const runner = new SyncTestRunner();
90
+
91
+ try {
92
+ await runner.setup();
93
+
94
+ console.log('[LIST] Running sync tests...\n');
95
+ // Change working directory to test directory
96
+ const originalCwd = process.cwd();
97
+ process.chdir(runner.testDir);
98
+
99
+ try {
100
+ // Test 1: Verify sync() handles all CLI files
101
+ await runner.test('Sync to all CLI config files', async () => {
102
+ await runner.manager.sync();
103
+
104
+ // Verify all files are created or updated
105
+ const cliFiles = [
106
+ 'AGENTS.md', 'claude.md', 'qwen.md', 'gemini.md',
107
+ 'iflow.md', 'qodercli.md', 'codebuddy.md', 'copilot.md', 'codex.md'
108
+ ];
109
+
110
+ for (const file of cliFiles) {
111
+ const exists = await fs.access(path.join(runner.testDir, file))
112
+ .then(() => true)
113
+ .catch(() => false);
114
+ assert(exists, `${file} not found`);
115
+ }
116
+ });
117
+
118
+ // Test 2: Verify skill content correctly inserted into each file
119
+ await runner.test('Verify skill content correctly inserted', async () => {
120
+ const cliFiles = [
121
+ 'AGENTS.md', 'claude.md', 'qwen.md', 'gemini.md',
122
+ 'iflow.md', 'qodercli.md', 'codebuddy.md', 'copilot.md', 'codex.md'
123
+ ];
124
+
125
+ for (const file of cliFiles) {
126
+ const content = await fs.readFile(path.join(runner.testDir, file), 'utf-8');
127
+ assert(content.includes('<!-- SKILLS_START -->'), `${file} missing skills start marker`);
128
+ assert(content.includes('<!-- SKILLS_END -->'), `${file} missing skills end marker`);
129
+ assert(content.includes('<available_skills>'), `${file} missing skills section`);
130
+ assert(content.includes('test-skill'), `${file} missing test-skill`);
131
+ }
132
+ });
133
+
134
+ // Test 3: Verify existing file content preserved
135
+ await runner.test('Verify existing file content preserved', async () => {
136
+ const existingFiles = ['AGENTS.md', 'claude.md', 'qwen.md'];
137
+
138
+ for (const file of existingFiles) {
139
+ const content = await fs.readFile(path.join(runner.testDir, file), 'utf-8');
140
+ assert(content.includes('Existing content'), `${file} lost existing content`);
141
+ }
142
+ });
143
+
144
+ // Test 4: Verify sync operation idempotency
145
+ await runner.test('Verify sync operation idempotency', async () => {
146
+ // Content after first sync
147
+ const firstSync = await fs.readFile(
148
+ path.join(runner.testDir, 'AGENTS.md'),
149
+ 'utf-8'
150
+ );
151
+
152
+ // Sync again
153
+ await runner.manager.sync();
154
+
155
+ // Content after second sync
156
+ const secondSync = await fs.readFile(
157
+ path.join(runner.testDir, 'AGENTS.md'),
158
+ 'utf-8'
159
+ );
160
+
161
+ // Should be identical
162
+ assert(firstSync === secondSync, 'Multiple syncs produced different results');
163
+ });
164
+
165
+ // Test 5: Verify newly created file format
166
+ await runner.test('Verify newly created file format is correct', async () => {
167
+ const newFiles = ['gemini.md', 'iflow.md', 'qodercli.md', 'codebuddy.md', 'copilot.md', 'codex.md'];
168
+
169
+ for (const file of newFiles) {
170
+ const content = await fs.readFile(path.join(runner.testDir, file), 'utf-8');
171
+ const cliName = file.replace('.md', '').toUpperCase();
172
+ assert(content.includes(`# ${cliName} Configuration`), `${file} missing header`);
173
+ }
174
+ });
175
+
176
+ } finally {
177
+ process.chdir(originalCwd);
178
+ }
179
+
180
+ return runner.summary();
181
+ } catch (err) {
182
+ console.error(`\n[X] Sync test failed: ${err.message}`);
183
+ console.error(err.stack);
184
+ return false;
185
+ } finally {
186
+ await runner.cleanup();
187
+ }
188
+ }
189
+
190
+ function assert(condition, message) {
191
+ if (!condition) {
192
+ throw new Error(message || 'Assertion failed');
193
+ }
194
+ }
195
+
196
+ // Run sync tests
197
+ console.log('[SUCCESS] Stigmergy Skills Sync Functionality Test\n');
198
+ console.log('Verify multi-CLI config file sync\n');
199
+
200
+ runSyncTests()
201
+ .then(success => {
202
+ if (success) {
203
+ console.log('\n[SUCCESS] All sync tests passed!');
204
+ }
205
+ process.exit(success ? 0 : 1);
206
+ })
207
+ .catch(err => {
208
+ console.error('[X] Test execution failed:', err);
209
+ process.exit(1);
210
+ });