@aisa-one/cli 0.1.6 → 0.1.8

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
@@ -54,7 +54,7 @@ echo "summarize this" | aisa chat # pipe support
54
54
 
55
55
  aisa models # list all models
56
56
  aisa models --provider anthropic # filter by provider
57
- aisa models show gpt-4.1 # model details + pricing
57
+ aisa models show gpt-4.1 # model details
58
58
  ```
59
59
 
60
60
  ### API Discovery & Execution
@@ -121,16 +121,16 @@ aisa usage --limit 20 # 🚧 WIP
121
121
 
122
122
  ## Skills
123
123
 
124
- Skills are markdown files that teach AI coding agents (Claude Code, Cursor, Copilot, etc.) how to use AISA APIs.
124
+ Skills are markdown files that teach AI coding agents (Claude Code, Cursor, Copilot, etc.) how to use AISA APIs. Skills are sourced from the [OpenClaw-Skills](https://github.com/AIsa-team/OpenClaw-Skills) repository.
125
125
 
126
126
  ### Browse & Install
127
127
 
128
128
  ```bash
129
- aisa skills list # 🚧 WIP — requires backend
130
- aisa skills search "financial analysis" # 🚧 WIP
131
- aisa skills show aisa-team/finance-analyst # 🚧 WIP
132
- aisa skills add aisa-team/finance-analyst # 🚧 WIP
133
- aisa skills remove aisa-team/finance-analyst # 🚧 WIP
129
+ aisa skills list # list all available skills
130
+ aisa skills search "financial analysis" # search by keyword
131
+ aisa skills show market # show skill details
132
+ aisa skills install market # install to agent directories
133
+ aisa skills remove market # uninstall
134
134
  ```
135
135
 
136
136
  Skills install to agent directories automatically:
@@ -145,19 +145,18 @@ Skills install to agent directories automatically:
145
145
  | Gemini | `~/.gemini/skills/` |
146
146
  | OpenClaw | `~/.openclaw/skills/` |
147
147
 
148
- ### Create & Publish
148
+ ### Create Skills
149
149
 
150
150
  ```bash
151
- aisa skills init my-skill # create from default template (works locally)
151
+ aisa skills init my-skill # create from default template
152
152
  aisa skills init my-skill --template finance # finance template
153
153
  aisa skills init my-skill --template llm # LLM template
154
- aisa skills submit ./my-skill # 🚧 WIP — requires backend
155
- aisa skills push owner/my-skill # 🚧 WIP
156
- aisa skills request-verification owner/my-skill # 🚧 WIP
157
154
  ```
158
155
 
159
156
  Available templates: `default`, `llm`, `search`, `finance`, `twitter`, `video`
160
157
 
158
+ To publish a skill, submit a pull request to [AIsa-team/OpenClaw-Skills](https://github.com/AIsa-team/OpenClaw-Skills).
159
+
161
160
  ## MCP Server
162
161
 
163
162
  Auto-configure AISA's MCP server for your AI agents:
@@ -1,22 +1,18 @@
1
- export declare function skillsListAction(options: {
1
+ export declare function skillsListAction(_options: {
2
2
  category?: string;
3
3
  limit?: string;
4
4
  }): Promise<void>;
5
- export declare function skillsSearchAction(query: string, options: {
5
+ export declare function skillsSearchAction(query: string, _options: {
6
6
  limit?: string;
7
7
  }): Promise<void>;
8
8
  export declare function skillsShowAction(slug: string): Promise<void>;
9
- export declare function skillsAddAction(slug: string, options: {
9
+ export declare function skillsInstallAction(slug: string, options: {
10
10
  agent?: string;
11
11
  }): Promise<void>;
12
12
  export declare function skillsRemoveAction(slug: string, options: {
13
13
  agent?: string;
14
- }): Promise<void>;
14
+ }): void;
15
15
  export declare function skillsInitAction(name: string, options: {
16
16
  template?: string;
17
17
  bare?: boolean;
18
18
  }): void;
19
- export declare function skillsSubmitAction(path: string): Promise<void>;
20
- export declare function skillsPushAction(slug: string): Promise<void>;
21
- export declare function skillsVerifyAction(slug: string): Promise<void>;
22
- export declare function skillsUpdateAction(slug?: string): Promise<void>;
@@ -2,24 +2,86 @@ import ora from "ora";
2
2
  import chalk from "chalk";
3
3
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
4
4
  import { join, resolve } from "node:path";
5
- import { requireApiKey } from "../config.js";
6
- import { apiRequest } from "../api.js";
7
- import { error, success, badge, hint, truncate } from "../utils/display.js";
8
- import { expandHome, ensureDir, writeSkillFiles, readSkillDir, removeDir, detectAgents } from "../utils/file.js";
5
+ import fetch from "node-fetch";
6
+ import { error, success, hint, truncate } from "../utils/display.js";
7
+ import { expandHome, ensureDir, writeSkillFiles, removeDir, detectAgents } from "../utils/file.js";
9
8
  import { AGENT_DIRS } from "../constants.js";
10
- // --- Skill Templates ---
9
+ // --- GitHub-backed skill registry ---
10
+ const SKILLS_REPO = "AIsa-team/OpenClaw-Skills";
11
+ const GH_API = `https://api.github.com/repos/${SKILLS_REPO}`;
12
+ const GH_RAW = `https://raw.githubusercontent.com/${SKILLS_REPO}/main`;
13
+ /** Fetch the repo tree and extract top-level skill folder names. */
14
+ async function fetchSkillSlugs() {
15
+ const res = await fetch(`${GH_API}/git/trees/main`);
16
+ if (!res.ok)
17
+ throw new Error(`GitHub API error: ${res.status}`);
18
+ const data = (await res.json());
19
+ // Top-level directories (excluding dotfiles, LICENSE, README) are skills
20
+ return data.tree
21
+ .filter((e) => e.type === "tree" &&
22
+ !e.path.startsWith(".") &&
23
+ e.path !== "node_modules")
24
+ .map((e) => e.path);
25
+ }
26
+ /** Fetch and parse SKILL.md frontmatter for a single skill. */
27
+ async function fetchSkillMeta(slug) {
28
+ const res = await fetch(`${GH_RAW}/${slug}/SKILL.md`);
29
+ if (!res.ok)
30
+ return null;
31
+ const text = await res.text();
32
+ return parseSkillFrontmatter(slug, text);
33
+ }
34
+ /** Parse YAML frontmatter from SKILL.md content. */
35
+ function parseSkillFrontmatter(slug, content) {
36
+ const info = { slug, name: slug, description: "", emoji: "" };
37
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
38
+ if (!match)
39
+ return info;
40
+ const frontmatter = match[1];
41
+ const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
42
+ if (nameMatch)
43
+ info.name = nameMatch[1].trim().replace(/^["']|["']$/g, "");
44
+ const descMatch = frontmatter.match(/^description:\s*(.+)$/m);
45
+ if (descMatch)
46
+ info.description = descMatch[1].trim().replace(/^["']|["']$/g, "");
47
+ const metaMatch = frontmatter.match(/^metadata:\s*(.+)$/m);
48
+ if (metaMatch) {
49
+ try {
50
+ const meta = JSON.parse(metaMatch[1]);
51
+ info.emoji = meta?.openclaw?.emoji || "";
52
+ }
53
+ catch {
54
+ // ignore malformed metadata
55
+ }
56
+ }
57
+ return info;
58
+ }
59
+ /** Fetch all files in a skill folder from GitHub. */
60
+ async function fetchSkillFiles(slug) {
61
+ const treeRes = await fetch(`${GH_API}/git/trees/main?recursive=1`);
62
+ if (!treeRes.ok)
63
+ throw new Error(`GitHub API error: ${treeRes.status}`);
64
+ const treeData = (await treeRes.json());
65
+ const prefix = `${slug}/`;
66
+ const blobs = treeData.tree.filter((e) => e.type === "blob" && e.path.startsWith(prefix));
67
+ const files = [];
68
+ await Promise.all(blobs.map(async (entry) => {
69
+ const res = await fetch(`${GH_RAW}/${entry.path}`);
70
+ if (!res.ok)
71
+ return;
72
+ const content = await res.text();
73
+ const relativePath = entry.path.slice(prefix.length);
74
+ files.push({ path: relativePath, content });
75
+ }));
76
+ return files;
77
+ }
78
+ // --- Skill Templates (for init) ---
11
79
  const TEMPLATES = {
12
80
  default: `---
13
81
  name: my-skill
14
82
  description: "Describe what this skill does."
15
- category: general
16
- tags: []
17
- metadata:
18
- aisa:
19
- apis: []
20
- requires:
21
- auth: true
22
- env: [AISA_API_KEY]
83
+ homepage: https://openclaw.ai
84
+ metadata: {"openclaw":{"emoji":"","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY"}}
23
85
  ---
24
86
 
25
87
  # My Skill
@@ -41,14 +103,8 @@ aisa run <slug> <path> -q "param=value"
41
103
  llm: `---
42
104
  name: llm-assistant
43
105
  description: "Use AISA's unified LLM gateway to chat with 70+ AI models."
44
- category: llm
45
- tags: [llm, chat, openai, claude, gemini]
46
- metadata:
47
- aisa:
48
- apis: [chat/completions, models]
49
- requires:
50
- auth: true
51
- env: [AISA_API_KEY]
106
+ homepage: https://openclaw.ai
107
+ metadata: {"openclaw":{"emoji":"🤖","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY"}}
52
108
  ---
53
109
 
54
110
  # LLM Assistant Skill
@@ -79,27 +135,12 @@ aisa chat "Explain quantum computing" --model claude-opus-4-6 --stream
79
135
  aisa models
80
136
  aisa models --provider anthropic
81
137
  \`\`\`
82
-
83
- ## REST API (OpenAI-compatible)
84
-
85
- \`\`\`bash
86
- curl -X POST https://api.aisa.one/v1/chat/completions \\
87
- -H "Authorization: Bearer $AISA_API_KEY" \\
88
- -H "Content-Type: application/json" \\
89
- -d '{"model": "gpt-4.1", "messages": [{"role": "user", "content": "Hello"}]}'
90
- \`\`\`
91
138
  `,
92
139
  search: `---
93
140
  name: web-search
94
141
  description: "Search the web, YouTube, and academic papers via AISA APIs."
95
- category: search
96
- tags: [search, web, youtube, scholar, tavily]
97
- metadata:
98
- aisa:
99
- apis: [search/smart, search/full, youtube/search, scholar/search, tavily/search]
100
- requires:
101
- auth: true
102
- env: [AISA_API_KEY]
142
+ homepage: https://openclaw.ai
143
+ metadata: {"openclaw":{"emoji":"🔍","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY"}}
103
144
  ---
104
145
 
105
146
  # Web Search Skill
@@ -129,30 +170,12 @@ aisa web-search "machine learning tutorial" --type youtube
129
170
  \`\`\`bash
130
171
  aisa scholar "transformer architecture"
131
172
  \`\`\`
132
-
133
- ## Tavily Deep Search
134
-
135
- \`\`\`bash
136
- aisa run tavily /search -d '{"query": "NVIDIA earnings 2025", "search_depth": "advanced"}'
137
- \`\`\`
138
-
139
- ## Tavily Extract (webpage content)
140
-
141
- \`\`\`bash
142
- aisa run tavily /extract -d '{"urls": ["https://example.com"]}'
143
- \`\`\`
144
173
  `,
145
174
  finance: `---
146
175
  name: finance-analyst
147
176
  description: "Access stock prices, earnings, SEC filings, and financial data via AISA."
148
- category: finance
149
- tags: [stocks, earnings, sec-filings, crypto, financial-analysis]
150
- metadata:
151
- aisa:
152
- apis: [stock/price, analyst-estimates, financial-statements, filings, insider-trades, crypto/price]
153
- requires:
154
- auth: true
155
- env: [AISA_API_KEY]
177
+ homepage: https://openclaw.ai
178
+ metadata: {"openclaw":{"emoji":"📊","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY"}}
156
179
  ---
157
180
 
158
181
  # Finance Analyst Skill
@@ -179,42 +202,12 @@ aisa stock TSLA --field filings
179
202
  aisa crypto BTC
180
203
  aisa crypto ETH --period 30d
181
204
  \`\`\`
182
-
183
- ## Stock Screener
184
-
185
- \`\`\`bash
186
- aisa screener --sector Technology --limit 20
187
- \`\`\`
188
-
189
- ## Financial Statements
190
-
191
- \`\`\`bash
192
- aisa run financial-statements -q "symbol=AAPL&period=annual"
193
- \`\`\`
194
-
195
- ## SEC Filings
196
-
197
- \`\`\`bash
198
- aisa run filings -q "symbol=TSLA&type=10-K"
199
- \`\`\`
200
-
201
- ## Insider Trades
202
-
203
- \`\`\`bash
204
- aisa stock NVDA --field insider
205
- \`\`\`
206
205
  `,
207
206
  twitter: `---
208
207
  name: twitter-manager
209
- description: "Post tweets, search Twitter, get user profiles and trends via AISA."
210
- category: twitter
211
- tags: [twitter, tweets, social-media, trends]
212
- metadata:
213
- aisa:
214
- apis: [twitter/create-tweet-v2, twitter/tweet/advanced-search, twitter/user/info, twitter/trends]
215
- requires:
216
- auth: true
217
- env: [AISA_API_KEY]
208
+ description: "Search Twitter, get user profiles and trends via AISA."
209
+ homepage: https://openclaw.ai
210
+ metadata: {"openclaw":{"emoji":"🐦","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY"}}
218
211
  ---
219
212
 
220
213
  # Twitter Manager Skill
@@ -227,12 +220,6 @@ Interact with Twitter/X through AISA's Twitter APIs.
227
220
  export AISA_API_KEY=sk-your-key
228
221
  \`\`\`
229
222
 
230
- ## Post a Tweet
231
-
232
- \`\`\`bash
233
- aisa tweet "Hello from AISA CLI!"
234
- \`\`\`
235
-
236
223
  ## Search Tweets
237
224
 
238
225
  \`\`\`bash
@@ -250,30 +237,12 @@ aisa twitter user elonmusk
250
237
  \`\`\`bash
251
238
  aisa twitter trends
252
239
  \`\`\`
253
-
254
- ## Advanced: Get Tweet Replies
255
-
256
- \`\`\`bash
257
- aisa run twitter/tweet/replies -q "id=1234567890"
258
- \`\`\`
259
-
260
- ## Advanced: Send DM
261
-
262
- \`\`\`bash
263
- aisa run twitter/send-dm-to-user -d '{"userId": "123", "text": "Hello!"}'
264
- \`\`\`
265
240
  `,
266
241
  video: `---
267
242
  name: video-generator
268
243
  description: "Generate videos from text prompts using AISA's video synthesis API."
269
- category: video
270
- tags: [video, generation, aigc, synthesis]
271
- metadata:
272
- aisa:
273
- apis: [services/aigc/video-generation/video-synthesis, services/aigc/tasks]
274
- requires:
275
- auth: true
276
- env: [AISA_API_KEY]
244
+ homepage: https://openclaw.ai
245
+ metadata: {"openclaw":{"emoji":"🎬","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY"}}
277
246
  ---
278
247
 
279
248
  # Video Generator Skill
@@ -306,120 +275,152 @@ aisa video status <task-id>
306
275
  `,
307
276
  };
308
277
  // --- Commands ---
309
- export async function skillsListAction(options) {
310
- const key = requireApiKey();
311
- const spinner = ora("Fetching skills...").start();
312
- const query = {};
313
- if (options.category)
314
- query.category = options.category;
315
- if (options.limit)
316
- query.limit = options.limit;
317
- const res = await apiRequest(key, "cli/skills", { query });
318
- if (!res.success || !res.data) {
319
- spinner.fail("Failed to fetch skills");
320
- error(res.error || "Unknown error");
321
- return;
322
- }
323
- spinner.stop();
324
- const { skills } = res.data;
325
- if (skills.length === 0) {
326
- console.log(" No skills found.");
327
- return;
278
+ export async function skillsListAction(_options) {
279
+ const spinner = ora("Fetching skills from GitHub...").start();
280
+ try {
281
+ const slugs = await fetchSkillSlugs();
282
+ // Fetch all SKILL.md frontmatters in parallel
283
+ const metas = await Promise.all(slugs.map((s) => fetchSkillMeta(s)));
284
+ const skills = metas.filter((m) => m !== null);
285
+ spinner.stop();
286
+ if (skills.length === 0) {
287
+ console.log(" No skills found.");
288
+ return;
289
+ }
290
+ console.log(chalk.bold(`\n ${skills.length} skills available\n`));
291
+ for (const s of skills) {
292
+ const emoji = s.emoji ? `${s.emoji} ` : "";
293
+ console.log(` ${emoji}${chalk.cyan.bold(s.name)} ${chalk.gray(s.slug)}`);
294
+ if (s.description) {
295
+ console.log(` ${chalk.gray(truncate(s.description, 80))}`);
296
+ }
297
+ console.log();
298
+ }
299
+ hint("Install: aisa skills install <slug>");
300
+ hint("Details: aisa skills show <slug>");
328
301
  }
329
- for (const s of skills) {
330
- const verified = s.verified ? chalk.green(" ✓") : "";
331
- console.log(`\n ${chalk.cyan.bold(s.name)}${verified} ${chalk.gray(s.slug)} ${badge(s.category)}`);
332
- console.log(` ${chalk.gray(truncate(s.description, 60))}`);
333
- console.log(` ${chalk.gray(`${s.installs} installs`)}`);
302
+ catch (err) {
303
+ spinner.fail("Failed to fetch skills");
304
+ error(err.message);
334
305
  }
335
306
  }
336
- export async function skillsSearchAction(query, options) {
337
- const key = requireApiKey();
307
+ export async function skillsSearchAction(query, _options) {
338
308
  const spinner = ora(`Searching skills: "${query}"...`).start();
339
- const res = await apiRequest(key, "cli/skills/search", {
340
- method: "POST",
341
- body: { query, limit: parseInt(options.limit || "10") },
342
- });
343
- if (!res.success || !res.data) {
344
- spinner.fail("Search failed");
345
- error(res.error || "Unknown error");
346
- return;
347
- }
348
- spinner.stop();
349
- const { skills } = res.data;
350
- if (skills.length === 0) {
351
- console.log(` No skills found for "${query}".`);
352
- return;
309
+ try {
310
+ const slugs = await fetchSkillSlugs();
311
+ const metas = await Promise.all(slugs.map((s) => fetchSkillMeta(s)));
312
+ const skills = metas.filter((m) => m !== null);
313
+ const q = query.toLowerCase();
314
+ const matches = skills.filter((s) => s.name.toLowerCase().includes(q) ||
315
+ s.slug.toLowerCase().includes(q) ||
316
+ s.description.toLowerCase().includes(q));
317
+ spinner.stop();
318
+ if (matches.length === 0) {
319
+ console.log(` No skills found for "${query}".`);
320
+ return;
321
+ }
322
+ console.log(chalk.bold(`\n ${matches.length} result(s)\n`));
323
+ for (const s of matches) {
324
+ const emoji = s.emoji ? `${s.emoji} ` : "";
325
+ console.log(` ${emoji}${chalk.cyan.bold(s.name)} ${chalk.gray(s.slug)}`);
326
+ if (s.description) {
327
+ console.log(` ${chalk.gray(truncate(s.description, 80))}`);
328
+ }
329
+ console.log();
330
+ }
331
+ hint("Install: aisa skills install <slug>");
353
332
  }
354
- for (const s of skills) {
355
- const verified = s.verified ? chalk.green(" ✓") : "";
356
- console.log(`\n ${chalk.cyan.bold(s.name)}${verified} ${chalk.gray(s.slug)} ${badge(s.category)}`);
357
- console.log(` ${chalk.gray(s.description)}`);
333
+ catch (err) {
334
+ spinner.fail("Search failed");
335
+ error(err.message);
358
336
  }
359
- console.log(chalk.gray("\n Run 'aisa skills add <owner/name>' to install"));
360
337
  }
361
338
  export async function skillsShowAction(slug) {
362
- const key = requireApiKey();
363
339
  const spinner = ora(`Loading ${slug}...`).start();
364
- const res = await apiRequest(key, `cli/skills/${slug}`);
365
- if (!res.success || !res.data) {
366
- spinner.fail("Skill not found");
367
- error(res.error || "Unknown error");
368
- return;
340
+ try {
341
+ // Fetch SKILL.md
342
+ const res = await fetch(`${GH_RAW}/${slug}/SKILL.md`);
343
+ if (!res.ok) {
344
+ spinner.fail("Skill not found");
345
+ error(`No skill "${slug}" in ${SKILLS_REPO}`);
346
+ return;
347
+ }
348
+ const content = await res.text();
349
+ const meta = parseSkillFrontmatter(slug, content);
350
+ // Fetch file list
351
+ const treeRes = await fetch(`${GH_API}/git/trees/main?recursive=1`);
352
+ const treeData = (await treeRes.json());
353
+ const prefix = `${slug}/`;
354
+ const files = treeData.tree
355
+ .filter((e) => e.type === "blob" && e.path.startsWith(prefix))
356
+ .map((e) => e.path.slice(prefix.length));
357
+ spinner.stop();
358
+ const emoji = meta.emoji ? `${meta.emoji} ` : "";
359
+ console.log(`\n ${emoji}${chalk.cyan.bold(meta.name)}`);
360
+ console.log(` ${meta.description}`);
361
+ console.log(` Slug: ${chalk.gray(slug)}`);
362
+ console.log(` Files: ${files.join(", ")}`);
363
+ console.log(` Source: ${chalk.gray(`https://github.com/${SKILLS_REPO}/tree/main/${slug}`)}`);
364
+ // Print SKILL.md body (after frontmatter)
365
+ const body = content.replace(/^---\n[\s\S]*?\n---\n*/, "").trim();
366
+ if (body) {
367
+ console.log(`\n${chalk.gray("─".repeat(60))}`);
368
+ console.log(body);
369
+ }
370
+ console.log();
371
+ hint(`Install: aisa skills install ${slug}`);
369
372
  }
370
- spinner.stop();
371
- const s = res.data;
372
- const verified = s.verified ? chalk.green(" ✓ verified") : "";
373
- console.log(`\n ${chalk.cyan.bold(s.name)}${verified}`);
374
- console.log(` ${s.description}`);
375
- console.log(` Category: ${badge(s.category)}`);
376
- console.log(` Tags: ${s.tags.join(", ")}`);
377
- console.log(` Installs: ${s.installs}`);
378
- console.log(` Files: ${s.files.map((f) => f.path).join(", ")}`);
379
- console.log(chalk.gray(`\n Install: aisa skills add ${s.slug}`));
380
- }
381
- export async function skillsAddAction(slug, options) {
382
- const key = requireApiKey();
383
- const spinner = ora(`Fetching skill '${slug}'...`).start();
384
- const res = await apiRequest(key, `cli/skills/${slug}`);
385
- if (!res.success || !res.data) {
386
- spinner.fail("Failed to fetch skill");
387
- error(res.error || "Unknown error");
388
- return;
373
+ catch (err) {
374
+ spinner.fail("Failed to load skill");
375
+ error(err.message);
389
376
  }
390
- spinner.stop();
391
- const skill = res.data;
392
- // Determine targets
393
- let targets;
394
- if (options.agent) {
395
- if (options.agent === "all") {
396
- targets = Object.keys(AGENT_DIRS);
377
+ }
378
+ export async function skillsInstallAction(slug, options) {
379
+ const spinner = ora(`Fetching skill '${slug}' from GitHub...`).start();
380
+ try {
381
+ const files = await fetchSkillFiles(slug);
382
+ if (files.length === 0) {
383
+ spinner.fail("Skill not found");
384
+ error(`No skill "${slug}" in ${SKILLS_REPO}`);
385
+ return;
386
+ }
387
+ spinner.stop();
388
+ // Determine targets
389
+ let targets;
390
+ if (options.agent) {
391
+ if (options.agent === "all") {
392
+ targets = Object.keys(AGENT_DIRS);
393
+ }
394
+ else {
395
+ if (!AGENT_DIRS[options.agent]) {
396
+ error(`Unknown agent: ${options.agent}. Valid: ${Object.keys(AGENT_DIRS).join(", ")}, all`);
397
+ return;
398
+ }
399
+ targets = [options.agent];
400
+ }
397
401
  }
398
402
  else {
399
- if (!AGENT_DIRS[options.agent]) {
400
- error(`Unknown agent: ${options.agent}. Valid: ${Object.keys(AGENT_DIRS).join(", ")}, all`);
401
- return;
403
+ targets = detectAgents(AGENT_DIRS);
404
+ if (targets.length === 0) {
405
+ targets = Object.keys(AGENT_DIRS);
402
406
  }
403
- targets = [options.agent];
404
407
  }
405
- }
406
- else {
407
- targets = detectAgents(AGENT_DIRS);
408
- if (targets.length === 0) {
409
- targets = Object.keys(AGENT_DIRS);
408
+ let installed = 0;
409
+ for (const agent of targets) {
410
+ const dir = expandHome(join(AGENT_DIRS[agent], slug));
411
+ ensureDir(dir);
412
+ writeSkillFiles(dir, files);
413
+ console.log(` ${chalk.green("✓")} ${AGENT_DIRS[agent]} (${agent})`);
414
+ installed++;
410
415
  }
416
+ success(`Skill '${slug}' installed to ${installed} agent(s)`);
411
417
  }
412
- let installed = 0;
413
- for (const agent of targets) {
414
- const dir = expandHome(join(AGENT_DIRS[agent], slug));
415
- ensureDir(dir);
416
- writeSkillFiles(dir, skill.files);
417
- console.log(` ${chalk.green("✓")} ${AGENT_DIRS[agent]} (${agent})`);
418
- installed++;
418
+ catch (err) {
419
+ spinner.fail("Failed to install skill");
420
+ error(err.message);
419
421
  }
420
- success(`Skill '${skill.name}' installed to ${installed} agent(s)`);
421
422
  }
422
- export async function skillsRemoveAction(slug, options) {
423
+ export function skillsRemoveAction(slug, options) {
423
424
  let targets;
424
425
  if (options.agent && options.agent !== "all") {
425
426
  targets = [options.agent];
@@ -469,87 +470,5 @@ export function skillsInitAction(name, options) {
469
470
  }
470
471
  success(`Skill initialized: ${name}/`);
471
472
  console.log(` ${chalk.gray(join(name, "SKILL.md"))}`);
472
- hint("Edit SKILL.md, then run 'aisa skills submit ./" + name + "'");
473
- }
474
- export async function skillsSubmitAction(path) {
475
- const key = requireApiKey();
476
- const dir = resolve(path);
477
- if (!existsSync(join(dir, "SKILL.md"))) {
478
- error("No SKILL.md found. Run 'aisa skills init <name>' first.");
479
- return;
480
- }
481
- const spinner = ora("Submitting skill...").start();
482
- const files = readSkillDir(dir);
483
- const res = await apiRequest(key, "cli/skills", {
484
- method: "POST",
485
- body: { files },
486
- });
487
- if (!res.success || !res.data) {
488
- spinner.fail("Failed to submit skill");
489
- error(res.error || "Unknown error");
490
- return;
491
- }
492
- spinner.stop();
493
- success(`Skill submitted: ${res.data.slug}`);
494
- hint(`Request verification: aisa skills request-verification ${res.data.slug}`);
495
- }
496
- export async function skillsPushAction(slug) {
497
- const key = requireApiKey();
498
- const dir = resolve(".");
499
- if (!existsSync(join(dir, "SKILL.md"))) {
500
- error("No SKILL.md in current directory.");
501
- return;
502
- }
503
- const spinner = ora(`Pushing updates to ${slug}...`).start();
504
- const files = readSkillDir(dir);
505
- const res = await apiRequest(key, `cli/skills/${slug}`, {
506
- method: "PUT",
507
- body: { files },
508
- });
509
- if (!res.success) {
510
- spinner.fail("Failed to push updates");
511
- error(res.error || "Unknown error");
512
- return;
513
- }
514
- spinner.stop();
515
- success(`Skill '${slug}' updated.`);
516
- }
517
- export async function skillsVerifyAction(slug) {
518
- const key = requireApiKey();
519
- const spinner = ora("Requesting verification...").start();
520
- const res = await apiRequest(key, `cli/skills/${slug}/verify`, { method: "POST" });
521
- if (!res.success) {
522
- spinner.fail("Failed to request verification");
523
- error(res.error || "Unknown error");
524
- return;
525
- }
526
- spinner.stop();
527
- success("Verification requested. The AISA team will review your skill.");
528
- }
529
- export async function skillsUpdateAction(slug) {
530
- const key = requireApiKey();
531
- if (slug) {
532
- // Update single skill
533
- const spinner = ora(`Updating ${slug}...`).start();
534
- const res = await apiRequest(key, `cli/skills/${slug}`);
535
- if (!res.success || !res.data) {
536
- spinner.fail("Failed to fetch skill");
537
- error(res.error || "Unknown error");
538
- return;
539
- }
540
- spinner.stop();
541
- const skill = res.data;
542
- const targets = detectAgents(AGENT_DIRS);
543
- for (const agent of targets) {
544
- const dir = expandHome(join(AGENT_DIRS[agent], slug));
545
- if (existsSync(dir)) {
546
- writeSkillFiles(dir, skill.files);
547
- console.log(` ${chalk.green("✓")} Updated in ${AGENT_DIRS[agent]}`);
548
- }
549
- }
550
- success(`Skill '${slug}' updated.`);
551
- }
552
- else {
553
- console.log(" Specify a skill to update: aisa skills update <owner/name>");
554
- }
473
+ hint(`Edit SKILL.md, then submit via PR: https://github.com/${SKILLS_REPO}`);
555
474
  }
@@ -36,6 +36,7 @@ export async function videoCreateAction(prompt, options) {
36
36
  // Poll for completion
37
37
  const pollSpinner = ora("Generating video...").start();
38
38
  let status = "PENDING";
39
+ let lastPollData;
39
40
  while (status === "PENDING" || status === "RUNNING") {
40
41
  await new Promise((r) => setTimeout(r, 5000));
41
42
  const pollRes = await apiRequest(key, "services/aigc/tasks", {
@@ -47,12 +48,13 @@ export async function videoCreateAction(prompt, options) {
47
48
  error(pollRes.error || "Unknown error");
48
49
  return;
49
50
  }
50
- status = pollRes.data.output?.task_status || pollRes.data.status || "UNKNOWN";
51
+ lastPollData = pollRes.data;
52
+ status = lastPollData.output?.task_status || lastPollData.status || "UNKNOWN";
51
53
  pollSpinner.text = `Generating video... (${status})`;
52
54
  }
53
55
  if (status === "SUCCEEDED" || status === "completed") {
54
56
  pollSpinner.succeed("Video generated!");
55
- const videoUrl = res.data.output?.video_url || res.data.resultUrl;
57
+ const videoUrl = lastPollData?.output?.video_url || lastPollData?.resultUrl;
56
58
  if (videoUrl) {
57
59
  console.log(` URL: ${chalk.cyan(videoUrl)}`);
58
60
  }
@@ -1,4 +1,4 @@
1
- export declare const VERSION = "0.1.6";
1
+ export declare const VERSION = "0.1.8";
2
2
  export declare const BASE_URL = "https://api.aisa.one";
3
3
  export declare const CLI_BASE_URL = "https://api.aisa.one/v1";
4
4
  export declare const APIS_BASE_URL = "https://api.aisa.one/apis/v1";
package/dist/constants.js CHANGED
@@ -1,4 +1,4 @@
1
- export const VERSION = "0.1.6";
1
+ export const VERSION = "0.1.8";
2
2
  export const BASE_URL = "https://api.aisa.one";
3
3
  export const CLI_BASE_URL = `${BASE_URL}/v1`;
4
4
  export const APIS_BASE_URL = `${BASE_URL}/apis/v1`;
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@ import { tweetAction, twitterSearchAction, twitterUserAction, twitterTrendsActio
22
22
  // Video
23
23
  import { videoCreateAction, videoStatusAction } from "./commands/video.js";
24
24
  // Skills
25
- import { skillsListAction, skillsSearchAction, skillsShowAction, skillsAddAction, skillsRemoveAction, skillsUpdateAction, skillsInitAction, skillsSubmitAction, skillsPushAction, skillsVerifyAction, } from "./commands/skills.js";
25
+ import { skillsListAction, skillsSearchAction, skillsShowAction, skillsInstallAction, skillsRemoveAction, skillsInitAction, } from "./commands/skills.js";
26
26
  // MCP
27
27
  import { mcpSetupAction, mcpStatusAction } from "./commands/mcp.js";
28
28
  // Config
@@ -202,51 +202,35 @@ video
202
202
  const skills = program.command("skills").description("Browse and manage agent skills");
203
203
  skills
204
204
  .command("list")
205
- .description("List available skills (WIP)")
205
+ .description("List available skills")
206
206
  .option("--category <cat>", "Filter by category")
207
207
  .option("--limit <n>", "Max results")
208
208
  .action(wrap(skillsListAction));
209
209
  skills
210
210
  .command("search <query>")
211
- .description("Search skills (WIP)")
211
+ .description("Search skills by keyword")
212
212
  .option("--limit <n>", "Max results")
213
213
  .action(wrap(skillsSearchAction));
214
214
  skills
215
215
  .command("show <slug>")
216
- .description("Show skill details (WIP)")
216
+ .description("Show skill details")
217
217
  .action(wrap(skillsShowAction));
218
218
  skills
219
- .command("add <slug>")
220
- .description("Install a skill to agent directories (WIP)")
219
+ .command("install <slug>")
220
+ .description("Install a skill to agent directories")
221
221
  .option("--agent <agent>", "Target agent: claude, cursor, copilot, windsurf, codex, gemini, openclaw, all")
222
- .action(wrap(skillsAddAction));
222
+ .action(wrap(skillsInstallAction));
223
223
  skills
224
224
  .command("remove <slug>")
225
- .description("Remove an installed skill (WIP)")
225
+ .description("Remove an installed skill")
226
226
  .option("--agent <agent>", "Target agent")
227
227
  .action(skillsRemoveAction);
228
- skills
229
- .command("update [slug]")
230
- .description("Update installed skill(s) (WIP)")
231
- .action(wrap(skillsUpdateAction));
232
228
  skills
233
229
  .command("init <name>")
234
- .description("Initialize a new skill")
230
+ .description("Initialize a new skill from template")
235
231
  .option("--template <template>", "Template: llm, search, finance, twitter, video")
236
232
  .option("--bare", "Minimal template")
237
233
  .action(skillsInitAction);
238
- skills
239
- .command("submit <path>")
240
- .description("Submit a skill to AISA (WIP)")
241
- .action(wrap(skillsSubmitAction));
242
- skills
243
- .command("push <slug>")
244
- .description("Push local changes to a submitted skill (WIP)")
245
- .action(wrap(skillsPushAction));
246
- skills
247
- .command("request-verification <slug>")
248
- .description("Request skill verification (WIP)")
249
- .action(wrap(skillsVerifyAction));
250
234
  // ── MCP ──
251
235
  const mcp = program.command("mcp").description("MCP server integration");
252
236
  mcp
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aisa-one/cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "CLI for the AISA unified AI infrastructure platform - access 70+ LLMs, web search, finance, Twitter, and video generation APIs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",