Skip to content

Conversation

@dingyi222666
Copy link
Member

Summary

Refactored the command tools from registering N individual tools (one per command) to just 2 unified tools with improved search and execution capabilities.

Changes

New Tools

  • command_search: Search and discover available Koishi commands
  • command_execute: Execute commands using plain text syntax

Key Features

  • Advanced fuzzy matching algorithm (e.g., "shot" matches "screenshot")
  • Alias support for command discovery
  • Multi-keyword search with relevance scoring
  • New commandAutoExecute config option (⚠️ disabled by default for safety)

Breaking Changes

  • Commands are no longer registered as individual tools
  • Tool interface now uses plain text command syntax instead of structured parameters

Technical Details

  • Reduced tool count from N (80+ in some cases) to 2
  • Improved search relevance with scoring algorithm
  • Better user experience with fuzzy matching

…nd execute interface

Replaced per-command tool registration (N tools) with two unified tools:
- command_search: Search and list available Koishi commands with fuzzy matching
- command_execute: Execute commands using plain text Koishi command syntax

Features:
- Advanced fuzzy matching algorithm supporting partial matches (e.g., "shot" matches "screenshot")
- Alias support: Commands can now be searched by their aliases
- Multi-keyword search with scoring and ranking
- Add commandAutoExecute option to allow commands to run without confirmation (disabled by default for safety)

Breaking Changes:
- Command tools are no longer registered individually
- Tools now use plain text command syntax instead of structured parameters
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 4, 2025

Walkthrough

在配置接口中新增布尔字段 commandAutoExecute(默认 false);命令插件由按命令动态注册改为两个通用工具 CommandSearchToolCommandExecuteTool,新增关键字匹配 matchCommand、别名支持、并调整命令列表结构与执行/确认流程。

Changes

Cohort / File(s) 变更摘要
配置架构更新
packages/extension-tools/src/config.ts
Config 接口中添加 commandAutoExecute: boolean(默认 false);在命令配置 Schema 中加入 commandAutoExecute 字段并带有危险性说明。
命令系统重构
packages/extension-tools/src/plugins/command.ts
移除按单命令动态注册,新增并导出 CommandSearchTool 与改造后的 CommandExecuteTool(构造器改为接收命令列表与 commandAutoExecute 标志);新增 matchCommand(keyword, targets) 匹配函数;扩展 PickCommandType 支持 alias?: string[]description? 等;getCommandList() 现在包含别名信息;搜索工具改为关键字驱动,执行工具改进确认/自动执行与结果处理(包括错误与图像返回处理)。

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 分钟

需要额外关注的点:

  • CommandExecuteTool 构造器与执行流从单命令到命令列表的逻辑变更
  • CommandSearchTool 的搜索/格式化输出(别名、详细描述)的正确性
  • matchCommand() 算法的准确性与性能
  • getCommandList() 中别名和映射是否完整且兼容旧客户端
  • commandAutoExecute 标志在路径中是否被正确传递与使用

Possibly related PRs

Poem

🐇✨ 新工具箱里两把钥匙,
搜寻与执行并肩齐飞,
别名如星在命令里闪烁,
匹配悄声带来准确回馈,
小兔敬礼,代码再展新辉!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 标题清晰准确地概括了主要变化:将命令工具从N个独立工具重构为统一接口,与changeset完全相符。
Description check ✅ Passed 描述详细说明了重构内容、新增工具、关键特性、破坏性变更和技术细节,与changeset紧密相关。
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/common-better-command

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/extension-tools/src/plugins/command.ts (2)

160-212: 考虑提取匹配分数为命名常量

匹配算法逻辑清晰,但建议将分数值提取为命名常量,以提高可维护性和可读性。

const MATCH_SCORES = {
    EXACT: 100,
    WORD_EXACT: 90,
    PREFIX: 80,
    WORD_PREFIX: 70,
    CONTAINS: 60,
    WORD_CONTAINS: 50,
    REVERSE_CONTAINS: 40
} as const

396-414: 建议记录未知内容类型

对于未知的内容类型,当前实现静默返回空字符串。建议添加日志以便调试潜在问题。

                              if ('image_url' in part) {
                                  // ... existing code
                              }
-                              return ''
+                              // Log unknown content type for debugging
+                              this.ctx.logger.debug('Unknown content part type: %o', part)
+                              return ''
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 048e153 and 5dc01b5.

⛔ Files ignored due to path filters (2)
  • packages/extension-tools/src/locales/en-US.schema.yml is excluded by !**/*.yml
  • packages/extension-tools/src/locales/zh-CN.schema.yml is 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)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/extension-tools/src/plugins/command.ts (2)
packages/core/src/llm-core/platform/types.ts (1)
  • ChatLunaToolRunnable (107-115)
packages/core/src/command.ts (1)
  • command (15-29)
⏰ 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 (4)
packages/extension-tools/src/config.ts (1)

155-159: 安全配置实现良好!

该配置项的设计合理:

  • 默认值设为 false 是正确的安全默认值
  • 危险警告描述清晰地告知用户风险
  • 正确放置在 command: Schema.const(true).required() 分支内,只在启用命令功能时才显示
packages/extension-tools/src/plugins/command.ts (3)

30-37: 确认 selector 逻辑是否符合预期

selector 始终返回 true,意味着这两个工具将始终对模型可用。考虑到原设计中单个命令有各自的 selector 配置,请确认这是预期行为。

如果需要保留基于上下文的选择逻辑,可以考虑在工具调用时检查命令级别的 selector。


219-312: CommandSearchTool 实现良好!

搜索工具的实现设计合理:

  • 支持多关键词搜索
  • 按相关性评分排序
  • 输出格式清晰,包含别名、参数和选项信息
  • 友好的空结果提示

381-381: Remove this review comment — the interpretation of the second parameter is incorrect.

The second parameter in session.execute(command, true) is a Next callback for Koishi's middleware chain, not a flag to enable chained command execution. Passing true allows the executed command to continue the middleware/next chain. This is standard Koishi behavior and does not introduce command chaining security risks.

Likely an incorrect or invalid review comment.

… in command execution

- Remove console.log(commandList) debug statement that should not be in production
- Fix matchedCommand finder to properly support command alias matching
- Ensure aliases are checked for exact, prefix, and namespace matches just like primary command names
- This fixes the issue where commands invoked via aliases (e.g., "ss" for "screenshot") would not match and cause null matchedCommand
@dingyi222666 dingyi222666 merged commit 9a965fa into v1-dev Dec 4, 2025
3 of 5 checks passed
@dingyi222666 dingyi222666 deleted the feat/common-better-command branch December 4, 2025 09:53
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/extension-tools/src/plugins/command.ts (1)

312-439: CommandExecuteTool 基本逻辑合理,但 baseCommandName 与错误信息有两个边界问题需要修正

  1. baseCommandName 未 trim 会在前导空格时失效
    当前写法:
    const baseCommandName = command.split(/\s+/)[0]
    对输入 " help" 这样的字符串,split 结果首元素是空串,导致 matchedCommand 永远为 undefined,进而总是走默认的 confirm: true 分支(即使配置了某些命令 confirm: false)。建议在 split 前先 trim()
  •    const baseCommandName = command.split(/\s+/)[0]
    
  •    const baseCommandName = command.trim().split(/\s+/)[0]
    
    
    
  1. 异常信息直接使用 e.message 在非 Error 场景下可能是 undefined
    这里 catch (e) 未声明类型,如果上游抛出的是字符串或普通对象,e.message 可能为 undefined,返回的错误信息就不友好:
    return `Failed to execute command "${command}". Error: ${e.message}`
    建议安全地归一化错误信息:
  •    } catch (e) {
    
  •        this.ctx.logger.error(e)
    
  •        return `Failed to execute command "${command}". Error: ${e.message}`
    
  •    }
    
  •    } catch (e) {
    
  •        this.ctx.logger.error(e)
    
  •        const errorMessage =
    
  •            e instanceof Error ? e.message : String(e)
    
  •        return `Failed to execute command "${command}". Error: ${errorMessage}`
    
  •    }
    
    
    

另外,新版对 alias 的匹配(含子命令前缀)已经覆盖了之前 review 中提到的别名场景,这点很好。

🧹 Nitpick comments (4)
packages/extension-tools/src/plugins/command.ts (4)

27-50: 统一注册 command_search / command_execute 的实现整体合理,但 commandList 为静态快照

commandListapply 阶段构建一次后直接闭包进两个工具,后续如果 Koishi 运行时有动态注册/卸载命令,搜索/执行工具将看不到最新变更。如果你预期命令集合在插件生命周期内基本固定,这样没问题;否则可以考虑在 createTool() 里延迟调用 getCommandList 或在工具内部提供刷新机制,以减少列表过期的风险。


91-100: alias 构建逻辑依赖 Koishi 私有字段,升级时可能易碎

这里通过 ctx.$commander._commandListcmd._aliases 取命令及别名,属于 Koishi 的内部实现细节,未来升级时可能静默失效。建议:

  • 如有公开 API(例如公开的 commands 列表或 alias 字段),优先改用公开接口;
  • 如果目前只能用内部字段,最好加一条注释说明“依赖 Koishi 内部字段 _commandList / _aliases”,方便后续升级重点检查。

当前 Object.keys(cmd._aliases) 生成 alias 数组并透传进 PickCommandType 的逻辑本身是合理的。


147-210: matchCommand 注释示例与实现略有出入,可顺便防御空 keyword

  • “Reverse check” 部分注释写的是 "screenshot" contains "shot",但实现是 lowerKeyword.includes(lowerTarget),实际对应的是“长 keyword 包含短 target”(例如 keyword "screenshot command" 命中 target "screenshot")。建议把注释示例改成和实现一致,避免后续阅读误解。
  • 为防止空字符串意外让所有 target 都得到分数,可以在函数开头对 keyword.trim() 为空的情况直接返回 0 或在调用前过滤掉空 keyword。

算法本身的分段打分设计清晰易懂,这里主要是可读性与健壮性的小优化。


217-309: CommandSearchTool 输出设计良好,但可更健壮地处理 arguments/options,并确认是否要继续尊重 selector

  • 如果某些命令 JSON 中的 argumentsoptions 是可选字段,当前直接调用 cmd.arguments.map(...) / cmd.options.filter(...) 在它们为 undefined 时会抛异常。建议增加兜底:
    const args = (cmd.arguments ?? []).map(...)
    const opts = (cmd.options ?? [])
        .filter((opt) => opt.name !== 'help')
        .map(...)
  • 现在搜索结果不再根据 selector 做过滤,等于所有在 commandList 里的命令都会被列出;与旧版“按命令单独注册 tool + selector”相比是行为变化。确认这是否是有意而为(只靠 confirm 和全局配置控制风险),还是希望在搜索阶段就按当前会话 / preset 等条件利用 selector 过滤掉不适用的命令。

整体上包含 alias、参数和选项说明的长描述对 LLM 非常友好,这块设计方向是对的。

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5dc01b5 and 32d914e.

⛔ Files ignored due to path filters (1)
  • packages/extension-tools/package.json is excluded by !**/*.json
📒 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 (3)
packages/core/src/llm-core/platform/types.ts (1)
  • ChatLunaToolRunnable (107-115)
packages/core/src/command.ts (1)
  • command (15-29)
packages/service-search/src/providers/wikipedia.ts (1)
  • content (173-183)
⏰ 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 (1)
packages/extension-tools/src/plugins/command.ts (1)

456-461: PickCommandType 新增 alias 字段与上游使用保持一致

alias?: string[]getCommandList 中构造的 alias 以及 CommandSearchTool / CommandExecuteTool 中的访问方式完全对齐,类型定义合理,没有发现问题。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants