antikit 1.0.1
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 +65 -0
- package/package.json +44 -0
- package/src/commands/install.js +77 -0
- package/src/commands/list.js +60 -0
- package/src/commands/local.js +33 -0
- package/src/commands/remove.js +24 -0
- package/src/config.js +17 -0
- package/src/index.js +43 -0
- package/src/utils/github.js +67 -0
- package/src/utils/local.js +84 -0
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# antikit
|
|
2
|
+
|
|
3
|
+
CLI tool to manage AI agent skills from the [antiskills](https://github.com/vunamhung/antiskills) repository.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g antikit
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### List available skills from remote
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
antikit list
|
|
17
|
+
# or
|
|
18
|
+
antikit ls
|
|
19
|
+
|
|
20
|
+
# Search skills by name
|
|
21
|
+
antikit list -s <query>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### List installed local skills
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
antikit local
|
|
28
|
+
# or
|
|
29
|
+
antikit l
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Install a skill
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
antikit install <skill-name>
|
|
36
|
+
# or
|
|
37
|
+
antikit i <skill-name>
|
|
38
|
+
|
|
39
|
+
# Force overwrite if exists
|
|
40
|
+
antikit install <skill-name> --force
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Remove a skill
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
antikit remove <skill-name>
|
|
47
|
+
# or
|
|
48
|
+
antikit rm <skill-name>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Requirements
|
|
52
|
+
|
|
53
|
+
- Node.js >= 18.0.0
|
|
54
|
+
- Git (for installing skills)
|
|
55
|
+
- A project with `.agent/skills` directory
|
|
56
|
+
|
|
57
|
+
## How it works
|
|
58
|
+
|
|
59
|
+
1. Skills are fetched from the `vunamhung/antiskills` GitHub repository
|
|
60
|
+
2. Each skill is a folder containing a `SKILL.md` file with YAML frontmatter
|
|
61
|
+
3. Skills are installed to the nearest `.agent/skills` directory in your project
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "antikit",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "CLI tool to manage AI agent skills from Anti Gravity skills repository",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"antikit": "src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node src/index.js --help"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"cli",
|
|
15
|
+
"skills",
|
|
16
|
+
"ai",
|
|
17
|
+
"agent",
|
|
18
|
+
"claude",
|
|
19
|
+
"gemini",
|
|
20
|
+
"llm"
|
|
21
|
+
],
|
|
22
|
+
"author": "vunamhung",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/vunamhung/antikit.git"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/vunamhung/antikit/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/vunamhung/antikit#readme",
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18.0.0"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"src/**/*"
|
|
37
|
+
],
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"chalk": "^5.3.0",
|
|
40
|
+
"commander": "^12.1.0",
|
|
41
|
+
"ora": "^8.1.1",
|
|
42
|
+
"simple-git": "^3.27.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import { existsSync, mkdirSync, cpSync, rmSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { CONFIG } from '../config.js';
|
|
7
|
+
import { findLocalSkillsDir, skillExists } from '../utils/local.js';
|
|
8
|
+
import { fetchRemoteSkills } from '../utils/github.js';
|
|
9
|
+
|
|
10
|
+
export async function installSkill(skillName, options) {
|
|
11
|
+
const skillsDir = findLocalSkillsDir();
|
|
12
|
+
|
|
13
|
+
if (!skillsDir) {
|
|
14
|
+
console.log(chalk.red('No .agent/skills directory found in current path.'));
|
|
15
|
+
console.log(chalk.dim('Make sure you are in a project with .agent/skills folder.'));
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Check if skill already exists
|
|
20
|
+
if (skillExists(skillName) && !options.force) {
|
|
21
|
+
console.log(chalk.yellow(`Skill "${skillName}" already exists.`));
|
|
22
|
+
console.log(chalk.dim(`Use ${chalk.white('--force')} to overwrite.`));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Verify skill exists in remote
|
|
27
|
+
const spinner = ora(`Checking if "${skillName}" exists...`).start();
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const remoteSkills = await fetchRemoteSkills();
|
|
31
|
+
const skillInfo = remoteSkills.find(s => s.name === skillName);
|
|
32
|
+
|
|
33
|
+
if (!skillInfo) {
|
|
34
|
+
spinner.fail(`Skill "${skillName}" not found in remote repository`);
|
|
35
|
+
console.log(chalk.dim(`Use ${chalk.white('antikit list')} to see available skills`));
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
spinner.text = `Cloning ${skillName}...`;
|
|
40
|
+
|
|
41
|
+
// Clone to temp directory
|
|
42
|
+
const tempDir = join(CONFIG.CACHE_DIR, 'temp', Date.now().toString());
|
|
43
|
+
mkdirSync(tempDir, { recursive: true });
|
|
44
|
+
|
|
45
|
+
// Sparse checkout only the skill folder
|
|
46
|
+
execSync(`git clone --depth 1 --filter=blob:none --sparse ${CONFIG.REPO_URL} repo`, {
|
|
47
|
+
cwd: tempDir,
|
|
48
|
+
stdio: 'pipe'
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
execSync(`git sparse-checkout set ${skillName}`, {
|
|
52
|
+
cwd: join(tempDir, 'repo'),
|
|
53
|
+
stdio: 'pipe'
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Copy skill to local
|
|
57
|
+
const sourcePath = join(tempDir, 'repo', skillName);
|
|
58
|
+
const destPath = join(skillsDir, skillName);
|
|
59
|
+
|
|
60
|
+
if (options.force && existsSync(destPath)) {
|
|
61
|
+
rmSync(destPath, { recursive: true, force: true });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
cpSync(sourcePath, destPath, { recursive: true });
|
|
65
|
+
|
|
66
|
+
// Cleanup temp
|
|
67
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
68
|
+
|
|
69
|
+
spinner.succeed(`Installed ${chalk.cyan.bold(skillName)}`);
|
|
70
|
+
console.log(chalk.dim(` → ${destPath}`));
|
|
71
|
+
|
|
72
|
+
} catch (error) {
|
|
73
|
+
spinner.fail(`Failed to install "${skillName}"`);
|
|
74
|
+
console.error(chalk.red(error.message));
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { fetchRemoteSkills, fetchSkillInfo } from '../utils/github.js';
|
|
4
|
+
import { skillExists } from '../utils/local.js';
|
|
5
|
+
|
|
6
|
+
export async function listRemoteSkills(options) {
|
|
7
|
+
const spinner = ora('Fetching skills from remote...').start();
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
let skills = await fetchRemoteSkills();
|
|
11
|
+
|
|
12
|
+
// Filter by search query
|
|
13
|
+
if (options.search) {
|
|
14
|
+
const query = options.search.toLowerCase();
|
|
15
|
+
skills = skills.filter(s => s.name.toLowerCase().includes(query));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
spinner.succeed(`Found ${skills.length} skills`);
|
|
19
|
+
console.log();
|
|
20
|
+
|
|
21
|
+
if (skills.length === 0) {
|
|
22
|
+
console.log(chalk.yellow('No skills found.'));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Fetch descriptions
|
|
27
|
+
const infoSpinner = ora('Fetching skill info...').start();
|
|
28
|
+
const skillsWithInfo = await Promise.all(
|
|
29
|
+
skills.map(async (skill) => {
|
|
30
|
+
const description = await fetchSkillInfo(skill.name);
|
|
31
|
+
const installed = skillExists(skill.name);
|
|
32
|
+
return { ...skill, description, installed };
|
|
33
|
+
})
|
|
34
|
+
);
|
|
35
|
+
infoSpinner.stop();
|
|
36
|
+
|
|
37
|
+
// Display
|
|
38
|
+
console.log(chalk.bold('Available Skills:'));
|
|
39
|
+
console.log(chalk.dim('─'.repeat(60)));
|
|
40
|
+
|
|
41
|
+
for (const skill of skillsWithInfo) {
|
|
42
|
+
const status = skill.installed
|
|
43
|
+
? chalk.green(' ✓')
|
|
44
|
+
: chalk.dim(' ');
|
|
45
|
+
|
|
46
|
+
console.log(`${status} ${chalk.cyan.bold(skill.name)}`);
|
|
47
|
+
if (skill.description) {
|
|
48
|
+
console.log(` ${chalk.dim(skill.description)}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
console.log();
|
|
53
|
+
console.log(chalk.dim(`Use ${chalk.white('antikit install <skill>')} to install a skill`));
|
|
54
|
+
|
|
55
|
+
} catch (error) {
|
|
56
|
+
spinner.fail('Failed to fetch skills');
|
|
57
|
+
console.error(chalk.red(error.message));
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { getLocalSkills, findLocalSkillsDir } from '../utils/local.js';
|
|
3
|
+
|
|
4
|
+
export async function listLocalSkills() {
|
|
5
|
+
const skillsDir = findLocalSkillsDir();
|
|
6
|
+
|
|
7
|
+
if (!skillsDir) {
|
|
8
|
+
console.log(chalk.yellow('No .agent/skills directory found in current path.'));
|
|
9
|
+
console.log(chalk.dim('Make sure you are in a project with .agent/skills folder.'));
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const skills = getLocalSkills();
|
|
14
|
+
|
|
15
|
+
if (skills.length === 0) {
|
|
16
|
+
console.log(chalk.yellow('No skills installed.'));
|
|
17
|
+
console.log(chalk.dim(`Use ${chalk.white('antikit install <skill>')} to install a skill`));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
console.log(chalk.bold(`Installed Skills (${skills.length}):`));
|
|
22
|
+
console.log(chalk.dim('─'.repeat(60)));
|
|
23
|
+
|
|
24
|
+
for (const skill of skills) {
|
|
25
|
+
console.log(` ${chalk.cyan.bold(skill.name)}`);
|
|
26
|
+
if (skill.description) {
|
|
27
|
+
console.log(` ${chalk.dim(skill.description)}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log();
|
|
32
|
+
console.log(chalk.dim(`Skills directory: ${skillsDir}`));
|
|
33
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { removeLocalSkill, skillExists, findLocalSkillsDir } from '../utils/local.js';
|
|
3
|
+
|
|
4
|
+
export async function removeSkill(skillName) {
|
|
5
|
+
const skillsDir = findLocalSkillsDir();
|
|
6
|
+
|
|
7
|
+
if (!skillsDir) {
|
|
8
|
+
console.log(chalk.red('No .agent/skills directory found in current path.'));
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (!skillExists(skillName)) {
|
|
13
|
+
console.log(chalk.yellow(`Skill "${skillName}" is not installed.`));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
removeLocalSkill(skillName);
|
|
19
|
+
console.log(chalk.green(`✓ Removed ${chalk.bold(skillName)}`));
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error(chalk.red(`Failed to remove: ${error.message}`));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/config.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { homedir } from 'os';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
|
|
4
|
+
export const CONFIG = {
|
|
5
|
+
// Remote repository
|
|
6
|
+
REPO_OWNER: 'vunamhung',
|
|
7
|
+
REPO_NAME: 'antiskills',
|
|
8
|
+
REPO_URL: 'https://github.com/vunamhung/antiskills.git',
|
|
9
|
+
GITHUB_API: 'https://api.github.com',
|
|
10
|
+
|
|
11
|
+
// Local paths
|
|
12
|
+
LOCAL_SKILLS_DIR: '.agent/skills',
|
|
13
|
+
|
|
14
|
+
// Cache
|
|
15
|
+
CACHE_DIR: join(homedir(), '.antikit'),
|
|
16
|
+
CACHE_TTL: 1000 * 60 * 60, // 1 hour
|
|
17
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { listRemoteSkills } from './commands/list.js';
|
|
6
|
+
import { listLocalSkills } from './commands/local.js';
|
|
7
|
+
import { installSkill } from './commands/install.js';
|
|
8
|
+
import { removeSkill } from './commands/remove.js';
|
|
9
|
+
|
|
10
|
+
const program = new Command();
|
|
11
|
+
|
|
12
|
+
program
|
|
13
|
+
.name('antikit')
|
|
14
|
+
.description('CLI tool to manage skills from antiskills repository')
|
|
15
|
+
.version('1.0.0');
|
|
16
|
+
|
|
17
|
+
program
|
|
18
|
+
.command('list')
|
|
19
|
+
.alias('ls')
|
|
20
|
+
.description('List available skills from remote repository')
|
|
21
|
+
.option('-s, --search <query>', 'Search skills by name')
|
|
22
|
+
.action(listRemoteSkills);
|
|
23
|
+
|
|
24
|
+
program
|
|
25
|
+
.command('local')
|
|
26
|
+
.alias('l')
|
|
27
|
+
.description('List installed skills in local .agent/skills directory')
|
|
28
|
+
.action(listLocalSkills);
|
|
29
|
+
|
|
30
|
+
program
|
|
31
|
+
.command('install <skill>')
|
|
32
|
+
.alias('i')
|
|
33
|
+
.description('Install a skill from remote repository')
|
|
34
|
+
.option('-f, --force', 'Force overwrite if skill already exists')
|
|
35
|
+
.action(installSkill);
|
|
36
|
+
|
|
37
|
+
program
|
|
38
|
+
.command('remove <skill>')
|
|
39
|
+
.alias('rm')
|
|
40
|
+
.description('Remove an installed skill')
|
|
41
|
+
.action(removeSkill);
|
|
42
|
+
|
|
43
|
+
program.parse();
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { CONFIG } from '../config.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fetch list of skills (directories) from GitHub repo
|
|
5
|
+
*/
|
|
6
|
+
export async function fetchRemoteSkills() {
|
|
7
|
+
const url = `${CONFIG.GITHUB_API}/repos/${CONFIG.REPO_OWNER}/${CONFIG.REPO_NAME}/contents`;
|
|
8
|
+
|
|
9
|
+
const response = await fetch(url, {
|
|
10
|
+
headers: {
|
|
11
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
12
|
+
'User-Agent': 'antikit-cli'
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
if (!response.ok) {
|
|
17
|
+
const data = await response.json().catch(() => ({}));
|
|
18
|
+
// Handle empty repository
|
|
19
|
+
if (data.message === 'This repository is empty.') {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
throw new Error(`Failed to fetch skills: ${response.statusText}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const contents = await response.json();
|
|
26
|
+
|
|
27
|
+
// Filter only directories (skills)
|
|
28
|
+
const skills = contents
|
|
29
|
+
.filter(item => item.type === 'dir' && !item.name.startsWith('.'))
|
|
30
|
+
.map(item => ({
|
|
31
|
+
name: item.name,
|
|
32
|
+
url: item.html_url,
|
|
33
|
+
path: item.path
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
return skills;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Fetch SKILL.md content for a specific skill
|
|
41
|
+
*/
|
|
42
|
+
export async function fetchSkillInfo(skillName) {
|
|
43
|
+
const url = `${CONFIG.GITHUB_API}/repos/${CONFIG.REPO_OWNER}/${CONFIG.REPO_NAME}/contents/${skillName}/SKILL.md`;
|
|
44
|
+
|
|
45
|
+
const response = await fetch(url, {
|
|
46
|
+
headers: {
|
|
47
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
48
|
+
'User-Agent': 'antikit-cli'
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const data = await response.json();
|
|
57
|
+
const content = Buffer.from(data.content, 'base64').toString('utf-8');
|
|
58
|
+
|
|
59
|
+
// Extract description from YAML frontmatter
|
|
60
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
61
|
+
if (match) {
|
|
62
|
+
const descMatch = match[1].match(/description:\s*(.+)/);
|
|
63
|
+
return descMatch ? descMatch[1].trim() : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, rmSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { CONFIG } from '../config.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Find local skills directory by traversing up from cwd
|
|
7
|
+
*/
|
|
8
|
+
export function findLocalSkillsDir() {
|
|
9
|
+
let dir = process.cwd();
|
|
10
|
+
|
|
11
|
+
while (dir !== '/') {
|
|
12
|
+
const skillsPath = join(dir, CONFIG.LOCAL_SKILLS_DIR);
|
|
13
|
+
if (existsSync(skillsPath)) {
|
|
14
|
+
return skillsPath;
|
|
15
|
+
}
|
|
16
|
+
dir = join(dir, '..');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get list of installed skills
|
|
24
|
+
*/
|
|
25
|
+
export function getLocalSkills() {
|
|
26
|
+
const skillsDir = findLocalSkillsDir();
|
|
27
|
+
|
|
28
|
+
if (!skillsDir) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const entries = readdirSync(skillsDir, { withFileTypes: true });
|
|
33
|
+
|
|
34
|
+
return entries
|
|
35
|
+
.filter(entry => entry.isDirectory() && !entry.name.startsWith('.'))
|
|
36
|
+
.map(entry => {
|
|
37
|
+
const skillPath = join(skillsDir, entry.name);
|
|
38
|
+
const skillMdPath = join(skillPath, 'SKILL.md');
|
|
39
|
+
|
|
40
|
+
let description = null;
|
|
41
|
+
if (existsSync(skillMdPath)) {
|
|
42
|
+
const content = readFileSync(skillMdPath, 'utf-8');
|
|
43
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
44
|
+
if (match) {
|
|
45
|
+
const descMatch = match[1].match(/description:\s*(.+)/);
|
|
46
|
+
description = descMatch ? descMatch[1].trim() : null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
name: entry.name,
|
|
52
|
+
path: skillPath,
|
|
53
|
+
description
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check if skill exists locally
|
|
60
|
+
*/
|
|
61
|
+
export function skillExists(skillName) {
|
|
62
|
+
const skillsDir = findLocalSkillsDir();
|
|
63
|
+
if (!skillsDir) return false;
|
|
64
|
+
|
|
65
|
+
const skillPath = join(skillsDir, skillName);
|
|
66
|
+
return existsSync(skillPath);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Remove a skill
|
|
71
|
+
*/
|
|
72
|
+
export function removeLocalSkill(skillName) {
|
|
73
|
+
const skillsDir = findLocalSkillsDir();
|
|
74
|
+
if (!skillsDir) {
|
|
75
|
+
throw new Error('No .agent/skills directory found');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const skillPath = join(skillsDir, skillName);
|
|
79
|
+
if (!existsSync(skillPath)) {
|
|
80
|
+
throw new Error(`Skill "${skillName}" not found`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
rmSync(skillPath, { recursive: true, force: true });
|
|
84
|
+
}
|