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 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
+ }