[Feature] Enhance command tool with improved normalization and blacklist support#599
Conversation
Summary of ChangesHello @dingyi222666, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly upgrades the command tool system by introducing robust command name normalization, including extensive Chinese-to-English translation, and a new configurable blacklist feature to control command exposure. It also refines tool schema descriptions for better AI model understanding and includes minor bug fixes and dependency updates, making the command system more flexible and intelligent. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (26)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including You can disable this status message by setting the Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. Walkthrough在配置中新增公有字段 Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Config as Config (含 commandBlacklist)
participant CmdPlugin as command plugin
participant Registry as Command Registry
Note over User,Config: 启动或加载配置
User->>Config: 读取配置
Config-->>CmdPlugin: 提供 commandList 与 commandBlacklist
Note over CmdPlugin,Registry: 获取原始命令
CmdPlugin->>Registry: 请求 rawCommandList
Registry-->>CmdPlugin: 返回 rawCommandList
Note over CmdPlugin: 新增过滤与规范化流程
CmdPlugin->>CmdPlugin: 根据 commandBlacklist 进行过滤(含子命令/前缀)
CmdPlugin->>CmdPlugin: normalizeCommandName(中英映射、alnum、UUID fallback)
CmdPlugin->>CmdPlugin: getDescription(zh-CN -> '' -> en-US -> fallback)
CmdPlugin-->>User: 返回过滤并规范化后的命令列表
代码审查难度评估🎯 4(Complex) | ⏱️ ~45 分钟 理由:变更涉及公共配置、插件函数签名和类型扩展、命令过滤与规范化逻辑、以及多语言描述处理等多方面,需要检查边界条件、黑名单匹配(子命令/前缀)、以及规范化与唯一性 fallback 的正确性。 可能相关的 PR
诗句
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces significant enhancements to the command tool system. The addition of a command blacklist and improved command name normalization, especially with Chinese-to-English mapping, are great features for better control and compatibility. The refactoring to include argument and option descriptions directly in the Zod schema is a solid improvement for AI model interaction.
I've identified a potential issue with the new command normalization logic that could lead to name collisions, and I've suggested a fix. I also found a small area of code duplication in the schema generation that could be refactored for better maintainability.
Overall, these are valuable additions that improve the robustness and usability of the command tool. Great work!
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (6)
packages/extension-tools/src/config.ts (1)
163-174: Schema 默认值合适,建议加 description 并在 UI 标明“前缀匹配”。当前仅标注了
role('table')与默认列表。建议添加描述,明确“命中精确名或以该前缀开头的子命令(a.b)也会被过滤”。可按下述方式补充描述与示例:
- commandBlacklist: Schema.array(Schema.string()) - .role('table') + commandBlacklist: Schema.array(Schema.string()) + .role('table') + .description('要屏蔽的命令名或前缀(命中后其所有子命令一并屏蔽,例如 "command" 也会屏蔽 "command.xxx")') .default([ 'command', 'channel', 'inspect', 'plugin', 'user', 'usage' ])packages/extension-tools/src/plugins/command.ts (5)
35-35: 规范化后的工具名可能碰撞,建议加去重后缀。多个不同命令(中英映射/清洗后)可能归并到同一
normalizedName,注册的toolId会被覆盖。建议在冲突时为尾部追加短哈希(基于原始 name)。
184-193: Zod 类型可强化:数值/整数使用 coerce 与 int,布尔使用 coerce.boolean。这样能更稳健地从字符串入参解析为目标类型,并约束整数/正整数场景。
可参考:
- const zodType = this.getZodType(arg.type) + const zodType = this.getZodType(arg.type) ... - schemaShape[arg.name] = arg.required + schemaShape[arg.name] = arg.required ? zodTypeWithDescription : zodTypeWithDescription.optional()并在 getZodType 中(文件内现有函数):
- private getZodType(type: string): z.ZodTypeAny { + private getZodType(type: string): z.ZodTypeAny { switch (type) { case 'text': case 'string': case 'date': - return z.string() + return z.string() case 'integer': case 'posint': case 'natural': case 'number': - return z.number() + return type === 'integer' || type === 'posint' || type === 'natural' + ? z.coerce.number().int().min(type === 'natural' ? 0 : 1) + : z.coerce.number() case 'boolean': - return z.boolean() + return z.coerce.boolean() default: return z.string() } }(如不希望引入 coerce,可保持现状。)
Also applies to: 194-203, 206-209
531-533: 动态正则来自变量键,存在 ReDoS 风险与不必要开销。键为常量映射时无需正则,建议用 split/join 或 replaceAll。
基于静态分析提示(regexp-from-variable)。
可改为:- for (const [chinese, english] of Object.entries(chineseToEnglish)) { - result = result.replace(new RegExp(chinese, 'g'), english) - } + for (const [chinese, english] of Object.entries(chineseToEnglish)) { + result = result.split(chinese).join(english) + }
536-540: 建议统一小写,避免同名不同大小写导致重复。清洗后追加
toLowerCase(),减少工具名冲突面。- result = result.replace(/[^a-zA-Z0-9.]/g, '') + result = result.replace(/[^a-zA-Z0-9.]/g, '').toLowerCase()
27-31: 黑名单生效与名称规范化的行为请补测。建议覆盖:
- 黑名单前缀命中与子命令级联过滤;
- 名称全中文/仅符号/数字开头的回退前缀;
- 多命令映射为同一规范名时的冲突处理(期望告警或自动去重)。
需要我补一组最小复现的单元测试样例吗?
Also applies to: 358-550
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (7)
package.jsonis excluded by!**/*.jsonpackages/adapter-gemini/package.jsonis excluded by!**/*.jsonpackages/core/package.jsonis excluded by!**/*.jsonpackages/extension-mcp/package.jsonis excluded by!**/*.jsonpackages/extension-tools/package.jsonis excluded by!**/*.jsonpackages/extension-tools/src/locales/en-US.schema.ymlis excluded by!**/*.ymlpackages/extension-tools/src/locales/zh-CN.schema.ymlis excluded by!**/*.yml
📒 Files selected for processing (2)
packages/extension-tools/src/config.ts(2 hunks)packages/extension-tools/src/plugins/command.ts(4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/extension-tools/src/plugins/command.ts (1)
packages/extension-tools/src/config.ts (3)
Config(4-50)Config(52-258)name(265-265)
🪛 ast-grep (0.39.6)
packages/extension-tools/src/plugins/command.ts
[warning] 531-531: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(chinese, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: lint
- GitHub Check: build
🔇 Additional comments (5)
packages/extension-tools/src/plugins/command.ts (4)
27-31: 将黑名单透传到 getCommandList:方向正确。入口处将
config.commandBlacklist传入,符合新配置设计。无阻塞问题。
74-75: 提示文案简化 OK。单命令提示精简为一行,直观明了。
82-88: 描述回退顺序合理,涵盖 zh-CN/空键/en-US。多语言兜底逻辑明确。
92-94: 签名扩展良好,默认空数组安全。
blacklist默认[]可避免调用方遗漏参数导致异常。packages/extension-tools/src/config.ts (1)
23-23:commandBlacklist已在 i18n 中包含完整说明,无需进一步改动已确认
packages/extension-tools/src/locales/中en-US.schema.yml和zh-CN.schema.yml都包含了该字段的详细描述:
- 中文: 屏蔽的一级指令列表(包括所有子指令)。默认屏蔽敏感的管理类指令。
- 英文: Blacklist of top-level commands to block (including all sub-commands). Sensitive management commands are blocked by default.
描述已说明支持子命令一并屏蔽的功能,原建议已得到实现。
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (4)
packages/extension-tools/src/plugins/command.ts (4)
165-204: 去重封装:参数与选项的 Zod 构建逻辑重复。可抽为一个小的辅助函数,减少重复与分支。
- this.command.arguments.forEach((arg) => { - const zodType = this.getZodType(arg.type) - const description = getDescription(arg.description) - const zodTypeWithDescription = zodType.describe(description) - - schemaShape[arg.name] = arg.required - ? zodTypeWithDescription - : zodTypeWithDescription.optional() - }) + const addPropertyToSchema = (prop: { + name: string + type: string + description: string | Record<string, string> + required: boolean + }) => { + if (prop.name === 'help') return + const zodType = this.getZodType(prop.type) + const desc = getDescription(prop.description) + const zType = zodType.describe(desc) + schemaShape[prop.name] = prop.required ? zType : zType.optional() + } + + this.command.arguments.forEach(addPropertyToSchema)- this.command.options.forEach((opt) => { - if (opt.name !== 'help') { - const zodType = this.getZodType(opt.type) - const description = getDescription(opt.description) - const zodTypeWithDescription = zodType.describe(description) - - schemaShape[opt.name] = opt.required - ? zodTypeWithDescription - : zodTypeWithDescription.optional() - } - }) + this.command.options.forEach(addPropertyToSchema)
17-17: 移除对crypto.randomUUID()的依赖,改用现有randomString。统一依赖与运行时兼容性,避免在非 Node 环境或打包场景下额外引入
crypto。- import { randomUUID } from 'crypto'- (result || - randomUUID() - .substring(0, 12) - .replace(/[^a-zA-Z0-9]/g, '')) + (result || randomString(12))Also applies to: 540-547
98-116: 避免误杀:includes('chatluna')过滤过宽,应仅匹配根命令或其子命令。当前写法会误过滤任意包含子串的命令名,属功能性问题。改为严格等于或前缀匹配。
建议修改:
- // Filter out chatluna commands - if (item.name.includes('chatluna')) { + // Filter out chatluna root and its sub-commands + if (item.name === 'chatluna' || item.name.startsWith('chatluna.')) { return false }
536-538: 用下划线替代非法字符,降低命名碰撞风险。直接去除会把
user-list与userlist归一到同名,可能导致工具注册被覆盖。- // Remove all non-alphanumeric characters except dots (for command hierarchy) - result = result.replace(/[^a-zA-Z0-9.]/g, '') + // Replace invalid chars with underscore to preserve boundaries + result = result + .replace(/[^a-zA-Z0-9.]+/g, '_') + .replace(/^_+|_+$/g, '')
🧹 Nitpick comments (4)
packages/extension-tools/src/plugins/command.ts (4)
133-141: 避免把 JSON 串写入描述:改用 getDescription 生成自然语言。
JSON.stringify(item.description)会把多语言对象直接序列化进提示词,影响可读性。- if ( - (rawCommand.description?.length ?? 0) < 1 && - item.description - ) { - description = JSON.stringify(item.description) - } + if ( + (rawCommand.description?.length ?? 0) < 1 && + item.description + ) { + // 统一转为可读字符串描述 + description = getDescription( + item.description as unknown as Record<string, string> | string + ) + }
531-534: 用纯字符串替换替代动态正则,规避 ReDoS 及不必要的开销。
new RegExp(chinese, 'g')来自变量,虽源自常量表,但可直接用 split/join 或 replaceAll。- for (const [chinese, english] of Object.entries(chineseToEnglish)) { - result = result.replace(new RegExp(chinese, 'g'), english) - } + for (const [chinese, english] of Object.entries(chineseToEnglish)) { + if (result.includes(chinese)) { + // 纯字符串替换,避免动态正则 + result = result.split(chinese).join(english) + } + }(依据静态分析提示)
34-71: 为规范化后的工具名增加去重保护,避免覆盖注册。即使用下划线替换,仍可能存在重名;建议在注册时做去重处理。
- for (const command of commandList) { + const usedNames = new Set<string>() + for (const command of commandList) { const prompt = generateSingleCommandPrompt(command) - const normalizedName = normalizeCommandName(command.name) + let normalizedName = normalizeCommandName(command.name) + while (usedNames.has(normalizedName)) { + normalizedName = `${normalizedName}_${randomString(4)}` + } + usedNames.add(normalizedName)
74-76: 提示语可加入命令名,提升可读性(可选)。- return `Tool Description: ${command.description || 'No description'}\n\n` + return `Tool: ${command.name}\nDescription: ${command.description || 'No description'}\n\n`
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/extension-tools/src/plugins/command.ts(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/extension-tools/src/plugins/command.ts (1)
packages/extension-tools/src/config.ts (3)
Config(4-50)Config(52-258)name(265-265)
🪛 ast-grep (0.39.6)
packages/extension-tools/src/plugins/command.ts
[warning] 532-532: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(chinese, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build
- GitHub Check: lint
🔇 Additional comments (3)
packages/extension-tools/src/plugins/command.ts (3)
28-32: 黑名单参数接入正确,逻辑清晰。将
commandBlacklist透传到getCommandList并统一过滤来源,设计合理。
207-211: 无参兜底输入设计合理。提供
input可选字符串作为兜底,提升可用性。
83-89: 多语言描述回退顺序合理。优先
zh-CN,再空键,后en-US,最后兜底。
…on and blacklist support - Refactor normalizeCommandName to translate Chinese/non-English characters to English equivalents - Remove underscore/punctuation from normalized command names, use alphanumeric characters only - Add commandBlacklist config to block top-level commands and their sub-commands - Default blacklist includes sensitive commands: command, channel, inspect, plugin, user, usage - Simplify tool prompt generation to only include description - Add parameter descriptions to Zod schema for better LLM understanding - Update i18n files (zh-CN, en-US) with blacklist configuration translations
Update koishi-plugin-chatluna-storage-service peer dependency from v0.0.10 to v0.0.11 across multiple packages to ensure compatibility with the latest storage service features and fixes. Affected packages: - packages/core - packages/adapter-gemini - packages/extension-mcp - packages/extension-tools
Replace crypto.randomUUID() with named import randomUUID() for better tree-shaking and explicit dependency declaration.
2167f4d to
2572b65
Compare
- Update peerDependencies to koishi-plugin-chatluna ^1.3.0-alpha.68 across all packages - Bump adapter-dify version from 1.3.0-alpha.11 to 1.3.0-alpha.12 - Bump adapter-gemini version from 1.3.0-alpha.19 to 1.3.0-alpha.20 - Standardize package.json formatting (2-space indentation) This ensures all packages are compatible with the latest chatluna core version.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (4)
packages/extension-tools/src/plugins/command.ts (4)
185-205: 参数和选项的模式生成逻辑存在重复。可以将重复的 Zod 类型生成逻辑提取为辅助函数以提高可维护性。
参考重构方案:
const addPropertyToSchema = (prop: { name: string; type: string; description: string | Record<string, string>; required: boolean }) => { if (prop.name === 'help') return; const zodType = this.getZodType(prop.type) const description = getDescription(prop.description) const zodTypeWithDescription = zodType.describe(description) schemaShape[prop.name] = prop.required ? zodTypeWithDescription : zodTypeWithDescription.optional() } this.command.arguments.forEach(addPropertyToSchema) this.command.options.forEach(addPropertyToSchema)
539-547: 建议使用现有的 randomString 函数。使用
crypto.randomUUID()引入了额外依赖。建议使用文件中已有的randomString辅助函数,保持代码一致性。应用此修改:
if (result.length === 0 || /^[0-9]/.test(result)) { result = 'cmd' + - (result || - randomUUID() - .substring(0, 12) - .replace(/[^a-zA-Z0-9]/g, '')) + (result || randomString(12)) }
98-103: chatluna 过滤条件仍然过宽。
includes('chatluna')会误匹配任何包含该子串的命令。建议改为精确匹配根命令或前缀匹配子命令。应用此修复:
- if (item.name.includes('chatluna')) { + if (item.name === 'chatluna' || item.name.startsWith('chatluna.')) { return false }
536-537: 命令名称规范化可能导致冲突。移除所有非字母数字字符(点号除外)可能导致不同命令规范化为相同名称,例如
user-list和userlist都会变成userlist。建议使用下划线替换无效字符以保持唯一性:
- result = result.replace(/[^a-zA-Z0-9.]/g, '') + result = result.replace(/[^a-zA-Z0-9.]+/g, '_')
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (7)
package.jsonis excluded by!**/*.jsonpackages/adapter-gemini/package.jsonis excluded by!**/*.jsonpackages/core/package.jsonis excluded by!**/*.jsonpackages/extension-mcp/package.jsonis excluded by!**/*.jsonpackages/extension-tools/package.jsonis excluded by!**/*.jsonpackages/extension-tools/src/locales/en-US.schema.ymlis excluded by!**/*.ymlpackages/extension-tools/src/locales/zh-CN.schema.ymlis excluded by!**/*.yml
📒 Files selected for processing (2)
packages/extension-tools/src/config.ts(2 hunks)packages/extension-tools/src/plugins/command.ts(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/extension-tools/src/config.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/extension-tools/src/plugins/command.ts (1)
packages/extension-tools/src/config.ts (3)
Config(4-50)Config(52-258)name(265-265)
🪛 ast-grep (0.39.6)
packages/extension-tools/src/plugins/command.ts
[warning] 532-532: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(chinese, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build
- GitHub Check: lint
🔇 Additional comments (6)
packages/extension-tools/src/plugins/command.ts (6)
28-32: 正确传递黑名单参数。将配置中的命令黑名单正确传递给
getCommandList函数,实现了黑名单过滤功能。
74-76: 简化了提示词生成逻辑。移除了详细的提示词构建,仅返回描述信息,使代码更加简洁。
78-89: 多语言描述处理逻辑完善。添加了完整的多语言回退机制(zh-CN → '' → en-US → 默认),提升了国际化支持。
104-113: 黑名单过滤逻辑实现正确。使用精确匹配和前缀匹配来过滤黑名单命令及其子命令,逻辑清晰且正确。
207-211: 为可选输入字段添加了描述。为空参数命令的默认输入字段添加描述,提升了 AI 模型的理解能力。
552-556: 类型定义与新功能一致。
PickCommandType正确扩展了confirm字段以支持命令确认功能。
This PR enhances the command tool system with improved command name normalization and adds blacklist support for better control over exposed commands.
New Features
Enhanced Command Name Normalization: Added comprehensive Chinese-to-English mapping for command names, supporting 150+ common command terms (e.g., 帮助→help, 查询→query, 执行→execute). This ensures better compatibility when dealing with Chinese command names.
Command Blacklist Support: Added configurable command blacklist feature that allows filtering out specific commands and their sub-commands from the tool system. Commands can be blocked by exact name or prefix matching (e.g., blocking "command" also blocks "command.xxx").
Improved Schema Descriptions: Tool arguments and options now include proper descriptions in the Zod schema, providing better context for AI models when using these tools.
Bug Fixes
Other Changes
koishi-plugin-chatluna-storage-servicepeer dependency from v0.0.10 to v0.0.11 across all packages