antikit 1.9.1 ā 1.10.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 +21 -0
- package/package.json +1 -1
- package/src/commands/config.js +53 -0
- package/src/commands/list.js +16 -4
- package/src/index.js +16 -0
- package/src/utils/configManager.js +26 -0
- package/src/utils/github.js +144 -34
package/README.md
CHANGED
|
@@ -167,6 +167,27 @@ dependencies:
|
|
|
167
167
|
...
|
|
168
168
|
```
|
|
169
169
|
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### š Authentication (Optional)
|
|
173
|
+
|
|
174
|
+
To increase GitHub API rate limits (avoiding "API rate limit exceeded" errors), you can configure a Personal Access Token.
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# Set token
|
|
178
|
+
antikit config set-token ghp_xxxxxxxxxxxx
|
|
179
|
+
|
|
180
|
+
# Check config
|
|
181
|
+
antikit config list
|
|
182
|
+
# or
|
|
183
|
+
antikit config ls
|
|
184
|
+
|
|
185
|
+
# Remove token
|
|
186
|
+
antikit config remove-token
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
170
191
|
## Requirements
|
|
171
192
|
|
|
172
193
|
- Node.js >= 18.0.0
|
package/package.json
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import {
|
|
3
|
+
setToken,
|
|
4
|
+
getToken,
|
|
5
|
+
removeToken,
|
|
6
|
+
getConfigPath,
|
|
7
|
+
loadConfig
|
|
8
|
+
} from '../utils/configManager.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* List all configurations
|
|
12
|
+
*/
|
|
13
|
+
export function listConfig() {
|
|
14
|
+
const config = loadConfig();
|
|
15
|
+
console.log(chalk.bold('\nCurrent Configuration:'));
|
|
16
|
+
console.log(chalk.dim('ā'.repeat(40)));
|
|
17
|
+
|
|
18
|
+
if (config.githubToken) {
|
|
19
|
+
console.log(`GitHub Token: ${chalk.green('********' + config.githubToken.slice(-4))}`);
|
|
20
|
+
} else {
|
|
21
|
+
console.log(`GitHub Token: ${chalk.dim('(not set)')}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// List other configs if any...
|
|
25
|
+
|
|
26
|
+
console.log();
|
|
27
|
+
console.log(chalk.dim(`Config file: ${getConfigPath()}`));
|
|
28
|
+
console.log();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Set GitHub Token
|
|
33
|
+
*/
|
|
34
|
+
export function setGitHubToken(token) {
|
|
35
|
+
if (!token) {
|
|
36
|
+
console.error(chalk.red('Error: Token is required'));
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
setToken(token);
|
|
41
|
+
console.log(chalk.green('\nā GitHub Token saved successfully.'));
|
|
42
|
+
console.log(chalk.dim('API rate limit increased.'));
|
|
43
|
+
console.log();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Remove GitHub Token
|
|
48
|
+
*/
|
|
49
|
+
export function removeGitHubToken() {
|
|
50
|
+
removeToken();
|
|
51
|
+
console.log(chalk.green('\nā GitHub Token removed.'));
|
|
52
|
+
console.log();
|
|
53
|
+
}
|
package/src/commands/list.js
CHANGED
|
@@ -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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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;
|
package/src/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { removeSkill } from './commands/remove.js';
|
|
|
10
10
|
import { updateCli } from './commands/update.js';
|
|
11
11
|
import { upgradeSkills } from './commands/upgrade.js';
|
|
12
12
|
import { listSources, addNewSource, removeExistingSource, setDefault } from './commands/source.js';
|
|
13
|
+
import { listConfig, setGitHubToken, removeGitHubToken } from './commands/config.js';
|
|
13
14
|
import { checkForUpdates } from './utils/updateNotifier.js';
|
|
14
15
|
import { setupCompletion } from './utils/completion.js';
|
|
15
16
|
|
|
@@ -110,4 +111,19 @@ sourceCmd
|
|
|
110
111
|
|
|
111
112
|
sourceCmd.command('default <name>').description('Set default source').action(setDefault);
|
|
112
113
|
|
|
114
|
+
// Config management commands
|
|
115
|
+
const configCmd = program.command('config').description('Manage CLI configuration');
|
|
116
|
+
|
|
117
|
+
configCmd.command('list').alias('ls').description('List current configuration').action(listConfig);
|
|
118
|
+
|
|
119
|
+
configCmd
|
|
120
|
+
.command('set-token <token>')
|
|
121
|
+
.description('Set GitHub Personal Access Token')
|
|
122
|
+
.action(setGitHubToken);
|
|
123
|
+
|
|
124
|
+
configCmd
|
|
125
|
+
.command('remove-token')
|
|
126
|
+
.description('Remove GitHub Personal Access Token')
|
|
127
|
+
.action(removeGitHubToken);
|
|
128
|
+
|
|
113
129
|
program.parse();
|
|
@@ -163,6 +163,32 @@ export function setDefaultSource(name) {
|
|
|
163
163
|
return config.sources;
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Set GitHub Token
|
|
168
|
+
*/
|
|
169
|
+
export function setToken(token) {
|
|
170
|
+
const config = loadConfig();
|
|
171
|
+
config.githubToken = token;
|
|
172
|
+
saveConfig(config);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Get GitHub Token
|
|
177
|
+
*/
|
|
178
|
+
export function getToken() {
|
|
179
|
+
const config = loadConfig();
|
|
180
|
+
return config.githubToken;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Remove GitHub Token
|
|
185
|
+
*/
|
|
186
|
+
export function removeToken() {
|
|
187
|
+
const config = loadConfig();
|
|
188
|
+
delete config.githubToken;
|
|
189
|
+
saveConfig(config);
|
|
190
|
+
}
|
|
191
|
+
|
|
166
192
|
/**
|
|
167
193
|
* Get config file path (for display)
|
|
168
194
|
*/
|
package/src/utils/github.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getSources } from './configManager.js';
|
|
1
|
+
import { getSources, getToken } from './configManager.js';
|
|
2
2
|
|
|
3
3
|
const GITHUB_API = 'https://api.github.com';
|
|
4
4
|
|
|
@@ -7,21 +7,126 @@ function getHeaders() {
|
|
|
7
7
|
Accept: 'application/vnd.github.v3+json',
|
|
8
8
|
'User-Agent': 'antikit-cli'
|
|
9
9
|
};
|
|
10
|
-
const token = process.env.ANTIKIT_GITHUB_TOKEN || process.env.GITHUB_TOKEN;
|
|
10
|
+
const token = getToken() || process.env.ANTIKIT_GITHUB_TOKEN || process.env.GITHUB_TOKEN;
|
|
11
11
|
if (token) {
|
|
12
12
|
headers.Authorization = `token ${token}`;
|
|
13
13
|
}
|
|
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,36 +166,19 @@ 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
|
|
|
67
173
|
return skills;
|
|
68
174
|
}
|
|
69
175
|
|
|
70
|
-
|
|
71
|
-
* Fetch list of skills from all configured sources
|
|
72
|
-
*/
|
|
73
|
-
export async function fetchRemoteSkills(sourceName = null) {
|
|
74
|
-
const sources = getSources();
|
|
75
|
-
|
|
76
|
-
// Filter by source name if provided
|
|
77
|
-
const targetSources = sourceName ? sources.filter(s => s.name === sourceName) : sources;
|
|
78
|
-
|
|
79
|
-
if (targetSources.length === 0) {
|
|
80
|
-
throw new Error(`Source "${sourceName}" not found.`);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Fetch from all sources in parallel
|
|
84
|
-
const results = await Promise.all(targetSources.map(source => fetchSkillsFromSource(source)));
|
|
85
|
-
|
|
86
|
-
// Flatten and return all skills
|
|
87
|
-
return results.flat();
|
|
88
|
-
}
|
|
176
|
+
// ... (fetchRemoteSkills remains same)
|
|
89
177
|
|
|
90
178
|
/**
|
|
91
179
|
* Fetch SKILL.md content for a specific skill
|
|
92
180
|
*/
|
|
93
|
-
export async function fetchSkillInfo(skillName, owner, repo, path = null) {
|
|
181
|
+
export async function fetchSkillInfo(skillName, owner, repo, path = null, branch = null) {
|
|
94
182
|
// If owner/repo not provided, search in all sources
|
|
95
183
|
if (!owner || !repo) {
|
|
96
184
|
const skills = await fetchRemoteSkills();
|
|
@@ -99,24 +187,46 @@ export async function fetchSkillInfo(skillName, owner, repo, path = null) {
|
|
|
99
187
|
owner = skill.owner;
|
|
100
188
|
repo = skill.repo;
|
|
101
189
|
path = skill.basePath;
|
|
190
|
+
branch = skill.branch;
|
|
102
191
|
}
|
|
103
192
|
|
|
104
|
-
let
|
|
105
|
-
|
|
106
|
-
|
|
193
|
+
let content = null;
|
|
194
|
+
|
|
195
|
+
// Optimized: Use Raw URL if branch is known (avoids API rate limit)
|
|
196
|
+
if (branch) {
|
|
197
|
+
let rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
|
|
198
|
+
if (path) rawUrl += `/${path}`;
|
|
199
|
+
rawUrl += `/${skillName}/SKILL.md`;
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
const res = await fetch(rawUrl);
|
|
203
|
+
if (res.ok) {
|
|
204
|
+
content = await res.text();
|
|
205
|
+
}
|
|
206
|
+
} catch (e) {
|
|
207
|
+
// Ignore fetch error, fallback to API
|
|
208
|
+
}
|
|
107
209
|
}
|
|
108
|
-
url += `/${skillName}/SKILL.md`;
|
|
109
210
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
211
|
+
// Fallback: Use API (Counts against rate limit, but works if branch is wrong/private repo needs Auth)
|
|
212
|
+
if (!content) {
|
|
213
|
+
let url = `${GITHUB_API}/repos/${owner}/${repo}/contents`;
|
|
214
|
+
if (path) {
|
|
215
|
+
url += `/${path}`;
|
|
216
|
+
}
|
|
217
|
+
url += `/${skillName}/SKILL.md`;
|
|
113
218
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
219
|
+
const response = await fetch(url, {
|
|
220
|
+
headers: getHeaders()
|
|
221
|
+
});
|
|
117
222
|
|
|
118
|
-
|
|
119
|
-
|
|
223
|
+
if (!response.ok) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const data = await response.json();
|
|
228
|
+
content = Buffer.from(data.content, 'base64').toString('utf-8');
|
|
229
|
+
}
|
|
120
230
|
|
|
121
231
|
// Extract info from YAML frontmatter
|
|
122
232
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|