@emmraan/ai-skills 0.0.1 ā 0.0.3
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 +24 -24
- package/bin/skills.js +3 -3
- package/dist/index.js +21 -21
- package/package.json +32 -34
- package/src/bin/skills.ts +8 -8
- package/src/commands/__tests__/install.test.ts +49 -49
- package/src/commands/generate-local.ts +38 -38
- package/src/commands/install.ts +31 -31
- package/src/commands/list.ts +41 -41
- package/src/commands/remove.ts +22 -22
- package/src/commands/update.ts +69 -69
- package/src/core/__tests__/downloader.test.ts +66 -66
- package/src/core/__tests__/lockfile.test.ts +90 -90
- package/src/core/config.ts +29 -29
- package/src/core/downloader.ts +27 -27
- package/src/core/installer.ts +101 -101
- package/src/core/lockfile.ts +70 -70
- package/src/index.ts +80 -80
- package/src/utils/__tests__/hash.test.ts +22 -22
- package/src/utils/hash.ts +5 -5
- package/src/utils/logger.ts +21 -21
- package/src/utils/retry.ts +20 -20
- package/tsconfig.json +11 -11
- package/vitest.config.ts +13 -13
package/README.md
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
# AI Skills CLI
|
|
2
|
-
|
|
3
|
-
Install framework-agnostic `SKILLS.md` files for AI agents.
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
- `npx ai-skills react` - Install React skill
|
|
8
|
-
- `npx ai-skills install react` - Install React skill (explicit command)
|
|
9
|
-
- `npx ai-skills list` - List available and installed skills
|
|
10
|
-
- `npx ai-skills update` - Update all installed skills
|
|
11
|
-
- `npx ai-skills remove react` - Remove React skill
|
|
12
|
-
- `npx ai-skills generate-local` - Run local backend generator
|
|
13
|
-
|
|
14
|
-
## Publish to npm
|
|
15
|
-
|
|
16
|
-
This package is the publishable CLI package for the `ai-skills` command.
|
|
17
|
-
|
|
18
|
-
1. Build from workspace root: `pnpm build`
|
|
19
|
-
2. Login to npm: `npm login`
|
|
20
|
-
3. Publish CLI package: `npm publish --access public` (run inside `packages/cli`)
|
|
21
|
-
|
|
22
|
-
After publishing, users can run:
|
|
23
|
-
|
|
24
|
-
- `npx ai-skills list`
|
|
1
|
+
# AI Skills CLI
|
|
2
|
+
|
|
3
|
+
Install framework-agnostic `SKILLS.md` files for AI agents.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
- `npx @emmraan/ai-skills react` - Install React skill
|
|
8
|
+
- `npx @emmraan/ai-skills install react` - Install React skill (explicit command)
|
|
9
|
+
- `npx @emmraan/ai-skills list` - List available and installed skills
|
|
10
|
+
- `npx @emmraan/ai-skills update` - Update all installed skills
|
|
11
|
+
- `npx @emmraan/ai-skills remove react` - Remove React skill
|
|
12
|
+
- `npx @emmraan/ai-skills generate-local` - Run local backend generator
|
|
13
|
+
|
|
14
|
+
## Publish to npm
|
|
15
|
+
|
|
16
|
+
This package is the publishable CLI package for the `ai-skills` command.
|
|
17
|
+
|
|
18
|
+
1. Build from workspace root: `pnpm build`
|
|
19
|
+
2. Login to npm: `npm login`
|
|
20
|
+
3. Publish CLI package: `npm publish --access public` (run inside `packages/cli`)
|
|
21
|
+
|
|
22
|
+
After publishing, users can run:
|
|
23
|
+
|
|
24
|
+
- `npx @emmraan/ai-skills list`
|
package/bin/skills.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import '../dist/bin/skills.js';
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import '../dist/bin/skills.js';
|
package/dist/index.js
CHANGED
|
@@ -57,27 +57,27 @@ export async function main() {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
function showHelp() {
|
|
60
|
-
info(`
|
|
61
|
-
AI Skills CLI - Install framework-agnostic SKILLS.md files
|
|
62
|
-
|
|
63
|
-
Usage:
|
|
64
|
-
ai-skills <skill> Install a skill
|
|
65
|
-
ai-skills install <skill> Install a skill
|
|
66
|
-
ai-skills list [--json] List installed and available skills
|
|
67
|
-
ai-skills remove <skill> Remove an installed skill
|
|
68
|
-
ai-skills update [--force] Update all installed skills
|
|
69
|
-
[--skill <name>] Update a specific skill
|
|
70
|
-
ai-skills generate-local [skill] Run local backend generator
|
|
71
|
-
|
|
72
|
-
Examples:
|
|
73
|
-
ai-skills react
|
|
74
|
-
ai-skills install typescript
|
|
75
|
-
ai-skills list
|
|
76
|
-
ai-skills remove python
|
|
77
|
-
ai-skills update --force
|
|
78
|
-
ai-skills update --skill react
|
|
79
|
-
ai-skills generate-local
|
|
80
|
-
ai-skills generate-local react
|
|
60
|
+
info(`
|
|
61
|
+
AI Skills CLI - Install framework-agnostic SKILLS.md files
|
|
62
|
+
|
|
63
|
+
Usage:
|
|
64
|
+
ai-skills <skill> Install a skill
|
|
65
|
+
ai-skills install <skill> Install a skill
|
|
66
|
+
ai-skills list [--json] List installed and available skills
|
|
67
|
+
ai-skills remove <skill> Remove an installed skill
|
|
68
|
+
ai-skills update [--force] Update all installed skills
|
|
69
|
+
[--skill <name>] Update a specific skill
|
|
70
|
+
ai-skills generate-local [skill] Run local backend generator
|
|
71
|
+
|
|
72
|
+
Examples:
|
|
73
|
+
ai-skills react
|
|
74
|
+
ai-skills install typescript
|
|
75
|
+
ai-skills list
|
|
76
|
+
ai-skills remove python
|
|
77
|
+
ai-skills update --force
|
|
78
|
+
ai-skills update --skill react
|
|
79
|
+
ai-skills generate-local
|
|
80
|
+
ai-skills generate-local react
|
|
81
81
|
`);
|
|
82
82
|
}
|
|
83
83
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,34 +1,32 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@emmraan/ai-skills",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "CLI tool for installing AI Skills across agent platforms",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"ai-skills": "bin/skills.js",
|
|
8
|
-
"skills": "bin/skills.js"
|
|
9
|
-
},
|
|
10
|
-
"main": "./dist/index.js",
|
|
11
|
-
"types": "./dist/index.d.ts",
|
|
12
|
-
"exports": {
|
|
13
|
-
".": {
|
|
14
|
-
"import": "./dist/index.js",
|
|
15
|
-
"types": "./dist/index.d.ts"
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
"scripts": {
|
|
19
|
-
"build": "tsc --project tsconfig.json",
|
|
20
|
-
"dev": "tsx src/bin/skills.ts",
|
|
21
|
-
"test": "vitest run",
|
|
22
|
-
"lint": "node -e \"process.exit(0)\"",
|
|
23
|
-
"typecheck": "tsc --noEmit --project tsconfig.json",
|
|
24
|
-
"clean": "rm -rf dist"
|
|
25
|
-
},
|
|
26
|
-
"
|
|
27
|
-
"@ai-skills/shared-types": "workspace:*"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@emmraan/ai-skills",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "CLI tool for installing AI Skills across agent platforms",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ai-skills": "bin/skills.js",
|
|
8
|
+
"skills": "bin/skills.js"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc --project tsconfig.json",
|
|
20
|
+
"dev": "tsx src/bin/skills.ts",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"lint": "node -e \"process.exit(0)\"",
|
|
23
|
+
"typecheck": "tsc --noEmit --project tsconfig.json",
|
|
24
|
+
"clean": "rm -rf dist"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@ai-skills/shared-types": "workspace:*",
|
|
28
|
+
"@types/node": "^20.10.0",
|
|
29
|
+
"tsx": "^4.7.0",
|
|
30
|
+
"vitest": "^1.1.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/bin/skills.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { main } from '../index.js';
|
|
4
|
-
|
|
5
|
-
main().catch((err) => {
|
|
6
|
-
console.error('Fatal error:', err);
|
|
7
|
-
process.exit(1);
|
|
8
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { main } from '../index.js';
|
|
4
|
+
|
|
5
|
+
main().catch((err) => {
|
|
6
|
+
console.error('Fatal error:', err);
|
|
7
|
+
process.exit(1);
|
|
8
|
+
});
|
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
|
|
3
|
-
vi.mock('../../core/downloader', () => ({
|
|
4
|
-
fetchSkill: vi.fn(),
|
|
5
|
-
fetchRegistryIndex: vi.fn(),
|
|
6
|
-
}));
|
|
7
|
-
|
|
8
|
-
vi.mock('../../core/installer', () => ({
|
|
9
|
-
installSkill: vi.fn(),
|
|
10
|
-
}));
|
|
11
|
-
|
|
12
|
-
vi.mock('../../core/lockfile', () => ({
|
|
13
|
-
addSkillToLockfile: vi.fn(),
|
|
14
|
-
}));
|
|
15
|
-
|
|
16
|
-
import { install } from '../../commands/install';
|
|
17
|
-
|
|
18
|
-
describe('install command', () => {
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
vi.clearAllMocks();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('uses registry index version when writing lockfile', async () => {
|
|
24
|
-
const downloader = await import('../../core/downloader');
|
|
25
|
-
const installer = await import('../../core/installer');
|
|
26
|
-
const lockfile = await import('../../core/lockfile');
|
|
27
|
-
|
|
28
|
-
vi.mocked(downloader.fetchSkill).mockResolvedValue('# React Skill');
|
|
29
|
-
vi.mocked(downloader.fetchRegistryIndex).mockResolvedValue({
|
|
30
|
-
skills: {
|
|
31
|
-
react: {
|
|
32
|
-
version: '18.2.0',
|
|
33
|
-
domains: ['frontend'],
|
|
34
|
-
lastGenerated: '2026-02-06T00:00:00Z',
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
vi.mocked(installer.installSkill).mockResolvedValue(['/tmp/.agents/skills/react/SKILLS.md']);
|
|
39
|
-
|
|
40
|
-
const ok = await install('react');
|
|
41
|
-
expect(ok).toBe(true);
|
|
42
|
-
expect(lockfile.addSkillToLockfile).toHaveBeenCalledWith(
|
|
43
|
-
'react',
|
|
44
|
-
'18.2.0',
|
|
45
|
-
expect.stringMatching(/^[a-f0-9]{64}$/),
|
|
46
|
-
['/tmp/.agents/skills/react/SKILLS.md']
|
|
47
|
-
);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
vi.mock('../../core/downloader', () => ({
|
|
4
|
+
fetchSkill: vi.fn(),
|
|
5
|
+
fetchRegistryIndex: vi.fn(),
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
vi.mock('../../core/installer', () => ({
|
|
9
|
+
installSkill: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
vi.mock('../../core/lockfile', () => ({
|
|
13
|
+
addSkillToLockfile: vi.fn(),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
import { install } from '../../commands/install';
|
|
17
|
+
|
|
18
|
+
describe('install command', () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
vi.clearAllMocks();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('uses registry index version when writing lockfile', async () => {
|
|
24
|
+
const downloader = await import('../../core/downloader');
|
|
25
|
+
const installer = await import('../../core/installer');
|
|
26
|
+
const lockfile = await import('../../core/lockfile');
|
|
27
|
+
|
|
28
|
+
vi.mocked(downloader.fetchSkill).mockResolvedValue('# React Skill');
|
|
29
|
+
vi.mocked(downloader.fetchRegistryIndex).mockResolvedValue({
|
|
30
|
+
skills: {
|
|
31
|
+
react: {
|
|
32
|
+
version: '18.2.0',
|
|
33
|
+
domains: ['frontend'],
|
|
34
|
+
lastGenerated: '2026-02-06T00:00:00Z',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
vi.mocked(installer.installSkill).mockResolvedValue(['/tmp/.agents/skills/react/SKILLS.md']);
|
|
39
|
+
|
|
40
|
+
const ok = await install('react');
|
|
41
|
+
expect(ok).toBe(true);
|
|
42
|
+
expect(lockfile.addSkillToLockfile).toHaveBeenCalledWith(
|
|
43
|
+
'react',
|
|
44
|
+
'18.2.0',
|
|
45
|
+
expect.stringMatching(/^[a-f0-9]{64}$/),
|
|
46
|
+
['/tmp/.agents/skills/react/SKILLS.md']
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import { spawn } from 'child_process';
|
|
2
|
-
import { resolve } from 'path';
|
|
3
|
-
import { error, info, success } from '../utils/logger.js';
|
|
4
|
-
|
|
5
|
-
export async function generateLocal(skill?: string): Promise<boolean> {
|
|
6
|
-
const backendDir = resolve(process.cwd(), 'backend');
|
|
7
|
-
const command = process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm';
|
|
8
|
-
const args = ['run', 'generate'];
|
|
9
|
-
|
|
10
|
-
if (skill) {
|
|
11
|
-
args.push('--', '--skill', skill);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
info(`Running local generator in ${backendDir}...`);
|
|
15
|
-
|
|
16
|
-
return new Promise((resolvePromise) => {
|
|
17
|
-
const child = spawn(command, args, {
|
|
18
|
-
cwd: backendDir,
|
|
19
|
-
stdio: 'inherit',
|
|
20
|
-
shell: false,
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
child.on('error', (err) => {
|
|
24
|
-
error(`Failed to start local generator: ${err.message}`);
|
|
25
|
-
resolvePromise(false);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
child.on('exit', (code) => {
|
|
29
|
-
if (code === 0) {
|
|
30
|
-
success('Local generation completed successfully');
|
|
31
|
-
resolvePromise(true);
|
|
32
|
-
} else {
|
|
33
|
-
error(`Local generation failed with exit code ${code ?? -1}`);
|
|
34
|
-
resolvePromise(false);
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
}
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
import { error, info, success } from '../utils/logger.js';
|
|
4
|
+
|
|
5
|
+
export async function generateLocal(skill?: string): Promise<boolean> {
|
|
6
|
+
const backendDir = resolve(process.cwd(), 'backend');
|
|
7
|
+
const command = process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm';
|
|
8
|
+
const args = ['run', 'generate'];
|
|
9
|
+
|
|
10
|
+
if (skill) {
|
|
11
|
+
args.push('--', '--skill', skill);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
info(`Running local generator in ${backendDir}...`);
|
|
15
|
+
|
|
16
|
+
return new Promise((resolvePromise) => {
|
|
17
|
+
const child = spawn(command, args, {
|
|
18
|
+
cwd: backendDir,
|
|
19
|
+
stdio: 'inherit',
|
|
20
|
+
shell: false,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
child.on('error', (err) => {
|
|
24
|
+
error(`Failed to start local generator: ${err.message}`);
|
|
25
|
+
resolvePromise(false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
child.on('exit', (code) => {
|
|
29
|
+
if (code === 0) {
|
|
30
|
+
success('Local generation completed successfully');
|
|
31
|
+
resolvePromise(true);
|
|
32
|
+
} else {
|
|
33
|
+
error(`Local generation failed with exit code ${code ?? -1}`);
|
|
34
|
+
resolvePromise(false);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
package/src/commands/install.ts
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import { fetchSkill, fetchRegistryIndex } from '../core/downloader.js';
|
|
2
|
-
import { installSkill } from '../core/installer.js';
|
|
3
|
-
import { addSkillToLockfile } from '../core/lockfile.js';
|
|
4
|
-
import { sha256 } from '../utils/hash.js';
|
|
5
|
-
import { error, success, info } from '../utils/logger.js';
|
|
6
|
-
|
|
7
|
-
export async function install(skillName: string): Promise<boolean> {
|
|
8
|
-
try {
|
|
9
|
-
info(`Fetching ${skillName}...`);
|
|
10
|
-
const skillContent = await fetchSkill(skillName);
|
|
11
|
-
const hash = sha256(skillContent);
|
|
12
|
-
|
|
13
|
-
info(`Installing ${skillName}...`);
|
|
14
|
-
const installPaths = await installSkill(skillName, skillContent);
|
|
15
|
-
|
|
16
|
-
if (installPaths.length === 0) {
|
|
17
|
-
error(`Failed to install ${skillName} to any location`);
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const index = await fetchRegistryIndex();
|
|
22
|
-
const skillVersion = index.skills[skillName]?.version || 'unknown';
|
|
23
|
-
|
|
24
|
-
await addSkillToLockfile(skillName, skillVersion, hash, installPaths);
|
|
25
|
-
success(`Installed ${skillName} (${skillVersion}) to ${installPaths.length} location(s)`);
|
|
26
|
-
return true;
|
|
27
|
-
} catch (err) {
|
|
28
|
-
error(`Failed to install ${skillName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
1
|
+
import { fetchSkill, fetchRegistryIndex } from '../core/downloader.js';
|
|
2
|
+
import { installSkill } from '../core/installer.js';
|
|
3
|
+
import { addSkillToLockfile } from '../core/lockfile.js';
|
|
4
|
+
import { sha256 } from '../utils/hash.js';
|
|
5
|
+
import { error, success, info } from '../utils/logger.js';
|
|
6
|
+
|
|
7
|
+
export async function install(skillName: string): Promise<boolean> {
|
|
8
|
+
try {
|
|
9
|
+
info(`Fetching ${skillName}...`);
|
|
10
|
+
const skillContent = await fetchSkill(skillName);
|
|
11
|
+
const hash = sha256(skillContent);
|
|
12
|
+
|
|
13
|
+
info(`Installing ${skillName}...`);
|
|
14
|
+
const installPaths = await installSkill(skillName, skillContent);
|
|
15
|
+
|
|
16
|
+
if (installPaths.length === 0) {
|
|
17
|
+
error(`Failed to install ${skillName} to any location`);
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const index = await fetchRegistryIndex();
|
|
22
|
+
const skillVersion = index.skills[skillName]?.version || 'unknown';
|
|
23
|
+
|
|
24
|
+
await addSkillToLockfile(skillName, skillVersion, hash, installPaths);
|
|
25
|
+
success(`Installed ${skillName} (${skillVersion}) to ${installPaths.length} location(s)`);
|
|
26
|
+
return true;
|
|
27
|
+
} catch (err) {
|
|
28
|
+
error(`Failed to install ${skillName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/commands/list.ts
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { getAvailableSkills } from '../core/downloader.js';
|
|
2
|
-
import { getInstalledSkills } from '../core/lockfile.js';
|
|
3
|
-
import { log } from '../utils/logger.js';
|
|
4
|
-
|
|
5
|
-
export async function list(json: boolean = false): Promise<void> {
|
|
6
|
-
const installedSkills = await getInstalledSkills();
|
|
7
|
-
const availableSkills = await getAvailableSkills();
|
|
8
|
-
|
|
9
|
-
const installed = installedSkills.map((s) => s.name);
|
|
10
|
-
|
|
11
|
-
if (json) {
|
|
12
|
-
const output = {
|
|
13
|
-
installed,
|
|
14
|
-
available: availableSkills,
|
|
15
|
-
summary: {
|
|
16
|
-
installedCount: installed.length,
|
|
17
|
-
availableCount: availableSkills.length,
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
log(JSON.stringify(output, null, 2));
|
|
21
|
-
} else {
|
|
22
|
-
log('\nš¦ Installed Skills:');
|
|
23
|
-
if (installed.length === 0) {
|
|
24
|
-
log(' (none)');
|
|
25
|
-
} else {
|
|
26
|
-
installed.forEach((name) => {
|
|
27
|
-
const entry = installedSkills.find((s) => s.name === name);
|
|
28
|
-
log(` ⢠${name} (v${entry?.version || 'unknown'})`);
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
log('\nš Available Skills:');
|
|
33
|
-
availableSkills.forEach((name) => {
|
|
34
|
-
const isInstalled = installed.includes(name);
|
|
35
|
-
const status = isInstalled ? ' [installed]' : '';
|
|
36
|
-
log(` ⢠${name}${status}`);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
log(`\nTotal: ${installed.length} installed, ${availableSkills.length} available\n`);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
1
|
+
import { getAvailableSkills } from '../core/downloader.js';
|
|
2
|
+
import { getInstalledSkills } from '../core/lockfile.js';
|
|
3
|
+
import { log } from '../utils/logger.js';
|
|
4
|
+
|
|
5
|
+
export async function list(json: boolean = false): Promise<void> {
|
|
6
|
+
const installedSkills = await getInstalledSkills();
|
|
7
|
+
const availableSkills = await getAvailableSkills();
|
|
8
|
+
|
|
9
|
+
const installed = installedSkills.map((s) => s.name);
|
|
10
|
+
|
|
11
|
+
if (json) {
|
|
12
|
+
const output = {
|
|
13
|
+
installed,
|
|
14
|
+
available: availableSkills,
|
|
15
|
+
summary: {
|
|
16
|
+
installedCount: installed.length,
|
|
17
|
+
availableCount: availableSkills.length,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
log(JSON.stringify(output, null, 2));
|
|
21
|
+
} else {
|
|
22
|
+
log('\nš¦ Installed Skills:');
|
|
23
|
+
if (installed.length === 0) {
|
|
24
|
+
log(' (none)');
|
|
25
|
+
} else {
|
|
26
|
+
installed.forEach((name) => {
|
|
27
|
+
const entry = installedSkills.find((s) => s.name === name);
|
|
28
|
+
log(` ⢠${name} (v${entry?.version || 'unknown'})`);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
log('\nš Available Skills:');
|
|
33
|
+
availableSkills.forEach((name) => {
|
|
34
|
+
const isInstalled = installed.includes(name);
|
|
35
|
+
const status = isInstalled ? ' [installed]' : '';
|
|
36
|
+
log(` ⢠${name}${status}`);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
log(`\nTotal: ${installed.length} installed, ${availableSkills.length} available\n`);
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/commands/remove.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { removeSkill } from '../core/installer.js';
|
|
2
|
-
import { removeSkillFromLockfile, getSkillEntry } from '../core/lockfile.js';
|
|
3
|
-
import { error, success, info } from '../utils/logger.js';
|
|
4
|
-
|
|
5
|
-
export async function remove(skillName: string): Promise<boolean> {
|
|
6
|
-
try {
|
|
7
|
-
const entry = await getSkillEntry(skillName);
|
|
8
|
-
if (!entry) {
|
|
9
|
-
info(`${skillName} is not installed`);
|
|
10
|
-
return true;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
info(`Removing ${skillName}...`);
|
|
14
|
-
await removeSkill(skillName);
|
|
15
|
-
await removeSkillFromLockfile(skillName);
|
|
16
|
-
success(`Removed ${skillName}`);
|
|
17
|
-
return true;
|
|
18
|
-
} catch (err) {
|
|
19
|
-
error(`Failed to remove ${skillName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
1
|
+
import { removeSkill } from '../core/installer.js';
|
|
2
|
+
import { removeSkillFromLockfile, getSkillEntry } from '../core/lockfile.js';
|
|
3
|
+
import { error, success, info } from '../utils/logger.js';
|
|
4
|
+
|
|
5
|
+
export async function remove(skillName: string): Promise<boolean> {
|
|
6
|
+
try {
|
|
7
|
+
const entry = await getSkillEntry(skillName);
|
|
8
|
+
if (!entry) {
|
|
9
|
+
info(`${skillName} is not installed`);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
info(`Removing ${skillName}...`);
|
|
14
|
+
await removeSkill(skillName);
|
|
15
|
+
await removeSkillFromLockfile(skillName);
|
|
16
|
+
success(`Removed ${skillName}`);
|
|
17
|
+
return true;
|
|
18
|
+
} catch (err) {
|
|
19
|
+
error(`Failed to remove ${skillName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|