@almyty/skills 1.0.0 → 1.0.2
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 +70 -0
- package/dist/auth.d.ts +1 -1
- package/dist/auth.js +4 -4
- package/dist/config.js +1 -1
- package/dist/index.js +4 -4
- package/package.json +1 -1
- package/dist/__tests__/client.test.d.ts +0 -1
- package/dist/__tests__/client.test.js +0 -145
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# @almyty/skills
|
|
2
|
+
|
|
3
|
+
Install and manage AI skills from [almyty](https://almyty.com) in 30+ coding agents.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install skills from a gateway
|
|
9
|
+
npx @almyty/skills install @your-org/your-gateway
|
|
10
|
+
|
|
11
|
+
# Keep skills synced (daemon mode)
|
|
12
|
+
npx @almyty/skills daemon
|
|
13
|
+
|
|
14
|
+
# Run a skill directly
|
|
15
|
+
npx @almyty/skills run @your-org/your-gateway/tool-name --param value
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## What It Does
|
|
19
|
+
|
|
20
|
+
almyty turns any API into AI-ready tools. This CLI installs those tools as **SKILL.md files** into your project, where they're automatically picked up by supported agents.
|
|
21
|
+
|
|
22
|
+
## Supported Agents
|
|
23
|
+
|
|
24
|
+
| Category | Agents |
|
|
25
|
+
|----------|--------|
|
|
26
|
+
| **IDE** | Claude Code, Cursor, Windsurf, VS Code Copilot, JetBrains |
|
|
27
|
+
| **AI Assistants** | Claude Desktop, ChatGPT, Gemini |
|
|
28
|
+
| **Dev Tools** | Aider, Continue, Cody, GitHub Copilot CLI |
|
|
29
|
+
| **Frameworks** | LangChain, LlamaIndex, CrewAI, AutoGPT |
|
|
30
|
+
|
|
31
|
+
## Commands
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
npx @almyty/skills login Authenticate with almyty
|
|
35
|
+
npx @almyty/skills logout Remove stored credentials
|
|
36
|
+
npx @almyty/skills daemon Start skill daemon (syncs all skills)
|
|
37
|
+
npx @almyty/skills install <ref> Install skills from a gateway
|
|
38
|
+
npx @almyty/skills list [ref] List available skills
|
|
39
|
+
npx @almyty/skills search <query> Search for skills
|
|
40
|
+
npx @almyty/skills run <ref> Execute a skill
|
|
41
|
+
npx @almyty/skills installed Show locally installed skills
|
|
42
|
+
npx @almyty/skills remove Remove all installed skills
|
|
43
|
+
npx @almyty/skills gateways List your gateways
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## References
|
|
47
|
+
|
|
48
|
+
Skills are referenced as `@org/gateway` or `@org/gateway/skill`:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx @almyty/skills install @acme/petstore # All skills from a gateway
|
|
52
|
+
npx @almyty/skills run @acme/petstore/get-pet # Run a specific skill
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Configuration
|
|
56
|
+
|
|
57
|
+
Create `.almytyrc` in your project or home directory:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"url": "https://api.almyty.com",
|
|
62
|
+
"token": "your-token"
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Or use environment variables: `ALMYTY_URL`, `ALMYTY_TOKEN`.
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
BSL-1.1
|
package/dist/auth.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Shares credentials with @almyty/mcp-server via ~/.almyty/credentials.json.
|
|
5
5
|
*
|
|
6
6
|
* Supports:
|
|
7
|
-
* 1. Environment variable (
|
|
7
|
+
* 1. Environment variable (ALMYTY_TOKEN) — for CI/scripts
|
|
8
8
|
* 2. Stored credentials (~/.almyty/credentials.json) — for interactive use
|
|
9
9
|
* 3. Interactive login (npx @almyty/skills login) — stores credentials
|
|
10
10
|
*/
|
package/dist/auth.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Shares credentials with @almyty/mcp-server via ~/.almyty/credentials.json.
|
|
5
5
|
*
|
|
6
6
|
* Supports:
|
|
7
|
-
* 1. Environment variable (
|
|
7
|
+
* 1. Environment variable (ALMYTY_TOKEN) — for CI/scripts
|
|
8
8
|
* 2. Stored credentials (~/.almyty/credentials.json) — for interactive use
|
|
9
9
|
* 3. Interactive login (npx @almyty/skills login) — stores credentials
|
|
10
10
|
*/
|
|
@@ -56,8 +56,8 @@ export function logout() {
|
|
|
56
56
|
* Resolve token: env var > stored credentials
|
|
57
57
|
*/
|
|
58
58
|
export function resolveAuth() {
|
|
59
|
-
const envToken = process.env.
|
|
60
|
-
const envUrl = process.env.
|
|
59
|
+
const envToken = process.env.ALMYTY_TOKEN;
|
|
60
|
+
const envUrl = process.env.ALMYTY_URL || 'https://api.almyty.com';
|
|
61
61
|
if (envToken) {
|
|
62
62
|
return { url: envUrl, token: envToken };
|
|
63
63
|
}
|
|
@@ -67,7 +67,7 @@ export function resolveAuth() {
|
|
|
67
67
|
}
|
|
68
68
|
console.error('Not authenticated. Run one of:');
|
|
69
69
|
console.error(' npx @almyty/skills login');
|
|
70
|
-
console.error(' export
|
|
70
|
+
console.error(' export ALMYTY_TOKEN=<your-token>');
|
|
71
71
|
process.exit(1);
|
|
72
72
|
}
|
|
73
73
|
/**
|
package/dist/config.js
CHANGED
|
@@ -3,7 +3,7 @@ import { homedir } from 'os';
|
|
|
3
3
|
import { join, resolve } from 'path';
|
|
4
4
|
import { detectAgents, getDefaultTargets, filterAgents } from './agents.js';
|
|
5
5
|
export function loadConfig(projectDir) {
|
|
6
|
-
const envDir = process.env.
|
|
6
|
+
const envDir = process.env.ALMYTY_SKILLS_DIR;
|
|
7
7
|
if (envDir) {
|
|
8
8
|
return { skillsDir: envDir };
|
|
9
9
|
}
|
package/dist/index.js
CHANGED
|
@@ -33,9 +33,9 @@ References:
|
|
|
33
33
|
|
|
34
34
|
Config:
|
|
35
35
|
.almytyrc JSON config file (project or home dir)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
ALMYTY_SKILLS_DIR Override skill installation directory
|
|
37
|
+
ALMYTY_URL Override API URL
|
|
38
|
+
ALMYTY_TOKEN Override auth token
|
|
39
39
|
|
|
40
40
|
Options:
|
|
41
41
|
--interval, -i <seconds> Daemon poll interval in seconds (default: 60)
|
|
@@ -143,7 +143,7 @@ async function main() {
|
|
|
143
143
|
const urlOverride = args.flags.url || config.url;
|
|
144
144
|
switch (command) {
|
|
145
145
|
case 'login': {
|
|
146
|
-
const url = urlOverride || process.env.
|
|
146
|
+
const url = urlOverride || process.env.ALMYTY_URL || 'https://api.almyty.com';
|
|
147
147
|
await login(url);
|
|
148
148
|
break;
|
|
149
149
|
}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { parseRef } from '../client.js';
|
|
3
|
-
import { generateMetaSkill } from '../meta-skill.js';
|
|
4
|
-
import { loadConfig } from '../config.js';
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
// parseRef()
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
describe('parseRef', () => {
|
|
9
|
-
it('parses @org/gateway as a gateway ref', () => {
|
|
10
|
-
const result = parseRef('@org/gateway');
|
|
11
|
-
expect(result).toEqual({
|
|
12
|
-
type: 'gateway',
|
|
13
|
-
orgSlug: 'org',
|
|
14
|
-
gatewaySlug: 'gateway',
|
|
15
|
-
raw: '@org/gateway',
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
it('parses @org/gateway/skill as a skill ref', () => {
|
|
19
|
-
const result = parseRef('@org/gateway/skill');
|
|
20
|
-
expect(result).toEqual({
|
|
21
|
-
type: 'skill',
|
|
22
|
-
orgSlug: 'org',
|
|
23
|
-
gatewaySlug: 'gateway',
|
|
24
|
-
skillName: 'skill',
|
|
25
|
-
raw: '@org/gateway/skill',
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
it('parses a UUID string as a uuid ref', () => {
|
|
29
|
-
const uuid = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
|
|
30
|
-
const result = parseRef(uuid);
|
|
31
|
-
expect(result).toEqual({
|
|
32
|
-
type: 'uuid',
|
|
33
|
-
uuid,
|
|
34
|
-
raw: uuid,
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
it('parses uppercase UUID correctly', () => {
|
|
38
|
-
const uuid = 'A1B2C3D4-E5F6-7890-ABCD-EF1234567890';
|
|
39
|
-
const result = parseRef(uuid);
|
|
40
|
-
expect(result.type).toBe('uuid');
|
|
41
|
-
expect(result.uuid).toBe(uuid);
|
|
42
|
-
});
|
|
43
|
-
it('returns search for a random string', () => {
|
|
44
|
-
const result = parseRef('some-random-query');
|
|
45
|
-
expect(result).toEqual({
|
|
46
|
-
type: 'search',
|
|
47
|
-
raw: 'some-random-query',
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
it('returns search for an empty string', () => {
|
|
51
|
-
const result = parseRef('');
|
|
52
|
-
expect(result).toEqual({
|
|
53
|
-
type: 'search',
|
|
54
|
-
raw: '',
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
it('returns search for @ with only one segment', () => {
|
|
58
|
-
const result = parseRef('@org');
|
|
59
|
-
expect(result).toEqual({
|
|
60
|
-
type: 'search',
|
|
61
|
-
raw: '@org',
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
it('returns search for @ with more than three segments', () => {
|
|
65
|
-
const result = parseRef('@org/gw/skill/extra');
|
|
66
|
-
expect(result).toEqual({
|
|
67
|
-
type: 'search',
|
|
68
|
-
raw: '@org/gw/skill/extra',
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
// ---------------------------------------------------------------------------
|
|
73
|
-
// generateMetaSkill()
|
|
74
|
-
// ---------------------------------------------------------------------------
|
|
75
|
-
describe('generateMetaSkill', () => {
|
|
76
|
-
it('returns a SkillFile with name "skills" and fileName "almyty-skills"', () => {
|
|
77
|
-
const skill = generateMetaSkill();
|
|
78
|
-
expect(skill.name).toBe('skills');
|
|
79
|
-
expect(skill.fileName).toBe('almyty-skills');
|
|
80
|
-
});
|
|
81
|
-
it('content includes npx @almyty/skills commands', () => {
|
|
82
|
-
const { content } = generateMetaSkill();
|
|
83
|
-
expect(content).toContain('npx @almyty/skills');
|
|
84
|
-
});
|
|
85
|
-
it('content references daemon command', () => {
|
|
86
|
-
const { content } = generateMetaSkill();
|
|
87
|
-
expect(content).toContain('daemon');
|
|
88
|
-
});
|
|
89
|
-
it('content references search command', () => {
|
|
90
|
-
const { content } = generateMetaSkill();
|
|
91
|
-
expect(content).toContain('search');
|
|
92
|
-
});
|
|
93
|
-
it('content references install command', () => {
|
|
94
|
-
const { content } = generateMetaSkill();
|
|
95
|
-
expect(content).toContain('install');
|
|
96
|
-
});
|
|
97
|
-
it('content references run command', () => {
|
|
98
|
-
const { content } = generateMetaSkill();
|
|
99
|
-
expect(content).toContain('run');
|
|
100
|
-
});
|
|
101
|
-
it('content includes SKILL.md front-matter', () => {
|
|
102
|
-
const { content } = generateMetaSkill();
|
|
103
|
-
expect(content).toContain('---');
|
|
104
|
-
expect(content).toContain('name: almyty-skills');
|
|
105
|
-
expect(content).toContain('description:');
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
// ---------------------------------------------------------------------------
|
|
109
|
-
// loadConfig()
|
|
110
|
-
// ---------------------------------------------------------------------------
|
|
111
|
-
describe('loadConfig', () => {
|
|
112
|
-
const originalEnv = process.env;
|
|
113
|
-
beforeEach(() => {
|
|
114
|
-
// Reset env between tests
|
|
115
|
-
process.env = { ...originalEnv };
|
|
116
|
-
delete process.env.APIFAI_SKILLS_DIR;
|
|
117
|
-
});
|
|
118
|
-
afterEach(() => {
|
|
119
|
-
process.env = originalEnv;
|
|
120
|
-
vi.restoreAllMocks();
|
|
121
|
-
});
|
|
122
|
-
it('returns empty config when no .almytyrc exists and no env var', () => {
|
|
123
|
-
// Use a temp directory that definitely has no .almytyrc
|
|
124
|
-
const config = loadConfig('/tmp/nonexistent-dir-' + Date.now());
|
|
125
|
-
expect(config).toEqual({});
|
|
126
|
-
});
|
|
127
|
-
it('uses APIFAI_SKILLS_DIR env var when set', () => {
|
|
128
|
-
process.env.APIFAI_SKILLS_DIR = '/custom/skills/dir';
|
|
129
|
-
const config = loadConfig();
|
|
130
|
-
expect(config).toEqual({ skillsDir: '/custom/skills/dir' });
|
|
131
|
-
});
|
|
132
|
-
it('env var takes precedence over .almytyrc files', () => {
|
|
133
|
-
process.env.APIFAI_SKILLS_DIR = '/env/override';
|
|
134
|
-
// Even if we pass a projectDir, env should take priority
|
|
135
|
-
const config = loadConfig('/some/project');
|
|
136
|
-
expect(config.skillsDir).toBe('/env/override');
|
|
137
|
-
});
|
|
138
|
-
it('returns empty config for a directory with no rc file and no home rc', () => {
|
|
139
|
-
// Use a deeply nested temp path that certainly has no .almytyrc anywhere
|
|
140
|
-
// and also won't match ~/.almytyrc (already handled by the missing file)
|
|
141
|
-
const fakePath = `/tmp/almyty-test-${Date.now()}/deeply/nested/project`;
|
|
142
|
-
const config = loadConfig(fakePath);
|
|
143
|
-
expect(config).toEqual({});
|
|
144
|
-
});
|
|
145
|
-
});
|