antikit 1.5.0 ā 1.7.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 +87 -18
- package/package.json +1 -1
- package/src/commands/install.js +12 -0
- package/src/commands/list.js +93 -21
- package/src/utils/github.js +10 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# antikit
|
|
2
2
|
|
|
3
|
-
CLI tool to manage AI agent skills from
|
|
3
|
+
CLI tool to manage AI agent skills from multiple repositories. Easily discover, install, and update skills for your AI agents.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,8 +10,9 @@ npm install -g antikit
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
-
###
|
|
13
|
+
### š¦ Manage Skills
|
|
14
14
|
|
|
15
|
+
#### List available skills
|
|
15
16
|
```bash
|
|
16
17
|
antikit list
|
|
17
18
|
# or
|
|
@@ -19,17 +20,16 @@ antikit ls
|
|
|
19
20
|
|
|
20
21
|
# Search skills by name
|
|
21
22
|
antikit list -s <query>
|
|
22
|
-
```
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
# Interactive mode (select and install)
|
|
25
|
+
antikit list -i
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
antikit
|
|
28
|
-
# or
|
|
29
|
-
antikit l
|
|
27
|
+
# Filter by source
|
|
28
|
+
antikit list --source official
|
|
30
29
|
```
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
#### Install a skill
|
|
32
|
+
Automatically installs dependencies defined in `SKILL.md`.
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
35
|
antikit install <skill-name>
|
|
@@ -40,25 +40,94 @@ antikit i <skill-name>
|
|
|
40
40
|
antikit install <skill-name> --force
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
#### Upgrade installed skills
|
|
44
|
+
Update your local skills to the latest version from their sources.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Upgrade all installed skills
|
|
48
|
+
antikit upgrade
|
|
49
|
+
|
|
50
|
+
# Upgrade a specific skill
|
|
51
|
+
antikit upgrade <skill-name>
|
|
52
|
+
|
|
53
|
+
# Upgrade without confirmation (good for scripts)
|
|
54
|
+
antikit upgrade --yes
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### List installed local skills
|
|
58
|
+
```bash
|
|
59
|
+
antikit local
|
|
60
|
+
# or
|
|
61
|
+
antikit l
|
|
62
|
+
```
|
|
44
63
|
|
|
64
|
+
#### Remove a skill
|
|
45
65
|
```bash
|
|
46
66
|
antikit remove <skill-name>
|
|
47
67
|
# or
|
|
48
68
|
antikit rm <skill-name>
|
|
49
69
|
```
|
|
50
70
|
|
|
51
|
-
|
|
71
|
+
---
|
|
52
72
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
73
|
+
### š” Manage Sources
|
|
74
|
+
You can fetch skills from multiple GitHub repositories.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# List configured sources
|
|
78
|
+
antikit source list
|
|
56
79
|
|
|
57
|
-
|
|
80
|
+
# Add a new source (GitHub owner/repo)
|
|
81
|
+
antikit source add vunamhung/another-repo
|
|
58
82
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
83
|
+
# Add with a custom name
|
|
84
|
+
antikit source add vunamhung/private-skills --name private
|
|
85
|
+
|
|
86
|
+
# Set a default source
|
|
87
|
+
antikit source default private
|
|
88
|
+
|
|
89
|
+
# Remove a source
|
|
90
|
+
antikit source remove private
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
### š Self Update
|
|
96
|
+
Update the `antikit` CLI tool itself to the latest version.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
antikit update
|
|
100
|
+
```
|
|
101
|
+
*Note: You will also be notified automatically if a new version is available when running any command.*
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Skill Development
|
|
106
|
+
|
|
107
|
+
### Skill Structure
|
|
108
|
+
A skill is a directory containing a `SKILL.md` file.
|
|
109
|
+
|
|
110
|
+
### Defining Dependencies
|
|
111
|
+
You can specify dependencies in the `SKILL.md` frontmatter. `antikit` will automatically install them.
|
|
112
|
+
|
|
113
|
+
```yaml
|
|
114
|
+
---
|
|
115
|
+
name: my-skill
|
|
116
|
+
description: A powerful skill that needs helpers
|
|
117
|
+
dependencies:
|
|
118
|
+
- sql-helper
|
|
119
|
+
- python-runner
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
# My Skill Content
|
|
123
|
+
...
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Requirements
|
|
127
|
+
|
|
128
|
+
- Node.js >= 18.0.0
|
|
129
|
+
- Git (for cloning skills)
|
|
130
|
+
- A project with `.agent/skills` directory (created automatically)
|
|
62
131
|
|
|
63
132
|
## License
|
|
64
133
|
|
package/package.json
CHANGED
package/src/commands/install.js
CHANGED
|
@@ -137,9 +137,21 @@ export async function installSkill(skillName, options = {}) {
|
|
|
137
137
|
|
|
138
138
|
// Save skill metadata for future upgrades
|
|
139
139
|
try {
|
|
140
|
+
let version = '0.0.0';
|
|
141
|
+
const mdPath = join(destPath, 'SKILL.md');
|
|
142
|
+
if (existsSync(mdPath)) {
|
|
143
|
+
// We likely already imported readFileSync if inside try block,
|
|
144
|
+
// but for safety use dynamic import or assume imported
|
|
145
|
+
const { readFileSync } = await import('fs');
|
|
146
|
+
const content = readFileSync(mdPath, 'utf-8');
|
|
147
|
+
const vMatch = content.match(/^version:\s*(.+)/m);
|
|
148
|
+
if (vMatch) version = vMatch[1].trim();
|
|
149
|
+
}
|
|
150
|
+
|
|
140
151
|
const metadata = {
|
|
141
152
|
name: skillName,
|
|
142
153
|
source: { owner, repo },
|
|
154
|
+
version,
|
|
143
155
|
installedAt: Date.now()
|
|
144
156
|
};
|
|
145
157
|
writeFileSync(join(destPath, '.antikit-skill.json'), JSON.stringify(metadata, null, 2));
|
package/src/commands/list.js
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
|
-
import { checkbox, confirm } from '@inquirer/prompts';
|
|
3
|
+
import { checkbox, confirm, Separator } from '@inquirer/prompts';
|
|
4
4
|
import { fetchRemoteSkills, fetchSkillInfo } from '../utils/github.js';
|
|
5
|
-
import { skillExists } from '../utils/local.js';
|
|
5
|
+
import { skillExists, getOrCreateSkillsDir } from '../utils/local.js';
|
|
6
6
|
import { installSkill } from './install.js';
|
|
7
|
+
import { existsSync, readFileSync } from 'fs';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
|
|
10
|
+
function compareVersions(v1, v2) {
|
|
11
|
+
if (!v1 || !v2) return 0;
|
|
12
|
+
const p1 = v1.split('.').map(Number);
|
|
13
|
+
const p2 = v2.split('.').map(Number);
|
|
14
|
+
for (let i = 0; i < 3; i++) {
|
|
15
|
+
if ((p1[i] || 0) > (p2[i] || 0)) return 1;
|
|
16
|
+
if ((p1[i] || 0) < (p2[i] || 0)) return -1;
|
|
17
|
+
}
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
7
20
|
|
|
8
21
|
export async function listRemoteSkills(options) {
|
|
9
22
|
const sourceName = options.source || null;
|
|
@@ -26,13 +39,32 @@ export async function listRemoteSkills(options) {
|
|
|
26
39
|
return;
|
|
27
40
|
}
|
|
28
41
|
|
|
29
|
-
|
|
42
|
+
const skillsDir = getOrCreateSkillsDir();
|
|
43
|
+
|
|
44
|
+
// Fetch descriptions & versions
|
|
30
45
|
const infoSpinner = ora('Fetching skill info...').start();
|
|
31
46
|
const skillsWithInfo = await Promise.all(
|
|
32
47
|
skills.map(async (skill) => {
|
|
33
|
-
const
|
|
48
|
+
const info = await fetchSkillInfo(skill.name, skill.owner, skill.repo);
|
|
49
|
+
const description = info ? info.description : null;
|
|
50
|
+
const remoteVersion = info ? info.version : '0.0.0';
|
|
51
|
+
|
|
34
52
|
const installed = skillExists(skill.name);
|
|
35
|
-
|
|
53
|
+
let updateAvailable = false;
|
|
54
|
+
let localVersion = '0.0.0';
|
|
55
|
+
|
|
56
|
+
if (installed) {
|
|
57
|
+
try {
|
|
58
|
+
const metaPath = join(skillsDir, skill.name, '.antikit-skill.json');
|
|
59
|
+
if (existsSync(metaPath)) {
|
|
60
|
+
const meta = JSON.parse(readFileSync(metaPath, 'utf8'));
|
|
61
|
+
localVersion = meta.version || '0.0.0';
|
|
62
|
+
updateAvailable = compareVersions(remoteVersion, localVersion) > 0;
|
|
63
|
+
}
|
|
64
|
+
} catch { }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { ...skill, description, installed, updateAvailable, localVersion, remoteVersion };
|
|
36
68
|
})
|
|
37
69
|
);
|
|
38
70
|
infoSpinner.stop();
|
|
@@ -68,11 +100,14 @@ function displaySkillsList(skills) {
|
|
|
68
100
|
for (const [sourceName, sourceSkills] of Object.entries(bySource)) {
|
|
69
101
|
console.log(chalk.magenta.bold(`\nš¦ ${sourceName}`));
|
|
70
102
|
for (const skill of sourceSkills) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
103
|
+
let status = chalk.dim(' ');
|
|
104
|
+
if (skill.installed) {
|
|
105
|
+
status = skill.updateAvailable
|
|
106
|
+
? chalk.yellow(' ā')
|
|
107
|
+
: chalk.green(' ā');
|
|
108
|
+
}
|
|
74
109
|
|
|
75
|
-
console.log(`${status} ${chalk.cyan.bold(skill.name)}`);
|
|
110
|
+
console.log(`${status} ${chalk.cyan.bold(skill.name)} ${skill.installed ? chalk.dim(`(v${skill.localVersion}${skill.updateAvailable ? ` ā v${skill.remoteVersion}` : ''})`) : ''}`);
|
|
76
111
|
if (skill.description) {
|
|
77
112
|
console.log(` ${chalk.dim(skill.description)}`);
|
|
78
113
|
}
|
|
@@ -84,18 +119,53 @@ function displaySkillsList(skills) {
|
|
|
84
119
|
}
|
|
85
120
|
|
|
86
121
|
async function interactiveInstall(skills) {
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
122
|
+
// Sort skills by Source then Name
|
|
123
|
+
skills.sort((a, b) => {
|
|
124
|
+
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
125
|
+
return a.name.localeCompare(b.name);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const choices = [];
|
|
129
|
+
let currentSource = null;
|
|
130
|
+
|
|
131
|
+
for (const skill of skills) {
|
|
132
|
+
// Add Separator for new source
|
|
133
|
+
if (skill.source !== currentSource) {
|
|
134
|
+
currentSource = skill.source;
|
|
135
|
+
choices.push(new Separator(` \n āāāāāāāā Source: ${chalk.bold.magenta(currentSource)} āāāāāāāā`));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let label = '';
|
|
139
|
+
let disabled = false;
|
|
140
|
+
|
|
141
|
+
if (skill.installed) {
|
|
142
|
+
if (skill.updateAvailable) {
|
|
143
|
+
label = `${chalk.yellow('ā')} ${chalk.cyan(skill.name)} ${chalk.yellow(`(Update: v${skill.localVersion} ā v${skill.remoteVersion})`)}`;
|
|
144
|
+
} else {
|
|
145
|
+
label = `${chalk.green('ā')} ${chalk.cyan(skill.name)} ${chalk.dim('(Installed)')}`;
|
|
146
|
+
disabled = true;
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
label = `${chalk.dim(' ')} ${chalk.cyan(skill.name)}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (skill.description) {
|
|
153
|
+
label += ` ${chalk.dim('- ' + skill.description.slice(0, 40) + '...')}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
choices.push({
|
|
157
|
+
name: label,
|
|
158
|
+
value: skill,
|
|
159
|
+
disabled
|
|
160
|
+
});
|
|
161
|
+
}
|
|
93
162
|
|
|
94
163
|
// Show checkbox selection
|
|
95
164
|
const selected = await checkbox({
|
|
96
|
-
message: 'Select skills to install
|
|
165
|
+
message: 'Select skills to install/update:',
|
|
97
166
|
choices,
|
|
98
|
-
pageSize:
|
|
167
|
+
pageSize: 20, // Increase page size for better view
|
|
168
|
+
loop: false
|
|
99
169
|
});
|
|
100
170
|
|
|
101
171
|
if (selected.length === 0) {
|
|
@@ -105,21 +175,23 @@ async function interactiveInstall(skills) {
|
|
|
105
175
|
|
|
106
176
|
// Confirm installation
|
|
107
177
|
const shouldInstall = await confirm({
|
|
108
|
-
message: `Install ${selected.length} skill(s)?`,
|
|
178
|
+
message: `Install/Update ${selected.length} skill(s)?`,
|
|
109
179
|
default: true
|
|
110
180
|
});
|
|
111
181
|
|
|
112
182
|
if (!shouldInstall) {
|
|
113
|
-
console.log(chalk.yellow('
|
|
183
|
+
console.log(chalk.yellow('Operation cancelled.'));
|
|
114
184
|
return;
|
|
115
185
|
}
|
|
116
186
|
|
|
117
187
|
// Install selected skills
|
|
118
188
|
console.log();
|
|
119
189
|
for (const skill of selected) {
|
|
120
|
-
|
|
190
|
+
// Force install if updating
|
|
191
|
+
const force = skill.installed && skill.updateAvailable;
|
|
192
|
+
await installSkill(skill.name, { force, owner: skill.owner, repo: skill.repo });
|
|
121
193
|
}
|
|
122
194
|
|
|
123
195
|
console.log();
|
|
124
|
-
console.log(chalk.green.bold(`ā
|
|
196
|
+
console.log(chalk.green.bold(`ā Processed ${selected.length} skill(s)`));
|
|
125
197
|
}
|
package/src/utils/github.js
CHANGED
|
@@ -95,14 +95,20 @@ export async function fetchSkillInfo(skillName, owner, repo) {
|
|
|
95
95
|
const data = await response.json();
|
|
96
96
|
const content = Buffer.from(data.content, 'base64').toString('utf-8');
|
|
97
97
|
|
|
98
|
-
// Extract
|
|
98
|
+
// Extract info from YAML frontmatter
|
|
99
99
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
100
100
|
if (match) {
|
|
101
|
-
const
|
|
102
|
-
|
|
101
|
+
const frontmatter = match[1];
|
|
102
|
+
const descMatch = frontmatter.match(/description:\s*(.+)/);
|
|
103
|
+
const versionMatch = frontmatter.match(/version:\s*(.+)/);
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
description: descMatch ? descMatch[1].trim() : null,
|
|
107
|
+
version: versionMatch ? versionMatch[1].trim() : '0.0.0'
|
|
108
|
+
};
|
|
103
109
|
}
|
|
104
110
|
|
|
105
|
-
return null;
|
|
111
|
+
return { description: null, version: '0.0.0' };
|
|
106
112
|
}
|
|
107
113
|
|
|
108
114
|
/**
|