@koishi-ce/plugin-help 1.0.0

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.
@@ -0,0 +1,22 @@
1
+ commands:
2
+ help:
3
+ description: Show help
4
+ shortcuts:
5
+ help: <></>
6
+ options:
7
+ help: show this message
8
+ authority: show authority requirements
9
+ showHidden: show hidden options and commands
10
+ messages:
11
+ not-found: Command not found.
12
+ hint-authority: this minimum authority is marked in parentheses
13
+ hint-subcommand: those marked with an asterisk have subcommands
14
+ command-title: 'Command: {0}'
15
+ command-aliases: 'Aliases: {0}.'
16
+ command-examples: 'Examples:'
17
+ command-authority: 'Minimal authority: {0}.'
18
+ subcommand-prolog: 'Available subcommands{0}:'
19
+ global-prolog: 'Available commands{0}:'
20
+ global-epilog: Type "{0}help <command>" to see syntax and examples for a specific command.
21
+ available-options: 'Available options:'
22
+ available-options-with-authority: 'Available options (parentheses indicate additional authority requirement):'
@@ -0,0 +1,22 @@
1
+ commands:
2
+ help:
3
+ description: 显示帮助信息
4
+ shortcuts:
5
+ help: 帮助
6
+ options:
7
+ help: 显示此信息
8
+ authority: 显示权限设置
9
+ showHidden: 查看隐藏的选项和指令
10
+ messages:
11
+ not-found: 指令未找到。
12
+ hint-authority: 括号内为对应的最低权限等级
13
+ hint-subcommand: 标有星号的表示含有子指令
14
+ command-title: 指令:{0}
15
+ command-aliases: 别名:{0}。
16
+ command-examples: 使用示例:
17
+ command-authority: 最低权限:{0} 级。
18
+ subcommand-prolog: 可用的子指令有{0}:
19
+ global-prolog: 当前可用的指令有{0}:
20
+ global-epilog: 输入“{0}help 指令名”查看特定指令的语法和使用示例。
21
+ available-options: 可用的选项有:
22
+ available-options-with-authority: 可用的选项有(括号内为额外要求的权限等级):
package/lib/index.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ import { Command, Computed, Context, Schema, Session } from "@koishi-ce/koishi";
2
+ //#region src/index.d.ts
3
+ declare module "@koishi-ce/koishi" {
4
+ interface Events {
5
+ "help/command"(output: string[], command: Command, session: Session<never, never>): void;
6
+ "help/option"(output: string, option: Argv.OptionVariant, command: Command, session: Session<never, never>): string;
7
+ }
8
+ namespace Command {
9
+ interface Config {
10
+ /** 默认隐藏所有选项 */
11
+ hideOptions?: boolean;
12
+ /** 在帮助中隐藏此指令 */
13
+ hidden?: Computed<boolean>;
14
+ /** 本地化参数 */
15
+ params?: object;
16
+ }
17
+ }
18
+ namespace Argv {
19
+ interface OptionConfig<T extends Argv.Type = Argv.Type> {
20
+ /** 在帮助中隐藏此选项 */
21
+ hidden?: Computed<boolean>;
22
+ /** 本地化参数 */
23
+ params?: object;
24
+ }
25
+ }
26
+ }
27
+ /** 配置项 */
28
+ interface Config {
29
+ /** 是否启用“帮助”快捷调用 */
30
+ shortcut?: boolean;
31
+ /** 是否为每个指令注入 `-h, --help` 选项 */
32
+ options?: boolean;
33
+ }
34
+ declare const Config: Schema<Config>;
35
+ declare const name = "help";
36
+ declare function apply(ctx: Context, config: Config): void;
37
+ //#endregion
38
+ export { Config, apply, name };
package/lib/index.mjs ADDED
@@ -0,0 +1,240 @@
1
+ import { Context, Schema, h } from "@koishi-ce/koishi";
2
+ import enUS from "./assets/en-US-f1RJaaO6.yml";
3
+ import zhCN from "./assets/zh-CN-DmsdBxOO.yml";
4
+ //#region src/index.ts
5
+ /**
6
+ * 帮助指令插件(help)。
7
+ *
8
+ * 提供 `help [command]` 指令(权限 0)与全局快捷调用“帮助”,
9
+ * 并默认为所有指令注入 `-h, --help` 选项;输出指令的描述、别名、
10
+ * 用法、选项、示例与子指令列表,支持按权限与 hidden 配置过滤。
11
+ * 其他插件可通过 `help/command`、`help/option` 事件改写帮助输出,
12
+ * 或通过指令 / 选项的 hidden、hideOptions、params 配置定制展示。
13
+ * 配置项:shortcut(启用快捷调用)、options(注入 -h 选项)。
14
+ */
15
+ const Config = Schema.object({
16
+ shortcut: Schema.boolean().default(true).description("是否启用快捷调用。"),
17
+ options: Schema.boolean().default(true).description("是否为每个指令添加 `-h, --help` 选项。")
18
+ });
19
+ /** 在当前会话中转执行 help 指令(供 -h 选项与无 action 的指令复用) */
20
+ function executeHelp(session, name) {
21
+ if (!session.app.$commander.get("help")) return;
22
+ return session.execute({
23
+ name: "help",
24
+ args: [name]
25
+ });
26
+ }
27
+ const name = "help";
28
+ function apply(ctx, config) {
29
+ ctx.i18n.define("zh-CN", zhCN);
30
+ ctx.i18n.define("en-US", enUS);
31
+ function enableHelp(command) {
32
+ command[Context.current] = ctx;
33
+ command.option("help", "-h", {
34
+ hidden: true,
35
+ notUsage: true,
36
+ descPath: "commands.help.options.help"
37
+ });
38
+ }
39
+ ctx.schema.extend("command", Schema.object({
40
+ hideOptions: Schema.boolean().description("是否隐藏所有选项。").default(false).hidden(),
41
+ hidden: Schema.computed(Schema.boolean()).description("在帮助菜单中隐藏指令。").default(false),
42
+ params: Schema.any().description("帮助信息的本地化参数。").hidden()
43
+ }), 900);
44
+ ctx.schema.extend("command-option", Schema.object({
45
+ hidden: Schema.computed(Schema.boolean()).description("在帮助菜单中隐藏选项。").default(false),
46
+ params: Schema.any().description("帮助信息的本地化参数。").hidden()
47
+ }), 900);
48
+ if (config.options !== false) {
49
+ ctx.$commander._commandList.forEach(enableHelp);
50
+ ctx.on("command-added", enableHelp);
51
+ }
52
+ ctx.before("command/execute", (argv) => {
53
+ const { command, options, session } = argv;
54
+ if (!command || !session || !options) return;
55
+ if (options["help"] && command._options["help"]) return executeHelp(session, command.name);
56
+ if (command["_actions"].length) return;
57
+ return executeHelp(session, command.name);
58
+ });
59
+ const $ = ctx.$commander;
60
+ /**
61
+ * 按名称解析目标指令;未命中时再按 i18n 快捷调用匹配
62
+ * @param target 用户输入的指令名或快捷调用文本
63
+ * @returns 指令对象;仅有模糊命中时返回候选列表
64
+ */
65
+ function findCommand(target, session) {
66
+ const command = $.resolve(target, session);
67
+ if (command?.ctx.filter(session)) return command;
68
+ const data = ctx.i18n.find("commands.(name).shortcuts.(variant)", target).map((item) => ({
69
+ ...item,
70
+ command: $.resolve(item.data.name, session)
71
+ })).filter((item) => item.command?.match(session));
72
+ const perfect = data.filter((item) => item.similarity === 1);
73
+ if (!perfect.length) return data;
74
+ return perfect[0]?.command;
75
+ }
76
+ const createCollector = (key) => (argv, fields) => {
77
+ const { args, session } = argv;
78
+ const [target] = args ?? [];
79
+ if (!session) return;
80
+ const result = findCommand(target, session);
81
+ if (!Array.isArray(result)) {
82
+ if (result) session.collect(key, {
83
+ ...argv,
84
+ command: result,
85
+ args: [],
86
+ options: { help: true }
87
+ }, fields);
88
+ return;
89
+ }
90
+ for (const { command } of result) {
91
+ if (!command) continue;
92
+ session.collect(key, {
93
+ ...argv,
94
+ command,
95
+ args: [],
96
+ options: { help: true }
97
+ }, fields);
98
+ }
99
+ };
100
+ /** 推断用户输入对应的指令;仅有模糊命中时发起相似度建议(“您要找的是不是…”) */
101
+ async function inferCommand(target, session) {
102
+ const result = findCommand(target, session);
103
+ if (!Array.isArray(result)) return result;
104
+ const expect = $.available(session).filter((name) => {
105
+ return name && session.app.i18n.compare(name, target);
106
+ });
107
+ for (const item of result) {
108
+ if (expect.includes(item.data.name)) continue;
109
+ expect.push(item.data.name);
110
+ }
111
+ const cache = /* @__PURE__ */ new Map();
112
+ const name = await session.suggest({
113
+ expect,
114
+ prefix: session.text(".not-found"),
115
+ suffix: session.text("internal.suggest-command"),
116
+ filter: (name) => {
117
+ const command = $.resolve(name, session);
118
+ if (!command) return false;
119
+ return ctx.permissions.test(`command:${command.name}`, session, cache);
120
+ }
121
+ });
122
+ if (!name) return;
123
+ return $.resolve(name, session);
124
+ }
125
+ const cmd = ctx.command("help [command:string]", {
126
+ authority: 0,
127
+ ...config
128
+ }).userFields(["authority"]).userFields(createCollector("user")).channelFields(createCollector("channel")).option("showHidden", "-H").action(async ({ session, options }, target) => {
129
+ if (!session || !options) return;
130
+ if (!target) {
131
+ const prefix = session.resolve(session.app.koishi.config.prefix)?.[0] ?? "";
132
+ const output = await formatCommands(".global-prolog", session, $._commandList.filter((cmd) => cmd.parent === null), options);
133
+ const epilog = session.text(".global-epilog", [prefix]);
134
+ if (epilog) output.push(epilog);
135
+ return output.filter(Boolean).join("\n");
136
+ }
137
+ const command = await inferCommand(target, session);
138
+ if (!command) return;
139
+ if (!await ctx.permissions.test(`command:${command.name}`, session)) return session.text("internal.low-authority");
140
+ return showHelp(command, session, options);
141
+ });
142
+ if (config.shortcut !== false) cmd.shortcut("help", {
143
+ i18n: true,
144
+ fuzzy: true
145
+ });
146
+ }
147
+ /** 深度优先遍历指令树,产出当前会话可见(未被 hidden 过滤)的指令 */
148
+ function* getCommands(session, commands, showHidden = false) {
149
+ for (const command of commands) {
150
+ if (!showHidden && session.resolve(command.config.hidden)) continue;
151
+ if (command.match(session) && Object.keys(command._aliases).length) yield command;
152
+ else yield* getCommands(session, command.children, showHidden);
153
+ }
154
+ }
155
+ /** 将一组指令格式化为帮助列表(标题行 + 每条指令一行的缩进展示) */
156
+ async function formatCommands(path, session, children, options) {
157
+ const cache = /* @__PURE__ */ new Map();
158
+ children = Array.from(getCommands(session, children, options.showHidden));
159
+ children = (await Promise.all(children.map(async (command) => {
160
+ return [command, await session.app.permissions.test(`command:${command.name}`, session, cache)];
161
+ }))).filter(([, result]) => result).map(([command]) => command);
162
+ children.sort((a, b) => a.displayName > b.displayName ? 1 : -1);
163
+ if (!children.length) return [];
164
+ const prefix = session.resolve(session.app.koishi.config.prefix)?.[0] ?? "";
165
+ const output = children.map(({ name, displayName, config }) => {
166
+ let output = ` ${prefix}${displayName.replace(/\./g, " ")}`;
167
+ output += ` ${session.text([`commands.${name}.description`, ""], config.params)}`;
168
+ return output;
169
+ });
170
+ const hints = [];
171
+ const hintText = hints.length ? session.text("general.paren", [hints.join(session.text("general.comma"))]) : "";
172
+ output.unshift(session.text(path, [hintText]));
173
+ return output;
174
+ }
175
+ /** 判断选项对当前会话是否可见(权限不足或被 hidden 标记隐藏时不可见) */
176
+ function getOptionVisibility(option, session) {
177
+ if (session.user && (option.authority ?? 0) > session.user.authority) return false;
178
+ return !session.resolve(option.hidden);
179
+ }
180
+ /** 生成指令的选项帮助段落(考虑 hideOptions、权限与 hidden 过滤) */
181
+ function getOptions(command, session, config) {
182
+ if (command.config.hideOptions && !config.showHidden) return [];
183
+ if (!(config.showHidden ? Object.values(command._options) : Object.values(command._options).filter((option) => getOptionVisibility(option, session))).length) return [];
184
+ const output = [];
185
+ Object.values(command._options).forEach((option) => {
186
+ function pushOption(option, name) {
187
+ if (!config.showHidden && !getOptionVisibility(option, session)) return;
188
+ let line = `${h.escape(option.syntax)}`;
189
+ const description = session.text(option.descPath ?? [`commands.${command.name}.options.${name}`, ""], option.params);
190
+ if (description) line += ` ${description}`;
191
+ line = command.ctx.chain("help/option", line, option, command, session);
192
+ output.push(` ${line}`);
193
+ }
194
+ if (!("value" in option)) pushOption(option, option.name ?? "");
195
+ for (const value in option.variants) {
196
+ const variant = option.variants[value];
197
+ if (!variant) continue;
198
+ pushOption(variant, `${option.name}.${value}`);
199
+ }
200
+ });
201
+ if (!output.length) return [];
202
+ output.unshift(session.text(".available-options"));
203
+ return output;
204
+ }
205
+ /** 生成单个指令的完整帮助文本(标题、描述、别名、用法、选项、示例、子指令) */
206
+ async function showHelp(command, session, config) {
207
+ const output = [session.text(".command-title", [command.displayName.replace(/\./g, " ") + command.declaration])];
208
+ const description = session.text([`commands.${command.name}.description`, ""], command.config.params);
209
+ if (description) output.push(description);
210
+ if (session.app.database) {
211
+ const argv = {
212
+ command,
213
+ args: [],
214
+ options: { help: true }
215
+ };
216
+ const userFields = session.collect("user", argv);
217
+ await session.observeUser(userFields);
218
+ if (!session.isDirect) {
219
+ const channelFields = session.collect("channel", argv);
220
+ await session.observeChannel(channelFields);
221
+ }
222
+ }
223
+ if (Object.keys(command._aliases).length > 1) output.push(session.text(".command-aliases", [Array.from(Object.keys(command._aliases).slice(1)).join(",")]));
224
+ session.app.emit(session, "help/command", output, command, session);
225
+ if (command._usage) output.push(typeof command._usage === "string" ? command._usage : await command._usage(session));
226
+ else {
227
+ const text = session.text([`commands.${command.name}.usage`, ""], command.config.params);
228
+ if (text) output.push(text);
229
+ }
230
+ output.push(...getOptions(command, session, config));
231
+ if (command._examples.length) output.push(session.text(".command-examples"), ...command._examples.map((example) => ` ${example}`));
232
+ else {
233
+ const text = session.text([`commands.${command.name}.examples`, ""], command.config.params);
234
+ if (text) output.push(session.text(".command-examples"), ...text.split("\n").map((line) => ` ${line}`));
235
+ }
236
+ output.push(...await formatCommands(".subcommand-prolog", session, command.children, config));
237
+ return output.filter(Boolean).join("\n");
238
+ }
239
+ //#endregion
240
+ export { Config, apply, name };
@@ -0,0 +1,22 @@
1
+ commands:
2
+ help:
3
+ description: 显示帮助信息
4
+ shortcuts:
5
+ help: Hilfe
6
+ options:
7
+ help: 显示此信息
8
+ authority: 显示权限设置
9
+ showHidden: 查看隐藏的选项和指令
10
+ messages:
11
+ not-found: 指令未找到。
12
+ hint-authority: 括号内为对应的最低权限等级
13
+ hint-subcommand: 标有星号的表示含有子指令
14
+ command-title: 指令:{0}
15
+ command-aliases: 别名:{0}。
16
+ command-examples: 使用示例:
17
+ command-authority: 最低权限:{0} 级。
18
+ subcommand-prolog: 可用的子指令有{0}:
19
+ global-prolog: 当前可用的指令有{0}:
20
+ global-epilog: 输入“{0}help 指令名”查看特定指令的语法和使用示例。
21
+ available-options: 可用的选项有:
22
+ available-options-with-authority: 可用的选项有(括号内为额外要求的权限等级):
@@ -0,0 +1,22 @@
1
+ commands:
2
+ help:
3
+ description: Show help
4
+ shortcuts:
5
+ help: <></>
6
+ options:
7
+ help: show this message
8
+ authority: show authority requirements
9
+ showHidden: show hidden options and commands
10
+ messages:
11
+ not-found: Command not found.
12
+ hint-authority: this minimum authority is marked in parentheses
13
+ hint-subcommand: those marked with an asterisk have subcommands
14
+ command-title: 'Command: {0}'
15
+ command-aliases: 'Aliases: {0}.'
16
+ command-examples: 'Examples:'
17
+ command-authority: 'Minimal authority: {0}.'
18
+ subcommand-prolog: 'Available subcommands{0}:'
19
+ global-prolog: 'Available commands{0}:'
20
+ global-epilog: Type "{0}help <command>" to see syntax and examples for a specific command.
21
+ available-options: 'Available options:'
22
+ available-options-with-authority: 'Available options (parentheses indicate additional authority requirement):'
@@ -0,0 +1,22 @@
1
+ commands:
2
+ help:
3
+ description: Afficher l'aide
4
+ shortcuts:
5
+ help: Aide
6
+ options:
7
+ help: afficher cette aide
8
+ authority: afficher les droits nécessaires
9
+ showHidden: afficher les options et commandes cachées
10
+ messages:
11
+ not-found: Commande inconnue.
12
+ hint-authority: les droits minimums sont indiqués entre parenthèses
13
+ hint-subcommand: ces commandes avec un astérisque ont des sous-commandes
14
+ command-title: 'Commande : {0}'
15
+ command-aliases: 'Alias : {0}.'
16
+ command-examples: 'Exemples :'
17
+ command-authority: 'Droit minimum : {0}.'
18
+ subcommand-prolog: 'Commandes disponibles{0} :'
19
+ global-prolog: 'Commandes disponibles{0} :'
20
+ global-epilog: Tapez "{0}help <commande>" pour voir la syntaxe et les exemples pour une commande spécifique.
21
+ available-options: 'Options disponibles :'
22
+ available-options-with-authority: 'Options disponibles (les parenthèses indiquent un droit supplémentaire) :'
@@ -0,0 +1,22 @@
1
+ commands:
2
+ help:
3
+ description: ヘルプを表示
4
+ shortcuts:
5
+ help: ヘルプ
6
+ options:
7
+ help: このメッセージを表示
8
+ authority: コマンドやオプションの権限を表示
9
+ showHidden: 隠しコマンドやオプションを表示
10
+ messages:
11
+ not-found: コマンドが見つかりません。
12
+ hint-authority: 最低権限は括弧にで表示されています
13
+ hint-subcommand: サブコマンドがあるコマンドはアスタリスクで表示されています
14
+ command-title: コマンド:{0}
15
+ command-aliases: 別名:{0}。
16
+ command-examples: 例:
17
+ command-authority: 最低権限:{0}。
18
+ subcommand-prolog: 利用可能なサブコマンド{0}:
19
+ global-prolog: 利用可能なコマンド{0}:
20
+ global-epilog: '「{0}help <command>」を送信してコマンドの使い方を表示します。'
21
+ available-options: 利用可能なオプション:
22
+ available-options-with-authority: 利用可能なオプション(必要な権限は括弧にで表示されています):
@@ -0,0 +1,22 @@
1
+ commands:
2
+ help:
3
+ description: 显示帮助信息
4
+ shortcuts:
5
+ help: Справка
6
+ options:
7
+ help: 显示此信息
8
+ authority: 显示权限设置
9
+ showHidden: 查看隐藏的选项和指令
10
+ messages:
11
+ not-found: 指令未找到。
12
+ hint-authority: 括号内为对应的最低权限等级
13
+ hint-subcommand: 标有星号的表示含有子指令
14
+ command-title: 指令:{0}
15
+ command-aliases: 别名:{0}。
16
+ command-examples: 使用示例:
17
+ command-authority: 最低权限:{0} 级。
18
+ subcommand-prolog: 可用的子指令有{0}:
19
+ global-prolog: 当前可用的指令有{0}:
20
+ global-epilog: 输入“{0}help 指令名”查看特定指令的语法和使用示例。
21
+ available-options: 可用的选项有:
22
+ available-options-with-authority: 可用的选项有(括号内为额外要求的权限等级):
@@ -0,0 +1,22 @@
1
+ commands:
2
+ help:
3
+ description: 显示帮助信息
4
+ shortcuts:
5
+ help: 帮助
6
+ options:
7
+ help: 显示此信息
8
+ authority: 显示权限设置
9
+ showHidden: 查看隐藏的选项和指令
10
+ messages:
11
+ not-found: 指令未找到。
12
+ hint-authority: 括号内为对应的最低权限等级
13
+ hint-subcommand: 标有星号的表示含有子指令
14
+ command-title: 指令:{0}
15
+ command-aliases: 别名:{0}。
16
+ command-examples: 使用示例:
17
+ command-authority: 最低权限:{0} 级。
18
+ subcommand-prolog: 可用的子指令有{0}:
19
+ global-prolog: 当前可用的指令有{0}:
20
+ global-epilog: 输入“{0}help 指令名”查看特定指令的语法和使用示例。
21
+ available-options: 可用的选项有:
22
+ available-options-with-authority: 可用的选项有(括号内为额外要求的权限等级):
@@ -0,0 +1,22 @@
1
+ commands:
2
+ help:
3
+ description: 顯示幫助信息
4
+ shortcuts:
5
+ help: 幫助
6
+ options:
7
+ help: 顯示此信息
8
+ authority: 顯示權限設定
9
+ showHidden: 查看隱藏的選項和指令
10
+ messages:
11
+ not-found: 指令未找到。
12
+ hint-authority: 括號內為對應的最低權限等級
13
+ hint-subcommand: 標有星號的表示含有子指令
14
+ command-title: 指令:{0}
15
+ command-aliases: 別名:{0}。
16
+ command-examples: 使用示例:
17
+ command-authority: 最低權限:{0} 級。
18
+ subcommand-prolog: 可用的子指令有{0}:
19
+ global-prolog: 當前可用的指令有{0}:
20
+ global-epilog: 輸入“{0}help 指令名”查看特定指令的語法和使用示例。
21
+ available-options: 可用的選項有:
22
+ available-options-with-authority: 可用的選項有(括號內為額外要求的權限等級):
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@koishi-ce/plugin-help",
3
+ "description": "Help plugin for Koishi",
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "main": "lib/index.mjs",
7
+ "typings": "lib/index.d.ts",
8
+ "files": [
9
+ "lib",
10
+ "src",
11
+ "locales"
12
+ ],
13
+ "contributors": [
14
+ "Shigma <shigma10826@gmail.com>",
15
+ "Oppenheymu <oppenheymu@gmail.com>"
16
+ ],
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/Koishi-CE/koishi.git",
21
+ "directory": "plugins/common/help"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/Koishi-CE/koishi/issues"
25
+ },
26
+ "homepage": "https://koishi.chat/plugins/common/help.html",
27
+ "keywords": [
28
+ "bot",
29
+ "chatbot",
30
+ "koishi",
31
+ "plugin",
32
+ "help"
33
+ ],
34
+ "koishi": {
35
+ "browser": true,
36
+ "category": "tool",
37
+ "description": {
38
+ "en": "Show help for commands",
39
+ "zh": "显示指令的帮助信息"
40
+ },
41
+ "locales": [
42
+ "zh",
43
+ "en",
44
+ "ja",
45
+ "fr",
46
+ "zh-TW"
47
+ ]
48
+ },
49
+ "peerDependencies": {
50
+ "koishi": "^4.18.11"
51
+ },
52
+ "devDependencies": {
53
+ "@koishi-ce/plugin-mock": "^1.0.0",
54
+ "@minatojs/driver-memory": "^3.7.0",
55
+ "@koishi-ce/koishi": "^1.0.0",
56
+ "minato": "^3.7.0"
57
+ },
58
+ "exports": {
59
+ ".": {
60
+ "source": "./src/index.ts",
61
+ "types": "./lib/index.d.ts",
62
+ "import": "./lib/index.mjs",
63
+ "default": "./lib/index.mjs"
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,214 @@
1
+ /**
2
+ * help 插件测试:覆盖帮助列表、指令属性(别名 / 用法 / 示例 / 权限)、
3
+ * 选项展示、子指令、无数据库场景与 shortcut / options 配置开关。
4
+ */
5
+ import { beforeAll, describe, it } from "bun:test";
6
+ import { App } from "@koishi-ce/koishi";
7
+ import * as help from "@koishi-ce/plugin-help";
8
+ import mock from "@koishi-ce/plugin-mock";
9
+ import memory from "@minatojs/driver-memory";
10
+
11
+ const app = new App({
12
+ minSimilarity: 0.64,
13
+ });
14
+
15
+ app.plugin(mock);
16
+ app.plugin(help);
17
+ app.plugin(memory);
18
+
19
+ app.i18n.define("$zh-CN", "commands.help.messages.global-epilog", "EPILOG");
20
+
21
+ const client = app.mock.client("123", "456");
22
+
23
+ beforeAll(async () => {
24
+ await app.start();
25
+ await app.mock.initUser("123", 2);
26
+ await app.mock.initChannel("456");
27
+ });
28
+
29
+ let message: string;
30
+
31
+ describe("@koishi-ce/plugin-help", () => {
32
+ // 验证全局帮助列表、“帮助”快捷调用、-h 选项与相似度建议(“您要找的是不是…”)
33
+ it("basic support", async () => {
34
+ await client.shouldReply(
35
+ "help",
36
+ (message = [
37
+ "当前可用的指令有:",
38
+ " help 显示帮助信息",
39
+ "EPILOG",
40
+ ].join("\n")),
41
+ );
42
+
43
+ // 全局快捷调用
44
+ await client.shouldReply("帮助", message);
45
+
46
+ await client.shouldReply(
47
+ "help help",
48
+ (message = [
49
+ "指令:help [command]",
50
+ "显示帮助信息",
51
+ "可用的选项有:",
52
+ " -H, --show-hidden 查看隐藏的选项和指令",
53
+ ].join("\n")),
54
+ );
55
+
56
+ await client.shouldReply("help xxxx", "指令未找到。");
57
+ await client.shouldReply(
58
+ "help heip",
59
+ "指令未找到。您要找的是不是“help”?回复句号以使用推测的指令。",
60
+ );
61
+ await client.shouldReply(".", message);
62
+ await client.shouldReply("help -h", message);
63
+ await client.shouldReply("help 帮助", message);
64
+ });
65
+
66
+ // 验证 description / 别名 / usage / example / authority 等指令属性在帮助中的呈现
67
+ it("command attributes", async () => {
68
+ app.command("foo1", "DESCRIPTION").alias("foo");
69
+ app.command("foo3", "DESCRIPTION").shortcut(/foobar/);
70
+ app.command("foo4", "DESCRIPTION").usage("USAGE TEXT");
71
+ app.command("foo5", "DESCRIPTION").usage(({ userId }) => `${userId}`);
72
+ app.command("foo6", "DESCRIPTION").example("EXAMPLE TEXT");
73
+ app.command("foo7", "DESCRIPTION", { authority: 3 });
74
+
75
+ await client.shouldReply(
76
+ "help foo1",
77
+ "指令:foo1\nDESCRIPTION\n别名:foo。",
78
+ );
79
+ await client.shouldReply("help foobar", "指令:foo3\nDESCRIPTION");
80
+ await client.shouldReply(
81
+ "help foo4",
82
+ "指令:foo4\nDESCRIPTION\nUSAGE TEXT",
83
+ );
84
+ await client.shouldReply("help foo5", "指令:foo5\nDESCRIPTION\n123");
85
+ await client.shouldReply(
86
+ "help foo6",
87
+ "指令:foo6\nDESCRIPTION\n使用示例:\n EXAMPLE TEXT",
88
+ );
89
+ await client.shouldReply("help foo7", "权限不足。");
90
+ });
91
+
92
+ // 验证 hideOptions、选项权限与 hidden 选项的过滤,以及 -H 的全量展示
93
+ it("command options", async () => {
94
+ const bar = app
95
+ .command("bar <arg:number>", "DESCRIPTION", { hideOptions: true })
96
+ .option("opt1", "选项1", { authority: 2 })
97
+ .option("opt1", "-n 选项2", { value: false })
98
+ .option("opt2", "[arg:boolean] 选项3")
99
+ .option("opt3", "-o [arg:boolean]", { hidden: true });
100
+
101
+ await client.shouldReply(
102
+ "help bar",
103
+ (message = "指令:bar <arg>\nDESCRIPTION"),
104
+ );
105
+
106
+ bar.config.hideOptions = false;
107
+
108
+ await client.shouldReply(
109
+ "help bar",
110
+ [
111
+ message,
112
+ "可用的选项有:",
113
+ " --opt1 选项1",
114
+ " -n 选项2",
115
+ " --opt2 [arg] 选项3",
116
+ ].join("\n"),
117
+ );
118
+
119
+ await client.shouldReply(
120
+ "help bar -H",
121
+ [
122
+ message,
123
+ "可用的选项有:",
124
+ " -h, --help 显示此信息",
125
+ " --opt1 选项1",
126
+ " -n 选项2",
127
+ " --opt2 [arg] 选项3",
128
+ " -o, --opt3 [arg]",
129
+ ].join("\n"),
130
+ );
131
+ });
132
+
133
+ // 验证多级子指令在父指令帮助中的逐层呈现
134
+ it("subcommand", async () => {
135
+ const foo2 = app.command("foo2", "DESCRIPTION", { authority: 0 });
136
+ const foo1 = foo2.subcommand("foo1");
137
+ foo1.subcommand("foo3");
138
+
139
+ await client.shouldReply(
140
+ "help foo2",
141
+ [
142
+ "指令:foo2",
143
+ "DESCRIPTION",
144
+ "可用的子指令有:",
145
+ " foo1 DESCRIPTION",
146
+ ].join("\n"),
147
+ );
148
+
149
+ await client.shouldReply(
150
+ "help foo1",
151
+ [
152
+ "指令:foo1",
153
+ "DESCRIPTION",
154
+ "别名:foo。",
155
+ "可用的子指令有:",
156
+ " foo3 DESCRIPTION",
157
+ ].join("\n"),
158
+ );
159
+ });
160
+
161
+ // 无数据库环境下 help 仍能正常列出指令
162
+ it("no database", async () => {
163
+ const app = new App();
164
+ app.plugin(help);
165
+ app.plugin(mock);
166
+ app.i18n.define("$zh-CN", "commands.help.messages.global-epilog", "");
167
+ await app.start();
168
+
169
+ const client = app.mock.client("123");
170
+ await client.shouldReply(
171
+ "help",
172
+ "当前可用的指令有:\n help 显示帮助信息",
173
+ );
174
+ });
175
+
176
+ // options: false 时不为指令注入 -h 选项
177
+ it("disable help options", async () => {
178
+ const app = new App();
179
+ app.plugin(help, { options: false });
180
+ app.plugin(mock);
181
+ app.command("foo").action(() => {});
182
+ await app.start();
183
+
184
+ const client = app.mock.client("123");
185
+ await client.shouldReply("help");
186
+ await client.shouldNotReply("foo -h");
187
+ });
188
+
189
+ // shortcut: false 时不注册“帮助”全局快捷调用
190
+ it("disable help shortcut", async () => {
191
+ const app = new App();
192
+ app.plugin(help, { shortcut: false });
193
+ app.plugin(mock);
194
+ await app.start();
195
+
196
+ const client = app.mock.client("123");
197
+ await client.shouldReply("help");
198
+ await client.shouldNotReply("帮助");
199
+ });
200
+
201
+ // 带 checkArgCount 的指令在缺参追问后仍可用 -h 查看帮助(回归 #769)
202
+ it("checkArgCount (#769)", async () => {
203
+ const app = new App();
204
+ app.plugin(help);
205
+ app.plugin(mock);
206
+ app.command("test <arg>", { checkArgCount: true }).action(() => "pass");
207
+ await app.start();
208
+
209
+ const client = app.mock.client("123");
210
+ await client.shouldReply("test", "请发送arg。");
211
+ await client.shouldReply("foo", "pass");
212
+ await client.shouldReply("test -h", "指令:test <arg>");
213
+ });
214
+ });
package/src/index.ts ADDED
@@ -0,0 +1,468 @@
1
+ /**
2
+ * 帮助指令插件(help)。
3
+ *
4
+ * 提供 `help [command]` 指令(权限 0)与全局快捷调用“帮助”,
5
+ * 并默认为所有指令注入 `-h, --help` 选项;输出指令的描述、别名、
6
+ * 用法、选项、示例与子指令列表,支持按权限与 hidden 配置过滤。
7
+ * 其他插件可通过 `help/command`、`help/option` 事件改写帮助输出,
8
+ * 或通过指令 / 选项的 hidden、hideOptions、params 配置定制展示。
9
+ * 配置项:shortcut(启用快捷调用)、options(注入 -h 选项)。
10
+ */
11
+ import {
12
+ type Argv,
13
+ type Command,
14
+ type Computed,
15
+ Context,
16
+ type FieldCollector,
17
+ h,
18
+ Schema,
19
+ type Session,
20
+ } from "@koishi-ce/koishi";
21
+ import enUS from "../locales/en-US.yml";
22
+ import zhCN from "../locales/zh-CN.yml";
23
+
24
+ declare module "@koishi-ce/koishi" {
25
+ interface Events {
26
+ "help/command"(
27
+ output: string[],
28
+ command: Command,
29
+ session: Session<never, never>,
30
+ ): void;
31
+ "help/option"(
32
+ output: string,
33
+ option: Argv.OptionVariant,
34
+ command: Command,
35
+ session: Session<never, never>,
36
+ ): string;
37
+ }
38
+
39
+ namespace Command {
40
+ interface Config {
41
+ /** 默认隐藏所有选项 */
42
+ hideOptions?: boolean;
43
+ /** 在帮助中隐藏此指令 */
44
+ hidden?: Computed<boolean>;
45
+ /** 本地化参数 */
46
+ params?: object;
47
+ }
48
+ }
49
+
50
+ namespace Argv {
51
+ interface OptionConfig<T extends Argv.Type = Argv.Type> {
52
+ /** 在帮助中隐藏此选项 */
53
+ hidden?: Computed<boolean>;
54
+ /** 本地化参数 */
55
+ params?: object;
56
+ }
57
+ }
58
+ }
59
+
60
+ /** 帮助输出的行为选项 */
61
+ interface HelpOptions {
62
+ /** 显示被 hidden 标记隐藏的指令与选项(对应 -H 选项) */
63
+ showHidden?: boolean;
64
+ }
65
+
66
+ /** 配置项 */
67
+ export interface Config {
68
+ /** 是否启用“帮助”快捷调用 */
69
+ shortcut?: boolean;
70
+ /** 是否为每个指令注入 `-h, --help` 选项 */
71
+ options?: boolean;
72
+ }
73
+
74
+ export const Config: Schema<Config> = Schema.object({
75
+ shortcut: Schema.boolean().default(true).description("是否启用快捷调用。"),
76
+ options: Schema.boolean()
77
+ .default(true)
78
+ .description("是否为每个指令添加 `-h, --help` 选项。"),
79
+ });
80
+
81
+ /** 在当前会话中转执行 help 指令(供 -h 选项与无 action 的指令复用) */
82
+ function executeHelp(session: Session<never, never>, name: string) {
83
+ if (!session.app.$commander.get("help")) return;
84
+ return session.execute({
85
+ name: "help",
86
+ args: [name],
87
+ });
88
+ }
89
+
90
+ export const name = "help";
91
+
92
+ export function apply(ctx: Context, config: Config) {
93
+ ctx.i18n.define("zh-CN", zhCN);
94
+ ctx.i18n.define("en-US", enUS);
95
+
96
+ // 为指令注入隐藏的 -h, --help 选项(不展示、不计入用法)
97
+ function enableHelp(command: Command) {
98
+ command[Context.current] = ctx;
99
+ command.option("help", "-h", {
100
+ hidden: true,
101
+ // @ts-expect-error
102
+ notUsage: true,
103
+ descPath: "commands.help.options.help",
104
+ });
105
+ }
106
+
107
+ ctx.schema.extend(
108
+ "command",
109
+ Schema.object({
110
+ hideOptions: Schema.boolean()
111
+ .description("是否隐藏所有选项。")
112
+ .default(false)
113
+ .hidden(),
114
+ hidden: Schema.computed(Schema.boolean())
115
+ .description("在帮助菜单中隐藏指令。")
116
+ .default(false),
117
+ params: Schema.any().description("帮助信息的本地化参数。").hidden(),
118
+ }),
119
+ 900,
120
+ );
121
+
122
+ ctx.schema.extend(
123
+ "command-option",
124
+ Schema.object({
125
+ hidden: Schema.computed(Schema.boolean())
126
+ .description("在帮助菜单中隐藏选项。")
127
+ .default(false),
128
+ params: Schema.any().description("帮助信息的本地化参数。").hidden(),
129
+ }),
130
+ 900,
131
+ );
132
+
133
+ if (config.options !== false) {
134
+ // 已注册的指令立即注入,之后新增的指令通过事件注入
135
+ ctx.$commander._commandList.forEach(enableHelp);
136
+ ctx.on("command-added", enableHelp);
137
+ }
138
+
139
+ // 指令执行前的拦截:带 -h 或指令本身没有 action 时,转而输出帮助
140
+ ctx.before(
141
+ "command/execute",
142
+ (argv: Argv<never, never, unknown[], { help?: boolean }>) => {
143
+ const { command, options, session } = argv;
144
+ if (!command || !session || !options) return;
145
+ if (options["help"] && command._options["help"]) {
146
+ return executeHelp(session, command.name);
147
+ }
148
+
149
+ if (command["_actions"].length) return;
150
+ return executeHelp(session, command.name);
151
+ },
152
+ );
153
+
154
+ const $ = ctx.$commander;
155
+
156
+ /**
157
+ * 按名称解析目标指令;未命中时再按 i18n 快捷调用匹配
158
+ * @param target 用户输入的指令名或快捷调用文本
159
+ * @returns 指令对象;仅有模糊命中时返回候选列表
160
+ */
161
+ function findCommand(target: string, session: Session<never, never>) {
162
+ const command = $.resolve(target, session);
163
+ if (command?.ctx.filter(session)) return command;
164
+
165
+ // 指令名未命中:转为在各语言的指令快捷调用文本中检索
166
+ const data = ctx.i18n
167
+ .find("commands.(name).shortcuts.(variant)", target)
168
+ .map((item) => ({ ...item, command: $.resolve(item.data.name, session) }))
169
+ .filter((item) => item.command?.match(session));
170
+ const perfect = data.filter((item) => item.similarity === 1);
171
+ if (!perfect.length) return data;
172
+ return perfect[0]?.command;
173
+ }
174
+
175
+ // 字段收集器:help 指令自身只用 authority,
176
+ // 但被查询的目标指令可能声明了额外的 user / channel 观察字段
177
+ const createCollector =
178
+ <T extends "user" | "channel">(key: T): FieldCollector<T> =>
179
+ (argv, fields) => {
180
+ const { args, session } = argv;
181
+ const [target] = args ?? [];
182
+ if (!session) return;
183
+ // target 是消息中的指令名;FieldCollector 擦除后 args 为 unknown[]
184
+ const result = findCommand(target as string, session);
185
+ if (!Array.isArray(result)) {
186
+ if (result) {
187
+ session.collect(
188
+ key,
189
+ { ...argv, command: result, args: [], options: { help: true } },
190
+ fields,
191
+ );
192
+ }
193
+ return;
194
+ }
195
+ for (const { command } of result) {
196
+ if (!command) continue;
197
+ session.collect(
198
+ key,
199
+ { ...argv, command, args: [], options: { help: true } },
200
+ fields,
201
+ );
202
+ }
203
+ };
204
+
205
+ /** 推断用户输入对应的指令;仅有模糊命中时发起相似度建议(“您要找的是不是…”) */
206
+ async function inferCommand(target: string, session: Session) {
207
+ const result = findCommand(target, session);
208
+ if (!Array.isArray(result)) return result;
209
+
210
+ // 候选 = 当前会话可见的相似指令名 + 快捷调用命中的指令名
211
+ const expect = $.available(session).filter((name) => {
212
+ return name && session.app.i18n.compare(name, target);
213
+ });
214
+ for (const item of result) {
215
+ if (expect.includes(item.data.name)) continue;
216
+ expect.push(item.data.name);
217
+ }
218
+ const cache = new Map<string, Promise<boolean>>();
219
+ const name = await session.suggest({
220
+ expect,
221
+ prefix: session.text(".not-found"),
222
+ suffix: session.text("internal.suggest-command"),
223
+ filter: (name) => {
224
+ const command = $.resolve(name, session);
225
+ if (!command) return false;
226
+ return ctx.permissions.test(`command:${command.name}`, session, cache);
227
+ },
228
+ });
229
+ if (!name) return;
230
+ return $.resolve(name, session);
231
+ }
232
+
233
+ // 主指令:无参数时列出全局指令清单,带参数时输出目标指令的详细帮助
234
+ const cmd = ctx
235
+ .command("help [command:string]", { authority: 0, ...config })
236
+ .userFields(["authority"])
237
+ .userFields(createCollector("user"))
238
+ .channelFields(createCollector("channel"))
239
+ .option("showHidden", "-H")
240
+ .action(async ({ session, options }, target) => {
241
+ if (!session || !options) return;
242
+ if (!target) {
243
+ const prefix =
244
+ session.resolve(session.app.koishi.config.prefix)?.[0] ?? "";
245
+ const commands = $._commandList.filter((cmd) => cmd.parent === null);
246
+ const output = await formatCommands(
247
+ ".global-prolog",
248
+ session,
249
+ commands,
250
+ options as HelpOptions,
251
+ );
252
+ const epilog = session.text(".global-epilog", [prefix]);
253
+ if (epilog) output.push(epilog);
254
+ return output.filter(Boolean).join("\n");
255
+ }
256
+
257
+ const command = await inferCommand(target, session);
258
+ if (!command) return;
259
+ if (!(await ctx.permissions.test(`command:${command.name}`, session))) {
260
+ return session.text("internal.low-authority");
261
+ }
262
+ return showHelp(command, session, options as HelpOptions);
263
+ });
264
+
265
+ // 注册全局快捷调用“帮助”(具体文本由各语言的 i18n 文本提供)
266
+ if (config.shortcut !== false)
267
+ cmd.shortcut("help", { i18n: true, fuzzy: true });
268
+ }
269
+
270
+ /** 深度优先遍历指令树,产出当前会话可见(未被 hidden 过滤)的指令 */
271
+ function* getCommands(
272
+ session: Session<"authority">,
273
+ commands: Command[],
274
+ showHidden = false,
275
+ ): Generator<Command> {
276
+ for (const command of commands) {
277
+ if (!showHidden && session.resolve(command.config.hidden)) continue;
278
+ // 自身可用则产出,否则下钻子指令(子指令可能单独可用)
279
+ if (command.match(session) && Object.keys(command._aliases).length) {
280
+ yield command;
281
+ } else {
282
+ yield* getCommands(session, command.children, showHidden);
283
+ }
284
+ }
285
+ }
286
+
287
+ /** 将一组指令格式化为帮助列表(标题行 + 每条指令一行的缩进展示) */
288
+ async function formatCommands(
289
+ path: string,
290
+ session: Session<"authority">,
291
+ children: Command[],
292
+ options: HelpOptions,
293
+ ) {
294
+ const cache = new Map<string, Promise<boolean>>();
295
+ // 第一步:按可见性过滤
296
+ children = Array.from(getCommands(session, children, options.showHidden));
297
+ // 第二步:按权限过滤(并行检测并缓存结果)
298
+ children = (
299
+ await Promise.all(
300
+ children.map(async (command) => {
301
+ return [
302
+ command,
303
+ await session.app.permissions.test(
304
+ `command:${command.name}`,
305
+ session,
306
+ cache,
307
+ ),
308
+ ] as const;
309
+ }),
310
+ )
311
+ )
312
+ .filter(([, result]) => result)
313
+ .map(([command]) => command);
314
+ // 第三步:按显示名排序
315
+ children.sort((a, b) => (a.displayName > b.displayName ? 1 : -1));
316
+ if (!children.length) return [];
317
+
318
+ const prefix = session.resolve(session.app.koishi.config.prefix)?.[0] ?? "";
319
+ const output = children.map(({ name, displayName, config }) => {
320
+ let output = ` ${prefix}${displayName.replace(/\./g, " ")}`;
321
+ output += ` ${session.text([`commands.${name}.description`, ""], config.params)}`;
322
+ return output;
323
+ });
324
+ const hints: string[] = [];
325
+ const hintText = hints.length
326
+ ? session.text("general.paren", [hints.join(session.text("general.comma"))])
327
+ : "";
328
+ output.unshift(session.text(path, [hintText]));
329
+ return output;
330
+ }
331
+
332
+ /** 判断选项对当前会话是否可见(权限不足或被 hidden 标记隐藏时不可见) */
333
+ function getOptionVisibility(
334
+ option: Argv.OptionConfig,
335
+ session: Session<"authority">,
336
+ ) {
337
+ if (session.user && (option.authority ?? 0) > session.user.authority) {
338
+ return false;
339
+ }
340
+ return !session.resolve(option.hidden);
341
+ }
342
+
343
+ /** 生成指令的选项帮助段落(考虑 hideOptions、权限与 hidden 过滤) */
344
+ function getOptions(
345
+ command: Command,
346
+ session: Session<"authority">,
347
+ config: HelpOptions,
348
+ ) {
349
+ if (command.config.hideOptions && !config.showHidden) return [];
350
+ const options = config.showHidden
351
+ ? Object.values(command._options)
352
+ : Object.values(command._options).filter((option) =>
353
+ getOptionVisibility(option, session),
354
+ );
355
+ if (!options.length) return [];
356
+
357
+ const output: string[] = [];
358
+ Object.values(command._options).forEach((option) => {
359
+ function pushOption(option: Argv.OptionVariant, name: string) {
360
+ if (!config.showHidden && !getOptionVisibility(option, session)) return;
361
+ let line = `${h.escape(option.syntax)}`;
362
+ const description = session.text(
363
+ option.descPath ?? [`commands.${command.name}.options.${name}`, ""],
364
+ option.params,
365
+ );
366
+ if (description) line += ` ${description}`;
367
+ line = command.ctx.chain("help/option", line, option, command, session);
368
+ output.push(` ${line}`);
369
+ }
370
+
371
+ // 无值选项直接输出;带值选项再逐个输出其语法变体
372
+ if (!("value" in option)) pushOption(option, option.name ?? "");
373
+ for (const value in option.variants) {
374
+ const variant = option.variants[value];
375
+ if (!variant) continue;
376
+ pushOption(variant, `${option.name}.${value}`);
377
+ }
378
+ });
379
+
380
+ if (!output.length) return [];
381
+ output.unshift(session.text(".available-options"));
382
+ return output;
383
+ }
384
+
385
+ /** 生成单个指令的完整帮助文本(标题、描述、别名、用法、选项、示例、子指令) */
386
+ async function showHelp(
387
+ command: Command,
388
+ session: Session<"authority">,
389
+ config: HelpOptions,
390
+ ) {
391
+ const output = [
392
+ session.text(".command-title", [
393
+ command.displayName.replace(/\./g, " ") + command.declaration,
394
+ ]),
395
+ ];
396
+
397
+ const description = session.text(
398
+ [`commands.${command.name}.description`, ""],
399
+ command.config.params,
400
+ );
401
+ if (description) output.push(description);
402
+
403
+ // 有数据库时按目标指令的声明预取 user / channel 字段(usage 等钩子可能用到)
404
+ if (session.app.database) {
405
+ const argv: Argv = { command, args: [], options: { help: true } };
406
+ const userFields = session.collect("user", argv);
407
+ await session.observeUser(userFields);
408
+ if (!session.isDirect) {
409
+ const channelFields = session.collect("channel", argv);
410
+ await session.observeChannel(channelFields);
411
+ }
412
+ }
413
+
414
+ if (Object.keys(command._aliases).length > 1) {
415
+ output.push(
416
+ session.text(".command-aliases", [
417
+ Array.from(Object.keys(command._aliases).slice(1)).join(","),
418
+ ]),
419
+ );
420
+ }
421
+
422
+ session.app.emit(session, "help/command", output, command, session);
423
+
424
+ if (command._usage) {
425
+ output.push(
426
+ typeof command._usage === "string"
427
+ ? command._usage
428
+ : // _usage 存储为擦除签名(见 core 的 CommandDefinition),此处还原实参
429
+ await command._usage(session as never),
430
+ );
431
+ } else {
432
+ const text = session.text(
433
+ [`commands.${command.name}.usage`, ""],
434
+ command.config.params,
435
+ );
436
+ if (text) output.push(text);
437
+ }
438
+
439
+ output.push(...getOptions(command, session, config));
440
+
441
+ if (command._examples.length) {
442
+ output.push(
443
+ session.text(".command-examples"),
444
+ ...command._examples.map((example) => ` ${example}`),
445
+ );
446
+ } else {
447
+ const text = session.text(
448
+ [`commands.${command.name}.examples`, ""],
449
+ command.config.params,
450
+ );
451
+ if (text)
452
+ output.push(
453
+ session.text(".command-examples"),
454
+ ...text.split("\n").map((line) => ` ${line}`),
455
+ );
456
+ }
457
+
458
+ output.push(
459
+ ...(await formatCommands(
460
+ ".subcommand-prolog",
461
+ session,
462
+ command.children,
463
+ config,
464
+ )),
465
+ );
466
+
467
+ return output.filter(Boolean).join("\n");
468
+ }