@solgridai/cli 0.2.0-beta.1 → 0.2.0-beta.2
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 +4 -3
- package/dist/index.js +98 -22
- package/package.json +1 -1
- package/skills/solgrid-cli/SKILL.md +8 -2
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ solgrid run -p <provider> -e <endpoint> -i '{"screen_name":"nasa"}' --wait
|
|
|
58
58
|
| `files get -i <fileId>` | 查看文件的类型、大小和状态 |
|
|
59
59
|
| `files url -i <fileId>` | 为已上传的文件重新签发 24 小时有效的 URL |
|
|
60
60
|
| `files download -i <fileId> [-o path] [--force]` | 通过签名 URL 下载文件到本地 |
|
|
61
|
-
| `skills list [-q text] [--kind k] [--mine] [-l n] [--page n]` | 在 SkillHub 搜索 Skill
|
|
61
|
+
| `skills list [-q text] [--kind k] [--mine] [-l n] [--page n]` | 在 SkillHub 搜索 Skill(`-q` 按标题搜索) |
|
|
62
62
|
| `skills get (-s slug \| -i id)` | 查看 Skill 详情 |
|
|
63
63
|
| `skills tags` | 列出 Skill 可用的标签 |
|
|
64
64
|
| `skills create [-s slug] -t <title> --kind <k> -d <text> (--dir <dir> \| --zip <file>) [--cover f] [--preview f] [--tag id]… [-y]` | 发布 Skill,创建后立即公开 |
|
|
@@ -102,8 +102,8 @@ solgrid skills create -t "Mecha portrait" --kind image \
|
|
|
102
102
|
|
|
103
103
|
用 `--dir` 时 slug 默认取 `name`,传了 `-s` 就必须和 `name` 一致;用 `--zip` 时必须传 `-s`,由服务端核对。`skills update` 换包时,新包的 `name` 也必须等于原来的 slug。
|
|
104
104
|
- `-d` 是显示在标题下的简短描述(纯文本,不按 Markdown 渲染)。Skill 的详细说明请写在 `SKILL.md` 里。
|
|
105
|
-
- `--kind` 表示 Skill 产出的作品类型:`image`、`video`、`web`、`audio`。除视频外都需要封面(`--cover
|
|
106
|
-
- `--tag` 填 `solgrid skills tags` 列出的标签 ID,最多 3
|
|
105
|
+
- `--kind` 表示 Skill 产出的作品类型:`image`、`video`、`web`、`audio`。除视频外都需要封面(`--cover`,图片)。预览(`--preview`)的类型由 kind 决定:图片和网页类可选,只能是图片;视频类必填,只能是视频;音频类必填,只能是音频。
|
|
106
|
+
- `--tag` 填 `solgrid skills tags` 列出的标签 ID,最多 3 个,不能重复。
|
|
107
107
|
- `create`、`delete` 会先请你确认;在脚本或 Agent 中使用时加 `-y`。
|
|
108
108
|
- `update` 只修改传入的选项;`--tag` 会替换全部标签,`--clear-tags` 清空标签,`--clear-preview` 去掉预览(仅图片和网页类 Skill)。只能修改、删除自己的 Skill。
|
|
109
109
|
|
|
@@ -132,6 +132,7 @@ solgrid run -p <provider> -e <endpoint> -i '<同样的输入>' --idempotency-key
|
|
|
132
132
|
| `~/.config/solgrid/cli/config.json` | 当前激活的 key |
|
|
133
133
|
| `SOLGRID_API_KEY` | 设置后优先于本机保存的 key,适合 CI 和 Agent |
|
|
134
134
|
| `SOLGRID_API_BASE_URL` | 覆盖 API 地址,默认为 `https://api.solgrid.ai` |
|
|
135
|
+
| `SOLGRID_LANG` / `--lang` | 服务端返回文本(标签名、错误信息)的语言:`en`(默认)或 `zh`;`--lang` 是全局选项,优先于环境变量。标签 ID、错误键和 CLI 自身的提示不变 |
|
|
135
136
|
| `NO_COLOR` / `FORCE_COLOR` | 关闭或强制彩色输出 |
|
|
136
137
|
|
|
137
138
|
## 开发
|
package/dist/index.js
CHANGED
|
@@ -2075,7 +2075,7 @@ function useColor() {
|
|
|
2075
2075
|
// node_modules/commander/index.js
|
|
2076
2076
|
var program = new Command;
|
|
2077
2077
|
// src/build-info.ts
|
|
2078
|
-
var buildInfo = { packageName: "@solgridai/cli", version: "0.2.0-beta.
|
|
2078
|
+
var buildInfo = { packageName: "@solgridai/cli", version: "0.2.0-beta.2" };
|
|
2079
2079
|
|
|
2080
2080
|
// src/core/errors.ts
|
|
2081
2081
|
class CliError extends Error {
|
|
@@ -2344,8 +2344,10 @@ var CONSOLE_URLS = {
|
|
|
2344
2344
|
};
|
|
2345
2345
|
var ENV = {
|
|
2346
2346
|
apiKey: "SOLGRID_API_KEY",
|
|
2347
|
-
apiBaseUrl: "SOLGRID_API_BASE_URL"
|
|
2347
|
+
apiBaseUrl: "SOLGRID_API_BASE_URL",
|
|
2348
|
+
language: "SOLGRID_LANG"
|
|
2348
2349
|
};
|
|
2350
|
+
var LANGUAGES = ["en", "zh"];
|
|
2349
2351
|
|
|
2350
2352
|
// src/core/config.ts
|
|
2351
2353
|
function resolveConfigDir(env, homeDir) {
|
|
@@ -2591,6 +2593,9 @@ class ApiClient {
|
|
|
2591
2593
|
this.options = options;
|
|
2592
2594
|
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
2593
2595
|
}
|
|
2596
|
+
languageHeader() {
|
|
2597
|
+
return this.options.language ? { "Accept-Language": this.options.language } : {};
|
|
2598
|
+
}
|
|
2594
2599
|
post(path, body = {}, headers = {}, request = {}) {
|
|
2595
2600
|
return this.send("POST", path, {
|
|
2596
2601
|
body: JSON.stringify(body),
|
|
@@ -2632,6 +2637,7 @@ class ApiClient {
|
|
|
2632
2637
|
method: "GET",
|
|
2633
2638
|
headers: {
|
|
2634
2639
|
"X-Solgrid-Client": this.options.clientId,
|
|
2640
|
+
...this.languageHeader(),
|
|
2635
2641
|
...this.options.apiKey ? { Authorization: `Bearer ${this.options.apiKey}` } : {}
|
|
2636
2642
|
},
|
|
2637
2643
|
signal: AbortSignal.timeout(TRANSFER_TIMEOUT_MS)
|
|
@@ -2658,6 +2664,7 @@ class ApiClient {
|
|
|
2658
2664
|
headers: {
|
|
2659
2665
|
Accept: "application/json",
|
|
2660
2666
|
"X-Solgrid-Client": this.options.clientId,
|
|
2667
|
+
...this.languageHeader(),
|
|
2661
2668
|
...this.options.apiKey ? { Authorization: `Bearer ${this.options.apiKey}` } : {},
|
|
2662
2669
|
...options.headers
|
|
2663
2670
|
},
|
|
@@ -3119,13 +3126,29 @@ function apiClient(ctx) {
|
|
|
3119
3126
|
});
|
|
3120
3127
|
}
|
|
3121
3128
|
sentKeys.set(ctx, resolved);
|
|
3129
|
+
const language = requestLanguage(ctx);
|
|
3122
3130
|
return new ApiClient({
|
|
3123
3131
|
baseUrl: ctx.env[ENV.apiBaseUrl]?.trim() || DEFAULT_API_BASE_URL,
|
|
3124
3132
|
apiKey: resolved.key,
|
|
3125
3133
|
clientId: `cli@${buildInfo.version}`,
|
|
3134
|
+
...language ? { language } : {},
|
|
3126
3135
|
fetch: ctx.fetch
|
|
3127
3136
|
});
|
|
3128
3137
|
}
|
|
3138
|
+
function requestLanguage(ctx) {
|
|
3139
|
+
const value = ctx.language ?? ctx.env[ENV.language]?.trim();
|
|
3140
|
+
if (!value)
|
|
3141
|
+
return;
|
|
3142
|
+
return parseLanguage(value, ctx.language === undefined ? ENV.language : "--lang");
|
|
3143
|
+
}
|
|
3144
|
+
function parseLanguage(value, source = "--lang") {
|
|
3145
|
+
const language = value.trim().toLowerCase();
|
|
3146
|
+
if (LANGUAGES.includes(language))
|
|
3147
|
+
return language;
|
|
3148
|
+
throw new CliError("USAGE", `Unsupported language in ${source}: ${value}.`, {
|
|
3149
|
+
hint: `Use one of: ${LANGUAGES.join(", ")}.`
|
|
3150
|
+
});
|
|
3151
|
+
}
|
|
3129
3152
|
function withAuthContext(ctx, error) {
|
|
3130
3153
|
const key = sentKeys.get(ctx);
|
|
3131
3154
|
if (!(error instanceof ApiError) || error.code !== "AUTH_FAILED" || !key)
|
|
@@ -4158,6 +4181,7 @@ function registerSessionIdCommand(program, ctx) {
|
|
|
4158
4181
|
|
|
4159
4182
|
// src/commands/skills.ts
|
|
4160
4183
|
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
|
|
4184
|
+
import { basename as basename4 } from "node:path";
|
|
4161
4185
|
|
|
4162
4186
|
// src/core/skill-markdown.ts
|
|
4163
4187
|
var MAX_SKILL_NAME_LENGTH = 64;
|
|
@@ -4609,6 +4633,10 @@ async function listSkillTags(client) {
|
|
|
4609
4633
|
return checked.tags;
|
|
4610
4634
|
}
|
|
4611
4635
|
function checkTags(tags, available) {
|
|
4636
|
+
const repeated = [...new Set(tags.filter((tag, index) => tags.indexOf(tag) !== index))];
|
|
4637
|
+
if (repeated.length > 0) {
|
|
4638
|
+
throw new CliError("USAGE", `Tag${repeated.length > 1 ? "s" : ""} given more than once: ${repeated.join(", ")}.`, { hint: "Pass each --tag once." });
|
|
4639
|
+
}
|
|
4612
4640
|
if (tags.length > MAX_TAGS) {
|
|
4613
4641
|
throw new CliError("USAGE", `A Skill takes at most ${MAX_TAGS} tags; ${tags.length} were given.`);
|
|
4614
4642
|
}
|
|
@@ -4620,6 +4648,9 @@ function checkTags(tags, available) {
|
|
|
4620
4648
|
});
|
|
4621
4649
|
}
|
|
4622
4650
|
}
|
|
4651
|
+
function previewKindFor(kind) {
|
|
4652
|
+
return kind === "video" ? "video" : kind === "audio" ? "audio" : "image";
|
|
4653
|
+
}
|
|
4623
4654
|
var PUBLIC_DISPLAY = "1";
|
|
4624
4655
|
function prepareSkillFile(path, role) {
|
|
4625
4656
|
let size;
|
|
@@ -4693,7 +4724,7 @@ function collect2(value, previous = []) {
|
|
|
4693
4724
|
}
|
|
4694
4725
|
function addContentOptions(command, required) {
|
|
4695
4726
|
const note = required ? "" : " (replaces the current one)";
|
|
4696
|
-
command.option("-t, --title <title>", `title${note}`).option("--kind <kind>", `what the Skill makes: ${SKILL_KINDS.join(", ")}${note}`, parseKind2).option("-d, --description <text>", `a short description shown under the title${note}`).option("--description-file <path>", "read the description from a file instead").option("--tag <id>", `a tag ID from \`solgrid skills tags\`; up to ${MAX_TAGS}, repeat for several${required ? "" : " (replaces all current tags)"}`, collect2).option("--cover <path>", `cover image; required unless the kind is video${note}`).option("--preview <path>", `preview image, video
|
|
4727
|
+
command.option("-t, --title <title>", `title${note}`).option("--kind <kind>", `what the Skill makes: ${SKILL_KINDS.join(", ")}${note}`, parseKind2).option("-d, --description <text>", `a short description shown under the title${note}`).option("--description-file <path>", "read the description from a file instead").option("--tag <id>", `a tag ID from \`solgrid skills tags\`; up to ${MAX_TAGS}, repeat for several${required ? "" : " (replaces all current tags)"}`, collect2).option("--cover <path>", `cover image; required unless the kind is video${note}`).option("--preview <path>", `preview of what the Skill makes: an image for image and web Skills (optional), a video for video Skills, an audio file for audio Skills${note}`).option("--dir <path>", `a Skill directory with SKILL.md at its top, packed for you${note}`).option("--zip <path>", `or an already packed Skill (.zip with SKILL.md)${note}`);
|
|
4697
4728
|
return command;
|
|
4698
4729
|
}
|
|
4699
4730
|
function readDescription(opts) {
|
|
@@ -4719,7 +4750,22 @@ function checkMedia(kind, hasCover, hasPreview) {
|
|
|
4719
4750
|
}
|
|
4720
4751
|
if ((kind === "video" || kind === "audio") && !hasPreview) {
|
|
4721
4752
|
throw new CliError("USAGE", `${aSkill(kind)} needs a preview.`, {
|
|
4722
|
-
hint: `Pass --preview <${kind}
|
|
4753
|
+
hint: `Pass --preview <${previewKindFor(kind)} file>.`
|
|
4754
|
+
});
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
var A_FILE = {
|
|
4758
|
+
image: "an image",
|
|
4759
|
+
video: "a video",
|
|
4760
|
+
audio: "an audio file",
|
|
4761
|
+
skill: "a Skill package"
|
|
4762
|
+
};
|
|
4763
|
+
function checkPreviewFile(kind, path) {
|
|
4764
|
+
const { kind: fileKind } = prepareSkillFile(path, "preview");
|
|
4765
|
+
const expected = previewKindFor(kind);
|
|
4766
|
+
if (fileKind !== expected) {
|
|
4767
|
+
throw new CliError("USAGE", `${aSkill(kind)}'s preview must be ${A_FILE[expected]}, not ${A_FILE[fileKind]}: ${basename4(path)}.`, {
|
|
4768
|
+
hint: "Image and web Skills take an image preview, video Skills a video, audio Skills an audio file."
|
|
4723
4769
|
});
|
|
4724
4770
|
}
|
|
4725
4771
|
}
|
|
@@ -4811,7 +4857,7 @@ function registerSkillsCommand(program, ctx) {
|
|
|
4811
4857
|
const skills = program.command("skills").description("Find, publish and manage Skills on SkillHub.").addHelpText("after", `
|
|
4812
4858
|
A Skill is public as soon as it is created. Its slug identifies it everywhere and cannot
|
|
4813
4859
|
be changed later.`);
|
|
4814
|
-
skills.command("list").description("Search Skills, newest first.").option("-q, --query <words>", "
|
|
4860
|
+
skills.command("list").description("Search Skills, newest first.").option("-q, --query <words>", "search by title").option("--kind <kind>", `only this kind: ${SKILL_KINDS.join(", ")}`, parseKind2).option("--mine", "only your own Skills").option("-l, --limit <n>", "Skills per page (default 20, up to 100)", parsePositiveInt).option("--page <n>", "page number (default 1)", parsePositiveInt).option(...JSON_FLAG).action((opts) => runAction(ctx, opts.json, async (out) => {
|
|
4815
4861
|
const client = apiClient(ctx);
|
|
4816
4862
|
out.progress.start("Searching Skills…");
|
|
4817
4863
|
const data = await listSkills(client, {
|
|
@@ -4876,7 +4922,7 @@ be changed later.`);
|
|
|
4876
4922
|
if (opts.cover)
|
|
4877
4923
|
prepareSkillFile(opts.cover, "cover");
|
|
4878
4924
|
if (opts.preview)
|
|
4879
|
-
|
|
4925
|
+
checkPreviewFile(kind, opts.preview);
|
|
4880
4926
|
const pkg = skillPackage(opts);
|
|
4881
4927
|
try {
|
|
4882
4928
|
const slug = slugFor(pkg, opts);
|
|
@@ -4957,8 +5003,9 @@ be changed later.`);
|
|
|
4957
5003
|
if (pkg?.name !== undefined && pkg.name !== skill.slug) {
|
|
4958
5004
|
throw nameMismatch(pkg.name, skill.slug);
|
|
4959
5005
|
}
|
|
5006
|
+
const current = contentOf(skill);
|
|
4960
5007
|
const next = {
|
|
4961
|
-
...
|
|
5008
|
+
...current,
|
|
4962
5009
|
...opts.title !== undefined ? { title: opts.title } : {},
|
|
4963
5010
|
...opts.kind !== undefined ? { kind: opts.kind } : {},
|
|
4964
5011
|
...description !== undefined ? { description } : {},
|
|
@@ -4971,6 +5018,14 @@ be changed later.`);
|
|
|
4971
5018
|
});
|
|
4972
5019
|
}
|
|
4973
5020
|
checkMedia(next.kind, Boolean(opts.cover ?? next.coverFileAssetId), Boolean(opts.preview ?? next.previewFileAssetId));
|
|
5021
|
+
if (opts.preview) {
|
|
5022
|
+
checkPreviewFile(next.kind, opts.preview);
|
|
5023
|
+
} else if (next.previewFileAssetId && previewKindFor(current.kind) !== previewKindFor(next.kind)) {
|
|
5024
|
+
const wanted = previewKindFor(next.kind);
|
|
5025
|
+
throw new CliError("USAGE", `Changing this ${current.kind} Skill to ${next.kind} needs a new preview: the current one is ${A_FILE[previewKindFor(current.kind)]}.`, {
|
|
5026
|
+
hint: `Pass --preview <${wanted} file>${wanted === "image" ? ", or --clear-preview" : ""}.`
|
|
5027
|
+
});
|
|
5028
|
+
}
|
|
4974
5029
|
await checkTagOptions(client, out, opts.tag);
|
|
4975
5030
|
next.skillFileAssetId = await uploadIfGiven(client, out, pkg?.path, "skill") ?? next.skillFileAssetId;
|
|
4976
5031
|
next.coverFileAssetId = await uploadIfGiven(client, out, opts.cover, "cover") ?? next.coverFileAssetId;
|
|
@@ -5394,11 +5449,15 @@ function renderInput(input, legacySchema, style) {
|
|
|
5394
5449
|
|
|
5395
5450
|
// src/program.ts
|
|
5396
5451
|
function createProgram(ctx, options = {}) {
|
|
5397
|
-
const program = new Command().name("solgrid").description("Discover, inspect and run Solgrid tools from your terminal or agent.").version(buildInfo.version, "-v, --version", "Print the CLI version.").helpOption("-h, --help", "Show help.").configureOutput({
|
|
5452
|
+
const program = new Command().name("solgrid").description("Discover, inspect and run Solgrid tools from your terminal or agent.").version(buildInfo.version, "-v, --version", "Print the CLI version.").helpOption("-h, --help", "Show help.").option("--lang <code>", `language for text the Solgrid API writes, such as tag names and error messages: ${LANGUAGES.join(" or ")} (default: en; or set ${ENV.language})`).configureOutput({
|
|
5398
5453
|
writeOut: (text) => ctx.stdout.write(text),
|
|
5399
5454
|
writeErr: (text) => options.jsonErrors ? undefined : ctx.stderr.write(text),
|
|
5400
5455
|
outputError: (text, write) => options.jsonErrors ? undefined : write(text)
|
|
5401
|
-
}).showHelpAfterError("(run with --help for usage)").exitOverride()
|
|
5456
|
+
}).showHelpAfterError("(run with --help for usage)").exitOverride().hook("preAction", () => {
|
|
5457
|
+
const { lang } = program.opts();
|
|
5458
|
+
if (lang !== undefined)
|
|
5459
|
+
ctx.language = lang;
|
|
5460
|
+
});
|
|
5402
5461
|
registerToolCommands(program, ctx);
|
|
5403
5462
|
registerRunCommand(program, ctx);
|
|
5404
5463
|
registerRunsCommand(program, ctx);
|
|
@@ -5434,16 +5493,8 @@ async function runCli(ctx, args) {
|
|
|
5434
5493
|
return ctx.exitCode;
|
|
5435
5494
|
}
|
|
5436
5495
|
function wantsJson(program, args) {
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
for (;index < args.length; index++) {
|
|
5440
|
-
const arg = args[index] ?? "";
|
|
5441
|
-
const next = command.commands.find((sub) => sub.name() === arg || sub.aliases().includes(arg));
|
|
5442
|
-
if (!next)
|
|
5443
|
-
break;
|
|
5444
|
-
command = next;
|
|
5445
|
-
}
|
|
5446
|
-
const rest = args.slice(index);
|
|
5496
|
+
const { command, index, local } = resolveCommand(program, args);
|
|
5497
|
+
const rest = local.slice(index);
|
|
5447
5498
|
if (command.commands.length > 0) {
|
|
5448
5499
|
const end = rest.indexOf("--");
|
|
5449
5500
|
return (end === -1 ? rest : rest.slice(0, end)).some((arg) => arg === "--json" || /^-[A-Za-z]*j[A-Za-z]*$/.test(arg));
|
|
@@ -5481,14 +5532,39 @@ function wantsJson(program, args) {
|
|
|
5481
5532
|
return false;
|
|
5482
5533
|
}
|
|
5483
5534
|
function withImplicitHelp(program, args) {
|
|
5535
|
+
const { command, index, local } = resolveCommand(program, args);
|
|
5536
|
+
if (index < local.length)
|
|
5537
|
+
return args;
|
|
5538
|
+
return command.commands.length > 0 ? [...args, "--help"] : args;
|
|
5539
|
+
}
|
|
5540
|
+
function resolveCommand(program, args) {
|
|
5541
|
+
const local = [];
|
|
5542
|
+
for (let i = 0;i < args.length; i++) {
|
|
5543
|
+
const arg = args[i] ?? "";
|
|
5544
|
+
if (arg === "--") {
|
|
5545
|
+
local.push(...args.slice(i));
|
|
5546
|
+
break;
|
|
5547
|
+
}
|
|
5548
|
+
const global = program.options.find((o) => o.long === arg || o.short === arg);
|
|
5549
|
+
if (global) {
|
|
5550
|
+
if (global.required)
|
|
5551
|
+
i++;
|
|
5552
|
+
continue;
|
|
5553
|
+
}
|
|
5554
|
+
if (program.options.some((o) => o.long !== undefined && arg.startsWith(`${o.long}=`)))
|
|
5555
|
+
continue;
|
|
5556
|
+
local.push(arg);
|
|
5557
|
+
}
|
|
5484
5558
|
let command = program;
|
|
5485
|
-
|
|
5559
|
+
let index = 0;
|
|
5560
|
+
for (;index < local.length; index++) {
|
|
5561
|
+
const arg = local[index] ?? "";
|
|
5486
5562
|
const next = command.commands.find((sub) => sub.name() === arg || sub.aliases().includes(arg));
|
|
5487
5563
|
if (!next)
|
|
5488
|
-
|
|
5564
|
+
break;
|
|
5489
5565
|
command = next;
|
|
5490
5566
|
}
|
|
5491
|
-
return command
|
|
5567
|
+
return { command, index, local };
|
|
5492
5568
|
}
|
|
5493
5569
|
|
|
5494
5570
|
// src/index.ts
|
package/package.json
CHANGED
|
@@ -215,7 +215,7 @@ Use these only when the user asks to publish, change or remove *their* Skill on
|
|
|
215
215
|
SkillHub; finding and downloading Skills is always fine.
|
|
216
216
|
|
|
217
217
|
```bash
|
|
218
|
-
solgrid skills list -q "<words>" --json # search
|
|
218
|
+
solgrid skills list -q "<words>" --json # search by title
|
|
219
219
|
solgrid skills get -s <slug> --json # details
|
|
220
220
|
solgrid skills download -s <slug> -o <path> # the Skill package (.zip)
|
|
221
221
|
```
|
|
@@ -248,11 +248,17 @@ solgrid skills create -t "<title>" --kind <image|video|web|audio> \
|
|
|
248
248
|
--tag <tag-id> --yes --json
|
|
249
249
|
```
|
|
250
250
|
|
|
251
|
+
Tag names come in English. To show them to a user in Chinese, add the global
|
|
252
|
+
`--lang zh` (e.g. `solgrid skills tags --lang zh --json`); it also turns the
|
|
253
|
+
server's error messages into Chinese. Always pass tag **IDs** to `--tag`.
|
|
254
|
+
|
|
251
255
|
- A Skill is **public as soon as it is created**, and its slug can never be
|
|
252
256
|
changed. Confirm the title, slug (the `name` in `SKILL.md`) and files with
|
|
253
257
|
the user before passing `--yes`.
|
|
254
258
|
- `--kind` is what the Skill produces. Every kind except `video` needs
|
|
255
|
-
`--cover
|
|
259
|
+
`--cover` (an image). The `--preview` type follows the kind: an image for
|
|
260
|
+
`image` and `web` (optional), a video for `video`, an audio file for `audio`
|
|
261
|
+
(both required).
|
|
256
262
|
- `-d` is a short plain-text line shown under the title; the Skill's real
|
|
257
263
|
instructions belong in `SKILL.md`.
|
|
258
264
|
- If the CLI refuses the directory, it says which rule it breaks (for example
|