antikit 1.3.1 → 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.3.1",
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,76 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { execSync } from 'child_process';
4
+ import { confirm } from '@inquirer/prompts';
5
+
6
+ // Since fetchLatestVersion was internal in updateNotifier.js, let's create a helper
7
+ // or I can duplicate the fetch logic here for simplicity, or refactor updateNotifier.js.
8
+ // Refactoring is better practice.
9
+
10
+ /**
11
+ * Update the CLI tool to the latest version
12
+ */
13
+ export async function updateCli() {
14
+ const spinner = ora('Checking for updates...').start();
15
+
16
+ try {
17
+ // Fetch latest version from npm
18
+ const response = await fetch(`https://registry.npmjs.org/antikit/latest`, {
19
+ headers: { 'Accept': 'application/json' },
20
+ signal: AbortSignal.timeout(5000)
21
+ });
22
+
23
+ if (!response.ok) {
24
+ throw new Error('Failed to fetch version info from npm registry');
25
+ }
26
+
27
+ const data = await response.json();
28
+ const latestVersion = data.version;
29
+
30
+ // Import package.json to get current version
31
+ // Using dynamic import to avoid requiring createRequire in this module if possible,
32
+ // but for consistency with index.js style
33
+ const { createRequire } = await import('module');
34
+ const require = createRequire(import.meta.url);
35
+ const pkg = require('../../package.json');
36
+ const currentVersion = pkg.version;
37
+
38
+ spinner.stop();
39
+
40
+ if (latestVersion === currentVersion) {
41
+ console.log(chalk.green(`\n✓ You are already on the latest version (${currentVersion})`));
42
+ return;
43
+ }
44
+
45
+ console.log(chalk.bold(`\nUpdate available: ${chalk.dim(currentVersion)} → ${chalk.green(latestVersion)}`));
46
+
47
+ const shouldUpdate = await confirm({
48
+ message: 'Do you want to update now?',
49
+ default: true
50
+ });
51
+
52
+ if (!shouldUpdate) {
53
+ console.log(chalk.yellow('Update cancelled.'));
54
+ return;
55
+ }
56
+
57
+ const updateSpinner = ora('Updating antikit...').start();
58
+
59
+ try {
60
+ execSync('npm install -g antikit@latest', { stdio: 'pipe' });
61
+ updateSpinner.succeed(chalk.green(`Updated to version ${latestVersion}`));
62
+ console.log(chalk.dim('\nYou may need to restart your terminal for changes to take effect.'));
63
+ } catch (err) {
64
+ updateSpinner.fail('Update failed');
65
+ console.log(chalk.red('Please try running: npm install -g antikit@latest'));
66
+ // Check for permission error
67
+ if (err.message.includes('EACCES')) {
68
+ console.log(chalk.yellow('Note: You might need to run with sudo'));
69
+ }
70
+ }
71
+
72
+ } catch (error) {
73
+ spinner.fail('Failed to check for updates');
74
+ console.error(chalk.red(error.message));
75
+ }
76
+ }
@@ -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
@@ -7,9 +7,12 @@ import { listRemoteSkills } from './commands/list.js';
7
7
  import { listLocalSkills } from './commands/local.js';
8
8
  import { installSkill } from './commands/install.js';
9
9
  import { removeSkill } from './commands/remove.js';
10
+ import { updateCli } from './commands/update.js';
11
+ import { upgradeSkills } from './commands/upgrade.js';
10
12
  import { listSources, addNewSource, removeExistingSource, setDefault } from './commands/source.js';
11
13
  import { checkForUpdates } from './utils/updateNotifier.js';
12
14
 
15
+
13
16
  const require = createRequire(import.meta.url);
14
17
  const pkg = require('../package.json');
15
18
 
@@ -52,6 +55,19 @@ program
52
55
  .description('Remove an installed skill')
53
56
  .action(removeSkill);
54
57
 
58
+ program
59
+ .command('update')
60
+ .alias('up')
61
+ .description('Update antikit to the latest version')
62
+ .action(updateCli);
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
+
55
71
  // Source management commands
56
72
  const sourceCmd = program
57
73
  .command('source')