@petersobhy/ai-toolkit 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.
Files changed (2) hide show
  1. package/index.js +51 -15
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -9,6 +9,7 @@ const REPO = 'Petersobhy/ai-toolkit';
9
9
  const BRANCH = 'main';
10
10
  const SKILLS_PATH = 'skills';
11
11
  const INSTALL_DIR = path.join(os.homedir(), '.claude', 'agents');
12
+ const SCRIPTS_DIR = path.join(INSTALL_DIR, 'scripts');
12
13
 
13
14
  const RAW_BASE = `https://raw.githubusercontent.com/${REPO}/${BRANCH}`;
14
15
  const API_BASE = `https://api.github.com/repos/${REPO}/contents`;
@@ -30,15 +31,20 @@ function get(url) {
30
31
  }
31
32
 
32
33
  function parseVersion(content) {
33
- const match = content.match(/^---[\s\S]*?^version:\s*(.+?)$/m);
34
- return match ? match[1].trim() : null;
34
+ // metadata.version: 1.0.0 (nested under metadata:)
35
+ const nested = content.match(/^metadata:\s*\n(?:[ \t]+\S[^\n]*\n)*?[ \t]+version:\s*(.+)$/m);
36
+ if (nested) return nested[1].trim();
37
+ // fallback: top-level version: (legacy)
38
+ const top = content.match(/^version:\s*(.+?)$/m);
39
+ return top ? top[1].trim() : null;
35
40
  }
36
41
 
42
+ // List skill folders from GitHub API (directories only, skip README)
37
43
  async function listAvailable() {
38
- const data = JSON.parse(await get(`${API_BASE}/${SKILLS_PATH}`));
39
- return data
40
- .filter(f => f.name.endsWith('.md') && f.name !== 'README.md')
41
- .map(f => f.name.replace(/\.md$/, ''));
44
+ const entries = JSON.parse(await get(`${API_BASE}/${SKILLS_PATH}`));
45
+ return entries
46
+ .filter(e => e.type === 'dir')
47
+ .map(e => e.name);
42
48
  }
43
49
 
44
50
  function listInstalled() {
@@ -48,18 +54,45 @@ function listInstalled() {
48
54
  .map(f => f.replace(/\.md$/, ''));
49
55
  }
50
56
 
57
+ // Fetch skill folder contents from GitHub API
58
+ async function getSkillContents(name) {
59
+ const entries = JSON.parse(await get(`${API_BASE}/${SKILLS_PATH}/${name}`));
60
+ return entries; // array of { name, type, download_url, ... }
61
+ }
62
+
51
63
  async function installSkill(name) {
52
- const url = `${RAW_BASE}/${SKILLS_PATH}/${name}.md`;
53
- let content;
64
+ let entries;
54
65
  try {
55
- content = await get(url);
66
+ entries = await getSkillContents(name);
56
67
  } catch {
57
- console.error(` ✗ ${name} — not found in repo`);
68
+ console.error(` ✗ ${name} — skill folder not found in repo`);
69
+ return false;
70
+ }
71
+
72
+ const skillFile = entries.find(e => e.name === 'SKILL.md');
73
+ if (!skillFile) {
74
+ console.error(` ✗ ${name} — missing SKILL.md in skill folder`);
58
75
  return false;
59
76
  }
77
+
78
+ // Install SKILL.md → ~/.claude/agents/<name>.md
79
+ const skillContent = await get(`${RAW_BASE}/${SKILLS_PATH}/${name}/SKILL.md`);
60
80
  fs.mkdirSync(INSTALL_DIR, { recursive: true });
61
- fs.writeFileSync(path.join(INSTALL_DIR, `${name}.md`), content);
62
- console.log(` ✓ ${name} → ${INSTALL_DIR}/${name}.md`);
81
+ fs.writeFileSync(path.join(INSTALL_DIR, `${name}.md`), skillContent);
82
+ console.log(` ✓ ${name}.md → ${INSTALL_DIR}`);
83
+
84
+ // Install companion scripts → ~/.claude/agents/scripts/<name>/
85
+ const scripts = entries.filter(e => e.type === 'file' && e.name !== 'SKILL.md' && e.name !== 'README.md');
86
+ if (scripts.length > 0) {
87
+ const scriptDir = path.join(SCRIPTS_DIR, name);
88
+ fs.mkdirSync(scriptDir, { recursive: true });
89
+ for (const script of scripts) {
90
+ const content = await get(`${RAW_BASE}/${SKILLS_PATH}/${name}/${script.name}`);
91
+ fs.writeFileSync(path.join(scriptDir, script.name), content);
92
+ console.log(` ✓ ${script.name} → ${scriptDir}`);
93
+ }
94
+ }
95
+
63
96
  return true;
64
97
  }
65
98
 
@@ -94,9 +127,12 @@ async function cmdList() {
94
127
  const installed = listInstalled();
95
128
  console.log('Available skills:\n');
96
129
  for (const name of available) {
97
- const content = await get(`${RAW_BASE}/${SKILLS_PATH}/${name}.md`);
98
- const version = parseVersion(content);
99
- const versionTag = version ? ` v${version}` : '';
130
+ let version = null;
131
+ try {
132
+ const content = await get(`${RAW_BASE}/${SKILLS_PATH}/${name}/SKILL.md`);
133
+ version = parseVersion(content);
134
+ } catch { /* skip if fetch fails */ }
135
+ const versionTag = version ? ` v${version}` : '';
100
136
  const installedTag = installed.includes(name) ? ' (installed)' : '';
101
137
  console.log(` ${name}${versionTag}${installedTag}`);
102
138
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@petersobhy/ai-toolkit",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "Install and manage Integrant squad AI skills for Claude Code",
5
5
  "bin": {
6
6
  "ai-toolkit": "./index.js"