@aiagenta2z/agtm 1.0.5 → 1.0.7
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 +89 -19
- package/agent.json +0 -1
- package/dist/agtm-cli.js +328 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,44 +1,84 @@
|
|
|
1
1
|
|
|
2
2
|
### agtm: Command Line CLI Tool for AI Agent Registry in the Open Source AI Agent Marketplace
|
|
3
3
|
|
|
4
|
-
[GitHub](https://github.com/aiagenta2z/agtm)|[AI Agent Marketplace CLI Doc](https://www.deepnlp.org/doc/ai_agent_marketplace)|[DeepNLP AI Agent Marketplace](https://www.deepnlp.org/store/ai-agent) | [OneKey Agent Router](https://www.deepnlp.org/agent/onekey-mcp-router) | [Agent MCP OneKey Router Ranking](https://www.deepnlp.org/agent/rankings)
|
|
4
|
+
[GitHub](https://github.com/aiagenta2z/agtm)|[AI Agent Marketplace CLI Doc](https://www.deepnlp.org/doc/ai_agent_marketplace)|[DeepNLP AI Agent Marketplace](https://www.deepnlp.org/store/ai-agent) | [OneKey Agent Router](https://www.deepnlp.org/agent/onekey-mcp-router) | [Agent MCP OneKey Router Ranking](https://www.deepnlp.org/agent/rankings) | [NodeJS agtm](https://www.npmjs.com/package/@aiagenta2z/agtm)
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
The command line `agtm
|
|
7
|
+
The command line `agtm upload` , `agtm search`, `agtm add` helps users to register and publish their AI Agents meta api information from agent.json/agent.yaml file
|
|
8
8
|
or a github repo URL in a few seconds.
|
|
9
9
|
|
|
10
|
-
'agtm' means 'ai agent marketplace' or 'ai agent manager'
|
|
10
|
+
'agtm' means 'ai agent marketplace' or 'ai agent manager', you can use this command line to manage agent meta, skills add,installation, etc.
|
|
11
11
|
To use the command line, you need to first install the package either using python or node environment
|
|
12
12
|
|
|
13
|
+
## Quickstart
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
Agtm CLI Options
|
|
16
|
+
|
|
17
|
+
### `search`
|
|
18
|
+
`agtm search` helps to search AI Agent MCP and skills marketplace by id or query keywords.
|
|
19
|
+
|
|
20
|
+
Example Usage
|
|
21
|
+
```shell
|
|
22
|
+
agtm search --q 'coding agent'
|
|
23
|
+
agtm search --id 'google-maps/google-maps'
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### `upload`
|
|
27
|
+
`agtm update` helps to update your local agent.json or agent.yaml meta information to DeepNLP AI Agent Marketplace Index.
|
|
28
|
+
Example Usage
|
|
29
|
+
```shell
|
|
30
|
+
agtm upload --github https://github.com/AI-Hub-Admin/My-First-AI-Coding-Agent
|
|
31
|
+
agtm upload --config ./agent.json
|
|
32
|
+
agtm upload --config ./agent.yaml
|
|
18
33
|
```
|
|
19
34
|
|
|
20
|
-
|
|
35
|
+
### `add`
|
|
36
|
+
`agtm add` Install Skills from Github
|
|
37
|
+
|
|
38
|
+
Example Usage
|
|
39
|
+
```shell
|
|
40
|
+
agtm add aiagenta2z/onekey-agent-router --skill google-maps ## install to your project folder
|
|
41
|
+
agtm add aiagenta2z/onekey-agent-router --skill google-maps -g ## install to global cache folder
|
|
42
|
+
agtm add https://github.com/aiagenta2z/onekey-agent-router --skill google-maps ## install one skill
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Expected Results install google-maps
|
|
46
|
+
```shell
|
|
47
|
+
Installed skill 'google-maps' to antigravity at /path/to/project/.agent/skills/google-maps
|
|
48
|
+
Installed skill 'google-maps' to codex at /path/to/project/.agents/skills/google-maps
|
|
49
|
+
Installed skill 'google-maps' to claude-code at /path/to/project/.claude/skills/google-maps
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## Tutorial
|
|
54
|
+
|
|
55
|
+
### 1. Installation
|
|
56
|
+
|
|
57
|
+
**Node**
|
|
21
58
|
``` NodeJS
|
|
22
59
|
npm install -g @aiagenta2z/agtm
|
|
23
60
|
```
|
|
24
61
|
|
|
25
|
-
|
|
26
62
|
note: run nodejs environment, you need to add -g to install, so you can just run command line `agtm` without the `npx agtm` prefix,
|
|
27
63
|
|
|
28
|
-
|
|
29
64
|
Command line to register your AI agent from GitHub, Local config(agent.json, agent.yaml) or search the AI agent marketplace.
|
|
30
65
|
To see detailed usage, visit the GitHub of `agtm` at Documentation (https://github.com/aiagenta2z/ai-agent-marketplace)
|
|
31
66
|
|
|
67
|
+
**Python**
|
|
68
|
+
```
|
|
69
|
+
pip install ai-agent-marketplace
|
|
70
|
+
```
|
|
32
71
|
|
|
33
|
-
|
|
72
|
+
The cli bin will be copied to python path. Future version, only 'node' command line will be supported.
|
|
34
73
|
|
|
74
|
+
### Setup
|
|
35
75
|
Let's say you have create a repo of your open source AI Agent (e.g. https://github.com/AI-Hub-Admin/My-First-AI-Coding-Agent), And you want to
|
|
36
76
|
register and get a registered detailed page of AI Agent in the marketplace and monitor the traffic.
|
|
37
77
|
|
|
38
|
-
**
|
|
78
|
+
**Setup Access Key**
|
|
39
79
|
|
|
40
80
|
First you need to setup an access_key in the environment to authenticate.
|
|
41
|
-
Register your AI Agent Marketplace Access Key here
|
|
81
|
+
Register your AI Agent Marketplace [Access Key here](https://deepnlp.org/workspace/keys)
|
|
42
82
|
|
|
43
83
|
```
|
|
44
84
|
export AI_AGENT_MARKETPLACE_ACCESS_KEY="{your_access_key}"
|
|
@@ -50,7 +90,38 @@ You can also use the test key for validation, which is associated with a test-on
|
|
|
50
90
|
export AI_AGENT_MARKETPLACE_ACCESS_KEY="TEST_KEY_AI_AGENT_REGISTRY"
|
|
51
91
|
```
|
|
52
92
|
|
|
53
|
-
|
|
93
|
+
### 2. Skills
|
|
94
|
+
|
|
95
|
+
Install skills from the `aiagenta2z/onekey-agent-router` Github repo (`user_name/repo_name`). The `google-maps` skill is a good starter example.
|
|
96
|
+
|
|
97
|
+
Under the `./skills/google-maps` folder of the target github repo.
|
|
98
|
+
|
|
99
|
+
```shell
|
|
100
|
+
agtm add aiagenta2z/onekey-agent-router --skill google-maps
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Install to a specific agent (codex):
|
|
104
|
+
|
|
105
|
+
```shell
|
|
106
|
+
agtm add aiagenta2z/onekey-agent-router --skill google-maps -a codex
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Install to a specific agent (openclaw):
|
|
110
|
+
|
|
111
|
+
```shell
|
|
112
|
+
agtm add aiagenta2z/onekey-agent-router --skill google-maps -a openclaw
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Install to global paths instead of local project paths:
|
|
116
|
+
|
|
117
|
+
```shell
|
|
118
|
+
agtm add aiagenta2z/onekey-agent-router --skill google-maps -g
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
### 3. AI Agent Registry
|
|
123
|
+
|
|
124
|
+
**Registry AI Agent from your GitHub**
|
|
54
125
|
|
|
55
126
|
The default registry provider endpoint includes: [DeepNLP AI Agent Registry Endpoint](https://www.deepnlp.org/api/ai_agent_marketplace/registry) |
|
|
56
127
|
[aiagenta2z.com Registry Endpoint](https://www.aiagenta2z.com/api/ai_agent_marketplace/registry)|[aiagenta2z.org Registry Endpoint](https://www.aiagenta2z.org/api/ai_agent_marketplace/registry), etc.
|
|
@@ -63,7 +134,7 @@ agtm upload --github https://github.com/AI-Hub-Admin/My-First-AI-Coding-Agent
|
|
|
63
134
|
```
|
|
64
135
|
|
|
65
136
|
|
|
66
|
-
**
|
|
137
|
+
**Registry AI Agent from agent.json or agent.yaml file**
|
|
67
138
|
|
|
68
139
|
|
|
69
140
|
```
|
|
@@ -116,7 +187,7 @@ price_subscription: "Basic: your basic plan introduction, Advanced: Your Advance
|
|
|
116
187
|
|
|
117
188
|
```
|
|
118
189
|
|
|
119
|
-
**
|
|
190
|
+
**Use your customized endpoint and schema definition**
|
|
120
191
|
|
|
121
192
|
You have the flexibility to use the AI Agent marketplace/manager cli `agtm` to submit customized ai agent schema to your customized endpoint.
|
|
122
193
|
|
|
@@ -131,7 +202,7 @@ Note: the keys in agent.json and schema.json should match. Package will select k
|
|
|
131
202
|
|
|
132
203
|
Remember to keep the `access_key` in safe place, the post request will post the `access_key` as well as schema to the endpoint.
|
|
133
204
|
|
|
134
|
-
|
|
205
|
+
#### default schema.json definition
|
|
135
206
|
```
|
|
136
207
|
{
|
|
137
208
|
"required": [
|
|
@@ -200,7 +271,7 @@ It will return an error message showing the endpoint and data, https://www.examp
|
|
|
200
271
|
```
|
|
201
272
|
|
|
202
273
|
|
|
203
|
-
**
|
|
274
|
+
**Search AI Agent Marketplace**
|
|
204
275
|
|
|
205
276
|
```
|
|
206
277
|
|
|
@@ -224,4 +295,3 @@ Retrieving specific agent with unique ID: AI-Hub-Admin/My-First-AI-Coding-Agent
|
|
|
224
295
|
|
|
225
296
|
{'total_hits': 1, 'id': 'AI-Hub-Admin/My-First-AI-Coding-Agent', 'items': [{'content_name': 'My-First-AI-Coding-Agent', 'publisher_id': 'pub-ai-hub-admin', 'detail_url': 'https://www.deepnlp.org/store/ai-agent/ai-agent/pub-ai-hub-admin/my-first-ai-coding-agent', 'website': 'https://github.com/AI-Hub-Admin/My-First-AI-Coding-Agent', 'github': 'https://github.com/AI-Hub-Admin/My-First-AI-Coding-Agent', 'review_cnt': '0', 'rating': '0.0', 'description': '# My-First-AI-Coding-Agent\nAI Agent Registry Demo project\n\n', 'subfield': 'AI AGENT', 'field': 'AI AGENT', 'id': 'ai-hub-admin/my-first-ai-coding-agent', 'content_tag_list': 'GitHub 0', 'thumbnail_picture': 'https://avatars.githubusercontent.com/u/184629057?v=4'}]}
|
|
226
297
|
```
|
|
227
|
-
|
package/agent.json
CHANGED
package/dist/agtm-cli.js
CHANGED
|
@@ -3,6 +3,10 @@ import { Command } from 'commander';
|
|
|
3
3
|
import axios from 'axios';
|
|
4
4
|
import * as fs from 'node:fs';
|
|
5
5
|
import * as yaml from 'js-yaml';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import * as os from 'node:os';
|
|
8
|
+
import { execFileSync } from 'node:child_process';
|
|
9
|
+
import { createInterface } from 'node:readline/promises';
|
|
6
10
|
// --- Configuration ---
|
|
7
11
|
const BASE_URL = 'https://www.deepnlp.org/api/ai_agent_marketplace';
|
|
8
12
|
const REGISTRY_ENDPOINT = `${BASE_URL}/registry`;
|
|
@@ -83,6 +87,314 @@ function fillItemInfoDict(file_content, required_keys, optional_keys) {
|
|
|
83
87
|
}
|
|
84
88
|
return item_info;
|
|
85
89
|
}
|
|
90
|
+
const AGENT_SPECS = [
|
|
91
|
+
{ id: 'amp', projectPath: '.agents/skills', globalPath: '.config/agents/skills' },
|
|
92
|
+
{ id: 'kimi-cli', projectPath: '.agents/skills', globalPath: '.config/agents/skills' },
|
|
93
|
+
{ id: 'replit', projectPath: '.agents/skills', globalPath: '.config/agents/skills' },
|
|
94
|
+
{ id: 'universal', projectPath: '.agents/skills', globalPath: '.config/agents/skills' },
|
|
95
|
+
{ id: 'antigravity', projectPath: '.agent/skills', globalPath: '.gemini/antigravity/skills' },
|
|
96
|
+
{ id: 'augment', projectPath: '.augment/skills', globalPath: '.augment/skills' },
|
|
97
|
+
{ id: 'claude-code', projectPath: '.claude/skills', globalPath: '.claude/skills' },
|
|
98
|
+
{ id: 'openclaw', projectPath: 'skills', globalPath: '.openclaw/skills' },
|
|
99
|
+
{ id: 'cline', projectPath: '.agents/skills', globalPath: '.agents/skills' },
|
|
100
|
+
{ id: 'codebuddy', projectPath: '.codebuddy/skills', globalPath: '.codebuddy/skills' },
|
|
101
|
+
{ id: 'codex', projectPath: '.agents/skills', globalPath: '.codex/skills' },
|
|
102
|
+
{ id: 'command-code', projectPath: '.commandcode/skills', globalPath: '.commandcode/skills' },
|
|
103
|
+
{ id: 'continue', projectPath: '.continue/skills', globalPath: '.continue/skills' },
|
|
104
|
+
{ id: 'cortex', projectPath: '.cortex/skills', globalPath: '.snowflake/cortex/skills' },
|
|
105
|
+
{ id: 'crush', projectPath: '.crush/skills', globalPath: '.config/crush/skills' },
|
|
106
|
+
{ id: 'cursor', projectPath: '.agents/skills', globalPath: '.cursor/skills' },
|
|
107
|
+
{ id: 'droid', projectPath: '.factory/skills', globalPath: '.factory/skills' },
|
|
108
|
+
{ id: 'gemini-cli', projectPath: '.agents/skills', globalPath: '.gemini/skills' },
|
|
109
|
+
{ id: 'github-copilot', projectPath: '.agents/skills', globalPath: '.copilot/skills' },
|
|
110
|
+
{ id: 'goose', projectPath: '.goose/skills', globalPath: '.config/goose/skills' },
|
|
111
|
+
{ id: 'junie', projectPath: '.junie/skills', globalPath: '.junie/skills' },
|
|
112
|
+
{ id: 'iflow-cli', projectPath: '.iflow/skills', globalPath: '.iflow/skills' },
|
|
113
|
+
{ id: 'kilo', projectPath: '.kilocode/skills', globalPath: '.kilocode/skills' },
|
|
114
|
+
{ id: 'kiro-cli', projectPath: '.kiro/skills', globalPath: '.kiro/skills' },
|
|
115
|
+
{ id: 'kode', projectPath: '.kode/skills', globalPath: '.kode/skills' },
|
|
116
|
+
{ id: 'mcpjam', projectPath: '.mcpjam/skills', globalPath: '.mcpjam/skills' },
|
|
117
|
+
{ id: 'mistral-vibe', projectPath: '.vibe/skills', globalPath: '.vibe/skills' },
|
|
118
|
+
{ id: 'mux', projectPath: '.mux/skills', globalPath: '.mux/skills' },
|
|
119
|
+
{ id: 'opencode', projectPath: '.agents/skills', globalPath: '.config/opencode/skills' },
|
|
120
|
+
{ id: 'openhands', projectPath: '.openhands/skills', globalPath: '.openhands/skills' },
|
|
121
|
+
{ id: 'pi', projectPath: '.pi/skills', globalPath: '.pi/agent/skills' },
|
|
122
|
+
{ id: 'qoder', projectPath: '.qoder/skills', globalPath: '.qoder/skills' },
|
|
123
|
+
{ id: 'qwen-code', projectPath: '.qwen/skills', globalPath: '.qwen/skills' },
|
|
124
|
+
{ id: 'roo', projectPath: '.roo/skills', globalPath: '.roo/skills' },
|
|
125
|
+
{ id: 'trae', projectPath: '.trae/skills', globalPath: '.trae/skills' },
|
|
126
|
+
{ id: 'trae-cn', projectPath: '.trae/skills', globalPath: '.trae-cn/skills' },
|
|
127
|
+
{ id: 'windsurf', projectPath: '.windsurf/skills', globalPath: '.codeium/windsurf/skills' },
|
|
128
|
+
{ id: 'zencoder', projectPath: '.zencoder/skills', globalPath: '.zencoder/skills' },
|
|
129
|
+
{ id: 'neovate', projectPath: '.neovate/skills', globalPath: '.neovate/skills' },
|
|
130
|
+
{ id: 'pochi', projectPath: '.pochi/skills', globalPath: '.pochi/skills' },
|
|
131
|
+
{ id: 'adal', projectPath: '.adal/skills', globalPath: '.adal/skills' }
|
|
132
|
+
];
|
|
133
|
+
const DEFAULT_AGENT_IDS = ['antigravity', 'codex', 'claude-code', 'openclaw'];
|
|
134
|
+
function normalizeAgentId(value) {
|
|
135
|
+
return value.toLowerCase().replace(/[\s_]+/g, '-').replace(/-+/g, '-').trim();
|
|
136
|
+
}
|
|
137
|
+
function resolveAgents(agentArgs) {
|
|
138
|
+
if (agentArgs && agentArgs.length > 0) {
|
|
139
|
+
const normalized = agentArgs.map(normalizeAgentId);
|
|
140
|
+
if (normalized.includes('*')) {
|
|
141
|
+
return AGENT_SPECS;
|
|
142
|
+
}
|
|
143
|
+
const selected = [];
|
|
144
|
+
for (const agentId of normalized) {
|
|
145
|
+
const match = AGENT_SPECS.find((spec) => spec.id === agentId);
|
|
146
|
+
if (!match) {
|
|
147
|
+
console.error(`\n❌ Error: Unknown agent '${agentId}'.`);
|
|
148
|
+
console.error(`Supported agents: ${AGENT_SPECS.map((spec) => spec.id).join(', ')}`);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
selected.push(match);
|
|
152
|
+
}
|
|
153
|
+
return selected;
|
|
154
|
+
}
|
|
155
|
+
const detected = AGENT_SPECS.filter((spec) => fs.existsSync(path.join(process.cwd(), spec.projectPath)));
|
|
156
|
+
if (detected.length > 0) {
|
|
157
|
+
return detected;
|
|
158
|
+
}
|
|
159
|
+
const fallback = AGENT_SPECS.find((spec) => spec.id === 'codex');
|
|
160
|
+
return fallback ? [fallback] : [];
|
|
161
|
+
}
|
|
162
|
+
function printAvailableAgents() {
|
|
163
|
+
console.log('\nAvailable agents:');
|
|
164
|
+
AGENT_SPECS.forEach((spec, index) => {
|
|
165
|
+
const marker = DEFAULT_AGENT_IDS.includes(spec.id) ? ' (default)' : '';
|
|
166
|
+
console.log(` ${index + 1}. ${spec.id}${marker}`);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
async function promptAgentSelection() {
|
|
170
|
+
if (!process.stdin.isTTY) {
|
|
171
|
+
return resolveAgents(DEFAULT_AGENT_IDS);
|
|
172
|
+
}
|
|
173
|
+
printAvailableAgents();
|
|
174
|
+
console.log('\nSelect agents by number or id (comma-separated).');
|
|
175
|
+
console.log(`Press Enter to use defaults: ${DEFAULT_AGENT_IDS.join(', ')}`);
|
|
176
|
+
console.log('Type "*" to select all agents.');
|
|
177
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
178
|
+
try {
|
|
179
|
+
const answerRaw = await rl.question('\nAgents: ');
|
|
180
|
+
const answer = answerRaw.trim();
|
|
181
|
+
if (answer === '') {
|
|
182
|
+
return resolveAgents(DEFAULT_AGENT_IDS);
|
|
183
|
+
}
|
|
184
|
+
if (answer === '*') {
|
|
185
|
+
return AGENT_SPECS;
|
|
186
|
+
}
|
|
187
|
+
const tokens = answer
|
|
188
|
+
.split(/[,\s]+/)
|
|
189
|
+
.map((token) => token.trim())
|
|
190
|
+
.filter(Boolean);
|
|
191
|
+
const ids = tokens.map((token) => {
|
|
192
|
+
if (/^\d+$/.test(token)) {
|
|
193
|
+
const index = Number(token) - 1;
|
|
194
|
+
if (index < 0 || index >= AGENT_SPECS.length) {
|
|
195
|
+
console.error(`\n❌ Error: Invalid agent number '${token}'.`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
return AGENT_SPECS[index].id;
|
|
199
|
+
}
|
|
200
|
+
return token;
|
|
201
|
+
});
|
|
202
|
+
return resolveAgents(ids);
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
rl.close();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function getAgentInstallPath(agent, useGlobal) {
|
|
209
|
+
if (useGlobal) {
|
|
210
|
+
return path.join(os.homedir(), agent.globalPath);
|
|
211
|
+
}
|
|
212
|
+
return path.join(process.cwd(), agent.projectPath);
|
|
213
|
+
}
|
|
214
|
+
function parseSkillFrontmatter(content) {
|
|
215
|
+
if (!content.startsWith('---')) {
|
|
216
|
+
return {};
|
|
217
|
+
}
|
|
218
|
+
const endIndex = content.indexOf('\n---', 3);
|
|
219
|
+
if (endIndex === -1) {
|
|
220
|
+
return {};
|
|
221
|
+
}
|
|
222
|
+
const frontmatter = content.slice(3, endIndex);
|
|
223
|
+
try {
|
|
224
|
+
const parsed = yaml.load(frontmatter);
|
|
225
|
+
if (parsed && typeof parsed === 'object' && 'name' in parsed) {
|
|
226
|
+
return { name: String(parsed.name || '') };
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
return {};
|
|
231
|
+
}
|
|
232
|
+
return {};
|
|
233
|
+
}
|
|
234
|
+
function findSkillFiles(startDir) {
|
|
235
|
+
const results = [];
|
|
236
|
+
const stack = [startDir];
|
|
237
|
+
while (stack.length > 0) {
|
|
238
|
+
const current = stack.pop();
|
|
239
|
+
if (!current) {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
let entries;
|
|
243
|
+
try {
|
|
244
|
+
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
for (const entry of entries) {
|
|
250
|
+
if (entry.name === 'node_modules' || entry.name === '.git') {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
const fullPath = path.join(current, entry.name);
|
|
254
|
+
if (entry.isDirectory()) {
|
|
255
|
+
stack.push(fullPath);
|
|
256
|
+
}
|
|
257
|
+
else if (entry.isFile() && entry.name === 'SKILL.md') {
|
|
258
|
+
results.push(fullPath);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return results;
|
|
263
|
+
}
|
|
264
|
+
function discoverSkills(basePath) {
|
|
265
|
+
const skills = [];
|
|
266
|
+
const directSkill = path.join(basePath, 'SKILL.md');
|
|
267
|
+
if (fs.existsSync(directSkill)) {
|
|
268
|
+
const content = fs.readFileSync(directSkill, 'utf8');
|
|
269
|
+
const meta = parseSkillFrontmatter(content);
|
|
270
|
+
const name = meta.name && meta.name.trim() ? meta.name.trim() : path.basename(basePath);
|
|
271
|
+
skills.push({ name, dir: basePath, source: directSkill });
|
|
272
|
+
return skills;
|
|
273
|
+
}
|
|
274
|
+
const skillsRoot = path.join(basePath, 'skills');
|
|
275
|
+
const searchRoot = fs.existsSync(skillsRoot) ? skillsRoot : basePath;
|
|
276
|
+
const skillFiles = findSkillFiles(searchRoot);
|
|
277
|
+
for (const skillFile of skillFiles) {
|
|
278
|
+
const skillDir = path.dirname(skillFile);
|
|
279
|
+
const content = fs.readFileSync(skillFile, 'utf8');
|
|
280
|
+
const meta = parseSkillFrontmatter(content);
|
|
281
|
+
const name = meta.name && meta.name.trim() ? meta.name.trim() : path.basename(skillDir);
|
|
282
|
+
skills.push({ name, dir: skillDir, source: skillFile });
|
|
283
|
+
}
|
|
284
|
+
return skills;
|
|
285
|
+
}
|
|
286
|
+
function parseGitHubSource(input) {
|
|
287
|
+
if (input.startsWith('https://github.com/')) {
|
|
288
|
+
const match = input.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/tree\/([^/]+)\/?(.*))?$/);
|
|
289
|
+
if (!match) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
const [, owner, repo, branch, subPath] = match;
|
|
293
|
+
return {
|
|
294
|
+
cloneUrl: `https://github.com/${owner}/${repo}.git`,
|
|
295
|
+
branch,
|
|
296
|
+
subPath: subPath ? subPath.replace(/^\/+/, '') : undefined
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
if (/^[^/]+\/[^/]+$/.test(input)) {
|
|
300
|
+
return {
|
|
301
|
+
cloneUrl: `https://github.com/${input}.git`
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
function resolveSkillSource(source) {
|
|
307
|
+
if (fs.existsSync(source)) {
|
|
308
|
+
return { root: path.resolve(source) };
|
|
309
|
+
}
|
|
310
|
+
const parsed = parseGitHubSource(source);
|
|
311
|
+
if (!parsed) {
|
|
312
|
+
console.error(`\n❌ Error: Unsupported source '${source}'. Provide a local path or GitHub URL.`);
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}
|
|
315
|
+
const parsedValue = parsed;
|
|
316
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agtm-skills-'));
|
|
317
|
+
const gitArgs = ['clone', '--depth', '1'];
|
|
318
|
+
if (parsedValue.branch) {
|
|
319
|
+
gitArgs.push('--branch', parsedValue.branch);
|
|
320
|
+
}
|
|
321
|
+
gitArgs.push(parsedValue.cloneUrl, tmpDir);
|
|
322
|
+
try {
|
|
323
|
+
execFileSync('git', gitArgs, { stdio: 'ignore' });
|
|
324
|
+
}
|
|
325
|
+
catch (e) {
|
|
326
|
+
console.error(`\n❌ Error: Failed to clone repository. Ensure 'git' is installed and the URL is correct.`);
|
|
327
|
+
process.exit(1);
|
|
328
|
+
}
|
|
329
|
+
const root = parsedValue.subPath ? path.join(tmpDir, parsedValue.subPath) : tmpDir;
|
|
330
|
+
if (!fs.existsSync(root)) {
|
|
331
|
+
console.error(`\n❌ Error: Path '${parsedValue.subPath}' does not exist in the repository.`);
|
|
332
|
+
process.exit(1);
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
root,
|
|
336
|
+
cleanup: () => {
|
|
337
|
+
try {
|
|
338
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
// ignore cleanup errors
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
function ensureDir(dirPath) {
|
|
347
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
348
|
+
}
|
|
349
|
+
function copySkillDir(sourceDir, targetDir) {
|
|
350
|
+
if (fs.existsSync(targetDir)) {
|
|
351
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
352
|
+
}
|
|
353
|
+
fs.cpSync(sourceDir, targetDir, { recursive: true });
|
|
354
|
+
}
|
|
355
|
+
async function handleAdd(options) {
|
|
356
|
+
const source = options.source;
|
|
357
|
+
if (!source) {
|
|
358
|
+
console.error("\n❌ Error: 'add' command requires a source path or GitHub URL.");
|
|
359
|
+
process.exit(1);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const resolved = resolveSkillSource(source);
|
|
363
|
+
const skills = discoverSkills(resolved.root);
|
|
364
|
+
if (skills.length === 0) {
|
|
365
|
+
console.error(`\n❌ Error: No skills found in source '${source}'.`);
|
|
366
|
+
resolved.cleanup?.();
|
|
367
|
+
process.exit(1);
|
|
368
|
+
}
|
|
369
|
+
const requestedSkills = (options.skill && options.skill.length > 0) ? options.skill : ['*'];
|
|
370
|
+
const normalizedRequested = requestedSkills.map((name) => name.toLowerCase());
|
|
371
|
+
const filteredSkills = normalizedRequested.includes('*')
|
|
372
|
+
? skills
|
|
373
|
+
: skills.filter((skill) => normalizedRequested.includes(skill.name.toLowerCase()));
|
|
374
|
+
if (filteredSkills.length === 0) {
|
|
375
|
+
console.error(`\n❌ Error: Requested skills not found. Available skills: ${skills.map((s) => s.name).join(', ')}`);
|
|
376
|
+
resolved.cleanup?.();
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
const agents = (options.agent && options.agent.length > 0)
|
|
380
|
+
? resolveAgents(options.agent)
|
|
381
|
+
: resolveAgents(DEFAULT_AGENT_IDS);
|
|
382
|
+
if (agents.length === 0) {
|
|
383
|
+
console.error('\n❌ Error: No target agents resolved.');
|
|
384
|
+
resolved.cleanup?.();
|
|
385
|
+
process.exit(1);
|
|
386
|
+
}
|
|
387
|
+
for (const agent of agents) {
|
|
388
|
+
const baseDir = getAgentInstallPath(agent, Boolean(options.global));
|
|
389
|
+
ensureDir(baseDir);
|
|
390
|
+
for (const skill of filteredSkills) {
|
|
391
|
+
const targetDir = path.join(baseDir, skill.name);
|
|
392
|
+
copySkillDir(skill.dir, targetDir);
|
|
393
|
+
console.log(`Installed skill '${skill.name}' to ${agent.id} at ${targetDir}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
resolved.cleanup?.();
|
|
397
|
+
}
|
|
86
398
|
// --- Command Handlers ---
|
|
87
399
|
const default_required_keys = ["name", "content"];
|
|
88
400
|
const default_optional_keys = [
|
|
@@ -235,4 +547,20 @@ program.command('search')
|
|
|
235
547
|
.option('--id <id>', 'The specific unique ID of the AI Agent to retrieve, e.g. "AI-Hub-Admin/my-first-ai-coding-agent" ')
|
|
236
548
|
.option('--count-per-page <count>', 'Count per page of search results returned.', (value) => parseInt(value, 10), 10) // default=10
|
|
237
549
|
.action(handleSearch);
|
|
550
|
+
// 3. ADD Command (Skills)
|
|
551
|
+
program.command('add')
|
|
552
|
+
.description('Download and install skills from a GitHub repo or local path.')
|
|
553
|
+
.argument('<source>', 'GitHub repo URL, owner/repo, or local path')
|
|
554
|
+
.option('-s, --skill <skill>', 'Install a specific skill (repeatable). Use "*" for all skills.', (value, prev) => {
|
|
555
|
+
const list = prev || [];
|
|
556
|
+
list.push(value);
|
|
557
|
+
return list;
|
|
558
|
+
}, [])
|
|
559
|
+
.option('-a, --agent <agent>', 'Target specific agents (repeatable). Use "*" for all agents.', (value, prev) => {
|
|
560
|
+
const list = prev || [];
|
|
561
|
+
list.push(value);
|
|
562
|
+
return list;
|
|
563
|
+
}, [])
|
|
564
|
+
.option('-g, --global', 'Install to global agent directories instead of project paths.')
|
|
565
|
+
.action((source, options) => handleAdd({ ...options, source }));
|
|
238
566
|
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiagenta2z/agtm",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "agtm: Open Source CLI for AI Agent Marketplace Registration, AI Agents Management, AI Agents Index and Search",
|
|
3
|
+
"version": "1.0.7",
|
|
4
|
+
"description": "agtm: Open Source CLI for AI Agent | Skills | MCP Marketplace Registration, AI Agents Management, AI Agents Index and Search",
|
|
5
5
|
"main": "dist/agtm-cli.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"agtm": "dist/agtm-cli.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "tsc"
|
|
11
|
+
"build": "tsc && node -e \"require('fs').chmodSync('dist/agtm-cli.js', 0o755)\""
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"dist",
|