stigmergy 1.2.8 → 1.2.10
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 +40 -6
- package/STIGMERGY.md +10 -0
- package/package.json +19 -5
- package/scripts/preuninstall.js +10 -0
- package/src/adapters/claude/install_claude_integration.js +21 -21
- package/src/adapters/codebuddy/install_codebuddy_integration.js +54 -51
- package/src/adapters/codex/install_codex_integration.js +27 -28
- package/src/adapters/gemini/install_gemini_integration.js +60 -60
- package/src/adapters/iflow/install_iflow_integration.js +72 -72
- package/src/adapters/qoder/install_qoder_integration.js +64 -64
- package/src/adapters/qwen/install_qwen_integration.js +7 -7
- package/src/cli/router.js +581 -175
- package/src/commands/skill-bridge.js +39 -0
- package/src/commands/skill-handler.js +150 -0
- package/src/commands/skill.js +127 -0
- package/src/core/cli_path_detector.js +573 -0
- package/src/core/cli_tools.js +72 -1
- package/src/core/coordination/nodejs/AdapterManager.js +29 -1
- package/src/core/directory_permission_manager.js +568 -0
- package/src/core/enhanced_cli_installer.js +609 -0
- package/src/core/installer.js +232 -88
- package/src/core/multilingual/language-pattern-manager.js +78 -50
- package/src/core/persistent_shell_configurator.js +468 -0
- package/src/core/skills/StigmergySkillManager.js +357 -0
- package/src/core/skills/__tests__/SkillInstaller.test.js +275 -0
- package/src/core/skills/__tests__/SkillParser.test.js +202 -0
- package/src/core/skills/__tests__/SkillReader.test.js +189 -0
- package/src/core/skills/cli-command-test.js +201 -0
- package/src/core/skills/comprehensive-e2e-test.js +473 -0
- package/src/core/skills/e2e-test.js +267 -0
- package/src/core/skills/embedded-openskills/SkillInstaller.js +438 -0
- package/src/core/skills/embedded-openskills/SkillParser.js +123 -0
- package/src/core/skills/embedded-openskills/SkillReader.js +143 -0
- package/src/core/skills/integration-test.js +248 -0
- package/src/core/skills/package.json +6 -0
- package/src/core/skills/regression-test.js +285 -0
- package/src/core/skills/run-all-tests.js +129 -0
- package/src/core/skills/sync-test.js +210 -0
- package/src/core/skills/test-runner.js +242 -0
- package/src/utils/helpers.js +3 -20
- package/src/auth.js +0 -173
- package/src/auth_command.js +0 -208
- package/src/calculator.js +0 -313
- package/src/core/enhanced_installer.js +0 -479
- package/src/core/enhanced_uninstaller.js +0 -638
- package/src/data_encryption.js +0 -143
- package/src/data_structures.js +0 -440
- package/src/deploy.js +0 -55
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SkillParser - Parse SKILL.md files
|
|
3
|
+
*
|
|
4
|
+
* Adapted from: https://github.com/numman-ali/openskills
|
|
5
|
+
* Original License: Apache 2.0
|
|
6
|
+
* Modifications: Copyright Stigmergy Project
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export class SkillParser {
|
|
10
|
+
/**
|
|
11
|
+
* Parse YAML frontmatter from skill content
|
|
12
|
+
* @param {string} content - Full SKILL.md content
|
|
13
|
+
* @returns {Object} Parsed metadata
|
|
14
|
+
*/
|
|
15
|
+
parseMetadata(content) {
|
|
16
|
+
const match = content.match(/^---\n(.*?)\n---/s);
|
|
17
|
+
if (!match) {
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const yamlContent = match[1];
|
|
22
|
+
const metadata = {};
|
|
23
|
+
|
|
24
|
+
const lines = yamlContent.split('\n');
|
|
25
|
+
let currentKey = null;
|
|
26
|
+
let currentArray = null;
|
|
27
|
+
|
|
28
|
+
for (const line of lines) {
|
|
29
|
+
// Handle array items
|
|
30
|
+
if (line.trim().startsWith('-') && currentKey) {
|
|
31
|
+
const value = line.trim().substring(1).trim();
|
|
32
|
+
if (!currentArray) {
|
|
33
|
+
currentArray = [];
|
|
34
|
+
metadata[currentKey] = currentArray;
|
|
35
|
+
}
|
|
36
|
+
currentArray.push(value);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Handle key-value pairs
|
|
41
|
+
const colonIndex = line.indexOf(':');
|
|
42
|
+
if (colonIndex > 0) {
|
|
43
|
+
currentKey = line.substring(0, colonIndex).trim();
|
|
44
|
+
let value = line.substring(colonIndex + 1).trim();
|
|
45
|
+
|
|
46
|
+
// Reset array tracking
|
|
47
|
+
currentArray = null;
|
|
48
|
+
|
|
49
|
+
// Handle multiline values (>)
|
|
50
|
+
if (value === '>') {
|
|
51
|
+
continue; // Next lines will be the value
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Remove quotes
|
|
55
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
56
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
57
|
+
value = value.slice(1, -1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (value) {
|
|
61
|
+
metadata[currentKey] = value;
|
|
62
|
+
}
|
|
63
|
+
} else if (currentKey && line.trim()) {
|
|
64
|
+
// Multiline continuation
|
|
65
|
+
const existingValue = metadata[currentKey];
|
|
66
|
+
metadata[currentKey] = existingValue
|
|
67
|
+
? `${existingValue} ${line.trim()}`
|
|
68
|
+
: line.trim();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return metadata;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Extract content after frontmatter
|
|
77
|
+
* @param {string} content - Full SKILL.md content
|
|
78
|
+
* @returns {string} Content without frontmatter
|
|
79
|
+
*/
|
|
80
|
+
extractContent(content) {
|
|
81
|
+
const match = content.match(/^---\n.*?\n---\n(.*)$/s);
|
|
82
|
+
return match ? match[1].trim() : content;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Validate skill structure and content
|
|
87
|
+
* @param {string} content - Full SKILL.md content
|
|
88
|
+
* @returns {Object} Validation result with valid flag and errors array
|
|
89
|
+
*/
|
|
90
|
+
validateSkill(content) {
|
|
91
|
+
const errors = [];
|
|
92
|
+
const metadata = this.parseMetadata(content);
|
|
93
|
+
|
|
94
|
+
// Required fields
|
|
95
|
+
if (!metadata.name) {
|
|
96
|
+
errors.push('Missing required field: name');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!metadata.description) {
|
|
100
|
+
errors.push('Missing required field: description');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Name format validation
|
|
104
|
+
if (metadata.name && !/^[a-z0-9-]+$/.test(metadata.name)) {
|
|
105
|
+
errors.push('Skill name must use lowercase and hyphens only');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Content length validation (approximate word count)
|
|
109
|
+
const mainContent = this.extractContent(content);
|
|
110
|
+
const wordCount = mainContent.split(/\s+/).length;
|
|
111
|
+
|
|
112
|
+
if (wordCount > 5000) {
|
|
113
|
+
errors.push('SKILL.md content exceeds 5000 words');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
valid: errors.length === 0,
|
|
118
|
+
errors: errors,
|
|
119
|
+
metadata: metadata,
|
|
120
|
+
wordCount: wordCount
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SkillReader - Read and locate skills
|
|
3
|
+
*
|
|
4
|
+
* Adapted from: https://github.com/numman-ali/openskills
|
|
5
|
+
* Original License: Apache 2.0
|
|
6
|
+
* Modifications: Copyright Stigmergy Project
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'fs/promises';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import os from 'os';
|
|
12
|
+
import { SkillParser } from './SkillParser.js';
|
|
13
|
+
|
|
14
|
+
export class SkillReader {
|
|
15
|
+
/**
|
|
16
|
+
* @param {string[]} customSearchPaths - Optional custom search paths
|
|
17
|
+
*/
|
|
18
|
+
constructor(customSearchPaths = null) {
|
|
19
|
+
this.parser = new SkillParser();
|
|
20
|
+
|
|
21
|
+
if (customSearchPaths) {
|
|
22
|
+
this.searchPaths = customSearchPaths;
|
|
23
|
+
} else {
|
|
24
|
+
// Default search paths (priority order)
|
|
25
|
+
this.searchPaths = [
|
|
26
|
+
path.join(process.cwd(), '.agent/skills'), // Project universal
|
|
27
|
+
path.join(os.homedir(), '.agent/skills'), // Global universal
|
|
28
|
+
path.join(process.cwd(), '.claude/skills'), // Project Claude
|
|
29
|
+
path.join(os.homedir(), '.claude/skills'), // Global Claude
|
|
30
|
+
path.join(os.homedir(), '.stigmergy/skills') // Stigmergy unified
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Find skill in search paths
|
|
37
|
+
* @param {string} name - Skill name
|
|
38
|
+
* @returns {Promise<Object|null>} Skill info or null if not found
|
|
39
|
+
*/
|
|
40
|
+
async findSkill(name) {
|
|
41
|
+
for (const basePath of this.searchPaths) {
|
|
42
|
+
const skillPath = path.join(basePath, name, 'SKILL.md');
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
await fs.access(skillPath);
|
|
46
|
+
return {
|
|
47
|
+
name: name,
|
|
48
|
+
path: skillPath,
|
|
49
|
+
baseDir: path.dirname(skillPath)
|
|
50
|
+
};
|
|
51
|
+
} catch {
|
|
52
|
+
// Continue to next path
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Read skill content
|
|
62
|
+
* @param {string} name - Skill name
|
|
63
|
+
* @returns {Promise<Object>} Skill data with name, baseDir, and content
|
|
64
|
+
*/
|
|
65
|
+
async readSkill(name) {
|
|
66
|
+
const skill = await this.findSkill(name);
|
|
67
|
+
|
|
68
|
+
if (!skill) {
|
|
69
|
+
throw new Error(`Skill '${name}' not found in any search path`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const content = await fs.readFile(skill.path, 'utf-8');
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
name: skill.name,
|
|
76
|
+
baseDir: skill.baseDir,
|
|
77
|
+
content: content
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* List all available skills
|
|
83
|
+
* @returns {Promise<Array>} Array of skill info objects
|
|
84
|
+
*/
|
|
85
|
+
async listSkills() {
|
|
86
|
+
const skills = [];
|
|
87
|
+
const seenNames = new Set();
|
|
88
|
+
|
|
89
|
+
for (const searchPath of this.searchPaths) {
|
|
90
|
+
try {
|
|
91
|
+
const entries = await fs.readdir(searchPath, { withFileTypes: true });
|
|
92
|
+
|
|
93
|
+
for (const entry of entries) {
|
|
94
|
+
if (!entry.isDirectory()) continue;
|
|
95
|
+
if (seenNames.has(entry.name)) continue; // Skip duplicates
|
|
96
|
+
|
|
97
|
+
const skillPath = path.join(searchPath, entry.name, 'SKILL.md');
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
await fs.access(skillPath);
|
|
101
|
+
const content = await fs.readFile(skillPath, 'utf-8');
|
|
102
|
+
const metadata = this.parser.parseMetadata(content);
|
|
103
|
+
|
|
104
|
+
skills.push({
|
|
105
|
+
name: entry.name,
|
|
106
|
+
description: metadata.description || '',
|
|
107
|
+
location: this.determineLocation(searchPath),
|
|
108
|
+
path: skillPath
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
seenNames.add(entry.name);
|
|
112
|
+
} catch {
|
|
113
|
+
// Not a valid skill directory, skip
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} catch {
|
|
118
|
+
// Search path doesn't exist, skip
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return skills;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Determine skill location type from path
|
|
128
|
+
* @private
|
|
129
|
+
*/
|
|
130
|
+
determineLocation(searchPath) {
|
|
131
|
+
if (searchPath.includes('.stigmergy')) {
|
|
132
|
+
return 'stigmergy';
|
|
133
|
+
} else if (searchPath.includes('.agent')) {
|
|
134
|
+
return 'universal';
|
|
135
|
+
} else if (searchPath.includes('.claude')) {
|
|
136
|
+
return 'claude';
|
|
137
|
+
} else if (searchPath.includes(process.cwd())) {
|
|
138
|
+
return 'project';
|
|
139
|
+
} else {
|
|
140
|
+
return 'global';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Test - End-to-end verification of Stigmergy Skills System
|
|
3
|
+
*
|
|
4
|
+
* TDD Verification:
|
|
5
|
+
* 1. Create test skills
|
|
6
|
+
* 2. Install skills
|
|
7
|
+
* 3. Read skills
|
|
8
|
+
* 4. List skills
|
|
9
|
+
* 5. Sync to AGENTS.md
|
|
10
|
+
* 6. Validate skills
|
|
11
|
+
* 7. Remove skills
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { StigmergySkillManager } from './StigmergySkillManager.js';
|
|
15
|
+
import fs from 'fs/promises';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import os from 'os';
|
|
18
|
+
|
|
19
|
+
class IntegrationTestRunner {
|
|
20
|
+
constructor() {
|
|
21
|
+
this.testDir = path.join(os.tmpdir(), `stigmergy-integration-test-${Date.now()}`);
|
|
22
|
+
this.manager = new StigmergySkillManager({
|
|
23
|
+
skillsDir: path.join(this.testDir, 'skills')
|
|
24
|
+
});
|
|
25
|
+
this.passed = 0;
|
|
26
|
+
this.failed = 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async test(name, fn) {
|
|
30
|
+
try {
|
|
31
|
+
await fn();
|
|
32
|
+
this.passed++;
|
|
33
|
+
console.log(`[OK] ${name}`);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
this.failed++;
|
|
36
|
+
console.error(`[X] ${name}`);
|
|
37
|
+
console.error(` Error: ${err.message}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async setup() {
|
|
42
|
+
console.log('[INFO] Setting up test environment...\n');
|
|
43
|
+
|
|
44
|
+
// Create test skill repository structure
|
|
45
|
+
const repoDir = path.join(this.testDir, 'test-repo');
|
|
46
|
+
await fs.mkdir(repoDir, { recursive: true });
|
|
47
|
+
|
|
48
|
+
// Create test skill #1
|
|
49
|
+
const skill1Dir = path.join(repoDir, 'test-analyzer');
|
|
50
|
+
await fs.mkdir(skill1Dir, { recursive: true });
|
|
51
|
+
await fs.writeFile(
|
|
52
|
+
path.join(skill1Dir, 'SKILL.md'),
|
|
53
|
+
`---
|
|
54
|
+
name: test-analyzer
|
|
55
|
+
description: Test data analysis skill for integration testing
|
|
56
|
+
version: 1.0.0
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
# Test Analyzer
|
|
60
|
+
|
|
61
|
+
## Trigger Conditions
|
|
62
|
+
When user asks to analyze test data.
|
|
63
|
+
|
|
64
|
+
## Core Workflow
|
|
65
|
+
|
|
66
|
+
### Step 1: Data Loading
|
|
67
|
+
1. Read input file
|
|
68
|
+
2. Validate format
|
|
69
|
+
|
|
70
|
+
### Step 2: Execute Analysis
|
|
71
|
+
Run analysis script:
|
|
72
|
+
\`\`\`bash
|
|
73
|
+
python scripts/analyze.py --input data.json
|
|
74
|
+
\`\`\`
|
|
75
|
+
|
|
76
|
+
### Step 3: Generate Report
|
|
77
|
+
Output results to report.json
|
|
78
|
+
`
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Add scripts directory
|
|
82
|
+
await fs.mkdir(path.join(skill1Dir, 'scripts'));
|
|
83
|
+
await fs.writeFile(
|
|
84
|
+
path.join(skill1Dir, 'scripts', 'analyze.py'),
|
|
85
|
+
`#!/usr/bin/env python3
|
|
86
|
+
import json
|
|
87
|
+
import sys
|
|
88
|
+
|
|
89
|
+
data = json.load(sys.stdin)
|
|
90
|
+
print(json.dumps({"status": "analyzed", "count": len(data)}, indent=2))
|
|
91
|
+
`
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// Create test skill #2
|
|
95
|
+
const skill2Dir = path.join(repoDir, 'test-formatter');
|
|
96
|
+
await fs.mkdir(skill2Dir);
|
|
97
|
+
await fs.writeFile(
|
|
98
|
+
path.join(skill2Dir, 'SKILL.md'),
|
|
99
|
+
`---
|
|
100
|
+
name: test-formatter
|
|
101
|
+
description: Test code formatting skill
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
# Test Formatter
|
|
105
|
+
|
|
106
|
+
Format code according to standards.
|
|
107
|
+
`
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
console.log(`[OK] Test repository created at ${repoDir}\n`);
|
|
111
|
+
return repoDir;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async cleanup() {
|
|
115
|
+
console.log('\n[INFO] Cleaning up...');
|
|
116
|
+
await fs.rm(this.testDir, { recursive: true, force: true });
|
|
117
|
+
console.log('[OK] Cleanup complete');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
summary() {
|
|
121
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
122
|
+
console.log(`Integration Test Total: ${this.passed + this.failed} tests`);
|
|
123
|
+
console.log(`[OK] Passed: ${this.passed}`);
|
|
124
|
+
console.log(`[X] Failed: ${this.failed}`);
|
|
125
|
+
console.log('='.repeat(60));
|
|
126
|
+
return this.failed === 0;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function runIntegrationTests() {
|
|
131
|
+
const runner = new IntegrationTestRunner();
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
// Setup
|
|
135
|
+
const repoDir = await runner.setup();
|
|
136
|
+
|
|
137
|
+
console.log('[LIST] Running integration tests...\n');
|
|
138
|
+
|
|
139
|
+
// Test 1: Scan skills
|
|
140
|
+
await runner.test('Scan local skill repository', async () => {
|
|
141
|
+
const skills = await runner.manager.installer.scanSkills(repoDir);
|
|
142
|
+
assert(skills.length === 2, `Expected 2 skills, got ${skills.length}`);
|
|
143
|
+
assert(skills.some(s => s.name === 'test-analyzer'), 'Missing test-analyzer');
|
|
144
|
+
assert(skills.some(s => s.name === 'test-formatter'), 'Missing test-formatter');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Test 2: Install skill manually
|
|
148
|
+
await runner.test('Install single skill', async () => {
|
|
149
|
+
const skills = await runner.manager.installer.scanSkills(repoDir);
|
|
150
|
+
const skill = skills.find(s => s.name === 'test-analyzer');
|
|
151
|
+
await runner.manager.installer.installSkill(skill);
|
|
152
|
+
|
|
153
|
+
// Verify installation
|
|
154
|
+
const installed = await fs.access(
|
|
155
|
+
path.join(runner.testDir, 'skills', 'test-analyzer', 'SKILL.md')
|
|
156
|
+
).then(() => true).catch(() => false);
|
|
157
|
+
assert(installed, 'Skill not installed');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Test 3: Read installed skill
|
|
161
|
+
await runner.test('Read installed skill', async () => {
|
|
162
|
+
const skill = await runner.manager.reader.readSkill('test-analyzer');
|
|
163
|
+
assert(skill.name === 'test-analyzer', `Expected name 'test-analyzer', got '${skill.name}'`);
|
|
164
|
+
assert(skill.content.includes('Test Analyzer'), 'Missing expected content');
|
|
165
|
+
assert(skill.baseDir.includes('test-analyzer'), 'Invalid base directory');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Test 4: List all skills
|
|
169
|
+
await runner.test('List all installed skills', async () => {
|
|
170
|
+
const skills = await runner.manager.reader.listSkills();
|
|
171
|
+
assert(skills.length >= 1, 'No skills found');
|
|
172
|
+
assert(skills.some(s => s.name === 'test-analyzer'));
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Test 5: Validate skill format
|
|
176
|
+
await runner.test('Validate skill format', async () => {
|
|
177
|
+
const validation = await runner.manager.validate('test-analyzer');
|
|
178
|
+
assert(validation.valid === true, `Skill validation failed: ${validation.errors.join(', ')}`);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Test 6: Sync to AGENTS.md
|
|
182
|
+
await runner.test('Sync skills to AGENTS.md', async () => {
|
|
183
|
+
// Create temporary AGENTS.md
|
|
184
|
+
const agentsMdPath = path.join(runner.testDir, 'AGENTS.md');
|
|
185
|
+
await fs.writeFile(agentsMdPath, '# Test AGENTS.md\n\n');
|
|
186
|
+
|
|
187
|
+
// Change working directory
|
|
188
|
+
const originalCwd = process.cwd();
|
|
189
|
+
process.chdir(runner.testDir);
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
await runner.manager.sync();
|
|
193
|
+
|
|
194
|
+
// Verify AGENTS.md updated
|
|
195
|
+
const content = await fs.readFile(agentsMdPath, 'utf-8');
|
|
196
|
+
assert(content.includes('<available_skills>'), 'Missing skills section');
|
|
197
|
+
assert(content.includes('test-analyzer'), 'Missing test-analyzer in AGENTS.md');
|
|
198
|
+
} finally {
|
|
199
|
+
process.chdir(originalCwd);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Test 7: Remove skill
|
|
204
|
+
await runner.test('Remove skill', async () => {
|
|
205
|
+
await runner.manager.installer.uninstallSkill('test-analyzer');
|
|
206
|
+
|
|
207
|
+
// Verify removal
|
|
208
|
+
const exists = await fs.access(
|
|
209
|
+
path.join(runner.testDir, 'skills', 'test-analyzer')
|
|
210
|
+
).then(() => true).catch(() => false);
|
|
211
|
+
assert(!exists, 'Skill still exists after removal');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return runner.summary();
|
|
215
|
+
} catch (err) {
|
|
216
|
+
console.error(`\n[X] Integration test failed: ${err.message}`);
|
|
217
|
+
console.error(err.stack);
|
|
218
|
+
return false;
|
|
219
|
+
} finally {
|
|
220
|
+
await runner.cleanup();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function assert(condition, message) {
|
|
225
|
+
if (!condition) {
|
|
226
|
+
throw new Error(message || 'Assertion failed');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Run integration tests
|
|
231
|
+
console.log('[SUCCESS] Stigmergy Skills Integration Test\n');
|
|
232
|
+
console.log('TDD-driven, embedded OpenSkills core\n');
|
|
233
|
+
|
|
234
|
+
runIntegrationTests()
|
|
235
|
+
.then(success => {
|
|
236
|
+
if (success) {
|
|
237
|
+
console.log('\n[SUCCESS] All integration tests passed!');
|
|
238
|
+
console.log('\nNext steps:');
|
|
239
|
+
console.log(' 1. Integrate into stigmergy CLI main command');
|
|
240
|
+
console.log(' 2. Test real GitHub repository installation');
|
|
241
|
+
console.log(' 3. Verify integration with all CLI tools');
|
|
242
|
+
}
|
|
243
|
+
process.exit(success ? 0 : 1);
|
|
244
|
+
})
|
|
245
|
+
.catch(err => {
|
|
246
|
+
console.error('[X] Test execution failed:', err);
|
|
247
|
+
process.exit(1);
|
|
248
|
+
});
|