Files
7418andClaude Opus 4.6 16edf9f59c feat: CLI Tools — curated tool catalog, detection, install, chat awareness
New sidebar page for managing system CLI tools (ffmpeg, jq, ripgrep, yt-dlp, pandoc).

Core modules:
- cli-tools-catalog.ts: static catalog of 6 curated tools + EXTRA_WELL_KNOWN_BINS
- cli-tools-detect.ts: system detection via which/where + --version (2min cache)
- cli-tools-context.ts: builds <available_cli_tools> block for chat system prompt

API routes:
- GET /api/cli-tools/catalog, /installed, /[id]/status, /[id]/detail
- POST /api/cli-tools/[id]/install (SSE streaming), /[id]/describe (AI gen)

UI components:
- CliToolsManager: installed + recommended sections with grid layout
- CliToolCard: two variants (installed/recommended) with status badges
- CliToolDetailDialog: intro, use cases, guide steps, example prompts
- CliToolInstallDialog: SSE-based install progress with live terminal log

Chat integration:
- Chat input toolbar: Terminal icon opens CLI tool picker popover
- System prompt injection: buildCliToolsContext() in /api/chat
- MessageInput: CLI popover with search, keyboard nav, locale-aware prefill
- NavRail + ChatListPanel: frosted glass (bg-sidebar/80 backdrop-blur-xl)
- SlashCommandButton: Lightning icon, unified 16px toolbar icons
- input-group: shadow-md → shadow-sm
- Model selector: searchable popover with provider grouping

Docs:
- docs/handover/cli-tools.md + apps/site/content/docs/{en,zh}/cli-tools.mdx

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 19:56:29 +08:00

140 lines
6.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLI Tools — 系统 CLI 工具管理与聊天感知
## 核心思路
AI 工作流中 CLI 工具(ffmpeg、jq、ripgrep 等)是重要基础设施,但用户往往不知道装什么、怎么装。本功能在侧边栏新增 "CLI Tools" 页面,提供精选工具推荐、一键安装、AI 补全描述,并在聊天时自动将已安装工具注入 system prompt,让 Claude 知道用户系统上有哪些工具可用。
## 目录结构
```
src/lib/
├── cli-tools-catalog.ts # 静态精选 catalog(6 个核心 + EXTRA_WELL_KNOWN_BINS)
├── cli-tools-detect.ts # 系统检测逻辑(which/where + --version)
├── cli-tools-context.ts # 聊天上下文构建(system prompt 注入块)
src/app/api/cli-tools/
├── catalog/route.ts # GET — 返回完整 catalog 列表
├── installed/route.ts # GET — 检测并返回已安装工具的 runtime info
└── [id]/
├── status/route.ts # GET — 单个工具状态 + 版本
├── install/route.ts # POST — SSE 流式安装日志
├── detail/route.ts # GET — 详情弹窗数据
└── describe/route.ts # POST — AI 生成双语工具描述
src/components/cli-tools/
├── CliToolsManager.tsx # 主管理容器(已安装区 + 推荐区)
├── CliToolCard.tsx # 工具卡片(installed / recommended 两种 variant)
├── CliToolDetailDialog.tsx # 详情弹窗(简介 / 场景 / 引导 / 示例提示词)
├── CliToolInstallDialog.tsx # SSE 安装进度弹窗
├── CliToolBatchDescribeDialog.tsx # 批量 AI 描述生成
└── CliToolExtraDetailDialog.tsx # 额外检测工具的详情弹窗
src/app/cli-tools/page.tsx # 页面入口
```
## 数据流
### 工具检测
```
页面加载 → CliToolsManager 并行请求:
GET /api/cli-tools/catalog → CLI_TOOLS_CATALOG(静态数据)
GET /api/cli-tools/installed → detectAllCliTools()
→ 遍历 catalog binNames + EXTRA_WELL_KNOWN_BINS
→ which/where 查找 → --version 提取版本
→ 模块级缓存(TTL 2 分钟)
→ 合并 catalog + runtime info → 分区渲染
```
### 工具安装
```
用户点击"安装" → 选择安装方式(brew/npm/pipx)
→ POST /api/cli-tools/[id]/install { method }
→ 服务端 spawn 子进程执行 catalog 中声明的 command
→ SSE 流式返回 stdout/stderr
→ CliToolInstallDialog 实时显示日志
→ 完成后重新检测工具状态
```
安全约束:只执行 catalog 中声明的 command,不接受用户自定义命令。
### AI 描述生成
```
用户点击"自动完善介绍" → POST /api/cli-tools/[id]/describe { providerId }
→ 校验 tool.supportsAutoDescribe === true
→ generateTextViaSdk() 生成中英双语介绍(60s 超时)
→ 返回 { zh, en } 文本
→ 前端存入 localStorage(key: cli-tool-desc-{id})
→ 刷新后从 localStorage 恢复
```
### 聊天上下文注入
```
用户发送消息 → POST /api/chat
→ buildCliToolsContext()
→ detectAllCliTools()(命中缓存则直接返回)
→ 格式化已安装工具为 <available_cli_tools> XML 块
→ 追加到 system prompt 末尾
→ Claude 获知用户系统上可用的 CLI 工具
```
### 聊天侧 CLI 选择器
```
聊天输入框工具栏 → 点击 Terminal 图标 → popoverMode = 'cli'
→ 异步 fetch /api/cli-tools/installed + /api/cli-tools/catalog
→ 搜索框过滤 → 选择工具
→ 若输入框为空:预填 "我想用 {tool} 工具完成:" (zh) / "I want to use {tool} to: " (en)
→ 若输入框有内容:附加 CliBadge { id, name }
→ 发送时 CliBadge → systemPromptAppend 注入到 system prompt(不显示在对话中)
```
## 类型定义
关键类型在 `src/types/index.ts`:
| 类型 | 用途 |
|------|------|
| `CliToolStatus` | `'not_installed' \| 'installed' \| 'needs_auth' \| 'ready'` |
| `CliToolCategory` | `'media' \| 'data' \| 'search' \| 'download' \| 'document' \| 'productivity'` |
| `InstallMethod` | `'brew' \| 'npm' \| 'pipx' \| 'cargo'` |
| `CliToolDefinition` | 完整的工具定义(名称、binNames、摘要、分类、安装方式、详情、示例提示词) |
| `CliToolRuntimeInfo` | 运行时检测结果(状态、版本、路径、AI 描述) |
| `CliToolInstallMethod` | 包含 `method`、`command`、`platforms` |
## 设计决策
### 为什么用静态 catalog 而不是动态发现?
安全性——只允许执行预审过的安装命令。可预测性——工具列表、描述、示例提示词都是策划过的内容,质量可控。
### 为什么检测用 which 而不是直接运行?
Electron 桌面环境中 PATH 往往不完整。通过 `getExpandedPath()`(`src/lib/platform.ts`)展开 `/usr/local/bin`、`~/.cargo/bin` 等常见路径后传给 `which`,兼容桌面和终端两种启动方式。
### 为什么 AI 描述存 localStorage 而不是 DB?
描述是锦上添花,不是核心数据。避免 schema 迁移负担,丢失可重新生成。
### 聊天侧 CLI 选择器的 popover 模式
采用 `PopoverMode = 'file' | 'skill' | 'cli' | null` 枚举,CLI 是 button-triggered(点击工具栏图标触发),与 skill 的 text-triggered(输入 `/` 触发)不同。button-triggered 弹窗的焦点策略是将焦点交给弹窗内的搜索框,而 text-triggered 弹窗焦点保留在 textarea。
### EXTRA_WELL_KNOWN_BINS
除 catalog 中的 6 个精选工具外,`cli-tools-catalog.ts` 还导出 `EXTRA_WELL_KNOWN_BINS` 数组——常见但不需要详情页的工具(如 python、node、go、docker 等),用于聊天上下文注入,让 Claude 知道系统上还有哪些工具。
## 修改文件清单
| 文件 | 改动 |
|------|------|
| `src/types/index.ts` | 新增 CLI tool 相关类型定义 |
| `src/components/layout/NavRail.tsx` | navItems 增加 CLI Tools 导航项 |
| `src/i18n/en.ts` | 新增 `nav.cliTools` + `cli_tools.*` 英文 key |
| `src/i18n/zh.ts` | 新增 `nav.cliTools` + `cli_tools.*` 中文 key |
| `src/app/api/chat/route.ts` | system prompt 末尾追加 CLI tools context |
| `src/components/chat/MessageInput.tsx` | CLI 选择器弹窗 + CliBadge + systemPromptAppend |