gm-skill 0.1.0

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 ADDED
@@ -0,0 +1,99 @@
1
+ # gm-skill
2
+
3
+ Unified skill library for gm platform implementations.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install gm-skill
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Daemon Bootstrap Functions
14
+
15
+ ```javascript
16
+ const {
17
+ ensureRsLearningDaemonRunning,
18
+ ensureRsCodeinsightDaemonRunning,
19
+ ensureRsSearchDaemonRunning,
20
+ ensureAcptoapiRunning,
21
+ checkPortReachable
22
+ } = require('gm-skill');
23
+ ```
24
+
25
+ #### Functions
26
+
27
+ - `ensureRsLearningDaemonRunning()` - Ensures rs-learn daemon is running
28
+ - `ensureRsCodeinsightDaemonRunning()` - Ensures rs-codeinsight daemon is running
29
+ - `ensureRsSearchDaemonRunning()` - Ensures rs-search daemon is running
30
+ - `ensureAcptoapiRunning()` - Ensures acptoapi daemon is running
31
+ - `checkPortReachable(host, port, timeoutMs)` - Check if a port is reachable
32
+
33
+ All functions are async and return an object with `ok` and additional metadata.
34
+
35
+ ### Spool Dispatch Helpers
36
+
37
+ ```javascript
38
+ const { spool } = require('gm-skill');
39
+ const { writeSpool, readSpoolOutput, waitForCompletion, getAllOutputs } = spool;
40
+ ```
41
+
42
+ #### Functions
43
+
44
+ - `writeSpool(body, lang, options)` - Write code to spool input directory; returns `{ id, path, lang, ext }`
45
+ - `body` (string) - Code to execute
46
+ - `lang` (string, default: `'nodejs'`) - Language: nodejs, python, bash, typescript, go, rust, c, cpp, java, deno
47
+ - `options` (object, optional) - `{ taskId, sessionId }`
48
+ - `readSpoolOutput(id)` - Read completed task output; returns `{ id, stdout, stderr, metadata, exitCode, durationMs, timedOut }`
49
+ - `waitForCompletion(id, timeoutMs)` - Poll for task completion; returns promise with `{ ok, ...output }`
50
+ - `getAllOutputs()` - Enumerate all completed tasks in spool directory; returns array of output objects
51
+
52
+ All paths are platform-aware (Windows and POSIX compatible).
53
+
54
+ #### Example
55
+
56
+ ```javascript
57
+ const { spool } = require('gm-skill');
58
+
59
+ const result = spool.writeSpool('console.log("hello")', 'nodejs');
60
+ console.log(result.id);
61
+
62
+ const output = await spool.waitForCompletion(result.id, 30000);
63
+ console.log(output.stdout);
64
+ ```
65
+
66
+ ### Skill Manifests
67
+
68
+ ```javascript
69
+ const { manifest } = require('gm-skill');
70
+ const { getManifest, getSkill, getAllSkills } = manifest;
71
+ ```
72
+
73
+ #### Functions
74
+
75
+ - `getManifest()` - Get full package manifest with all 4 core skills; returns `{ name, version, description, skills: [...] }`
76
+ - `getSkill(name)` - Get individual skill manifest by name (gm, gm-execute, gm-emit, gm-complete); returns skill object with metadata
77
+ - `getAllSkills()` - Get array of all 4 core skills with full metadata; returns `[{ name, description, allowedTools, compatiblePlatforms, endToEnd, skillMdContent }, ...]`
78
+
79
+ Skill metadata includes:
80
+ - `name` - Skill identifier
81
+ - `description` - Human-readable skill purpose
82
+ - `allowedTools` - Array of tool names the skill permits
83
+ - `compatiblePlatforms` - Array of platforms this skill targets
84
+ - `endToEnd` - Boolean flag indicating end-to-end skill vs helper
85
+ - `skillMdContent` - Full SKILL.md file content
86
+
87
+ #### Example
88
+
89
+ ```javascript
90
+ const { manifest } = require('gm-skill');
91
+
92
+ const allSkills = manifest.getAllSkills();
93
+ allSkills.forEach(skill => {
94
+ console.log(`${skill.name}: ${skill.description}`);
95
+ });
96
+
97
+ const gmSkill = manifest.getSkill('gm');
98
+ console.log('Allowed tools:', gmSkill.allowedTools);
99
+ ```
package/index.js ADDED
@@ -0,0 +1,9 @@
1
+ const daemonBootstrap = require('../gm-starter/lib/daemon-bootstrap.js');
2
+ const spool = require('../gm-starter/lib/spool.js');
3
+ const manifest = require('./lib/manifest.js');
4
+
5
+ module.exports = {
6
+ ...daemonBootstrap,
7
+ spool,
8
+ manifest
9
+ };
@@ -0,0 +1,96 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ function parseSkillMarkdown(content) {
5
+ const normalized = content.replace(/\r\n/g, '\n');
6
+ const match = normalized.match(/^---\n([\s\S]*?)\n---/);
7
+
8
+ if (!match) return {};
9
+
10
+ const frontmatter = match[1];
11
+ const metadata = {};
12
+
13
+ frontmatter.split('\n').forEach(line => {
14
+ const colonIdx = line.indexOf(':');
15
+ if (colonIdx === -1) return;
16
+
17
+ const key = line.substring(0, colonIdx).trim();
18
+ const value = line.substring(colonIdx + 1).trim();
19
+
20
+ if (key === 'allowed-tools') {
21
+ metadata.allowedTools = value
22
+ .split(',')
23
+ .map(t => t.trim().replace(/^['\"]|['\"]$/g, ''));
24
+ } else {
25
+ metadata[key] = value;
26
+ }
27
+ });
28
+
29
+ return metadata;
30
+ }
31
+
32
+ function readSkillManifest(skillName) {
33
+ const skillMdPath = path.join(
34
+ __dirname,
35
+ '..',
36
+ '..',
37
+ 'gm-starter',
38
+ 'skills',
39
+ skillName,
40
+ 'SKILL.md'
41
+ );
42
+
43
+ if (!fs.existsSync(skillMdPath)) {
44
+ return {
45
+ name: skillName,
46
+ description: '',
47
+ allowedTools: [],
48
+ compatiblePlatforms: [],
49
+ endToEnd: false,
50
+ skillMdContent: ''
51
+ };
52
+ }
53
+
54
+ const content = fs.readFileSync(skillMdPath, 'utf8');
55
+ const metadata = parseSkillMarkdown(content);
56
+
57
+ return {
58
+ name: metadata.name || skillName,
59
+ description: metadata.description || '',
60
+ version: metadata.version || '1.0.0',
61
+ category: metadata.category || 'skill',
62
+ allowedTools: metadata.allowedTools || [],
63
+ compatiblePlatforms: metadata['compatible-platforms']
64
+ ? metadata['compatible-platforms'].split(',').map(p => p.trim())
65
+ : [],
66
+ endToEnd: metadata['end-to-end'] === 'true',
67
+ skillMdContent: content
68
+ };
69
+ }
70
+
71
+ function getManifest() {
72
+ const skills = ['gm', 'gm-execute', 'gm-emit', 'gm-complete'];
73
+ return {
74
+ name: 'gm-skill',
75
+ version: '1.0.0',
76
+ description: 'gm skill manifest and daemon bootstrap integration',
77
+ skills: skills.map(name => readSkillManifest(name))
78
+ };
79
+ }
80
+
81
+ function getSkill(name) {
82
+ return readSkillManifest(name);
83
+ }
84
+
85
+ function getAllSkills() {
86
+ const skills = ['gm', 'gm-execute', 'gm-emit', 'gm-complete'];
87
+ return skills.map(name => readSkillManifest(name));
88
+ }
89
+
90
+ module.exports = {
91
+ getManifest,
92
+ getSkill,
93
+ getAllSkills,
94
+ parseSkillMarkdown,
95
+ readSkillManifest
96
+ };
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "gm-skill",
3
+ "version": "0.1.0",
4
+ "description": "Unified skill library for gm platform implementations",
5
+ "main": "index.js",
6
+ "exports": {
7
+ ".": "./index.js",
8
+ "./daemon-bootstrap": "./index.js",
9
+ "./manifest": "./lib/manifest.js"
10
+ },
11
+ "scripts": {
12
+ "test": "node test.js"
13
+ },
14
+ "keywords": [
15
+ "gm",
16
+ "skills",
17
+ "daemon",
18
+ "bootstrap"
19
+ ],
20
+ "author": "AnEntrypoint",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/AnEntrypoint/gm.git"
25
+ },
26
+ "homepage": "https://github.com/AnEntrypoint/gm#readme",
27
+ "engines": {
28
+ "node": ">=16.0.0"
29
+ }
30
+ }
package/test.js ADDED
@@ -0,0 +1,89 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+ const assert = require('assert');
5
+ const { spool } = require('./index.js');
6
+
7
+ const testDir = path.join(os.tmpdir(), 'gm-skill-test');
8
+
9
+ function cleanup() {
10
+ if (fs.existsSync(testDir)) {
11
+ fs.rmSync(testDir, { recursive: true });
12
+ }
13
+ }
14
+
15
+ async function runTests() {
16
+ cleanup();
17
+ process.chdir(testDir);
18
+ fs.mkdirSync(testDir, { recursive: true });
19
+
20
+ console.log('Testing spool.writeSpool...');
21
+ const writeResult = spool.writeSpool('console.log("test output")', 'nodejs');
22
+ assert(writeResult.id, 'writeSpool returns id');
23
+ assert(writeResult.path, 'writeSpool returns path');
24
+ assert(writeResult.lang === 'nodejs', 'writeSpool returns correct lang');
25
+ assert(writeResult.ext === 'js', 'writeSpool returns correct ext');
26
+ assert(fs.existsSync(writeResult.path), 'spool file created on disk');
27
+ const fileContent = fs.readFileSync(writeResult.path, 'utf-8');
28
+ assert(fileContent.includes('console.log("test output")'), 'spool file contains body');
29
+ console.log('✓ writeSpool works');
30
+
31
+ console.log('\nTesting spool.readSpoolOutput (empty case)...');
32
+ const nonExistentId = 'does-not-exist';
33
+ const emptyRead = spool.readSpoolOutput(nonExistentId);
34
+ assert.strictEqual(emptyRead.id, nonExistentId, 'readSpoolOutput returns id');
35
+ assert.strictEqual(emptyRead.stdout, '', 'readSpoolOutput returns empty stdout when no output');
36
+ assert.strictEqual(emptyRead.stderr, '', 'readSpoolOutput returns empty stderr when no output');
37
+ assert(emptyRead.metadata, 'readSpoolOutput returns metadata object');
38
+ console.log('✓ readSpoolOutput handles missing files gracefully');
39
+
40
+ console.log('\nTesting spool.getAllOutputs...');
41
+ const baseDir = spool.getSpoolBaseDir();
42
+ const outDir = path.join(baseDir, 'out');
43
+ fs.mkdirSync(outDir, { recursive: true });
44
+
45
+ fs.writeFileSync(path.join(outDir, 'task1.out'), 'output1');
46
+ fs.writeFileSync(path.join(outDir, 'task1.json'), JSON.stringify({ exitCode: 0 }));
47
+ fs.writeFileSync(path.join(outDir, 'task2.err'), 'error2');
48
+ fs.writeFileSync(path.join(outDir, 'task2.json'), JSON.stringify({ exitCode: 1 }));
49
+
50
+ const allOutputs = spool.getAllOutputs();
51
+ assert(allOutputs.length === 2, 'getAllOutputs returns 2 tasks');
52
+ const ids = allOutputs.map(o => o.id).sort();
53
+ assert(ids.includes('task1') && ids.includes('task2'), 'getAllOutputs returns correct task ids');
54
+ const task1 = allOutputs.find(o => o.id === 'task1');
55
+ assert(task1.stdout === 'output1', 'getAllOutputs includes stdout');
56
+ const task2 = allOutputs.find(o => o.id === 'task2');
57
+ assert(task2.stderr === 'error2', 'getAllOutputs includes stderr');
58
+ console.log('✓ getAllOutputs enumerates completed tasks');
59
+
60
+ console.log('\nTesting spool.waitForCompletion (timeout)...');
61
+ const timeoutResult = await spool.waitForCompletion('instant-timeout', 100);
62
+ assert(timeoutResult.timedOut === true, 'waitForCompletion times out gracefully');
63
+ assert(timeoutResult.ok === false, 'waitForCompletion returns ok:false on timeout');
64
+ console.log('✓ waitForCompletion respects timeout');
65
+
66
+ console.log('\nTesting spool helpers with different languages...');
67
+ const pyResult = spool.writeSpool('print("hello")', 'python');
68
+ assert(pyResult.ext === 'py', 'writeSpool maps python to .py');
69
+ const tsResult = spool.writeSpool('console.log()', 'typescript');
70
+ assert(tsResult.ext === 'ts', 'writeSpool maps typescript to .ts');
71
+ const bashResult = spool.writeSpool('echo hello', 'bash');
72
+ assert(bashResult.ext === 'sh', 'writeSpool maps bash to .sh');
73
+ console.log('✓ writeSpool handles language variants');
74
+
75
+ console.log('\nTesting platform-aware paths...');
76
+ const baseDir2 = spool.getSpoolBaseDir();
77
+ assert(baseDir2, 'getSpoolBaseDir returns a path');
78
+ assert(baseDir2.includes('.gm'), 'spool path contains .gm directory');
79
+ console.log('✓ Platform paths are set correctly');
80
+
81
+ cleanup();
82
+ console.log('\n✓✓✓ All tests passed ✓✓✓');
83
+ }
84
+
85
+ runTests().catch(e => {
86
+ console.error('Test failed:', e.message);
87
+ cleanup();
88
+ process.exit(1);
89
+ });