antikit 1.4.0 → 1.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antikit",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "CLI tool to manage AI agent skills from Anti Gravity skills repository",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -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,84 @@ 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
+ const metadata = {
141
+ name: skillName,
142
+ source: { owner, repo },
143
+ installedAt: Date.now()
144
+ };
145
+ writeFileSync(join(destPath, '.antikit-skill.json'), JSON.stringify(metadata, null, 2));
146
+ } catch (e) {
147
+ // Ignore metadata write error, not critical
148
+ }
149
+
81
150
  // Cleanup temp
82
151
  rmSync(tempDir, { recursive: true, force: true });
83
152
 
@@ -87,7 +156,9 @@ export async function installSkill(skillName, options = {}) {
87
156
  } catch (error) {
88
157
  spinner.fail(`Failed to install "${skillName}"`);
89
158
  console.error(chalk.red(error.message));
159
+ if (options.noExit) {
160
+ throw error;
161
+ }
90
162
  process.exit(1);
91
163
  }
92
164
  }
93
-
@@ -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')