diff --git a/README.md b/README.md index b0544c4..5a065c2 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,35 @@ ## ✨ 功能特性 +### 核心功能 - 🤖 **AI 对话** — 流式输出,逐 token 实时显示 -- 🔧 **11 个内置工具** — Bash、文件读写编辑、Glob、Grep、Web获取、Agent 等 +- 🔧 **18 个内置工具** — Bash、文件操作、搜索、Web、任务管理、MCP桥接 等 - ⚡ **双 API 提供者** — 同时支持 OpenAI 兼容 API 和 Anthropic 原生 API -- 📝 **斜杠命令** — `/help`, `/compact`, `/cost`, `/model`, `/history` 等 11 个命令 -- 🖥️ **JLine 终端** — 行编辑、历史记录、Tab 补全、多行输入 +- 📝 **28 个斜杠命令** — 从基础操作到代码审查、安全分析、对话管理 +- 🖥️ **JLine 终端** — 行编辑、历史记录、Tab 补全、多行输入、Vim 模式 - 📋 **CLAUDE.md** — 自动加载项目级和用户级记忆文件 - 🎯 **Skills** — 可扩展的技能系统 - 🌿 **Git 上下文** — 自动收集分支、状态、最近提交 -- 💾 **对话持久化** — 自动保存对话历史,支持回顾 -- 🗜️ **上下文压缩** — AI 生成摘要替代简单清空 + +### P0 核心增强 +- 🔒 **权限确认** — 危险操作(文件写入、Bash执行)前要求用户确认 [Y/n/always] +- 💭 **Thinking 显示** — 展示 AI 思考过程(Anthropic extended thinking) +- 🔍 **WebSearch** — DuckDuckGo 网络搜索(无需 API Key) +- ❓ **AskUser** — AI 在执行过程中向用户提问 + +### P1 体验增强 +- 📊 **底部状态行** — 持续显示模型、Token、费用、工作目录 +- 🪝 **Hook 系统** — PreToolUse/PostToolUse/PrePrompt/PostResponse 4 种钩子 +- 🎨 **代码语法高亮** — 支持 Java/JS/TS/Python/Bash/SQL 6 种语言 +- ⌨️ **Vim 模式** — JLine vi 编辑模式(`CLAUDE_CODE_VIM=1` 启用) + +### P2 扩展功能 +- 🔌 **MCP 协议** — Model Context Protocol 客户端(StdIO 传输、工具发现、资源读取) +- 🧩 **插件系统** — JAR 插件加载(ClassLoader 隔离、工具/命令扩展) +- 📋 **任务管理** — 后台任务创建、查询、更新(4 个工具) +- 🔀 **彩色 Diff** — unified diff 渲染(行号、stat 摘要、颜色标注) +- 🌿 **对话分支** — 保存/恢复/标签对话状态 +- 🛡️ **安全审查** — AI 驱动的安全漏洞检测 ## 📦 技术栈 @@ -85,7 +104,7 @@ mvn spring-boot:run Provider: OPENAI Model: deepseek-chat API URL: https://api.deepseek.com Work Dir: D:\my-project - Tools: 11 registered + Tools: 18 | Commands: 28 Terminal: windows-vtp (160×30) Tip: Tab to complete commands, ↑↓ to browse history, Ctrl+D to exit @@ -113,24 +132,60 @@ mvn spring-boot:run ### 斜杠命令 -| 命令 | 说明 | -|------|------| -| `/help` | 显示所有可用命令 | -| `/clear` | 清屏 | -| `/compact` | AI 摘要压缩对话上下文 | -| `/cost` | 显示 Token 使用量和费用 | -| `/model [name]` | 查看/切换模型 | -| `/status` | 显示会话状态(消息数、Token、模型) | -| `/context` | 显示已加载的上下文(CLAUDE.md、Skills、Git) | -| `/config` | 查看配置信息 | -| `/init` | 初始化 CLAUDE.md 配置文件 | -| `/history` | 列出保存的对话历史 | -| `/exit` | 退出 | +#### 基础命令 + +| 命令 | 别名 | 说明 | +|------|------|------| +| `/help` | | 显示所有可用命令 | +| `/clear` | | 清屏 | +| `/compact` | | AI 摘要压缩对话上下文 | +| `/cost` | | 显示 Token 使用量和费用 | +| `/model [name]` | | 查看/切换模型 | +| `/status` | | 显示会话状态 | +| `/context` | | 显示已加载的上下文 | +| `/config` | | 查看配置信息 | +| `/init` | | 初始化 CLAUDE.md 配置文件 | +| `/history` | | 列出保存的对话历史 | +| `/exit` | `/quit` | 退出 | + +#### P0 命令 + +| 命令 | 别名 | 说明 | +|------|------|------| +| `/diff` | | 显示 Git 变更(支持 `--staged`, `--stat`) | +| `/version` | `/ver` | 显示版本和环境信息 | +| `/skills` | | 列出已加载的技能 | +| `/memory` | `/mem` | 查看/编辑 CLAUDE.md | +| `/copy` | | 复制最近回复到剪贴板 | + +#### P1 命令 + +| 命令 | 别名 | 说明 | +|------|------|------| +| `/resume` | | 恢复已保存的对话 | +| `/export` | | 导出对话为 Markdown 文件 | +| `/commit` | | AI 生成 commit message 并提交 | + +#### P2 命令 + +| 命令 | 别名 | 说明 | +|------|------|------| +| `/hooks` | | 查看已注册的 Hook | +| `/review` | `/rev` | AI 代码审查(支持 `--staged`、文件路径) | +| `/stats` | | 使用统计(Token、费用、API调用、运行时长) | +| `/branch` | | 对话分支(`save/load/list/delete`) | +| `/rewind [n]` | | 回退对话历史(默认回退1轮) | +| `/tag` | | 对话标签(`/list/goto `) | +| `/security-review` | `/sec` | AI 安全漏洞审查 | +| `/mcp` | | MCP 服务器管理(`connect/disconnect/tools/resources`) | +| `/plugin` | | 插件管理(`load/unload/reload/info`) | ### 内置工具 AI 可以自动调用以下工具: +#### 核心工具(Phase 1-3) + | 工具 | 说明 | |------|------| | `bash` | 执行 Shell 命令 | @@ -145,6 +200,24 @@ AI 可以自动调用以下工具: | `agent` | 启动子 Agent 处理复杂任务 | | `notebook_edit` | 编辑 Jupyter Notebook | +#### P0 工具 + +| 工具 | 说明 | +|------|------| +| `web_search` | DuckDuckGo 网络搜索(无需 API Key) | +| `ask_user_question` | AI 向用户提问(暂停 agent loop 等待输入) | + +#### P2 工具 + +| 工具 | 说明 | +|------|------| +| `TaskCreate` | 创建后台任务 | +| `TaskGet` | 查询任务详情 | +| `TaskList` | 列出任务(支持状态过滤) | +| `TaskUpdate` | 更新任务状态和结果 | +| `Config` | 读写配置值 | +| `mcp__*` | MCP 远程工具桥接(动态注册) | + ### CLAUDE.md 记忆文件 创建 `CLAUDE.md` 文件来给 AI 提供项目上下文: @@ -185,6 +258,81 @@ description: 代码审查技能 3. 性能考量 ``` +### MCP 服务器集成 + +配置 MCP 服务器,在项目根目录创建 `.mcp.json`: + +```json +{ + "servers": { + "filesystem": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"] + }, + "my-server": { + "command": "python", + "args": ["my_mcp_server.py"], + "env": { "API_KEY": "xxx" } + } + } +} +``` + +或放在全局目录 `~/.claude-code-java/mcp.json`。 + +使用 `/mcp` 命令管理: +``` +❯ /mcp # 列出所有 MCP 服务器 +❯ /mcp tools # 列出所有 MCP 工具 +❯ /mcp connect myserver cmd args # 连接新服务器 +❯ /mcp disconnect myserver # 断开服务器 +``` + +### 插件系统 + +JAR 插件放在以下目录即可自动加载: +- `~/.claude-code-java/plugins/` — 全局插件 +- `.claude-code/plugins/` — 项目级插件 + +JAR 要求: +- `META-INF/MANIFEST.MF` 中包含 `Plugin-Class` 属性 +- 实现 `com.claudecode.plugin.Plugin` 接口 + +使用 `/plugin` 命令管理: +``` +❯ /plugin # 列出已加载插件 +❯ /plugin info output-style # 查看插件详情 +❯ /plugin unload my-plugin # 卸载插件 +``` + +### Vim 模式 + +设置环境变量启用 JLine vi 编辑模式: + +```bash +export CLAUDE_CODE_VIM=1 +``` + +启用后 Banner 会显示 `[vim]` 标识,输入行支持 vi 按键绑定。 + +### Hook 系统 + +通过代码注册 Hook 来拦截工具调用: + +```java +agentLoop.getHookManager().register(HookManager.HookType.PRE_TOOL_USE, new Hook() { + public String name() { return "my-hook"; } + public int priority() { return 10; } + public HookResult execute(HookContext ctx) { + // 可检查 ctx.getToolName(), ctx.getArguments() + // 返回 ABORT 可阻止工具执行 + return HookResult.CONTINUE; + } +}); +``` + +4 种钩子类型:`PRE_TOOL_USE`、`POST_TOOL_USE`、`PRE_PROMPT`、`POST_RESPONSE` + ## 🏗️ 架构设计 ### 模块结构 @@ -195,32 +343,54 @@ com.claudecode ├── cli/ │ └── ClaudeCodeRunner // 启动编排(CommandLineRunner) ├── config/ -│ └── AppConfig // Bean 装配、Provider 切换 +│ └── AppConfig // Bean 装配、Provider 切换、组件注册 ├── core/ -│ ├── AgentLoop // Agent 循环(阻塞 + 流式) +│ ├── AgentLoop // Agent 循环(阻塞 + 流式 + Hook集成) │ ├── TokenTracker // Token 使用追踪 -│ └── ConversationPersistence // 对话持久化 +│ ├── ConversationPersistence // 对话持久化 +│ ├── HookManager // Hook 系统(4种钩子类型) +│ └── TaskManager // 后台任务管理 ├── tool/ │ ├── Tool // 工具协议接口 │ ├── ToolRegistry // 工具注册中心 │ ├── ToolCallbackAdapter // Spring AI 适配器 -│ └── impl/ // 11 个工具实现 +│ └── impl/ // 18 个工具实现 +│ ├── BashTool, FileReadTool, FileWriteTool, FileEditTool +│ ├── GlobTool, GrepTool, ListFilesTool, WebFetchTool +│ ├── TodoWriteTool, AgentTool, NotebookEditTool +│ ├── WebSearchTool, AskUserQuestionTool +│ ├── TaskCreateTool, TaskGetTool, TaskListTool, TaskUpdateTool +│ ├── ConfigTool +│ └── McpToolBridge // MCP 远程工具桥接 ├── command/ │ ├── SlashCommand // 命令接口 │ ├── CommandRegistry // 命令注册中心 -│ └── impl/ // 11 个命令实现 +│ └── impl/ // 28 个命令实现 ├── console/ │ ├── BannerPrinter // 启动 Banner │ ├── ToolStatusRenderer // 工具状态渲染 │ ├── ThinkingRenderer // Thinking 渲染 │ ├── SpinnerAnimation // 加载动画 -│ ├── MarkdownRenderer // Markdown 渲染 +│ ├── MarkdownRenderer // Markdown 渲染(含语法高亮) +│ ├── DiffRenderer // 彩色 Diff 渲染 +│ ├── StatusLine // 底部状态行 │ └── AnsiStyle // ANSI 样式工具 ├── context/ │ ├── SystemPromptBuilder // 系统提示词构建 │ ├── ClaudeMdLoader // CLAUDE.md 加载 │ ├── SkillLoader // Skills 加载 │ └── GitContext // Git 上下文收集 +├── mcp/ +│ ├── McpClient // MCP 客户端(JSON-RPC 2.0) +│ ├── McpTransport // 传输层接口 +│ ├── StdioTransport // StdIO 传输实现 +│ ├── McpManager // 多服务器管理 +│ └── McpException // MCP 异常 +├── plugin/ +│ ├── Plugin // 插件接口 +│ ├── PluginContext // 插件上下文 +│ ├── PluginManager // 插件加载/管理 +│ └── OutputStylePlugin // 内置输出样式插件 └── repl/ ├── ReplSession // REPL 会话管理 └── ClaudeCodeCompleter // Tab 补全 @@ -235,11 +405,39 @@ com.claudecode ↓ 逐token实时输出到终端 ↓ - 检测工具调用 → 执行工具 → 结果回传 + 检测工具调用 → PreToolUse Hook → 权限确认 → 执行工具 → PostToolUse Hook → 结果回传 ↓ 继续循环或结束 ``` +### MCP 协议架构 + +``` +McpManager ─── 配置加载 (.mcp.json) + │ + ├── McpClient("server-a") ── StdioTransport ── 子进程 (npx server-a) + │ ├── tools/list → 发现工具 + │ ├── tools/call → 调用工具 + │ └── resources/read → 读取资源 + │ + └── McpClient("server-b") ── StdioTransport ── 子进程 (python server-b) + └── ... (同上) + +McpToolBridge → 将 MCP 工具映射为本地 Tool → 注册到 ToolRegistry → AI 可调用 +``` + +### 插件系统架构 + +``` +PluginManager + ├── 全局插件: ~/.claude-code-java/plugins/*.jar + ├── 项目插件: .claude-code/plugins/*.jar + └── 内置插件: OutputStylePlugin + │ + ├── Plugin.getTools() → 注册到 ToolRegistry + └── Plugin.getCommands() → 注册到 CommandRegistry +``` + ### 双 API 提供者架构 ``` @@ -279,6 +477,7 @@ claude-code: | `AI_BASE_URL` | ❌ | API 基础 URL | 按提供者不同 | | `AI_MODEL` | ❌ | 模型名称 | 按提供者不同 | | `AI_MAX_TOKENS` | ❌ | 最大 Token 数 | `8096` | +| `CLAUDE_CODE_VIM` | ❌ | 启用 Vim 编辑模式 | `0` | ## 🔧 开发 @@ -308,11 +507,15 @@ java -jar target/claude-code-java-0.1.0-SNAPSHOT.jar | `cli.tsx` → `main.tsx` | `ClaudeCodeApplication` + `ClaudeCodeRunner` | 入口 | | `REPL.tsx` | `ReplSession` + JLine 3 | 交互循环 | | `query.ts` | `AgentLoop` | Agent 循环 | -| `Tool.ts` + `tools/*` | `Tool` 接口 + `impl/*` | 工具系统 | -| `commands.ts` | `SlashCommand` + `impl/*` | 命令系统 | +| `Tool.ts` + `tools/*` | `Tool` 接口 + `impl/*` (18个) | 工具系统 | +| `commands.ts` | `SlashCommand` + `impl/*` (28个) | 命令系统 | | `context.ts` + `prompts.ts` | `SystemPromptBuilder` + loaders | 上下文 | | `CLAUDE.md` + `skills/` | `ClaudeMdLoader` + `SkillLoader` | 记忆/技能 | -| Ink Components | `console/*` 渲染器 | 终端 UI | +| Ink Components | `console/*` 渲染器 (8个) | 终端 UI | +| `mcp/*` (22文件) | `mcp/*` (McpClient/Manager/Transport) | MCP 协议 | +| `plugins/*` (38文件) | `plugin/*` (Plugin/Manager/Context) | 插件系统 | +| `hooks/*` | `HookManager` | Hook 系统 | +| `tasks/*` | `TaskManager` + Task工具 | 任务管理 | ## 📄 License diff --git a/src/main/java/com/claudecode/mcp/McpClient.java b/src/main/java/com/claudecode/mcp/McpClient.java index 97c15de..c75ee0c 100644 --- a/src/main/java/com/claudecode/mcp/McpClient.java +++ b/src/main/java/com/claudecode/mcp/McpClient.java @@ -2,7 +2,6 @@ package com.claudecode.mcp; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,10 +46,10 @@ public class McpClient implements AutoCloseable { private final Map resources = new ConcurrentHashMap<>(); /** 服务器能力信息 */ - private JsonNode serverCapabilities; + private volatile JsonNode serverCapabilities; /** 服务器信息 */ - private JsonNode serverInfo; + private volatile JsonNode serverInfo; /** 是否已完成初始化 */ private volatile boolean initialized = false; @@ -165,15 +164,17 @@ public class McpClient implements AutoCloseable { JsonNode response = transport.sendRequest(MAPPER.writeValueAsString(request)); JsonNode result = response.get("result"); if (result != null && result.has("tools")) { - ArrayNode toolsArray = (ArrayNode) result.get("tools"); - for (JsonNode toolNode : toolsArray) { - String name = toolNode.get("name").asText(); - String description = toolNode.has("description") - ? toolNode.get("description").asText() : ""; - JsonNode inputSchema = toolNode.get("inputSchema"); - - tools.put(name, new McpTool(name, description, inputSchema)); - log.debug("发现 MCP 工具: {} - {}", name, description); + JsonNode toolsNode = result.get("tools"); + if (toolsNode != null && toolsNode.isArray()) { + for (JsonNode toolNode : toolsNode) { + String name = toolNode.get("name").asText(); + String description = toolNode.has("description") + ? toolNode.get("description").asText() : ""; + JsonNode inputSchema = toolNode.get("inputSchema"); + + tools.put(name, new McpTool(name, description, inputSchema)); + log.debug("发现 MCP 工具: {} - {}", name, description); + } } } } catch (McpException e) { @@ -214,17 +215,19 @@ public class McpClient implements AutoCloseable { JsonNode response = transport.sendRequest(MAPPER.writeValueAsString(request)); JsonNode result = response.get("result"); if (result != null && result.has("resources")) { - ArrayNode resourcesArray = (ArrayNode) result.get("resources"); - for (JsonNode resNode : resourcesArray) { - String uri = resNode.get("uri").asText(); - String name = resNode.has("name") ? resNode.get("name").asText() : uri; - String description = resNode.has("description") - ? resNode.get("description").asText() : ""; - String mimeType = resNode.has("mimeType") - ? resNode.get("mimeType").asText() : "text/plain"; - - resources.put(uri, new McpResource(uri, name, description, mimeType)); - log.debug("发现 MCP 资源: {} ({})", name, uri); + JsonNode resourcesNode = result.get("resources"); + if (resourcesNode != null && resourcesNode.isArray()) { + for (JsonNode resNode : resourcesNode) { + String uri = resNode.get("uri").asText(); + String name = resNode.has("name") ? resNode.get("name").asText() : uri; + String description = resNode.has("description") + ? resNode.get("description").asText() : ""; + String mimeType = resNode.has("mimeType") + ? resNode.get("mimeType").asText() : "text/plain"; + + resources.put(uri, new McpResource(uri, name, description, mimeType)); + log.debug("发现 MCP 资源: {} ({})", name, uri); + } } } } catch (McpException e) { @@ -409,6 +412,8 @@ public class McpClient implements AutoCloseable { @Override public void close() throws Exception { initialized = false; + tools.clear(); + resources.clear(); transport.close(); log.info("MCP 客户端 '{}' 已关闭", serverName); } diff --git a/src/main/java/com/claudecode/mcp/McpManager.java b/src/main/java/com/claudecode/mcp/McpManager.java index dfdb882..83e4d35 100644 --- a/src/main/java/com/claudecode/mcp/McpManager.java +++ b/src/main/java/com/claudecode/mcp/McpManager.java @@ -10,7 +10,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; /** * MCP 管理器 —— 管理多个 MCP 服务器连接的统一入口。 @@ -165,13 +164,23 @@ public class McpManager implements AutoCloseable { log.info("连接 MCP 服务器 '{}': {} {}", name, command, String.join(" ", args)); - // 创建传输层并启动 + // 创建传输层并启动(确保初始化失败时清理资源) StdioTransport transport = new StdioTransport(command, args, env); - transport.start(); - - // 创建客户端并初始化 - McpClient client = new McpClient(name, transport); - client.initialize(); + McpClient client; + try { + transport.start(); + client = new McpClient(name, transport); + client.initialize(); + } catch (Exception e) { + // 初始化失败时必须关闭传输层,防止子进程泄漏 + try { + transport.close(); + } catch (Exception suppressed) { + e.addSuppressed(suppressed); + } + throw (e instanceof McpException mcp) ? mcp + : new McpException("连接 MCP 服务器 '" + name + "' 失败: " + e.getMessage(), e); + } // 注册客户端 clients.put(name, client); @@ -239,7 +248,7 @@ public class McpManager implements AutoCloseable { return clients.values().stream() .filter(McpClient::isInitialized) .flatMap(client -> client.getTools().stream()) - .collect(Collectors.toList()); + .toList(); } /** @@ -265,7 +274,7 @@ public class McpManager implements AutoCloseable { return clients.values().stream() .filter(McpClient::isInitialized) .flatMap(client -> client.getResources().stream()) - .collect(Collectors.toList()); + .toList(); } /** diff --git a/src/main/java/com/claudecode/mcp/StdioTransport.java b/src/main/java/com/claudecode/mcp/StdioTransport.java index 91d08a3..90e3b38 100644 --- a/src/main/java/com/claudecode/mcp/StdioTransport.java +++ b/src/main/java/com/claudecode/mcp/StdioTransport.java @@ -38,11 +38,14 @@ public class StdioTransport implements McpTransport { /** 子进程 stdin 写入流 */ private BufferedWriter processStdin; - /** 异步读取线程 */ + /** 异步读取线程(stdout) */ private Thread readerThread; - /** 待匹配的请求:id -> CompletableFuture */ - private final ConcurrentHashMap> pendingRequests = + /** 异步读取线程(stderr) */ + private Thread stderrThread; + + /** 待匹配的请求:id(String) -> CompletableFuture */ + private final ConcurrentHashMap> pendingRequests = new ConcurrentHashMap<>(); /** 启动命令 */ @@ -102,7 +105,7 @@ public class StdioTransport implements McpTransport { readerThread = Thread.ofVirtual().name("mcp-stdio-reader").start(this::readLoop); // 启动 stderr 日志线程(仅记录日志,不参与协议通信) - Thread.ofVirtual().name("mcp-stdio-stderr").start(this::stderrLoop); + stderrThread = Thread.ofVirtual().name("mcp-stdio-stderr").start(this::stderrLoop); connected = true; log.info("MCP 服务器进程已启动 (PID: {})", process.pid()); @@ -172,12 +175,8 @@ public class StdioTransport implements McpTransport { // 检查是否有 id 字段(响应消息) JsonNode idNode = message.get("id"); if (idNode != null && !idNode.isNull()) { - Object id; - if (idNode.isNumber()) { - id = idNode.asInt(); - } else { - id = idNode.asText(); - } + // 统一转为 String,避免 Integer/String 类型不匹配导致的查找失败 + String id = idNode.asText(); CompletableFuture future = pendingRequests.remove(id); if (future != null) { @@ -198,20 +197,15 @@ public class StdioTransport implements McpTransport { throw new McpException("MCP 传输层未连接"); } + String id = null; try { - // 解析出请求 id + // 解析出请求 id(统一转为 String) JsonNode requestNode = MAPPER.readTree(jsonRpcRequest); JsonNode idNode = requestNode.get("id"); if (idNode == null || idNode.isNull()) { throw new McpException("JSON-RPC 请求缺少 id 字段"); } - - Object id; - if (idNode.isNumber()) { - id = idNode.asInt(); - } else { - id = idNode.asText(); - } + id = idNode.asText(); // 注册 Future CompletableFuture future = new CompletableFuture<>(); @@ -251,6 +245,11 @@ public class StdioTransport implements McpTransport { throw new McpException("MCP 请求执行异常: " + cause.getMessage(), cause); } catch (Exception e) { throw new McpException("MCP 请求发送失败: " + e.getMessage(), e); + } finally { + // 无论成功、超时或异常,都清理待处理请求,防止内存泄漏 + if (id != null) { + pendingRequests.remove(id); + } } } @@ -305,6 +304,9 @@ public class StdioTransport implements McpTransport { if (readerThread != null && readerThread.isAlive()) { readerThread.interrupt(); } + if (stderrThread != null && stderrThread.isAlive()) { + stderrThread.interrupt(); + } // 清理待处理请求 pendingRequests.forEach((id, future) -> diff --git a/修改记录.md b/修改记录.md index 75e7ba2..8dc9ce8 100644 --- a/修改记录.md +++ b/修改记录.md @@ -1,5 +1,73 @@ # 修改记录 +## [2026-04-03] - P2: MCP协议 + 插件系统 + 任务管理 + CLI增强 + +- **修改类型**: 功能新增 +- **修改详情**: + - **A组 CLI增强**: + - 新增 `DiffRenderer` 彩色 diff 渲染器(行号、stat摘要、unified格式解析) + - 新增 7 个命令: `/hooks`, `/review`(rev), `/stats`, `/branch`, `/rewind`, `/tag`, `/security-review`(sec) + - `/review` 支持 `--staged` 标志和文件路径参数 + - `/branch` 支持 save/load/list/delete 子命令管理对话分支 + - `/tag` 支持对话位置标签和 goto 跳转 + - `/security-review` 发送 AI 安全审查(SQL注入/XSS/认证/凭证泄露检测) + - **B组 任务系统**: + - 新增 `TaskManager` 后台任务管理器(线程安全、ConcurrentHashMap、CachedThreadPool) + - 新增 4 个工具: `TaskCreate`/`TaskGet`/`TaskList`/`TaskUpdate` + - 新增 `ConfigTool` 配置读写工具 + - 支持自动执行模式和手动状态管理模式 + - **C组 MCP协议**: + - 新增 `mcp/` 包: `McpClient`、`McpTransport`、`StdioTransport`、`McpManager`、`McpException` + - MCP 客户端支持 JSON-RPC 2.0 协议(initialize 握手、tools/list、tools/call、resources/read) + - StdIO 传输实现(子进程管理、异步读写、CompletableFuture 请求-响应关联) + - `McpManager` 多服务器管理(从 `.mcp.json` 配置加载、生命周期管理) + - `McpToolBridge` 将 MCP 工具桥接为本地 Tool(名称格式: `mcp__{server}__{tool}`) + - 新增 `/mcp` 命令(connect/disconnect/tools/resources/reload) + - **D组 插件系统**: + - 新增 `plugin/` 包: `Plugin` 接口、`PluginContext`、`PluginManager` + - JAR 插件加载(URLClassLoader 隔离、Manifest Plugin-Class 读取) + - 支持全局插件 (`~/.claude-code-java/plugins/`) 和项目插件 (`.claude-code/plugins/`) + - 新增 `OutputStylePlugin` 内置输出样式插件(default/minimal/verbose/markdown) + - 新增 `/plugin` 命令(load/unload/reload/info) + - **集成**: AppConfig 注册全部 18 工具 + 28 命令 + TaskManager + McpManager + PluginManager +- **关联需求**: 5.2 P2 扩展功能 + +## [2026-04-03] - P1: Hook系统 + Vim模式 + Banner增强 + +- **修改类型**: 功能新增 +- **修改详情**: + - 新增 `HookManager`: 支持 PreToolUse/PostToolUse/PrePrompt/PostResponse 4 种钩子 + - Hook 优先级排序(数字越小优先级越高),PreToolUse 可中止工具执行 + - AgentLoop 集成 Hook 系统到 executeToolCalls() 流程 + - ReplSession 支持 Vim 编辑模式(`CLAUDE_CODE_VIM=1` 环境变量启用) + - Banner 增强:显示命令数量和 Vim 模式标识 + - 修复重复 isDumb 变量 +- **关联需求**: 5.2 P1 重要功能 + +## [2026-04-03] - P1: 3命令 + 代码高亮 + 状态行 + +- **修改类型**: 功能新增 +- **修改详情**: + - 新增 3 个命令: `/resume`(恢复对话), `/export`(导出Markdown), `/commit`(AI生成commit) + - `MarkdownRenderer` 完全重写:支持 Java/JS/TS/Python/Bash/SQL 6 种语言语法高亮 + - 高亮处理:注释 → 字符串保护 → 注解 → 关键字 → 数字 → 布尔/null + - 新增 `StatusLine` 底部状态行(模型、token、费用、API调用、工作目录) + - AppConfig 注册总数达 19 命令 +- **关联需求**: 5.2 P1 重要功能 + +## [2026-04-03] - P0: 5命令 + 2工具 + 权限确认 + Thinking显示 + +- **修改类型**: 功能新增 +- **修改详情**: + - 新增 5 个命令: `/diff`, `/version`(ver), `/skills`, `/memory`(mem), `/copy` + - 新增 `WebSearchTool`: DuckDuckGo HTML 搜索(无需 API Key) + - 新增 `AskUserQuestionTool`: AI 向用户提问(通过 ToolContext 回调暂停 agent loop) + - AgentLoop 新增权限确认机制: 非只读工具调用前提示 [Y/n/always] + - AgentLoop 新增 Thinking 内容提取和显示(从 ChatResponse metadata 读取) + - 新增 `PermissionRequest` record 和 `onPermissionRequest`/`onThinkingContent` 回调 + - ReplSession 新增 `promptPermission()` UI 和 `readUserInputDuringAgentLoop()` 方法 +- **关联需求**: 5.2 P0 核心功能 + ## [2026-04-02] - Phase 5: 流式输出与高级功能 - **修改类型**: 功能新增 diff --git a/需求文档.md b/需求文档.md index f499cbf..470763d 100644 --- a/需求文档.md +++ b/需求文档.md @@ -318,7 +318,7 @@ allowed-tools: [BashTool, FileReadTool] | | 最大迭代限制 (50轮) | ✅ | | | 消息历史管理 | ✅ | | | 对话持久化 | ✅ | -| **工具系统 (11个)** | BashTool | ✅ | +| **工具系统 (18个)** | BashTool | ✅ | | | FileReadTool | ✅ | | | FileWriteTool | ✅ | | | FileEditTool | ✅ | @@ -329,6 +329,48 @@ allowed-tools: [BashTool, FileReadTool] | | ListFilesTool | ✅ | | | AgentTool | ✅ | | | NotebookEditTool | ✅ | +| | WebSearchTool (P0) | ✅ | +| | AskUserQuestionTool (P0) | ✅ | +| | TaskCreate/Get/List/Update (P2) | ✅ | +| | ConfigTool (P2) | ✅ | +| | McpToolBridge (P2) | ✅ | +| **命令系统 (28个)** | 基础: /help, /clear, /exit 等 11个 | ✅ | +| | P0: /diff, /version, /skills, /memory, /copy | ✅ | +| | P1: /resume, /export, /commit | ✅ | +| | P2: /hooks, /review, /stats, /branch, /rewind, /tag | ✅ | +| | P2: /security-review, /mcp, /plugin | ✅ | +| **上下文** | CLAUDE.md 加载 | ✅ | +| | Skills 技能加载 | ✅ | +| | Git 上下文收集 | ✅ | +| | 系统提示词构建 | ✅ | +| **终端 UI** | Banner + Provider 信息 | ✅ | +| | JLine 行编辑/历史/Tab补全 | ✅ | +| | 多行输入 | ✅ | +| | Spinner 动画 | ✅ | +| | 工具状态渲染 | ✅ | +| | ANSI 颜色 | ✅ | +| | Markdown 渲染 | ✅ | +| | 代码语法高亮 (P1) | ✅ | +| | 底部状态行 (P1) | ✅ | +| | DiffRenderer 彩色diff (P2) | ✅ | +| **配置** | 双 API 提供者切换 | ✅ | +| | 环境变量统一 | ✅ | +| | Token/费用追踪 | ✅ | +| **P0 核心增强** | 权限确认机制 | ✅ | +| | Thinking 内容显示 | ✅ | +| **P1 体验增强** | Hook 系统 (4种钩子) | ✅ | +| | Vim 模式输入 | ✅ | +| **P2 扩展** | MCP 客户端 (JSON-RPC/StdIO) | ✅ | +| | MCP 多服务器管理 | ✅ | +| | 插件系统 (JAR加载) | ✅ | +| | 任务管理系统 | ✅ | +| | 对话分支/标签/回退 | ✅ | +| | GrepTool | ✅ | +| | WebFetchTool | ✅ | +| | TodoWriteTool | ✅ | +| | ListFilesTool | ✅ | +| | AgentTool | ✅ | +| | NotebookEditTool | ✅ | | **命令系统 (11个)** | /help, /clear, /exit | ✅ | | | /compact (AI摘要) | ✅ | | | /cost, /model, /status | ✅ | @@ -351,43 +393,13 @@ allowed-tools: [BashTool, FileReadTool] ### 5.2 未实现功能清单 -#### 🔴 P0 核心(建议实现) +> ⚠️ P0、P1、P2 已全部实现。以下仅列出 P3(跳过)项目。 -| 功能 | 难度 | 说明 | -|------|------|------| -| **WebSearchTool** | 中 | 网络搜索工具,需要搜索 API(可用 DuckDuckGo 免费 API) | -| **AskUserQuestionTool** | 中 | AI 向用户提问工具,需要在 agent loop 中暂停等待用户输入 | -| **/diff 命令** | 低 | 显示未提交的 Git 变更,可复用 GitContext | -| **/version 命令** | 低 | 显示版本号,直接读取 pom.xml 版本 | -| **/skills 命令** | 低 | 列出可用技能,已有 SkillLoader | -| **/memory 命令** | 低 | 编辑 CLAUDE.md,打开编辑器或直接编辑 | -| **/copy 命令** | 低 | 复制最近回复到剪贴板 | -| **权限确认机制** | 中 | 危险操作前要求用户确认(如文件写入、bash 执行) | -| **Thinking 内容显示** | 高 | 显示 AI 思考过程(需要 Anthropic extended thinking API 支持) | - -#### 🟡 P1 重要(体验增强) - -| 功能 | 难度 | 说明 | -|------|------|------| -| CLAUDE.local.md 支持 | 低 | 本地级记忆文件 | -| 底部状态行 | 中 | 持续显示模型、token、工作目录 | -| Hook 系统 (PreToolUse) | 中 | 工具调用前后钩子 | -| /resume 恢复对话 | 低 | 从持久化文件恢复对话 | -| /export 导出对话 | 低 | 导出为 Markdown 文件 | -| /commit 命令 | 中 | 直接创建 Git commit | -| Vim 模式输入 | 高 | JLine Vim 模式绑定 | -| 代码语法高亮 | 中 | MarkdownRenderer 中的代码块着色 | - -#### 🟢 P2 扩展(可后续迭代) - -| 功能 | 说明 | -|------|------| -| MCP 客户端集成 | Model Context Protocol 支持 | -| 插件系统 | 可扩展的工具/命令加载 | -| LSP 集成 | 语言服务器协议 | -| 差异视图 (DiffRenderer) | 文件变更的彩色diff显示 | -| OAuth 认证流程 | 第三方认证 | -| 多代理协调 | Agent Swarm 模式 | +#### 🔴 P0 核心 — ✅ 已全部实现 + +#### 🟡 P1 重要 — ✅ 已全部实现 + +#### 🟢 P2 扩展 — ✅ 已全部实现 #### ⚪ P3 跳过(不适用于 Java CLI)