@aiagenta2z/agtm 1.0.4 → 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 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 --upload` , `agtm --search` helps users to register and publish their AI Agents meta api information from agent.json/agent.yaml file
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
- #### Install and Use `agtm` CLI
15
- Python
16
- ``` Python
17
- pip install ai-agent-marketplace
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
- Node
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
- #### Tutorial
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
- **1. Setup Access Key**
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 (https://deepnlp.org/workspace/keys)
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
- **2. Registry AI Agent from your GitHub**
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
- **3. Registry AI Agent from agent.json or agent.yaml file**
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
- **4. Use your customized endpoint and schema definition**
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
- ## default schema.json definition
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
- **5. Search AI Agent Marketplace**
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
@@ -13,5 +13,4 @@
13
13
  "price_per_call_credit": 0.0,
14
14
  "price_fixed_credit" : 0.0,
15
15
  "price_subscription": "Basic: your basic plan introduction, Advanced: Your Advanced Plan introduction, etc.",
16
- "new_key_1": "new_value_1"
17
16
  }
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",
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",
@@ -33,5 +33,6 @@
33
33
  "repository": {
34
34
  "type": "git",
35
35
  "url": "https://github.com/aiagenta2z/agtm"
36
- }
36
+ },
37
+ "homepage": "https://www.deepnlp.org/store/ai-agent"
37
38
  }