antikit 1.10.0 → 1.10.2

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.10.0",
3
+ "version": "1.10.2",
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",
@@ -47,10 +47,22 @@ export async function listRemoteSkills(options) {
47
47
  const infoSpinner = ora('Fetching skill info...').start();
48
48
  const skillsWithInfo = await Promise.all(
49
49
  skills.map(async skill => {
50
- // Pass basePath to fetch correct SKILL.md location
51
- const info = await fetchSkillInfo(skill.name, skill.owner, skill.repo, skill.basePath);
52
- const description = info ? info.description : null;
53
- const remoteVersion = info ? info.version : '0.0.0';
50
+ let description = skill.description;
51
+ let remoteVersion = skill.version || '0.0.0';
52
+
53
+ // Only fetch info if not already fetched (REST fallback)
54
+ if (description === undefined || description === null) {
55
+ // Pass basePath and branch to fetch correct SKILL.md location optimized
56
+ const info = await fetchSkillInfo(
57
+ skill.name,
58
+ skill.owner,
59
+ skill.repo,
60
+ skill.basePath,
61
+ skill.branch
62
+ );
63
+ description = info ? info.description : null;
64
+ remoteVersion = info ? info.version : '0.0.0';
65
+ }
54
66
 
55
67
  const installed = skillExists(skill.name);
56
68
  let updateAvailable = false;
@@ -14,14 +14,119 @@ function getHeaders() {
14
14
  return headers;
15
15
  }
16
16
 
17
+ /**
18
+ * Fetch skills using GraphQL (Optimized: 1 request per source)
19
+ */
20
+ async function fetchSkillsViaGraphQL(source, token) {
21
+ const query = `
22
+ query ($owner: String!, $repo: String!, $expression: String!) {
23
+ repository(owner: $owner, name: $repo) {
24
+ object(expression: $expression) {
25
+ ... on Tree {
26
+ entries {
27
+ name
28
+ type
29
+ object {
30
+ ... on Tree {
31
+ file: entries(name: "SKILL.md") {
32
+ object {
33
+ ... on Blob {
34
+ text
35
+ }
36
+ }
37
+ }
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ `;
46
+
47
+ const branch = source.branch || 'main'; // This logic might need verifying branch exists, but usually main/master
48
+ // Correct expression for path. If path is provided, it's "branch:path", else just "branch:"
49
+ const expression = source.path ? `${branch}:${source.path}` : `${branch}:`;
50
+
51
+ try {
52
+ const response = await fetch('https://api.github.com/graphql', {
53
+ method: 'POST',
54
+ headers: {
55
+ Authorization: `token ${token}`,
56
+ 'User-Agent': 'antikit-cli'
57
+ },
58
+ body: JSON.stringify({
59
+ query,
60
+ variables: {
61
+ owner: source.owner,
62
+ repo: source.repo,
63
+ expression
64
+ }
65
+ })
66
+ });
67
+
68
+ const { data, errors } = await response.json();
69
+
70
+ if (errors || !data || !data.repository || !data.repository.object) {
71
+ return null; // Fallback to REST
72
+ }
73
+
74
+ const entries = data.repository.object.entries || [];
75
+
76
+ return entries
77
+ .filter(item => item.type === 'tree' && !item.name.startsWith('.'))
78
+ .map(item => {
79
+ let description = null;
80
+ let version = '0.0.0';
81
+
82
+ // Attempt to parse SKILL.md content if it exists
83
+ const skillFile = item.object.file && item.object.file[0];
84
+ if (skillFile && skillFile.object && skillFile.object.text) {
85
+ const content = skillFile.object.text;
86
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
87
+ if (match) {
88
+ const frontmatter = match[1];
89
+ const descMatch = frontmatter.match(/description:\s*(.+)/);
90
+ const verMatch = frontmatter.match(/version:\s*(.+)/);
91
+ if (descMatch) description = descMatch[1].trim();
92
+ if (verMatch) version = verMatch[1].trim();
93
+ }
94
+ }
95
+
96
+ return {
97
+ name: item.name,
98
+ url: `https://github.com/${source.owner}/${source.repo}/tree/${branch}/${source.path ? source.path + '/' : ''}${item.name}`,
99
+ path: source.path ? `${source.path}/${item.name}` : item.name,
100
+ source: source.name,
101
+ owner: source.owner,
102
+ repo: source.repo,
103
+ basePath: source.path,
104
+ description, // Pre-fetched!
105
+ version // Pre-fetched!
106
+ };
107
+ });
108
+ } catch (e) {
109
+ return null; // Fallback
110
+ }
111
+ }
112
+
17
113
  /**
18
114
  * Fetch list of skills from a specific source
19
115
  */
20
116
  async function fetchSkillsFromSource(source) {
117
+ // Try GraphQL first if token exists (Much faster)
118
+ const token = getToken() || process.env.ANTIKIT_GITHUB_TOKEN || process.env.GITHUB_TOKEN;
119
+ if (token) {
120
+ const gqlResult = await fetchSkillsViaGraphQL(source, token);
121
+ if (gqlResult) return gqlResult;
122
+ }
123
+
124
+ // Fallback to REST API
21
125
  let url = `${GITHUB_API}/repos/${source.owner}/${source.repo}/contents`;
22
126
  if (source.path) {
23
127
  url += `/${source.path}`;
24
128
  }
129
+ // ... rest of function
25
130
 
26
131
  const response = await fetch(url, {
27
132
  headers: getHeaders()
@@ -61,6 +166,7 @@ async function fetchSkillsFromSource(source) {
61
166
  source: source.name,
62
167
  owner: source.owner,
63
168
  repo: source.repo,
169
+ branch: source.branch || 'main',
64
170
  basePath: source.path // Keep track of base path
65
171
  }));
66
172
 
@@ -90,7 +196,7 @@ export async function fetchRemoteSkills(sourceName = null) {
90
196
  /**
91
197
  * Fetch SKILL.md content for a specific skill
92
198
  */
93
- export async function fetchSkillInfo(skillName, owner, repo, path = null) {
199
+ export async function fetchSkillInfo(skillName, owner, repo, path = null, branch = null) {
94
200
  // If owner/repo not provided, search in all sources
95
201
  if (!owner || !repo) {
96
202
  const skills = await fetchRemoteSkills();
@@ -99,24 +205,46 @@ export async function fetchSkillInfo(skillName, owner, repo, path = null) {
99
205
  owner = skill.owner;
100
206
  repo = skill.repo;
101
207
  path = skill.basePath;
208
+ branch = skill.branch;
102
209
  }
103
210
 
104
- let url = `${GITHUB_API}/repos/${owner}/${repo}/contents`;
105
- if (path) {
106
- url += `/${path}`;
107
- }
108
- url += `/${skillName}/SKILL.md`;
211
+ let content = null;
109
212
 
110
- const response = await fetch(url, {
111
- headers: getHeaders()
112
- });
213
+ // Optimized: Use Raw URL if branch is known (avoids API rate limit)
214
+ if (branch) {
215
+ let rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
216
+ if (path) rawUrl += `/${path}`;
217
+ rawUrl += `/${skillName}/SKILL.md`;
113
218
 
114
- if (!response.ok) {
115
- return null;
219
+ try {
220
+ const res = await fetch(rawUrl);
221
+ if (res.ok) {
222
+ content = await res.text();
223
+ }
224
+ } catch (e) {
225
+ // Ignore fetch error, fallback to API
226
+ }
116
227
  }
117
228
 
118
- const data = await response.json();
119
- const content = Buffer.from(data.content, 'base64').toString('utf-8');
229
+ // Fallback: Use API (Counts against rate limit, but works if branch is wrong/private repo needs Auth)
230
+ if (!content) {
231
+ let url = `${GITHUB_API}/repos/${owner}/${repo}/contents`;
232
+ if (path) {
233
+ url += `/${path}`;
234
+ }
235
+ url += `/${skillName}/SKILL.md`;
236
+
237
+ const response = await fetch(url, {
238
+ headers: getHeaders()
239
+ });
240
+
241
+ if (!response.ok) {
242
+ return null;
243
+ }
244
+
245
+ const data = await response.json();
246
+ content = Buffer.from(data.content, 'base64').toString('utf-8');
247
+ }
120
248
 
121
249
  // Extract info from YAML frontmatter
122
250
  const match = content.match(/^---\n([\s\S]*?)\n---/);