@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 +11 -12
- package/dist/commands/skills.d.ts +4 -8
- package/dist/commands/skills.js +214 -295
- package/dist/commands/video.js +4 -2
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/index.js +9 -25
- package/package.json +1 -1
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
|
|
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 #
|
|
130
|
-
aisa skills search "financial analysis" #
|
|
131
|
-
aisa skills show
|
|
132
|
-
aisa skills
|
|
133
|
-
aisa skills remove
|
|
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
|
|
148
|
+
### Create Skills
|
|
149
149
|
|
|
150
150
|
```bash
|
|
151
|
-
aisa skills init my-skill # create from default template
|
|
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(
|
|
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,
|
|
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
|
|
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
|
-
}):
|
|
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>;
|
package/dist/commands/skills.js
CHANGED
|
@@ -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
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
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
|
-
// ---
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
96
|
-
|
|
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
|
-
|
|
149
|
-
|
|
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: "
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
270
|
-
|
|
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(
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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,
|
|
337
|
-
const key = requireApiKey();
|
|
307
|
+
export async function skillsSearchAction(query, _options) {
|
|
338
308
|
const spinner = ora(`Searching skills: "${query}"...`).start();
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
if (
|
|
396
|
-
|
|
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
|
-
|
|
400
|
-
|
|
401
|
-
|
|
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
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
|
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(
|
|
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
|
}
|
package/dist/commands/video.js
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
57
|
+
const videoUrl = lastPollData?.output?.video_url || lastPollData?.resultUrl;
|
|
56
58
|
if (videoUrl) {
|
|
57
59
|
console.log(` URL: ${chalk.cyan(videoUrl)}`);
|
|
58
60
|
}
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
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
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,
|
|
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
|
|
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
|
|
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
|
|
216
|
+
.description("Show skill details")
|
|
217
217
|
.action(wrap(skillsShowAction));
|
|
218
218
|
skills
|
|
219
|
-
.command("
|
|
220
|
-
.description("Install a skill to agent directories
|
|
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(
|
|
222
|
+
.action(wrap(skillsInstallAction));
|
|
223
223
|
skills
|
|
224
224
|
.command("remove <slug>")
|
|
225
|
-
.description("Remove an installed skill
|
|
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