@cli-skill/cli 0.0.1-beta.20260402112811 → 0.0.2-beta.20260402145701

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
@@ -22,6 +22,7 @@
22
22
  ```bash
23
23
  cli-skill create <skillName> --cli-name <cliName> [--template <templateName>]
24
24
  cli-skill list
25
+ cli-skill tools <skillName>
25
26
  cli-skill install <skillName> [--packageName <packageName>]
26
27
  cli-skill uninstall <packageName>
27
28
  cli-skill config get [keyPath]
@@ -70,6 +71,13 @@ cli-skill mount
70
71
  - `src/skill/*`
71
72
  - 文档模板源目录
72
73
 
74
+ 模板生成出来的 skill 包会同时依赖:
75
+
76
+ - `@cli-skill/core`
77
+ - 运行 skill
78
+ - `@cli-skill/cli`
79
+ - 在项目目录内调用 `cli-skill build`、`cli-skill mount` 等命令
80
+
73
81
  执行 `cli-skill build` 后会生成:
74
82
 
75
83
  - `skill/SKILL.md`
@@ -127,3 +135,26 @@ cli-skill mount
127
135
  ```bash
128
136
  cli-skill install fx --packageName @scope/cli-skill-fx
129
137
  ```
138
+
139
+ ## skill bin 的行为
140
+
141
+ 生成出来的 skill 自带一个很薄的 bin。
142
+
143
+ 例如:
144
+
145
+ ```bash
146
+ my-skill list
147
+ my-skill open_page '{"url":"https://example.com"}'
148
+ ```
149
+
150
+ 它适合做:
151
+
152
+ - 查看这个 skill 的 tool 列表
153
+ - 直接执行某个 tool
154
+
155
+ 它不承担项目管理命令。以下操作仍然只通过 `cli-skill` 使用:
156
+
157
+ - `build`
158
+ - `mount`
159
+ - `unmount`
160
+ - `publish`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cli-skill/cli",
3
- "version": "0.0.1-beta.20260402112811",
3
+ "version": "0.0.2-beta.20260402145701",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -17,9 +17,9 @@
17
17
  "test": "tsc -p tsconfig.test.json && bun test ./test"
18
18
  },
19
19
  "dependencies": {
20
- "@cli-skill/core": "^0.0.1-beta.20260402112811",
20
+ "@cli-skill/core": "^0.0.2-beta.20260402145701",
21
21
  "cac": "^6.7.14",
22
22
  "lodash": "^4.17.21",
23
- "zod": "^3.25.76"
23
+ "zod": "^4.3.6"
24
24
  }
25
25
  }
package/skill/SKILL.md CHANGED
@@ -28,6 +28,7 @@ description: 当任务涉及 cli skill 的创建、挂载、安装、发布或
28
28
  - 平台命令
29
29
  - `cli-skill create`
30
30
  - `cli-skill list`
31
+ - `cli-skill tools <skill-name>`
31
32
  - `cli-skill install`
32
33
  - `cli-skill uninstall`
33
34
  - `cli-skill config ...`
@@ -40,7 +41,14 @@ description: 当任务涉及 cli skill 的创建、挂载、安装、发布或
40
41
  - 已注册 skill 执行命令
41
42
  - `cli-skill exec <skill-name> ...`
42
43
 
43
- skill 自己的 bin 只是一个转发入口,最终会落到 `cli-skill exec <skill-name> ...`。
44
+ skill 自己的 bin 只是一个转发入口:
45
+
46
+ - `skill-name list`
47
+ - 查看这个 skill 的 tools
48
+ - `skill-name <tool>`
49
+ - 执行这个 skill 的 tool
50
+
51
+ 项目级命令仍然通过 `cli-skill` 使用,不通过 skill bin 使用。
44
52
 
45
53
  ## 默认流程
46
54
 
@@ -84,6 +92,7 @@ cli-skill mount
84
92
  | --- | --- |
85
93
  | 创建 skill | `cli-skill create <skill-name> --cli-name <cli-name> [--template <templateName>]` |
86
94
  | 查看 skill 列表 | `cli-skill list` |
95
+ | 查看已注册 skill 的 tool 列表 | `cli-skill tools <skill-name>` |
87
96
  | 安装已发布 skill | `cli-skill install <skill-name>` |
88
97
  | 卸载已发布 skill | `cli-skill uninstall <package-name>` |
89
98
  | 查看 tool 列表 | `cli-skill tools` |
@@ -109,6 +118,10 @@ cli-skill mount
109
118
  - `mount` 不应隐式执行 `install`。
110
119
  - `install` / `uninstall` 面向已发布 skill。
111
120
  - `publish` 只针对当前目录的本地 skill。
121
+ - skill bin 只负责:
122
+ - `list`
123
+ - 直接执行 tool
124
+ - `config get/set/unset`
112
125
 
113
126
  默认目录:
114
127
 
@@ -156,4 +169,5 @@ cli-skill build
156
169
  - 不要把 `create` 当成“已经可执行”
157
170
  - 不要手动改 `~/.agents/skills`
158
171
  - 不要把 skill bin 当成独立平台
172
+ - 不要期待 skill bin 提供 `build` / `mount` / `publish`
159
173
  - 不要在修改已有 skill 时重新 `create`
@@ -67,6 +67,8 @@ cli-skill build
67
67
 
68
68
  也就是说,skill 只做“组织”和“说明”,不直接承担运行时能力注入。
69
69
 
70
+ 命令解析也不在 `core`。`core` 只负责类型、runtime、tool 执行。
71
+
70
72
  ## `defineTool` 负责什么
71
73
 
72
74
  `defineTool` 才是运行能力的核心。
@@ -211,6 +213,8 @@ export default defineSkill({
211
213
 
212
214
  这也是为什么生成出来的 skill 模板会把 `zod` 放在自己的 `dependencies` 里。
213
215
 
216
+ skill 项目默认只依赖 `@cli-skill/core`。平台 CLI 通过全局安装的 `cli-skill` 提供,不会再写进模板依赖里。
217
+
214
218
  ## 什么时候继续往下看
215
219
 
216
220
  如果你需要的是:
package/src/build.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { type ZodRawShape, type ZodTypeAny } from "zod";
3
+ import { type ZodTypeAny } from "zod";
4
4
  import type { SkillDefinition } from "@cli-skill/core";
5
5
 
6
6
  interface FieldInfo {
@@ -15,27 +15,39 @@ interface DocRow {
15
15
  notes: string;
16
16
  }
17
17
 
18
+ type ShapeLike = Record<string, ZodTypeAny>;
19
+
20
+ function getSchemaDef(schema: ZodTypeAny): Record<string, unknown> | undefined {
21
+ return (schema as ZodTypeAny & { _def?: Record<string, unknown>; def?: Record<string, unknown> })._def
22
+ ?? (schema as ZodTypeAny & { def?: Record<string, unknown> }).def;
23
+ }
24
+
18
25
  function unwrapField(schema: ZodTypeAny): FieldInfo {
19
26
  let current = schema;
20
27
  let optional = false;
21
28
  let defaultValue: unknown;
22
29
 
23
30
  while (true) {
24
- if (current?._def?.typeName === "ZodOptional") {
31
+ const def = getSchemaDef(current);
32
+ const typeName = def?.typeName;
33
+ const type = def?.type;
34
+
35
+ if ((typeName === "ZodOptional" || type === "optional") && def?.innerType) {
25
36
  optional = true;
26
- current = current._def.innerType;
37
+ current = def.innerType as ZodTypeAny;
27
38
  continue;
28
39
  }
29
40
 
30
- if (current?._def?.typeName === "ZodDefault") {
41
+ if ((typeName === "ZodDefault" || type === "default") && def?.innerType) {
31
42
  optional = true;
32
- defaultValue = current._def.defaultValue();
33
- current = current._def.innerType;
43
+ const rawDefault = def.defaultValue;
44
+ defaultValue = typeof rawDefault === "function" ? rawDefault() : rawDefault;
45
+ current = def.innerType as ZodTypeAny;
34
46
  continue;
35
47
  }
36
48
 
37
- if (current?._def?.typeName === "ZodNullable") {
38
- current = current._def.innerType;
49
+ if ((typeName === "ZodNullable" || type === "nullable") && def?.innerType) {
50
+ current = def.innerType as ZodTypeAny;
39
51
  continue;
40
52
  }
41
53
 
@@ -46,25 +58,30 @@ function unwrapField(schema: ZodTypeAny): FieldInfo {
46
58
  }
47
59
 
48
60
  function getTypeName(schema: ZodTypeAny): string | undefined {
49
- return schema?._def?.typeName;
61
+ const def = getSchemaDef(schema);
62
+ return (def?.typeName as string | undefined) ?? (def?.type as string | undefined);
50
63
  }
51
64
 
52
- function getObjectShape(schema: ZodTypeAny): ZodRawShape | null {
53
- if (getTypeName(schema) !== "ZodObject") {
65
+ function getObjectShape(schema: ZodTypeAny): ShapeLike | null {
66
+ const typeName = getTypeName(schema);
67
+ if (typeName !== "ZodObject" && typeName !== "object") {
54
68
  return null;
55
69
  }
56
70
 
57
71
  const objectSchema = schema as ZodTypeAny & {
58
- _def: { shape?: (() => ZodRawShape) | ZodRawShape };
59
- shape?: ZodRawShape;
72
+ _def?: { shape?: (() => ShapeLike) | ShapeLike };
73
+ def?: { shape?: (() => ShapeLike) | ShapeLike };
74
+ shape?: ShapeLike;
60
75
  };
76
+ const def = getSchemaDef(objectSchema);
77
+ const shapeValue = def?.shape as (() => ShapeLike) | ShapeLike | undefined;
61
78
 
62
- if (typeof objectSchema._def.shape === "function") {
63
- return objectSchema._def.shape();
79
+ if (typeof shapeValue === "function") {
80
+ return shapeValue();
64
81
  }
65
82
 
66
- if (objectSchema._def.shape) {
67
- return objectSchema._def.shape;
83
+ if (shapeValue) {
84
+ return shapeValue;
68
85
  }
69
86
 
70
87
  return objectSchema.shape ?? null;
@@ -87,21 +104,32 @@ function describeType(schema: ZodTypeAny): string {
87
104
  return "boolean";
88
105
  }
89
106
 
90
- if (typeName === "ZodLiteral") {
91
- return JSON.stringify(base._def.value);
107
+ const def = getSchemaDef(base);
108
+
109
+ if (typeName === "ZodLiteral" || typeName === "literal") {
110
+ const values = def?.values;
111
+ if (Array.isArray(values) && values.length === 1) {
112
+ return JSON.stringify(values[0]);
113
+ }
114
+
115
+ return JSON.stringify(def?.value);
92
116
  }
93
117
 
94
- if (typeName === "ZodEnum") {
95
- return (base as ZodTypeAny & { options: string[] }).options
96
- .map((item: string) => JSON.stringify(item))
118
+ if (typeName === "ZodEnum" || typeName === "enum") {
119
+ const entries = (def?.entries as Record<string, string> | undefined)
120
+ ?? ((base as ZodTypeAny & { options?: string[] }).options
121
+ ? Object.fromEntries(((base as ZodTypeAny & { options: string[] }).options).map((item) => [item, item]))
122
+ : undefined);
123
+ return Object.values(entries ?? {})
124
+ .map((item) => JSON.stringify(item))
97
125
  .join(" | ");
98
126
  }
99
127
 
100
- if (typeName === "ZodArray") {
101
- return `array<${describeType(base._def.type)}>`;
128
+ if (typeName === "ZodArray" || typeName === "array") {
129
+ return `array<${describeType((def?.type ?? def?.element) as ZodTypeAny)}>`;
102
130
  }
103
131
 
104
- if (typeName === "ZodObject") {
132
+ if (typeName === "ZodObject" || typeName === "object") {
105
133
  return "object";
106
134
  }
107
135
 
@@ -123,7 +151,7 @@ function describeNotes(schema: ZodTypeAny): string {
123
151
  return notes.join("; ");
124
152
  }
125
153
 
126
- function collectShapeRows(shape: ZodRawShape, prefix = ""): DocRow[] {
154
+ function collectShapeRows(shape: ShapeLike, prefix = ""): DocRow[] {
127
155
  const rows: DocRow[] = [];
128
156
 
129
157
  for (const [key, schema] of Object.entries(shape)) {
@@ -180,7 +208,7 @@ function renderTable(headers: string[], rows: string[][]): string {
180
208
  }
181
209
 
182
210
  function renderConfigSection(skill: SkillDefinition): string {
183
- const rows = collectShapeRows(skill.config).map((row) => [row.path, row.type, row.notes || ""]);
211
+ const rows = collectShapeRows(skill.config as ShapeLike).map((row) => [row.path, row.type, row.notes || ""]);
184
212
  return renderTable(["字段", "类型", "说明"], rows.length > 0 ? rows : [["-", "-", "-"]]);
185
213
  }
186
214
 
@@ -1,5 +1,5 @@
1
1
  import type { CAC } from "cac";
2
- import { runCli } from "@cli-skill/core";
2
+ import { listTools, runTool } from "@cli-skill/core";
3
3
  import { writeSkillDocsMarkdown } from "../build";
4
4
  import { runBunStreaming } from "../bun";
5
5
  import { loadSkillDefinition } from "../project";
@@ -16,16 +16,14 @@ export function registerSkillCommands(cli: CAC): void {
16
16
  .action(async (skillName: string, toolName: string, rawInput?: string) => {
17
17
  const resolved = await resolveRegisteredSkillProject(skillName);
18
18
  const skill = await loadSkillDefinition(resolved.projectPath);
19
- const exitCode = await runCli(skill, ["run", toolName, ...(typeof rawInput === "string" ? [rawInput] : [])], {
20
- rootDir: resolved.projectPath,
21
- });
19
+ const exitCode = await runTool(skill, toolName, rawInput, { rootDir: resolved.projectPath });
22
20
  process.exitCode = exitCode;
23
21
  });
24
22
 
25
- cli.command("tools", "List tools in the current cli skill project").action(async () => {
26
- const resolved = await getCurrentSkillProject();
23
+ cli.command("tools [skillName]", "List tools in the current cli skill project or a registered cli skill").action(async (skillName?: string) => {
24
+ const resolved = skillName ? await resolveRegisteredSkillProject(skillName) : await getCurrentSkillProject();
27
25
  const skill = await loadSkillDefinition(resolved.projectPath);
28
- const exitCode = await runCli(skill, ["list"], { rootDir: resolved.projectPath });
26
+ const exitCode = await listTools(skill, { rootDir: resolved.projectPath });
29
27
  process.exitCode = exitCode;
30
28
  });
31
29
 
@@ -34,9 +32,7 @@ export function registerSkillCommands(cli: CAC): void {
34
32
  .action(async (toolName: string, rawInput?: string) => {
35
33
  const resolved = await getCurrentSkillProject();
36
34
  const skill = await loadSkillDefinition(resolved.projectPath);
37
- const exitCode = await runCli(skill, ["run", toolName, ...(typeof rawInput === "string" ? [rawInput] : [])], {
38
- rootDir: resolved.projectPath,
39
- });
35
+ const exitCode = await runTool(skill, toolName, rawInput, { rootDir: resolved.projectPath });
40
36
  process.exitCode = exitCode;
41
37
  });
42
38
 
@@ -3,6 +3,16 @@ import { rm } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { resolveRegisteredSkillProject, removeRegistryEntry, unregisterProjectBins } from "../registry";
5
5
 
6
+ function getInstalledSkillRoot(projectPath: string): string {
7
+ const segments = projectPath.split(path.sep);
8
+ const nodeModulesIndex = segments.lastIndexOf("node_modules");
9
+ if (nodeModulesIndex === -1) {
10
+ return projectPath;
11
+ }
12
+
13
+ return segments.slice(0, nodeModulesIndex).join(path.sep) || path.sep;
14
+ }
15
+
6
16
  export function registerUninstallCommand(cli: CAC): void {
7
17
  cli
8
18
  .command("uninstall <packageName>", "Uninstall a managed cli skill")
@@ -13,7 +23,9 @@ export function registerUninstallCommand(cli: CAC): void {
13
23
  for (const agentPath of resolved.agentPaths) {
14
24
  await rm(agentPath, { recursive: true, force: true });
15
25
  }
16
- await rm(path.join(resolved.projectPath, "..", ".."), { recursive: true, force: true });
26
+ if (resolved.source === "installed") {
27
+ await rm(getInstalledSkillRoot(resolved.projectPath), { recursive: true, force: true });
28
+ }
17
29
  await removeRegistryEntry(skillName);
18
30
  console.log(resolved.projectPath);
19
31
  });