antikit 1.4.0 ā 1.6.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 +85 -2
- package/src/commands/list.js +75 -18
- package/src/commands/upgrade.js +90 -0
- package/src/index.js +9 -0
- 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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
3
|
import { execSync } from 'child_process';
|
|
4
|
-
import { existsSync, mkdirSync, cpSync, rmSync } from 'fs';
|
|
4
|
+
import { existsSync, mkdirSync, cpSync, rmSync, writeFileSync } from 'fs';
|
|
5
5
|
import { join } from 'path';
|
|
6
6
|
import { homedir } from 'os';
|
|
7
7
|
import { getOrCreateSkillsDir, skillExists } from '../utils/local.js';
|
|
@@ -69,15 +69,96 @@ export async function installSkill(skillName, options = {}) {
|
|
|
69
69
|
if (!existsSync(sourcePath)) {
|
|
70
70
|
rmSync(tempDir, { recursive: true, force: true });
|
|
71
71
|
spinner.fail(`Skill "${skillName}" not found in ${owner}/${repo}`);
|
|
72
|
+
if (options.noExit) throw new Error('Skill not found');
|
|
72
73
|
process.exit(1);
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
// --- Check dependencies ---
|
|
77
|
+
try {
|
|
78
|
+
const mdPath = join(sourcePath, 'SKILL.md');
|
|
79
|
+
if (existsSync(mdPath)) {
|
|
80
|
+
// Import locally to avoid cluttering top imports if possible, or just use fs
|
|
81
|
+
const { readFileSync } = await import('fs');
|
|
82
|
+
const content = readFileSync(mdPath, 'utf-8');
|
|
83
|
+
|
|
84
|
+
// Parse frontmatter dependencies
|
|
85
|
+
// Supports inline: dependencies: [a, b]
|
|
86
|
+
// Supports list:
|
|
87
|
+
// dependencies:
|
|
88
|
+
// - a
|
|
89
|
+
// - b
|
|
90
|
+
let deps = [];
|
|
91
|
+
|
|
92
|
+
// Try inline
|
|
93
|
+
const inlineMatch = content.match(/^dependencies:\s*\[(.*?)\]/m);
|
|
94
|
+
if (inlineMatch) {
|
|
95
|
+
deps = inlineMatch[1].split(',').map(s => s.trim().replace(/['"]/g, '')).filter(Boolean);
|
|
96
|
+
} else {
|
|
97
|
+
// Try list
|
|
98
|
+
const listMatch = content.match(/^dependencies:\s*\n((?:\s*-\s*.+\n?)+)/m);
|
|
99
|
+
if (listMatch) {
|
|
100
|
+
deps = listMatch[1].split('\n')
|
|
101
|
+
.map(l => l.replace(/^\s*-\s*/, '').trim())
|
|
102
|
+
.filter(Boolean);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (deps.length > 0) {
|
|
107
|
+
spinner.stop();
|
|
108
|
+
console.log(chalk.blue(`\nSkill "${skillName}" requires: ${deps.join(', ')}`));
|
|
109
|
+
|
|
110
|
+
for (const dep of deps) {
|
|
111
|
+
// Prevent infinite recursion if self-referencing
|
|
112
|
+
if (dep === skillName) continue;
|
|
113
|
+
|
|
114
|
+
if (!skillExists(dep)) {
|
|
115
|
+
console.log(chalk.dim(`Installing dependency: ${dep}...`));
|
|
116
|
+
// Recursive install
|
|
117
|
+
await installSkill(dep, {
|
|
118
|
+
...options,
|
|
119
|
+
force: false,
|
|
120
|
+
noExit: true // Don't exit on dependency failure, just log
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
spinner.start(`Resuming installation of "${skillName}"...`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} catch (e) {
|
|
128
|
+
// Dep check failed, but continue installing main skill
|
|
129
|
+
// console.error(e);
|
|
130
|
+
}
|
|
131
|
+
|
|
75
132
|
if (options.force && existsSync(destPath)) {
|
|
76
133
|
rmSync(destPath, { recursive: true, force: true });
|
|
77
134
|
}
|
|
78
135
|
|
|
79
136
|
cpSync(sourcePath, destPath, { recursive: true });
|
|
80
137
|
|
|
138
|
+
// Save skill metadata for future upgrades
|
|
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
|
+
|
|
151
|
+
const metadata = {
|
|
152
|
+
name: skillName,
|
|
153
|
+
source: { owner, repo },
|
|
154
|
+
version,
|
|
155
|
+
installedAt: Date.now()
|
|
156
|
+
};
|
|
157
|
+
writeFileSync(join(destPath, '.antikit-skill.json'), JSON.stringify(metadata, null, 2));
|
|
158
|
+
} catch (e) {
|
|
159
|
+
// Ignore metadata write error, not critical
|
|
160
|
+
}
|
|
161
|
+
|
|
81
162
|
// Cleanup temp
|
|
82
163
|
rmSync(tempDir, { recursive: true, force: true });
|
|
83
164
|
|
|
@@ -87,7 +168,9 @@ export async function installSkill(skillName, options = {}) {
|
|
|
87
168
|
} catch (error) {
|
|
88
169
|
spinner.fail(`Failed to install "${skillName}"`);
|
|
89
170
|
console.error(chalk.red(error.message));
|
|
171
|
+
if (options.noExit) {
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
90
174
|
process.exit(1);
|
|
91
175
|
}
|
|
92
176
|
}
|
|
93
|
-
|
package/src/commands/list.js
CHANGED
|
@@ -2,8 +2,21 @@ import chalk from 'chalk';
|
|
|
2
2
|
import ora from 'ora';
|
|
3
3
|
import { checkbox, confirm } 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
|
}
|
|
@@ -85,15 +120,35 @@ function displaySkillsList(skills) {
|
|
|
85
120
|
|
|
86
121
|
async function interactiveInstall(skills) {
|
|
87
122
|
// Prepare choices for checkbox
|
|
88
|
-
const choices = skills.map(skill =>
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
123
|
+
const choices = skills.map(skill => {
|
|
124
|
+
let label = '';
|
|
125
|
+
let disabled = false;
|
|
126
|
+
|
|
127
|
+
if (skill.installed) {
|
|
128
|
+
if (skill.updateAvailable) {
|
|
129
|
+
label = `${chalk.yellow('ā')} ${chalk.cyan(skill.name)} ${chalk.yellow(`(Update: v${skill.localVersion} ā v${skill.remoteVersion})`)}`;
|
|
130
|
+
} else {
|
|
131
|
+
label = `${chalk.green('ā')} ${chalk.cyan(skill.name)} ${chalk.dim('(Installed)')}`;
|
|
132
|
+
disabled = true; // Disable if installed and no update
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
label = `${chalk.dim(' ')} ${chalk.cyan(skill.name)}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (skill.description) {
|
|
139
|
+
label += ` ${chalk.dim('- ' + skill.description.slice(0, 40) + '...')}`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
name: label,
|
|
144
|
+
value: skill,
|
|
145
|
+
disabled
|
|
146
|
+
};
|
|
147
|
+
});
|
|
93
148
|
|
|
94
149
|
// Show checkbox selection
|
|
95
150
|
const selected = await checkbox({
|
|
96
|
-
message: 'Select skills to install (Space to select, Enter to confirm):',
|
|
151
|
+
message: 'Select skills to install/update (Space to select, Enter to confirm):',
|
|
97
152
|
choices,
|
|
98
153
|
pageSize: 15
|
|
99
154
|
});
|
|
@@ -105,21 +160,23 @@ async function interactiveInstall(skills) {
|
|
|
105
160
|
|
|
106
161
|
// Confirm installation
|
|
107
162
|
const shouldInstall = await confirm({
|
|
108
|
-
message: `Install ${selected.length} skill(s)?`,
|
|
163
|
+
message: `Install/Update ${selected.length} skill(s)?`,
|
|
109
164
|
default: true
|
|
110
165
|
});
|
|
111
166
|
|
|
112
167
|
if (!shouldInstall) {
|
|
113
|
-
console.log(chalk.yellow('
|
|
168
|
+
console.log(chalk.yellow('Operation cancelled.'));
|
|
114
169
|
return;
|
|
115
170
|
}
|
|
116
171
|
|
|
117
172
|
// Install selected skills
|
|
118
173
|
console.log();
|
|
119
174
|
for (const skill of selected) {
|
|
120
|
-
|
|
175
|
+
// Force install if updating
|
|
176
|
+
const force = skill.installed && skill.updateAvailable;
|
|
177
|
+
await installSkill(skill.name, { force, owner: skill.owner, repo: skill.repo });
|
|
121
178
|
}
|
|
122
179
|
|
|
123
180
|
console.log();
|
|
124
|
-
console.log(chalk.green.bold(`ā
|
|
181
|
+
console.log(chalk.green.bold(`ā Processed ${selected.length} skill(s)`));
|
|
125
182
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { readdirSync, existsSync, readFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { getOrCreateSkillsDir } from '../utils/local.js';
|
|
5
|
+
import { installSkill } from './install.js';
|
|
6
|
+
import { confirm } from '@inquirer/prompts';
|
|
7
|
+
|
|
8
|
+
export async function upgradeSkills(skillName, options = {}) {
|
|
9
|
+
const skillsDir = getOrCreateSkillsDir();
|
|
10
|
+
|
|
11
|
+
// 1. Upgrade specific skill
|
|
12
|
+
if (skillName) {
|
|
13
|
+
try {
|
|
14
|
+
await upgradeSingleSkill(skillsDir, skillName);
|
|
15
|
+
} catch {
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 2. Upgrade all skills
|
|
22
|
+
const skills = readdirSync(skillsDir).filter(f =>
|
|
23
|
+
existsSync(join(skillsDir, f)) &&
|
|
24
|
+
!f.startsWith('.')
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
if (skills.length === 0) {
|
|
28
|
+
console.log(chalk.yellow('No skills installed.'));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
console.log(chalk.blue(`Found ${skills.length} installed skills.`));
|
|
33
|
+
|
|
34
|
+
let shouldProceed = options.yes;
|
|
35
|
+
if (!shouldProceed) {
|
|
36
|
+
shouldProceed = await confirm({ message: 'Upgrade all skills?', default: true });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!shouldProceed) return;
|
|
40
|
+
|
|
41
|
+
let successCount = 0;
|
|
42
|
+
let failCount = 0;
|
|
43
|
+
|
|
44
|
+
for (const skill of skills) {
|
|
45
|
+
try {
|
|
46
|
+
await upgradeSingleSkill(skillsDir, skill);
|
|
47
|
+
successCount++;
|
|
48
|
+
} catch {
|
|
49
|
+
failCount++;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
54
|
+
if (failCount === 0) {
|
|
55
|
+
console.log(chalk.green(`ā All ${successCount} skills upgraded successfully`));
|
|
56
|
+
} else {
|
|
57
|
+
console.log(chalk.yellow(`ā Upgraded ${successCount} skills, ${failCount} failed`));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function upgradeSingleSkill(skillsDir, skillName) {
|
|
62
|
+
const skillPath = join(skillsDir, skillName);
|
|
63
|
+
const metaPath = join(skillPath, '.antikit-skill.json');
|
|
64
|
+
|
|
65
|
+
if (!existsSync(metaPath)) {
|
|
66
|
+
console.log(chalk.yellow(`ā Skipping "${skillName}": Missing metadata (install again to fix)`));
|
|
67
|
+
throw new Error('Missing metadata');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const meta = JSON.parse(readFileSync(metaPath, 'utf-8'));
|
|
72
|
+
if (!meta.source || !meta.source.owner || !meta.source.repo) {
|
|
73
|
+
console.log(chalk.yellow(`ā Skipping "${skillName}": Invalid metadata`));
|
|
74
|
+
throw new Error('Invalid metadata');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log(chalk.bold.cyan(`\nUpgrading ${skillName}...`));
|
|
78
|
+
|
|
79
|
+
await installSkill(skillName, {
|
|
80
|
+
force: true,
|
|
81
|
+
owner: meta.source.owner,
|
|
82
|
+
repo: meta.source.repo,
|
|
83
|
+
noExit: true // Don't kill process on error
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
} catch (error) {
|
|
87
|
+
// Error already logged by installSkill or above
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}
|
package/src/index.js
CHANGED
|
@@ -8,9 +8,11 @@ import { listLocalSkills } from './commands/local.js';
|
|
|
8
8
|
import { installSkill } from './commands/install.js';
|
|
9
9
|
import { removeSkill } from './commands/remove.js';
|
|
10
10
|
import { updateCli } from './commands/update.js';
|
|
11
|
+
import { upgradeSkills } from './commands/upgrade.js';
|
|
11
12
|
import { listSources, addNewSource, removeExistingSource, setDefault } from './commands/source.js';
|
|
12
13
|
import { checkForUpdates } from './utils/updateNotifier.js';
|
|
13
14
|
|
|
15
|
+
|
|
14
16
|
const require = createRequire(import.meta.url);
|
|
15
17
|
const pkg = require('../package.json');
|
|
16
18
|
|
|
@@ -59,6 +61,13 @@ program
|
|
|
59
61
|
.description('Update antikit to the latest version')
|
|
60
62
|
.action(updateCli);
|
|
61
63
|
|
|
64
|
+
program
|
|
65
|
+
.command('upgrade [skill]')
|
|
66
|
+
.alias('ug')
|
|
67
|
+
.description('Upgrade installed skills')
|
|
68
|
+
.option('-y, --yes', 'Skip confirmation')
|
|
69
|
+
.action(upgradeSkills);
|
|
70
|
+
|
|
62
71
|
// Source management commands
|
|
63
72
|
const sourceCmd = program
|
|
64
73
|
.command('source')
|
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
|
/**
|